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. 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. 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 82 PerfRecorder::PerfRecorder() : PerfRecorder({"/usr/bin/perf"}) {} 83 84 PerfRecorder::PerfRecorder(const std::vector<string>& perf_binary_command) 85 : perf_binary_command_(perf_binary_command) {} 86 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