blob: 1441811dd2873e3685e853ac4e113196a75e52b6 [file] [log] [blame]
Ahmad Sharifae1714d2013-01-17 11:29:37 -08001// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Alex Vakulenko262be3f2014-07-30 15:25:50 -07005#include "debugd/src/perf_tool.h"
Ahmad Sharifae1714d2013-01-17 11:29:37 -08006
David Sharpe01f7252015-06-30 16:24:05 -07007#include <base/strings/string_number_conversions.h>
Ben Chan9953a592014-02-05 23:32:00 -08008#include <base/strings/string_split.h>
Simon Que21bb7902014-07-28 16:17:20 -07009#include <base/strings/string_util.h>
Simon Que795327b2015-03-16 17:42:35 -070010#include <sys/utsname.h>
Simon Que21bb7902014-07-28 16:17:20 -070011
12#include <algorithm>
David Sharpe01f7252015-06-30 16:24:05 -070013#include <map>
Ahmad Sharif3abea3c2013-05-13 20:43:04 -070014
Alex Vakulenko262be3f2014-07-30 15:25:50 -070015#include "debugd/src/cpu_info_parser.h"
16#include "debugd/src/process_with_output.h"
Simon Que795327b2015-03-16 17:42:35 -070017#include "debugd/src/random_selector.h"
Ahmad Sharifae1714d2013-01-17 11:29:37 -080018
Ben Chan55903dd2014-04-24 00:29:04 -070019using base::StringPrintf;
20
David Sharpe01f7252015-06-30 16:24:05 -070021namespace debugd {
22
Ahmad Shariff5597f62013-04-25 12:25:41 -070023namespace {
24
25// Location of quipper on ChromeOS.
26const char kQuipperLocation[] = "/usr/bin/quipper";
27
Simon Que21bb7902014-07-28 16:17:20 -070028// This is registered trademark symbol that appears in model name strings.
29const char kRegisteredTrademarkSymbol[] = "(R)";
30
Ahmad Sharif3abea3c2013-05-13 20:43:04 -070031// Processor model name substrings for which we have perf commands.
Simon Que795327b2015-03-16 17:42:35 -070032
33// For 64-bit x86 processors.
34const char* kx86_64CPUOddsFiles[] = {
Simon Que795327b2015-03-16 17:42:35 -070035 NULL,
Ahmad Sharif3abea3c2013-05-13 20:43:04 -070036};
Ahmad Shariff5597f62013-04-25 12:25:41 -070037
Simon Que795327b2015-03-16 17:42:35 -070038// For 32-bit x86 processors.
39const char* kx86_32CPUOddsFiles[] = {
40 // 32-bit x86 doesn't have any special cases, so all processors use the
41 // default commands. Add future special cases here.
42 NULL,
43};
44
45// For ARMv7 processors.
46const char* kARMv7CPUOddsFiles[] = {
47 // ARMv7 doesn't have any special cases, so all processors use the default
48 // commands. Add future special cases here.
49 NULL,
50};
51
52// For miscellaneous processors models of a known architecture.
53const char kMiscCPUModelOddsFile[] = "default";
54
55// Odds file name for miscellaneous processor architectures.
56const char kMiscCPUArchOddsFile[] = "unknown";
57
Ahmad Sharif3abea3c2013-05-13 20:43:04 -070058// Prefix path to attach to the CPU odds file.
Ahmad Sharifbdb33912013-07-16 11:44:49 -040059const char kCPUOddsFilePrefix[] = "/etc/perf_commands/";
Ahmad Shariff5597f62013-04-25 12:25:41 -070060
Ahmad Sharif3abea3c2013-05-13 20:43:04 -070061// Suffix to attach to the CPU odds file.
62const char kCPUOddsFileSuffix[] = ".txt";
Ahmad Shariff5597f62013-04-25 12:25:41 -070063
David Sharpe01f7252015-06-30 16:24:05 -070064const std::map<std::string, std::string> kIntelUarchFileTable {
65 // These were found on various sources on the Internet. Main ones are:
66 // http://instlatx64.atw.hu/ for CPUID to model name and
67 // http://www.cpu-world.com for model name to microarchitecture
68 // {"06_1C", "Bonnell"}, // Atom
69 // {"06_26", "Bonnell"}, // Atom
70 // {"06_36", "Saltwell"}, // Atom
71 // {"06_4C", "Airmont"}, // Braswell
72 // {"06_4E", "Skylake"},
73 // {"06_3D", "Broadwell"},
74 {"06_3C", "Haswell"},
75 {"06_3F", "Haswell"},
76 {"06_45", "Haswell"},
77 {"06_46", "Haswell"},
78 {"06_3A", "IvyBridge"},
79 {"06_3E", "IvyBridge"},
80 {"06_2A", "SandyBridge"},
81 {"06_2D", "SandyBridge"},
82 // {"06_0F", "Merom"},
83 // {"06_16", "Merom"},
84 // {"06_17", "Nehalem"},
85 // {"06_1A", "Nehalem"},
86 // {"06_1D", "Nehalem"},
87 // {"06_1E", "Nehalem"},
88 // {"06_1F", "Nehalem"},
89 // {"06_2E", "Nehalem"},
90 // {"06_0D", "Dothan"},
91 // {"06_09", "Banias"},
92 // {"0F_03", "Prescott"},
93 // {"0F_04", "Prescott"},
94 // {"0F_06", "Presler"},
95 // {"06_25", "Westmere"},
96 // {"06_2C", "Westmere"},
97 // {"06_2F", "Westmere"},
98};
99
Simon Que21bb7902014-07-28 16:17:20 -0700100// Converts an CPU model name string into a format that can be used as a file
101// name. The rules are:
102// - Replace spaces with hyphens.
103// - Strip all "(R)" symbols.
104// - Convert to lower case.
105std::string ModelNameToFileName(const std::string& model_name) {
106 std::string result = model_name;
107 std::replace(result.begin(), result.end(), ' ', '-');
108 ReplaceSubstringsAfterOffset(&result, 0, kRegisteredTrademarkSymbol, "");
Ben Chane1ca84e2014-09-05 08:20:59 -0700109 return base::StringToLowerASCII(result);
Simon Que21bb7902014-07-28 16:17:20 -0700110}
111
David Sharpe01f7252015-06-30 16:24:05 -0700112// For the given |cpu_info|, look for the CPU odds file that corresponds to
113// this CPU. If no matches are found for |cpu_info.arch()|, return the odds
114// file for unknown CPU types. If the arch is valid, but no matches are
115// found for |cpu_info.model_name()|, return the odds file for unknown models
116// of the CPU architecture.
117std::string GetOddsFilenameForCPU(const PerfTool::CPUInfoReader& cpu_info) {
118 const std::string& arch = cpu_info.arch();
119 const std::string& model_name = cpu_info.model_name();
Simon Que795327b2015-03-16 17:42:35 -0700120 const char** cpu_odds_file_list = NULL;
David Sharpe01f7252015-06-30 16:24:05 -0700121 if (arch == "i386" || arch == "i486" || arch == "i586" || arch == "i686") {
Simon Que795327b2015-03-16 17:42:35 -0700122 cpu_odds_file_list = kx86_32CPUOddsFiles;
David Sharpe01f7252015-06-30 16:24:05 -0700123 } else if (arch == "amd64" || arch == "x86_64") {
Simon Que795327b2015-03-16 17:42:35 -0700124 cpu_odds_file_list = kx86_64CPUOddsFiles;
David Sharpe01f7252015-06-30 16:24:05 -0700125 } else if (arch == "armv7l") {
Simon Que795327b2015-03-16 17:42:35 -0700126 cpu_odds_file_list = kARMv7CPUOddsFiles;
127 } else {
128 // If the CPU arch doesn't match any of the recognized arch families, just
129 // use the CPU odds file for unknown CPU types.
130 return kMiscCPUArchOddsFile;
131 }
132
David Sharpe01f7252015-06-30 16:24:05 -0700133 std::string adjusted_model_name = ModelNameToFileName(model_name);
Simon Que795327b2015-03-16 17:42:35 -0700134 for (size_t i = 0; cpu_odds_file_list[i]; ++i) {
135 if (adjusted_model_name.find(cpu_odds_file_list[i]) != std::string::npos) {
David Sharpe01f7252015-06-30 16:24:05 -0700136 return arch + "/" + cpu_odds_file_list[i];
Ahmad Sharif3abea3c2013-05-13 20:43:04 -0700137 }
138 }
David Sharpe01f7252015-06-30 16:24:05 -0700139
140 if (!cpu_info.intel_family_model().empty()) {
141 // See if we have a microarchitecture-specific file.
142 const auto& it = kIntelUarchFileTable.find(cpu_info.intel_family_model());
143 if (it != kIntelUarchFileTable.end()) {
144 const std::string& uarch = it->second;
145 return arch + "/" + uarch;
146 }
147 }
148
Simon Que795327b2015-03-16 17:42:35 -0700149 // If there isn't an odds file for the particular model, use the generic odds
150 // for the CPU arch.
David Sharpe01f7252015-06-30 16:24:05 -0700151 return arch + "/" + kMiscCPUModelOddsFile;
Ahmad Shariff5597f62013-04-25 12:25:41 -0700152}
153
154} // namespace
155
Simon Que795327b2015-03-16 17:42:35 -0700156PerfTool::PerfTool() : PerfTool(CPUInfoReader(), new RandomSelector) {}
157
158PerfTool::PerfTool(const CPUInfoReader& cpu_info,
159 RandomSelector* random_selector)
160 : random_selector_(random_selector) {
David Sharpe01f7252015-06-30 16:24:05 -0700161 std::string odds_filename = GetOddsFilenameForCPU(cpu_info);
Simon Que795327b2015-03-16 17:42:35 -0700162 random_selector_->SetOddsFromFile(
163 kCPUOddsFilePrefix + odds_filename + kCPUOddsFileSuffix);
Ahmad Shariff5597f62013-04-25 12:25:41 -0700164}
Ahmad Sharifae1714d2013-01-17 11:29:37 -0800165
Ben Chan49d264c2014-08-06 17:38:16 -0700166std::vector<uint8_t> PerfTool::GetRichPerfData(const uint32_t& duration_secs,
167 DBus::Error* error) {
Simon Que795327b2015-03-16 17:42:35 -0700168 const std::vector<std::string>& perf_args = random_selector_->GetNext();
Simon Queac5f9cf2015-06-20 13:50:22 -0700169 if (perf_args[1] != "record")
170 return std::vector<uint8_t>();
171
Ahmad Shariff5597f62013-04-25 12:25:41 -0700172 std::string output_string;
Simon Queac5f9cf2015-06-20 13:50:22 -0700173 int result =
174 GetPerfOutputHelper(duration_secs, perf_args, error, &output_string);
175
176 if (result > 0)
177 return std::vector<uint8_t>();
178
Ben Chan49d264c2014-08-06 17:38:16 -0700179 return std::vector<uint8_t>(output_string.begin(), output_string.end());
Ahmad Shariff5597f62013-04-25 12:25:41 -0700180}
181
Simon Queac5f9cf2015-06-20 13:50:22 -0700182int PerfTool::GetRandomPerfOutput(const uint32_t& duration_secs,
183 std::vector<uint8_t>* perf_data,
184 std::vector<uint8_t>* perf_stat,
185 DBus::Error* error) {
186 const std::vector<std::string>& perf_args = random_selector_->GetNext();
187 std::string output_string;
188 int result =
189 GetPerfOutputHelper(duration_secs, perf_args, error, &output_string);
190
191 if (perf_args[1] == "record")
192 perf_data->assign(output_string.begin(), output_string.end());
193 else if (perf_args[1] == "stat")
194 perf_stat->assign(output_string.begin(), output_string.end());
195
196 return result;
197}
198
Simon Que795327b2015-03-16 17:42:35 -0700199PerfTool::CPUInfoReader::CPUInfoReader() {
200 // Get CPU model name, e.g. "Intel(R) Celeron(R) 2955U @ 1.40GHz".
201 debugd::CPUInfoParser cpu_info_parser;
David Sharpe01f7252015-06-30 16:24:05 -0700202 cpu_info_parser.GetKey("model name", &model_name_);
203 std::string vendor;
204 cpu_info_parser.GetKey("vendor_id", &vendor);
205 if (vendor == "GenuineIntel") {
206 std::string cpu_family;
207 cpu_info_parser.GetKey("cpu family", &cpu_family);
208 unsigned int cpu_family_int;
209 base::StringToUint(cpu_family, &cpu_family_int);
210
211 std::string model;
212 cpu_info_parser.GetKey("model", &model);
213 unsigned int model_int;
214 base::StringToUint(model, &model_int);
215
216 intel_family_model_ = StringPrintf("%02x_%02x", cpu_family_int, model_int);
217 }
Simon Que795327b2015-03-16 17:42:35 -0700218
219 // Get CPU machine hardware class, e.g. "i686", "x86_64", "armv7l".
220 struct utsname uname_info;
221 if (!uname(&uname_info))
222 arch_ = uname_info.machine;
223}
224
Simon Queac5f9cf2015-06-20 13:50:22 -0700225int PerfTool::GetPerfOutputHelper(const uint32_t& duration_secs,
226 const std::vector<std::string>& perf_args,
227 DBus::Error* error,
228 std::string* data_string) {
Ahmad Sharifae1714d2013-01-17 11:29:37 -0800229 // This whole method is synchronous, so we create a subprocess, let it run to
230 // completion, then gather up its output to return it.
231 ProcessWithOutput process;
232 process.SandboxAs("root", "root");
233 if (!process.Init())
234 *data_string = "<process init failed>";
235 // If you're going to add switches to a command, have a look at the Process
236 // interface; there's support for adding options specifically.
Ahmad Shariff5597f62013-04-25 12:25:41 -0700237 process.AddArg(kQuipperLocation);
238 process.AddArg(StringPrintf("%u", duration_secs));
David Sharpdada3d02015-02-09 18:24:48 -0800239 for (const auto& arg : perf_args) {
240 process.AddArg(arg);
241 }
Ahmad Sharifae1714d2013-01-17 11:29:37 -0800242 // Run the process to completion. If the process might take a while, you may
243 // have to make this asynchronous using .Start().
244 int status = process.Run();
245 if (status != 0)
246 *data_string = StringPrintf("<process exited with status: %d", status);
247 process.GetOutput(data_string);
Simon Queac5f9cf2015-06-20 13:50:22 -0700248
249 return status;
Ahmad Sharifae1714d2013-01-17 11:29:37 -0800250}
251
Ben Chana0011d82014-05-13 00:19:29 -0700252} // namespace debugd