blob: b81f3b1ad577bdff6fe7633e2efdeab9d6853757 [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
Eric Carusocc7106c2017-04-27 14:22:42 -070012#include "debugd/src/error_utils.h"
Alex Vakulenko262be3f2014-07-30 15:25:50 -070013#include "debugd/src/process_with_output.h"
Ahmad Sharifae1714d2013-01-17 11:29:37 -080014
David Sharpe01f7252015-06-30 16:24:05 -070015namespace debugd {
16
Ahmad Shariff5597f62013-04-25 12:25:41 -070017namespace {
18
David Sharp61901312015-08-05 13:32:07 -070019const char kUnsupportedPerfToolErrorName[] =
20 "org.chromium.debugd.error.UnsupportedPerfTool";
David Sharp16d35652016-04-05 17:20:08 -070021const char kProcessErrorName[] = "org.chromium.debugd.error.RunProcess";
David Sharp61901312015-08-05 13:32:07 -070022
Eric Carusocc7106c2017-04-27 14:22:42 -070023const char kArgsError[] = "perf_args must begin with {\"perf\", \"record\"}, "
24 " {\"perf\", \"stat\"}, or {\"perf\", \"mem\"}";
25
Ahmad Shariff5597f62013-04-25 12:25:41 -070026// Location of quipper on ChromeOS.
27const char kQuipperLocation[] = "/usr/bin/quipper";
28
Simon Queecb08352015-10-02 17:38:12 -070029enum PerfSubcommand {
30 PERF_COMMAND_RECORD,
31 PERF_COMMAND_STAT,
32 PERF_COMMAND_MEM,
33 PERF_COMMAND_UNSUPPORTED,
34};
35
36// Returns one of the above enums given an vector of perf arguments, starting
37// with "perf" itself in |args[0]|.
38PerfSubcommand GetPerfSubcommandType(const std::vector<std::string>& args) {
39 if (args[0] == "perf" && args.size() > 1) {
40 if (args[1] == "record")
41 return PERF_COMMAND_RECORD;
42 if (args[1] == "stat")
43 return PERF_COMMAND_STAT;
44 if (args[1] == "mem")
45 return PERF_COMMAND_MEM;
46 }
47
48 return PERF_COMMAND_UNSUPPORTED;
49}
50
David Sharp16d35652016-04-05 17:20:08 -070051void AddQuipperArguments(brillo::Process* process,
52 const uint32_t duration_secs,
53 const std::vector<std::string>& perf_args) {
54 process->AddArg(kQuipperLocation);
Eric Caruso96d03d32017-04-25 18:01:17 -070055 process->AddArg(base::StringPrintf("%u", duration_secs));
David Sharp16d35652016-04-05 17:20:08 -070056 for (const auto& arg : perf_args) {
57 process->AddArg(arg);
58 }
59}
60
61// For use with brillo::Process::SetPreExecCallback(), this runs after
62// the fork() in the child process, but before exec(). Call fork() again
63// and exit the parent (child of main process). The grandchild will get
64// orphaned, meaning init will wait() for it up so we don't have to.
65bool Orphan() {
66 if (fork() == 0) { // (grand-)child
67 return true;
68 }
69 // parent
70 ::_Exit(EXIT_SUCCESS);
71}
72
73
Ahmad Shariff5597f62013-04-25 12:25:41 -070074} // namespace
75
David Sharp131e86b2015-11-17 12:55:31 -080076PerfTool::PerfTool() {}
Ahmad Sharifae1714d2013-01-17 11:29:37 -080077
Eric Caruso56eacb22017-08-04 11:00:37 -070078bool PerfTool::GetPerfOutput(uint32_t duration_secs,
79 const std::vector<std::string>& perf_args,
80 std::vector<uint8_t>* perf_data,
81 std::vector<uint8_t>* perf_stat,
82 int32_t* status,
83 brillo::ErrorPtr* error) {
Simon Queecb08352015-10-02 17:38:12 -070084 PerfSubcommand subcommand = GetPerfSubcommandType(perf_args);
85 if (subcommand == PERF_COMMAND_UNSUPPORTED) {
Eric Carusocc7106c2017-04-27 14:22:42 -070086 DEBUGD_ADD_ERROR(error, kUnsupportedPerfToolErrorName, kArgsError);
Eric Caruso56eacb22017-08-04 11:00:37 -070087 return false;
David Sharp61901312015-08-05 13:32:07 -070088 }
89
Eric Caruso56eacb22017-08-04 11:00:37 -070090 // This whole method is synchronous, so we create a subprocess, let it run to
91 // completion, then gather up its output to return it.
92 ProcessWithOutput process;
93 process.SandboxAs("root", "root");
94 if (!process.Init()) {
95 DEBUGD_ADD_ERROR(
96 error, kProcessErrorName, "Process initialization failure.");
97 return false;
98 }
99
100 AddQuipperArguments(&process, duration_secs, perf_args);
101
Simon Queac5f9cf2015-06-20 13:50:22 -0700102 std::string output_string;
Eric Caruso56eacb22017-08-04 11:00:37 -0700103 *status = process.Run();
104 if (*status != 0) {
105 output_string =
106 base::StringPrintf("<process exited with status: %d>", *status);
107 } else {
108 process.GetOutput(&output_string);
109 }
Simon Queac5f9cf2015-06-20 13:50:22 -0700110
Simon Queecb08352015-10-02 17:38:12 -0700111 switch (subcommand) {
112 case PERF_COMMAND_RECORD:
113 case PERF_COMMAND_MEM:
Simon Queac5f9cf2015-06-20 13:50:22 -0700114 perf_data->assign(output_string.begin(), output_string.end());
Simon Queecb08352015-10-02 17:38:12 -0700115 break;
116 case PERF_COMMAND_STAT:
Simon Queac5f9cf2015-06-20 13:50:22 -0700117 perf_stat->assign(output_string.begin(), output_string.end());
Simon Queecb08352015-10-02 17:38:12 -0700118 break;
119 default:
120 // Discard the output.
121 break;
122 }
Simon Queac5f9cf2015-06-20 13:50:22 -0700123
Eric Caruso56eacb22017-08-04 11:00:37 -0700124 return true;
Simon Queac5f9cf2015-06-20 13:50:22 -0700125}
126
Eric Caruso56eacb22017-08-04 11:00:37 -0700127bool PerfTool::GetPerfOutputFd(uint32_t duration_secs,
David Sharp16d35652016-04-05 17:20:08 -0700128 const std::vector<std::string>& perf_args,
Eric Carusocc7106c2017-04-27 14:22:42 -0700129 const dbus::FileDescriptor& stdout_fd,
130 brillo::ErrorPtr* error) {
David Sharp16d35652016-04-05 17:20:08 -0700131 PerfSubcommand subcommand = GetPerfSubcommandType(perf_args);
132 if (subcommand == PERF_COMMAND_UNSUPPORTED) {
Eric Carusocc7106c2017-04-27 14:22:42 -0700133 DEBUGD_ADD_ERROR(error, kUnsupportedPerfToolErrorName, kArgsError);
Eric Caruso8fe49c72017-04-25 10:43:59 -0700134 return false;
David Sharp16d35652016-04-05 17:20:08 -0700135 }
136
137 SandboxedProcess process;
138 process.SandboxAs("root", "root");
139 if (!process.Init()) {
Eric Carusocc7106c2017-04-27 14:22:42 -0700140 DEBUGD_ADD_ERROR(
141 error, kProcessErrorName, "Process initialization failure.");
Eric Caruso8fe49c72017-04-25 10:43:59 -0700142 return false;
David Sharp16d35652016-04-05 17:20:08 -0700143 }
144
145 AddQuipperArguments(&process, duration_secs, perf_args);
146
147 process.SetPreExecCallback(base::Bind(Orphan));
Eric Carusocc7106c2017-04-27 14:22:42 -0700148 process.BindFd(stdout_fd.value(), 1);
David Sharp16d35652016-04-05 17:20:08 -0700149
150 if (process.Run() != 0) {
Eric Carusocc7106c2017-04-27 14:22:42 -0700151 DEBUGD_ADD_ERROR(error, kProcessErrorName, "Process start failure.");
Eric Caruso8fe49c72017-04-25 10:43:59 -0700152 return false;
David Sharp16d35652016-04-05 17:20:08 -0700153 }
Eric Caruso8fe49c72017-04-25 10:43:59 -0700154 return true;
David Sharp16d35652016-04-05 17:20:08 -0700155}
156
Ben Chana0011d82014-05-13 00:19:29 -0700157} // namespace debugd