blob: 34097bace98ace597ead1491732a91db116e1cae [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
9#include <algorithm>
David Sharpe01f7252015-06-30 16:24:05 -070010#include <map>
Ahmad Sharif3abea3c2013-05-13 20:43:04 -070011
David Sharp16d35652016-04-05 17:20:08 -070012#include <base/callback.h>
13#include <base/strings/string_number_conversions.h>
14#include <base/strings/string_split.h>
15#include <base/strings/string_util.h>
16
Alex Vakulenko262be3f2014-07-30 15:25:50 -070017#include "debugd/src/process_with_output.h"
Ahmad Sharifae1714d2013-01-17 11:29:37 -080018
Ben Chan55903dd2014-04-24 00:29:04 -070019using base::StringPrintf;
20
David Sharpe01f7252015-06-30 16:24:05 -070021namespace debugd {
22
Ahmad Shariff5597f62013-04-25 12:25:41 -070023namespace {
24
David Sharp61901312015-08-05 13:32:07 -070025const char kUnsupportedPerfToolErrorName[] =
26 "org.chromium.debugd.error.UnsupportedPerfTool";
David Sharp16d35652016-04-05 17:20:08 -070027const char kProcessErrorName[] = "org.chromium.debugd.error.RunProcess";
David Sharp61901312015-08-05 13:32:07 -070028
Ahmad Shariff5597f62013-04-25 12:25:41 -070029// Location of quipper on ChromeOS.
30const char kQuipperLocation[] = "/usr/bin/quipper";
31
Simon Queecb08352015-10-02 17:38:12 -070032enum PerfSubcommand {
33 PERF_COMMAND_RECORD,
34 PERF_COMMAND_STAT,
35 PERF_COMMAND_MEM,
36 PERF_COMMAND_UNSUPPORTED,
37};
38
39// Returns one of the above enums given an vector of perf arguments, starting
40// with "perf" itself in |args[0]|.
41PerfSubcommand GetPerfSubcommandType(const std::vector<std::string>& args) {
42 if (args[0] == "perf" && args.size() > 1) {
43 if (args[1] == "record")
44 return PERF_COMMAND_RECORD;
45 if (args[1] == "stat")
46 return PERF_COMMAND_STAT;
47 if (args[1] == "mem")
48 return PERF_COMMAND_MEM;
49 }
50
51 return PERF_COMMAND_UNSUPPORTED;
52}
53
David Sharp16d35652016-04-05 17:20:08 -070054void AddQuipperArguments(brillo::Process* process,
55 const uint32_t duration_secs,
56 const std::vector<std::string>& perf_args) {
57 process->AddArg(kQuipperLocation);
58 process->AddArg(StringPrintf("%u", duration_secs));
59 for (const auto& arg : perf_args) {
60 process->AddArg(arg);
61 }
62}
63
64// For use with brillo::Process::SetPreExecCallback(), this runs after
65// the fork() in the child process, but before exec(). Call fork() again
66// and exit the parent (child of main process). The grandchild will get
67// orphaned, meaning init will wait() for it up so we don't have to.
68bool Orphan() {
69 if (fork() == 0) { // (grand-)child
70 return true;
71 }
72 // parent
73 ::_Exit(EXIT_SUCCESS);
74}
75
76
Ahmad Shariff5597f62013-04-25 12:25:41 -070077} // namespace
78
David Sharp131e86b2015-11-17 12:55:31 -080079PerfTool::PerfTool() {}
Ahmad Sharifae1714d2013-01-17 11:29:37 -080080
David Sharp61901312015-08-05 13:32:07 -070081int PerfTool::GetPerfOutput(const uint32_t& duration_secs,
82 const std::vector<std::string>& perf_args,
83 std::vector<uint8_t>* perf_data,
84 std::vector<uint8_t>* perf_stat,
85 DBus::Error* error) {
Simon Queecb08352015-10-02 17:38:12 -070086 PerfSubcommand subcommand = GetPerfSubcommandType(perf_args);
87 if (subcommand == PERF_COMMAND_UNSUPPORTED) {
David Sharp61901312015-08-05 13:32:07 -070088 error->set(kUnsupportedPerfToolErrorName,
Simon Queecb08352015-10-02 17:38:12 -070089 "perf_args must begin with {\"perf\", \"record\"}, "
90 " {\"perf\", \"stat\"}, or {\"perf\", \"mem\"}");
David Sharp61901312015-08-05 13:32:07 -070091 return -1;
92 }
93
Simon Queac5f9cf2015-06-20 13:50:22 -070094 std::string output_string;
95 int result =
96 GetPerfOutputHelper(duration_secs, perf_args, error, &output_string);
97
Simon Queecb08352015-10-02 17:38:12 -070098 switch (subcommand) {
99 case PERF_COMMAND_RECORD:
100 case PERF_COMMAND_MEM:
Simon Queac5f9cf2015-06-20 13:50:22 -0700101 perf_data->assign(output_string.begin(), output_string.end());
Simon Queecb08352015-10-02 17:38:12 -0700102 break;
103 case PERF_COMMAND_STAT:
Simon Queac5f9cf2015-06-20 13:50:22 -0700104 perf_stat->assign(output_string.begin(), output_string.end());
Simon Queecb08352015-10-02 17:38:12 -0700105 break;
106 default:
107 // Discard the output.
108 break;
109 }
Simon Queac5f9cf2015-06-20 13:50:22 -0700110
111 return result;
112}
113
David Sharp16d35652016-04-05 17:20:08 -0700114void PerfTool::GetPerfOutputFd(const uint32_t& duration_secs,
115 const std::vector<std::string>& perf_args,
116 const DBus::FileDescriptor& stdout_fd,
117 DBus::Error* error) {
118 PerfSubcommand subcommand = GetPerfSubcommandType(perf_args);
119 if (subcommand == PERF_COMMAND_UNSUPPORTED) {
120 error->set(kUnsupportedPerfToolErrorName,
121 "perf_args must begin with {\"perf\", \"record\"}, "
122 " {\"perf\", \"stat\"}, or {\"perf\", \"mem\"}");
123 return;
124 }
125
126 SandboxedProcess process;
127 process.SandboxAs("root", "root");
128 if (!process.Init()) {
129 error->set(kProcessErrorName, "Process initialization failure.");
130 return;
131 }
132
133 AddQuipperArguments(&process, duration_secs, perf_args);
134
135 process.SetPreExecCallback(base::Bind(Orphan));
136 process.BindFd(stdout_fd.get(), 1);
137
138 if (process.Run() != 0) {
139 error->set(kProcessErrorName, "Process start failure.");
140 }
141}
142
Simon Queac5f9cf2015-06-20 13:50:22 -0700143int PerfTool::GetPerfOutputHelper(const uint32_t& duration_secs,
144 const std::vector<std::string>& perf_args,
145 DBus::Error* error,
146 std::string* data_string) {
Ahmad Sharifae1714d2013-01-17 11:29:37 -0800147 // This whole method is synchronous, so we create a subprocess, let it run to
148 // completion, then gather up its output to return it.
149 ProcessWithOutput process;
150 process.SandboxAs("root", "root");
151 if (!process.Init())
152 *data_string = "<process init failed>";
David Sharp16d35652016-04-05 17:20:08 -0700153
154 AddQuipperArguments(&process, duration_secs, perf_args);
155
Ahmad Sharifae1714d2013-01-17 11:29:37 -0800156 // Run the process to completion. If the process might take a while, you may
157 // have to make this asynchronous using .Start().
158 int status = process.Run();
159 if (status != 0)
160 *data_string = StringPrintf("<process exited with status: %d", status);
161 process.GetOutput(data_string);
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