// Copyright (c) 2012 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "perf_recorder.h" #include #include #include #include #include #include "binary_data_utils.h" #include "compat/proto.h" #include "compat/string.h" #include "perf_option_parser.h" #include "perf_parser.h" #include "perf_protobuf_io.h" #include "perf_stat_parser.h" #include "run_command.h" #include "scoped_temp_path.h" namespace quipper { namespace { // Supported perf subcommands. const char kPerfRecordCommand[] = "record"; const char kPerfStatCommand[] = "stat"; const char kPerfMemCommand[] = "mem"; // Reads a perf data file and converts it to a PerfDataProto, which is stored as // a serialized string in |output_string|. Returns true on success. bool ParsePerfDataFileToString(const string& filename, string* output_string) { // Now convert it into a protobuf. PerfParserOptions options; // Make sure to remap address for security reasons. options.do_remap = true; // Discard unused perf events to reduce the protobuf size. options.discard_unused_events = true; // Read buildids from the filesystem ourself. options.read_missing_buildids = true; // Resolve split huge pages mappings. options.deduce_huge_page_mappings = true; PerfDataProto perf_data; return SerializeFromFileWithOptions(filename, options, &perf_data) && perf_data.SerializeToString(output_string); } // Reads a perf data file and converts it to a PerfStatProto, which is stored as // a serialized string in |output_string|. Returns true on success. bool ParsePerfStatFileToString(const string& filename, const std::vector& command_line_args, string* output_string) { PerfStatProto perf_stat; if (!ParsePerfStatFileToProto(filename, &perf_stat)) { LOG(ERROR) << "Failed to parse PerfStatProto from " << filename; return false; } // Fill in the command line field of the protobuf. string command_line; for (size_t i = 0; i < command_line_args.size(); ++i) { const string& arg = command_line_args[i]; // Strip the output file argument from the command line. if (arg == "-o") { ++i; continue; } command_line.append(arg + " "); } command_line.resize(command_line.size() - 1); perf_stat.mutable_command_line()->assign(command_line); return perf_stat.SerializeToString(output_string); } } // namespace PerfRecorder::PerfRecorder() : PerfRecorder({"/usr/bin/perf"}) {} PerfRecorder::PerfRecorder(const std::vector& perf_binary_command) : perf_binary_command_(perf_binary_command) {} bool PerfRecorder::RunCommandAndGetSerializedOutput( const std::vector& perf_args, const double time_sec, string* output_string) { if (!ValidatePerfCommandLine(perf_args)) { LOG(ERROR) << "Perf arguments are not safe to run"; return false; } // ValidatePerfCommandLine should have checked perf_args[0] == "perf", and // that perf_args[1] is a supported sub-command (e.g. "record" or "stat"). const string& perf_type = perf_args[1]; if (perf_type != kPerfRecordCommand && perf_type != kPerfStatCommand && perf_type != kPerfMemCommand) { LOG(ERROR) << "Unsupported perf subcommand: " << perf_type; return false; } ScopedTempFile output_file; // Assemble the full command line: // - Replace "perf" in |perf_args[0]| with |perf_binary_command_| to // guarantee we're running a binary we believe we can trust. // - Add our own paramters. std::vector full_perf_args(perf_binary_command_); full_perf_args.insert(full_perf_args.end(), perf_args.begin() + 1, // skip "perf" perf_args.end()); full_perf_args.insert(full_perf_args.end(), {"-o", output_file.path()}); // The perf stat output parser requires raw data from verbose output. if (perf_type == kPerfStatCommand) full_perf_args.emplace_back("-v"); // Append the sleep command to run perf for |time_sec| seconds. std::stringstream time_string; time_string << time_sec; full_perf_args.insert(full_perf_args.end(), {"--", "sleep", time_string.str()}); // The perf command writes the output to a file, so ignore stdout. int status = RunCommand(full_perf_args, nullptr); if (status != 0) { PLOG(ERROR) << "perf command failed with status: " << status << ", Error"; return false; } if (perf_type == kPerfRecordCommand || perf_type == kPerfMemCommand) return ParsePerfDataFileToString(output_file.path(), output_string); // Otherwise, parse as perf stat output. return ParsePerfStatFileToString(output_file.path(), full_perf_args, output_string); } } // namespace quipper