• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 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_recorder.h"
6 
7 #include <algorithm>
8 #include <cstdio>
9 #include <cstring>
10 #include <sstream>
11 #include <vector>
12 
13 #include "binary_data_utils.h"
14 #include "compat/proto.h"
15 #include "compat/string.h"
16 #include "perf_option_parser.h"
17 #include "perf_parser.h"
18 #include "perf_protobuf_io.h"
19 #include "perf_stat_parser.h"
20 #include "run_command.h"
21 #include "scoped_temp_path.h"
22 
23 namespace quipper {
24 
25 namespace {
26 
27 // Supported perf subcommands.
28 const char kPerfRecordCommand[] = "record";
29 const char kPerfStatCommand[] = "stat";
30 const char kPerfMemCommand[] = "mem";
31 
32 // Reads a perf data file and converts it to a PerfDataProto, which is stored as
33 // a serialized string in |output_string|. Returns true on success.
ParsePerfDataFileToString(const string & filename,string * output_string)34 bool ParsePerfDataFileToString(const string& filename, string* output_string) {
35   // Now convert it into a protobuf.
36 
37   PerfParserOptions options;
38   // Make sure to remap address for security reasons.
39   options.do_remap = true;
40   // Discard unused perf events to reduce the protobuf size.
41   options.discard_unused_events = true;
42   // Read buildids from the filesystem ourself.
43   options.read_missing_buildids = true;
44   // Resolve split huge pages mappings.
45   options.deduce_huge_page_mappings = true;
46 
47   PerfDataProto perf_data;
48   return SerializeFromFileWithOptions(filename, options, &perf_data) &&
49          perf_data.SerializeToString(output_string);
50 }
51 
52 // Reads a perf data file and converts it to a PerfStatProto, which is stored as
53 // a serialized string in |output_string|. Returns true on success.
ParsePerfStatFileToString(const string & filename,const std::vector<string> & command_line_args,string * output_string)54 bool ParsePerfStatFileToString(const string& filename,
55                                const std::vector<string>& command_line_args,
56                                string* output_string) {
57   PerfStatProto perf_stat;
58   if (!ParsePerfStatFileToProto(filename, &perf_stat)) {
59     LOG(ERROR) << "Failed to parse PerfStatProto from " << filename;
60     return false;
61   }
62 
63   // Fill in the command line field of the protobuf.
64   string command_line;
65   for (size_t i = 0; i < command_line_args.size(); ++i) {
66     const string& arg = command_line_args[i];
67     // Strip the output file argument from the command line.
68     if (arg == "-o") {
69       ++i;
70       continue;
71     }
72     command_line.append(arg + " ");
73   }
74   command_line.resize(command_line.size() - 1);
75   perf_stat.mutable_command_line()->assign(command_line);
76 
77   return perf_stat.SerializeToString(output_string);
78 }
79 
80 }  // namespace
81 
PerfRecorder()82 PerfRecorder::PerfRecorder() : PerfRecorder({"/usr/bin/perf"}) {}
83 
PerfRecorder(const std::vector<string> & perf_binary_command)84 PerfRecorder::PerfRecorder(const std::vector<string>& perf_binary_command)
85     : perf_binary_command_(perf_binary_command) {}
86 
RunCommandAndGetSerializedOutput(const std::vector<string> & perf_args,const double time_sec,string * output_string)87 bool PerfRecorder::RunCommandAndGetSerializedOutput(
88     const std::vector<string>& perf_args, const double time_sec,
89     string* output_string) {
90   if (!ValidatePerfCommandLine(perf_args)) {
91     LOG(ERROR) << "Perf arguments are not safe to run";
92     return false;
93   }
94 
95   // ValidatePerfCommandLine should have checked perf_args[0] == "perf", and
96   // that perf_args[1] is a supported sub-command (e.g. "record" or "stat").
97 
98   const string& perf_type = perf_args[1];
99 
100   if (perf_type != kPerfRecordCommand && perf_type != kPerfStatCommand &&
101       perf_type != kPerfMemCommand) {
102     LOG(ERROR) << "Unsupported perf subcommand: " << perf_type;
103     return false;
104   }
105 
106   ScopedTempFile output_file;
107 
108   // Assemble the full command line:
109   // - Replace "perf" in |perf_args[0]| with |perf_binary_command_| to
110   //   guarantee we're running a binary we believe we can trust.
111   // - Add our own paramters.
112 
113   std::vector<string> full_perf_args(perf_binary_command_);
114   full_perf_args.insert(full_perf_args.end(),
115                         perf_args.begin() + 1,  // skip "perf"
116                         perf_args.end());
117   full_perf_args.insert(full_perf_args.end(), {"-o", output_file.path()});
118 
119   // The perf stat output parser requires raw data from verbose output.
120   if (perf_type == kPerfStatCommand) full_perf_args.emplace_back("-v");
121 
122   // Append the sleep command to run perf for |time_sec| seconds.
123   std::stringstream time_string;
124   time_string << time_sec;
125   full_perf_args.insert(full_perf_args.end(),
126                         {"--", "sleep", time_string.str()});
127 
128   // The perf command writes the output to a file, so ignore stdout.
129   int status = RunCommand(full_perf_args, nullptr);
130   if (status != 0) {
131     PLOG(ERROR) << "perf command failed with status: " << status << ", Error";
132     return false;
133   }
134 
135   if (perf_type == kPerfRecordCommand || perf_type == kPerfMemCommand)
136     return ParsePerfDataFileToString(output_file.path(), output_string);
137 
138   // Otherwise, parse as perf stat output.
139   return ParsePerfStatFileToString(output_file.path(), full_perf_args,
140                                    output_string);
141 }
142 
143 }  // namespace quipper
144