blob: ff27e9e585c74a5735ad2452dc01f4335518d13f [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
Ben Chan9953a592014-02-05 23:32:00 -08007#include <base/strings/string_split.h>
Simon Que21bb7902014-07-28 16:17:20 -07008#include <base/strings/string_util.h>
Simon Que795327b2015-03-16 17:42:35 -07009#include <sys/utsname.h>
Simon Que21bb7902014-07-28 16:17:20 -070010
11#include <algorithm>
Ahmad Sharif3abea3c2013-05-13 20:43:04 -070012
Alex Vakulenko262be3f2014-07-30 15:25:50 -070013#include "debugd/src/cpu_info_parser.h"
14#include "debugd/src/process_with_output.h"
Simon Que795327b2015-03-16 17:42:35 -070015#include "debugd/src/random_selector.h"
Ahmad Sharifae1714d2013-01-17 11:29:37 -080016
Ben Chan55903dd2014-04-24 00:29:04 -070017using base::StringPrintf;
18
Ahmad Shariff5597f62013-04-25 12:25:41 -070019namespace {
20
21// Location of quipper on ChromeOS.
22const char kQuipperLocation[] = "/usr/bin/quipper";
23
Ahmad Sharif3abea3c2013-05-13 20:43:04 -070024// This is the key in /proc/cpuinfo whose value is the model name of the CPU.
25const char kCPUModelNameKey[] = "model name";
Ahmad Shariff5597f62013-04-25 12:25:41 -070026
Simon Que21bb7902014-07-28 16:17:20 -070027// This is registered trademark symbol that appears in model name strings.
28const char kRegisteredTrademarkSymbol[] = "(R)";
29
Ahmad Sharif3abea3c2013-05-13 20:43:04 -070030// Processor model name substrings for which we have perf commands.
Simon Que795327b2015-03-16 17:42:35 -070031
32// For 64-bit x86 processors.
33const char* kx86_64CPUOddsFiles[] = {
Simon Que21bb7902014-07-28 16:17:20 -070034 "celeron-2955u",
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
Simon Que21bb7902014-07-28 16:17:20 -070064// Converts an CPU model name string into a format that can be used as a file
65// name. The rules are:
66// - Replace spaces with hyphens.
67// - Strip all "(R)" symbols.
68// - Convert to lower case.
69std::string ModelNameToFileName(const std::string& model_name) {
70 std::string result = model_name;
71 std::replace(result.begin(), result.end(), ' ', '-');
72 ReplaceSubstringsAfterOffset(&result, 0, kRegisteredTrademarkSymbol, "");
Ben Chane1ca84e2014-09-05 08:20:59 -070073 return base::StringToLowerASCII(result);
Simon Que21bb7902014-07-28 16:17:20 -070074}
75
Simon Que795327b2015-03-16 17:42:35 -070076// For the given |cpu_arch| and |cpu_model_name|, look for the CPU odds file
77// that corresponds to these strings. If no matches are found for |CPU_arch|,
78// return the odds file for unknown CPU types. If |cpu_arch| is valid, but no
79// matches are found |cpu_model_name|, return the odds file for unknown models
80// of class |cpu_arch|.
81std::string GetOddsFilenameForCPU(const std::string& cpu_arch,
82 const std::string& cpu_model_name) {
83 const char** cpu_odds_file_list = NULL;
84 if (cpu_arch == "i386" || cpu_arch == "i486" || cpu_arch == "i586" ||
85 cpu_arch == "i686") {
86 cpu_odds_file_list = kx86_32CPUOddsFiles;
87 } else if (cpu_arch == "amd64" || cpu_arch == "x86_64") {
88 cpu_odds_file_list = kx86_64CPUOddsFiles;
89 } else if (cpu_arch == "armv7l") {
90 cpu_odds_file_list = kARMv7CPUOddsFiles;
91 } else {
92 // If the CPU arch doesn't match any of the recognized arch families, just
93 // use the CPU odds file for unknown CPU types.
94 return kMiscCPUArchOddsFile;
95 }
96
Simon Que21bb7902014-07-28 16:17:20 -070097 std::string adjusted_model_name = ModelNameToFileName(cpu_model_name);
Simon Que795327b2015-03-16 17:42:35 -070098 for (size_t i = 0; cpu_odds_file_list[i]; ++i) {
99 if (adjusted_model_name.find(cpu_odds_file_list[i]) != std::string::npos) {
100 return cpu_arch + "/" + cpu_odds_file_list[i];
Ahmad Sharif3abea3c2013-05-13 20:43:04 -0700101 }
102 }
Simon Que795327b2015-03-16 17:42:35 -0700103 // If there isn't an odds file for the particular model, use the generic odds
104 // for the CPU arch.
105 return cpu_arch + "/" + kMiscCPUModelOddsFile;
Ahmad Shariff5597f62013-04-25 12:25:41 -0700106}
107
108} // namespace
109
Ahmad Sharifae1714d2013-01-17 11:29:37 -0800110namespace debugd {
111
Simon Que795327b2015-03-16 17:42:35 -0700112PerfTool::PerfTool() : PerfTool(CPUInfoReader(), new RandomSelector) {}
113
114PerfTool::PerfTool(const CPUInfoReader& cpu_info,
115 RandomSelector* random_selector)
116 : random_selector_(random_selector) {
117 std::string odds_filename =
118 GetOddsFilenameForCPU(cpu_info.arch(), cpu_info.model());
119 random_selector_->SetOddsFromFile(
120 kCPUOddsFilePrefix + odds_filename + kCPUOddsFileSuffix);
Ahmad Shariff5597f62013-04-25 12:25:41 -0700121}
Ahmad Sharifae1714d2013-01-17 11:29:37 -0800122
Ben Chan49d264c2014-08-06 17:38:16 -0700123std::vector<uint8_t> PerfTool::GetRichPerfData(const uint32_t& duration_secs,
124 DBus::Error* error) {
Simon Que795327b2015-03-16 17:42:35 -0700125 const std::vector<std::string>& perf_args = random_selector_->GetNext();
Simon Queac5f9cf2015-06-20 13:50:22 -0700126 if (perf_args[1] != "record")
127 return std::vector<uint8_t>();
128
Ahmad Shariff5597f62013-04-25 12:25:41 -0700129 std::string output_string;
Simon Queac5f9cf2015-06-20 13:50:22 -0700130 int result =
131 GetPerfOutputHelper(duration_secs, perf_args, error, &output_string);
132
133 if (result > 0)
134 return std::vector<uint8_t>();
135
Ben Chan49d264c2014-08-06 17:38:16 -0700136 return std::vector<uint8_t>(output_string.begin(), output_string.end());
Ahmad Shariff5597f62013-04-25 12:25:41 -0700137}
138
Simon Queac5f9cf2015-06-20 13:50:22 -0700139int PerfTool::GetRandomPerfOutput(const uint32_t& duration_secs,
140 std::vector<uint8_t>* perf_data,
141 std::vector<uint8_t>* perf_stat,
142 DBus::Error* error) {
143 const std::vector<std::string>& perf_args = random_selector_->GetNext();
144 std::string output_string;
145 int result =
146 GetPerfOutputHelper(duration_secs, perf_args, error, &output_string);
147
148 if (perf_args[1] == "record")
149 perf_data->assign(output_string.begin(), output_string.end());
150 else if (perf_args[1] == "stat")
151 perf_stat->assign(output_string.begin(), output_string.end());
152
153 return result;
154}
155
Simon Que795327b2015-03-16 17:42:35 -0700156PerfTool::CPUInfoReader::CPUInfoReader() {
157 // Get CPU model name, e.g. "Intel(R) Celeron(R) 2955U @ 1.40GHz".
158 debugd::CPUInfoParser cpu_info_parser;
159 cpu_info_parser.GetKey(kCPUModelNameKey, &model_);
160
161 // Get CPU machine hardware class, e.g. "i686", "x86_64", "armv7l".
162 struct utsname uname_info;
163 if (!uname(&uname_info))
164 arch_ = uname_info.machine;
165}
166
Simon Queac5f9cf2015-06-20 13:50:22 -0700167int PerfTool::GetPerfOutputHelper(const uint32_t& duration_secs,
168 const std::vector<std::string>& perf_args,
169 DBus::Error* error,
170 std::string* data_string) {
Ahmad Sharifae1714d2013-01-17 11:29:37 -0800171 // This whole method is synchronous, so we create a subprocess, let it run to
172 // completion, then gather up its output to return it.
173 ProcessWithOutput process;
174 process.SandboxAs("root", "root");
175 if (!process.Init())
176 *data_string = "<process init failed>";
177 // If you're going to add switches to a command, have a look at the Process
178 // interface; there's support for adding options specifically.
Ahmad Shariff5597f62013-04-25 12:25:41 -0700179 process.AddArg(kQuipperLocation);
180 process.AddArg(StringPrintf("%u", duration_secs));
David Sharpdada3d02015-02-09 18:24:48 -0800181 for (const auto& arg : perf_args) {
182 process.AddArg(arg);
183 }
Ahmad Sharifae1714d2013-01-17 11:29:37 -0800184 // Run the process to completion. If the process might take a while, you may
185 // have to make this asynchronous using .Start().
186 int status = process.Run();
187 if (status != 0)
188 *data_string = StringPrintf("<process exited with status: %d", status);
189 process.GetOutput(data_string);
Simon Queac5f9cf2015-06-20 13:50:22 -0700190
191 return status;
Ahmad Sharifae1714d2013-01-17 11:29:37 -0800192}
193
Ben Chana0011d82014-05-13 00:19:29 -0700194} // namespace debugd