blob: 3df1c271b224f69e640f7040fda45affb7f37340 [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
Ben Chan55903dd2014-04-24 00:29:04 -070014using base::StringPrintf;
15
David Sharpe01f7252015-06-30 16:24:05 -070016namespace debugd {
17
Ahmad Shariff5597f62013-04-25 12:25:41 -070018namespace {
19
David Sharp61901312015-08-05 13:32:07 -070020const char kUnsupportedPerfToolErrorName[] =
21 "org.chromium.debugd.error.UnsupportedPerfTool";
David Sharp16d35652016-04-05 17:20:08 -070022const char kProcessErrorName[] = "org.chromium.debugd.error.RunProcess";
David Sharp61901312015-08-05 13:32:07 -070023
Ahmad Shariff5597f62013-04-25 12:25:41 -070024// Location of quipper on ChromeOS.
25const char kQuipperLocation[] = "/usr/bin/quipper";
26
Simon Queecb08352015-10-02 17:38:12 -070027enum PerfSubcommand {
28 PERF_COMMAND_RECORD,
29 PERF_COMMAND_STAT,
30 PERF_COMMAND_MEM,
31 PERF_COMMAND_UNSUPPORTED,
32};
33
34// Returns one of the above enums given an vector of perf arguments, starting
35// with "perf" itself in |args[0]|.
36PerfSubcommand GetPerfSubcommandType(const std::vector<std::string>& args) {
37 if (args[0] == "perf" && args.size() > 1) {
38 if (args[1] == "record")
39 return PERF_COMMAND_RECORD;
40 if (args[1] == "stat")
41 return PERF_COMMAND_STAT;
42 if (args[1] == "mem")
43 return PERF_COMMAND_MEM;
44 }
45
46 return PERF_COMMAND_UNSUPPORTED;
47}
48
David Sharp16d35652016-04-05 17:20:08 -070049void AddQuipperArguments(brillo::Process* process,
50 const uint32_t duration_secs,
51 const std::vector<std::string>& perf_args) {
52 process->AddArg(kQuipperLocation);
53 process->AddArg(StringPrintf("%u", duration_secs));
54 for (const auto& arg : perf_args) {
55 process->AddArg(arg);
56 }
57}
58
59// For use with brillo::Process::SetPreExecCallback(), this runs after
60// the fork() in the child process, but before exec(). Call fork() again
61// and exit the parent (child of main process). The grandchild will get
62// orphaned, meaning init will wait() for it up so we don't have to.
63bool Orphan() {
64 if (fork() == 0) { // (grand-)child
65 return true;
66 }
67 // parent
68 ::_Exit(EXIT_SUCCESS);
69}
70
71
Ahmad Shariff5597f62013-04-25 12:25:41 -070072} // namespace
73
David Sharp131e86b2015-11-17 12:55:31 -080074PerfTool::PerfTool() {}
Ahmad Sharifae1714d2013-01-17 11:29:37 -080075
David Sharp61901312015-08-05 13:32:07 -070076int PerfTool::GetPerfOutput(const uint32_t& duration_secs,
77 const std::vector<std::string>& perf_args,
78 std::vector<uint8_t>* perf_data,
79 std::vector<uint8_t>* perf_stat,
80 DBus::Error* error) {
Simon Queecb08352015-10-02 17:38:12 -070081 PerfSubcommand subcommand = GetPerfSubcommandType(perf_args);
82 if (subcommand == PERF_COMMAND_UNSUPPORTED) {
David Sharp61901312015-08-05 13:32:07 -070083 error->set(kUnsupportedPerfToolErrorName,
Simon Queecb08352015-10-02 17:38:12 -070084 "perf_args must begin with {\"perf\", \"record\"}, "
85 " {\"perf\", \"stat\"}, or {\"perf\", \"mem\"}");
David Sharp61901312015-08-05 13:32:07 -070086 return -1;
87 }
88
Simon Queac5f9cf2015-06-20 13:50:22 -070089 std::string output_string;
90 int result =
91 GetPerfOutputHelper(duration_secs, perf_args, error, &output_string);
92
Simon Queecb08352015-10-02 17:38:12 -070093 switch (subcommand) {
94 case PERF_COMMAND_RECORD:
95 case PERF_COMMAND_MEM:
Simon Queac5f9cf2015-06-20 13:50:22 -070096 perf_data->assign(output_string.begin(), output_string.end());
Simon Queecb08352015-10-02 17:38:12 -070097 break;
98 case PERF_COMMAND_STAT:
Simon Queac5f9cf2015-06-20 13:50:22 -070099 perf_stat->assign(output_string.begin(), output_string.end());
Simon Queecb08352015-10-02 17:38:12 -0700100 break;
101 default:
102 // Discard the output.
103 break;
104 }
Simon Queac5f9cf2015-06-20 13:50:22 -0700105
106 return result;
107}
108
David Sharp16d35652016-04-05 17:20:08 -0700109void PerfTool::GetPerfOutputFd(const uint32_t& duration_secs,
110 const std::vector<std::string>& perf_args,
111 const DBus::FileDescriptor& stdout_fd,
112 DBus::Error* error) {
113 PerfSubcommand subcommand = GetPerfSubcommandType(perf_args);
114 if (subcommand == PERF_COMMAND_UNSUPPORTED) {
115 error->set(kUnsupportedPerfToolErrorName,
116 "perf_args must begin with {\"perf\", \"record\"}, "
117 " {\"perf\", \"stat\"}, or {\"perf\", \"mem\"}");
118 return;
119 }
120
121 SandboxedProcess process;
122 process.SandboxAs("root", "root");
123 if (!process.Init()) {
124 error->set(kProcessErrorName, "Process initialization failure.");
125 return;
126 }
127
128 AddQuipperArguments(&process, duration_secs, perf_args);
129
130 process.SetPreExecCallback(base::Bind(Orphan));
131 process.BindFd(stdout_fd.get(), 1);
132
133 if (process.Run() != 0) {
134 error->set(kProcessErrorName, "Process start failure.");
135 }
136}
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();
156 if (status != 0)
Eric Caruso6e4d21a2017-04-26 15:26:04 -0700157 *data_string = StringPrintf("<process exited with status: %d>", status);
158 else
159 process.GetOutput(data_string);
Simon Queac5f9cf2015-06-20 13:50:22 -0700160
161 return status;
Ahmad Sharifae1714d2013-01-17 11:29:37 -0800162}
163
Ben Chana0011d82014-05-13 00:19:29 -0700164} // namespace debugd