blob: 3e586c11b5c11680d266e4c55644ec61329cb25a [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
5#include "perf_tool.h"
6
Ahmad Sharif3abea3c2013-05-13 20:43:04 -07007#include <fstream>
8
9#include <base/string_split.h>
10
11#include "cpu_info_parser.h"
Ahmad Sharifae1714d2013-01-17 11:29:37 -080012#include "process_with_output.h"
13
Ahmad Shariff5597f62013-04-25 12:25:41 -070014namespace {
15
16// Location of quipper on ChromeOS.
17const char kQuipperLocation[] = "/usr/bin/quipper";
18
19// Base perf command line to be used.
20const char kPerfRecord[] = "/usr/sbin/perf record -a";
21
Ahmad Sharif3abea3c2013-05-13 20:43:04 -070022// This is the key in /proc/cpuinfo whose value is the model name of the CPU.
23const char kCPUModelNameKey[] = "model name";
Ahmad Shariff5597f62013-04-25 12:25:41 -070024
Ahmad Sharif3abea3c2013-05-13 20:43:04 -070025// Processor model name substrings for which we have perf commands.
26const char* kCPUOddsFiles[] = {
27 "unknown",
28 "core",
29 "arm",
30};
Ahmad Shariff5597f62013-04-25 12:25:41 -070031
Ahmad Sharif3abea3c2013-05-13 20:43:04 -070032// Prefix path to attach to the CPU odds file.
33const char kCPUOddsFilePrefix[] = "/etc/init/perf_commands/";
Ahmad Shariff5597f62013-04-25 12:25:41 -070034
Ahmad Sharif3abea3c2013-05-13 20:43:04 -070035// Suffix to attach to the CPU odds file.
36const char kCPUOddsFileSuffix[] = ".txt";
Ahmad Shariff5597f62013-04-25 12:25:41 -070037
Ahmad Sharif3abea3c2013-05-13 20:43:04 -070038// Goes through the list of kCPUOddsFiles, and if the any of those strings is a
39// substring of the |cpu_model_name|, returns that string. If no matches are
40// found, returns the first string of |kCPUOddsFiles| ("unknown").
41void GetOddsFilenameForCPU(const std::string& cpu_model_name,
42 std::string* odds_filename) {
43 std::string lowered_cpu_model_name = StringToLowerASCII(cpu_model_name);
44 for (size_t i = 0; i < arraysize(kCPUOddsFiles); ++i) {
45 if (lowered_cpu_model_name.find(kCPUOddsFiles[i]) != std::string::npos) {
46 *odds_filename = kCPUOddsFiles[i];
47 return;
48 }
49 }
50 *odds_filename = kCPUOddsFiles[0];
Ahmad Shariff5597f62013-04-25 12:25:41 -070051}
52
53} // namespace
54
Ahmad Sharifae1714d2013-01-17 11:29:37 -080055namespace debugd {
56
Ahmad Shariff5597f62013-04-25 12:25:41 -070057PerfTool::PerfTool() {
Ahmad Sharif3abea3c2013-05-13 20:43:04 -070058 std::string cpu_model_name;
59 debugd::CPUInfoParser cpu_info_parser;
60 cpu_info_parser.GetKey(kCPUModelNameKey, &cpu_model_name);
61 std::string odds_filename;
62 GetOddsFilenameForCPU(cpu_model_name, &odds_filename);
63 std::string odds_file_path = std::string(kCPUOddsFilePrefix) +
64 odds_filename +
65 kCPUOddsFileSuffix;
66 random_selector_.SetOddsFromFile(odds_file_path);
Ahmad Shariff5597f62013-04-25 12:25:41 -070067}
Ahmad Sharifae1714d2013-01-17 11:29:37 -080068
69PerfTool::~PerfTool() { }
70
71// Tool methods have the same signature as the generated DBus adaptors. Most
72// pertinently, this means they take their DBus::Error argument as a non-const
73// reference (hence the NOLINT). Tool methods are generally written in
74// can't-fail style, since their output is usually going to be displayed to the
75// user; instead of returning a DBus exception, we tend to return a string
76// indicating what went wrong.
Ahmad Shariff5597f62013-04-25 12:25:41 -070077std::vector<uint8> PerfTool::GetPerfData(const uint32_t& duration_secs,
78 DBus::Error& error) { // NOLINT
Ahmad Sharifae1714d2013-01-17 11:29:37 -080079 std::string output_string;
Ahmad Shariff5597f62013-04-25 12:25:41 -070080 GetPerfDataHelper(duration_secs, kPerfRecord, error, &output_string);
Ahmad Sharifae1714d2013-01-17 11:29:37 -080081 std::vector<uint8> output_vector(output_string.begin(),
82 output_string.end());
83 return output_vector;
84}
85
Ahmad Shariff5597f62013-04-25 12:25:41 -070086std::vector<uint8> PerfTool::GetRichPerfData(const uint32_t& duration_secs,
87 DBus::Error& error) { // NOLINT
88 std::string perf_command_line;
89 random_selector_.GetNext(&perf_command_line);
90 std::string output_string;
91 GetPerfDataHelper(duration_secs, perf_command_line, error, &output_string);
92 return std::vector<uint8>(output_string.begin(), output_string.end());
93}
94
95void PerfTool::GetPerfDataHelper(const uint32_t& duration_secs,
96 const std::string& perf_command_line,
Ahmad Sharifae1714d2013-01-17 11:29:37 -080097 DBus::Error& error,
98 std::string* data_string) { // NOLINT
Ahmad Sharifae1714d2013-01-17 11:29:37 -080099 // This whole method is synchronous, so we create a subprocess, let it run to
100 // completion, then gather up its output to return it.
101 ProcessWithOutput process;
102 process.SandboxAs("root", "root");
103 if (!process.Init())
104 *data_string = "<process init failed>";
105 // If you're going to add switches to a command, have a look at the Process
106 // interface; there's support for adding options specifically.
Ahmad Shariff5597f62013-04-25 12:25:41 -0700107 process.AddArg(kQuipperLocation);
108 process.AddArg(StringPrintf("%u", duration_secs));
109 process.AddArg(perf_command_line);
Ahmad Sharifae1714d2013-01-17 11:29:37 -0800110 // Run the process to completion. If the process might take a while, you may
111 // have to make this asynchronous using .Start().
112 int status = process.Run();
113 if (status != 0)
114 *data_string = StringPrintf("<process exited with status: %d", status);
115 process.GetOutput(data_string);
116}
117
118}; // namespace debugd
119