blob: fc7a21668d3a2af7252dfb4b40b1fa2891564ac3 [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
David Sharp16d35652016-04-05 17:20:08 -07007#include <unistd.h>
Simon Que21bb7902014-07-28 16:17:20 -07008
Ben Chan51b0c142017-01-06 18:10:27 -08009#include <base/bind.h>
10#include <base/strings/stringprintf.h>
David Sharp16d35652016-04-05 17:20:08 -070011
Alex Vakulenko262be3f2014-07-30 15:25:50 -070012#include "debugd/src/process_with_output.h"
Ahmad Sharifae1714d2013-01-17 11:29:37 -080013
David Sharpe01f7252015-06-30 16:24:05 -070014namespace debugd {
15
Ahmad Shariff5597f62013-04-25 12:25:41 -070016namespace {
17
David Sharp61901312015-08-05 13:32:07 -070018const char kUnsupportedPerfToolErrorName[] =
19 "org.chromium.debugd.error.UnsupportedPerfTool";
David Sharp16d35652016-04-05 17:20:08 -070020const char kProcessErrorName[] = "org.chromium.debugd.error.RunProcess";
David Sharp61901312015-08-05 13:32:07 -070021
Ahmad Shariff5597f62013-04-25 12:25:41 -070022// Location of quipper on ChromeOS.
23const char kQuipperLocation[] = "/usr/bin/quipper";
24
Simon Queecb08352015-10-02 17:38:12 -070025enum PerfSubcommand {
26 PERF_COMMAND_RECORD,
27 PERF_COMMAND_STAT,
28 PERF_COMMAND_MEM,
29 PERF_COMMAND_UNSUPPORTED,
30};
31
32// Returns one of the above enums given an vector of perf arguments, starting
33// with "perf" itself in |args[0]|.
34PerfSubcommand GetPerfSubcommandType(const std::vector<std::string>& args) {
35 if (args[0] == "perf" && args.size() > 1) {
36 if (args[1] == "record")
37 return PERF_COMMAND_RECORD;
38 if (args[1] == "stat")
39 return PERF_COMMAND_STAT;
40 if (args[1] == "mem")
41 return PERF_COMMAND_MEM;
42 }
43
44 return PERF_COMMAND_UNSUPPORTED;
45}
46
David Sharp16d35652016-04-05 17:20:08 -070047void AddQuipperArguments(brillo::Process* process,
48 const uint32_t duration_secs,
49 const std::vector<std::string>& perf_args) {
50 process->AddArg(kQuipperLocation);
Eric Caruso96d03d32017-04-25 18:01:17 -070051 process->AddArg(base::StringPrintf("%u", duration_secs));
David Sharp16d35652016-04-05 17:20:08 -070052 for (const auto& arg : perf_args) {
53 process->AddArg(arg);
54 }
55}
56
57// For use with brillo::Process::SetPreExecCallback(), this runs after
58// the fork() in the child process, but before exec(). Call fork() again
59// and exit the parent (child of main process). The grandchild will get
60// orphaned, meaning init will wait() for it up so we don't have to.
61bool Orphan() {
62 if (fork() == 0) { // (grand-)child
63 return true;
64 }
65 // parent
66 ::_Exit(EXIT_SUCCESS);
67}
68
69
Ahmad Shariff5597f62013-04-25 12:25:41 -070070} // namespace
71
David Sharp131e86b2015-11-17 12:55:31 -080072PerfTool::PerfTool() {}
Ahmad Sharifae1714d2013-01-17 11:29:37 -080073
David Sharp61901312015-08-05 13:32:07 -070074int PerfTool::GetPerfOutput(const uint32_t& duration_secs,
75 const std::vector<std::string>& perf_args,
76 std::vector<uint8_t>* perf_data,
77 std::vector<uint8_t>* perf_stat,
78 DBus::Error* error) {
Simon Queecb08352015-10-02 17:38:12 -070079 PerfSubcommand subcommand = GetPerfSubcommandType(perf_args);
80 if (subcommand == PERF_COMMAND_UNSUPPORTED) {
David Sharp61901312015-08-05 13:32:07 -070081 error->set(kUnsupportedPerfToolErrorName,
Simon Queecb08352015-10-02 17:38:12 -070082 "perf_args must begin with {\"perf\", \"record\"}, "
83 " {\"perf\", \"stat\"}, or {\"perf\", \"mem\"}");
David Sharp61901312015-08-05 13:32:07 -070084 return -1;
85 }
86
Simon Queac5f9cf2015-06-20 13:50:22 -070087 std::string output_string;
88 int result =
89 GetPerfOutputHelper(duration_secs, perf_args, error, &output_string);
90
Simon Queecb08352015-10-02 17:38:12 -070091 switch (subcommand) {
92 case PERF_COMMAND_RECORD:
93 case PERF_COMMAND_MEM:
Simon Queac5f9cf2015-06-20 13:50:22 -070094 perf_data->assign(output_string.begin(), output_string.end());
Simon Queecb08352015-10-02 17:38:12 -070095 break;
96 case PERF_COMMAND_STAT:
Simon Queac5f9cf2015-06-20 13:50:22 -070097 perf_stat->assign(output_string.begin(), output_string.end());
Simon Queecb08352015-10-02 17:38:12 -070098 break;
99 default:
100 // Discard the output.
101 break;
102 }
Simon Queac5f9cf2015-06-20 13:50:22 -0700103
104 return result;
105}
106
Eric Caruso8fe49c72017-04-25 10:43:59 -0700107bool PerfTool::GetPerfOutputFd(const uint32_t& duration_secs,
David Sharp16d35652016-04-05 17:20:08 -0700108 const std::vector<std::string>& perf_args,
109 const DBus::FileDescriptor& stdout_fd,
110 DBus::Error* error) {
111 PerfSubcommand subcommand = GetPerfSubcommandType(perf_args);
112 if (subcommand == PERF_COMMAND_UNSUPPORTED) {
113 error->set(kUnsupportedPerfToolErrorName,
114 "perf_args must begin with {\"perf\", \"record\"}, "
115 " {\"perf\", \"stat\"}, or {\"perf\", \"mem\"}");
Eric Caruso8fe49c72017-04-25 10:43:59 -0700116 return false;
David Sharp16d35652016-04-05 17:20:08 -0700117 }
118
119 SandboxedProcess process;
120 process.SandboxAs("root", "root");
121 if (!process.Init()) {
122 error->set(kProcessErrorName, "Process initialization failure.");
Eric Caruso8fe49c72017-04-25 10:43:59 -0700123 return false;
David Sharp16d35652016-04-05 17:20:08 -0700124 }
125
126 AddQuipperArguments(&process, duration_secs, perf_args);
127
128 process.SetPreExecCallback(base::Bind(Orphan));
129 process.BindFd(stdout_fd.get(), 1);
130
131 if (process.Run() != 0) {
132 error->set(kProcessErrorName, "Process start failure.");
Eric Caruso8fe49c72017-04-25 10:43:59 -0700133 return false;
David Sharp16d35652016-04-05 17:20:08 -0700134 }
Eric Caruso8fe49c72017-04-25 10:43:59 -0700135 return true;
David Sharp16d35652016-04-05 17:20:08 -0700136}
137
Simon Queac5f9cf2015-06-20 13:50:22 -0700138int PerfTool::GetPerfOutputHelper(const uint32_t& duration_secs,
139 const std::vector<std::string>& perf_args,
140 DBus::Error* error,
141 std::string* data_string) {
Ahmad Sharifae1714d2013-01-17 11:29:37 -0800142 // This whole method is synchronous, so we create a subprocess, let it run to
143 // completion, then gather up its output to return it.
144 ProcessWithOutput process;
145 process.SandboxAs("root", "root");
Eric Caruso6e4d21a2017-04-26 15:26:04 -0700146 if (!process.Init()) {
Ahmad Sharifae1714d2013-01-17 11:29:37 -0800147 *data_string = "<process init failed>";
Eric Caruso6e4d21a2017-04-26 15:26:04 -0700148 return -1;
149 }
David Sharp16d35652016-04-05 17:20:08 -0700150
151 AddQuipperArguments(&process, duration_secs, perf_args);
152
Ahmad Sharifae1714d2013-01-17 11:29:37 -0800153 // Run the process to completion. If the process might take a while, you may
154 // have to make this asynchronous using .Start().
155 int status = process.Run();
Eric Caruso96d03d32017-04-25 18:01:17 -0700156 if (status != 0) {
157 *data_string =
158 base::StringPrintf("<process exited with status: %d>", status);
159 } else {
Eric Caruso6e4d21a2017-04-26 15:26:04 -0700160 process.GetOutput(data_string);
Eric Caruso96d03d32017-04-25 18:01:17 -0700161 }
Simon Queac5f9cf2015-06-20 13:50:22 -0700162
163 return status;
Ahmad Sharifae1714d2013-01-17 11:29:37 -0800164}
165
Ben Chana0011d82014-05-13 00:19:29 -0700166} // namespace debugd