Refactorings in preparation for tracebox
Minor refactoring in prepration for the next CLs:
- Split perfetto_cmd's main() into two stages, one
that does the cmdline parsing and one that connects
to traced and does the rest. This will be used to
parse the cmdline before deciding to spawn the
services.
- Print usage on stderr rather than via ELOG. ELOG
adds an unaesthetic source+line number and also
spams logcat for no reasons.
- Move GetCurExecutablePath() from test/utils to
utils as it will be soon required in production
code.
#fixit
Bug: 158465724
Bug: 187945217
Change-Id: I85653bafd93b066d7d376b3816787b8779c52ac6
diff --git a/src/base/subprocess_posix.cc b/src/base/subprocess_posix.cc
index 85df339..5e09544 100644
--- a/src/base/subprocess_posix.cc
+++ b/src/base/subprocess_posix.cc
@@ -82,6 +82,12 @@
_exit(128);
};
+ if (args->create_args->posix_proc_group_id.has_value()) {
+ if (setpgid(0 /*self*/, args->create_args->posix_proc_group_id.value())) {
+ die("setpgid() failed");
+ }
+ }
+
auto set_fd_close_on_exec = [&die](int fd, bool close_on_exec) {
int flags = fcntl(fd, F_GETFD, 0);
if (flags < 0)
@@ -150,12 +156,15 @@
}
}
- // Clears O_CLOEXEC from stdin/out/err. These are the only FDs that we want
- // to be preserved after the exec().
+ // Clears O_CLOEXEC from stdin/out/err and the |preserve_fds| list. These are
+ // the only FDs that we want to be preserved after the exec().
set_fd_close_on_exec(STDIN_FILENO, false);
set_fd_close_on_exec(STDOUT_FILENO, false);
set_fd_close_on_exec(STDERR_FILENO, false);
+ for (auto fd : preserve_fds)
+ set_fd_close_on_exec(fd, false);
+
// If the caller specified a std::function entrypoint, run that first.
if (args->create_args->posix_entrypoint_for_testing)
args->create_args->posix_entrypoint_for_testing();
diff --git a/src/base/test/utils.cc b/src/base/test/utils.cc
index 0a56611..ff00f64 100644
--- a/src/base/test/utils.cc
+++ b/src/base/test/utils.cc
@@ -23,55 +23,11 @@
#include "perfetto/base/build_config.h"
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/file_utils.h"
-
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
- PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
- PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) || \
- PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
-#include <limits.h>
-#include <unistd.h>
-#endif
-
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-#include <Windows.h>
-#include <io.h>
-#endif
-
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
-#include <mach-o/dyld.h>
-#endif
+#include "perfetto/ext/base/utils.h"
namespace perfetto {
namespace base {
-std::string GetCurExecutableDir() {
- std::string self_path;
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
- PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
- PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
- char buf[PATH_MAX];
- ssize_t size = readlink("/proc/self/exe", buf, sizeof(buf));
- PERFETTO_CHECK(size != -1);
- // readlink does not null terminate.
- self_path = std::string(buf, static_cast<size_t>(size));
-#elif PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
- uint32_t size = 0;
- PERFETTO_CHECK(_NSGetExecutablePath(nullptr, &size));
- self_path.resize(size);
- PERFETTO_CHECK(_NSGetExecutablePath(&self_path[0], &size) == 0);
-#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
- char buf[MAX_PATH];
- auto len = ::GetModuleFileNameA(nullptr /*current*/, buf, sizeof(buf));
- self_path = std::string(buf, len);
- self_path = self_path.substr(0, self_path.find_last_of("\\"));
-#else
- PERFETTO_FATAL(
- "GetCurExecutableDir() not implemented on the current platform");
-#endif
- // Cut binary name.
- return self_path.substr(0, self_path.find_last_of("/"));
-}
-
std::string GetTestDataPath(const std::string& path) {
std::string self_path = GetCurExecutableDir();
std::string full_path = self_path + "/../../" + path;
diff --git a/src/base/test/utils.h b/src/base/test/utils.h
index e53b786..032194b 100644
--- a/src/base/test/utils.h
+++ b/src/base/test/utils.h
@@ -50,7 +50,6 @@
namespace perfetto {
namespace base {
-std::string GetCurExecutableDir();
std::string GetTestDataPath(const std::string& path);
// Returns a xxd-style hex dump (hex + ascii chars) of the input data.
diff --git a/src/base/utils.cc b/src/base/utils.cc
index d5bd5a0..041e010 100644
--- a/src/base/utils.cc
+++ b/src/base/utils.cc
@@ -14,22 +14,32 @@
* limitations under the License.
*/
-#include "perfetto/ext/base/file_utils.h"
#include "perfetto/ext/base/utils.h"
+#include <string>
+
#include "perfetto/base/build_config.h"
#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/file_utils.h"
#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
- PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
+#include <limits.h>
#include <unistd.h> // For getpagesize() and geteuid() & fork()
#endif
#if PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+#include <mach-o/dyld.h>
#include <mach/vm_page_size.h>
#endif
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <Windows.h>
+#include <io.h>
+#endif
+
#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
#include <dlfcn.h>
#include <malloc.h>
@@ -39,7 +49,7 @@
#else
// Only available in in-tree builds and on newer SDKs.
#define PERFETTO_M_PURGE -101
-#endif
+#endif // M_PURGE
namespace {
extern "C" {
@@ -110,35 +120,71 @@
}
void Daemonize() {
- #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
- PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
- PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
- pid_t pid;
- switch (pid = fork()) {
- case -1:
- PERFETTO_FATAL("fork");
- case 0: {
- PERFETTO_CHECK(setsid() != -1);
- base::ignore_result(chdir("/"));
- base::ScopedFile null = base::OpenFile("/dev/null", O_RDONLY);
- PERFETTO_CHECK(null);
- PERFETTO_CHECK(dup2(*null, STDIN_FILENO) != -1);
- PERFETTO_CHECK(dup2(*null, STDOUT_FILENO) != -1);
- PERFETTO_CHECK(dup2(*null, STDERR_FILENO) != -1);
- // Do not accidentally close stdin/stdout/stderr.
- if (*null <= 2)
- null.release();
- break;
- }
- default:
- printf("%d\n", pid);
- exit(0);
- }
- #else
- // Avoid -Wunreachable warnings.
- if (reinterpret_cast<intptr_t>(&Daemonize) != 16)
- PERFETTO_FATAL("--background is only supported on Linux/Android/Mac");
- #endif // OS_WIN
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+ pid_t pid;
+ switch (pid = fork()) {
+ case -1:
+ PERFETTO_FATAL("fork");
+ case 0: {
+ PERFETTO_CHECK(setsid() != -1);
+ base::ignore_result(chdir("/"));
+ base::ScopedFile null = base::OpenFile("/dev/null", O_RDONLY);
+ PERFETTO_CHECK(null);
+ PERFETTO_CHECK(dup2(*null, STDIN_FILENO) != -1);
+ PERFETTO_CHECK(dup2(*null, STDOUT_FILENO) != -1);
+ PERFETTO_CHECK(dup2(*null, STDERR_FILENO) != -1);
+ // Do not accidentally close stdin/stdout/stderr.
+ if (*null <= 2)
+ null.release();
+ break;
+ }
+ default:
+ printf("%d\n", pid);
+ exit(0);
+ }
+#else
+ // Avoid -Wunreachable warnings.
+ if (reinterpret_cast<intptr_t>(&Daemonize) != 16)
+ PERFETTO_FATAL("--background is only supported on Linux/Android/Mac");
+#endif // OS_WIN
+}
+
+std::string GetCurExecutablePath() {
+ std::string self_path;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
+ char buf[PATH_MAX];
+ ssize_t size = readlink("/proc/self/exe", buf, sizeof(buf));
+ PERFETTO_CHECK(size != -1);
+ // readlink does not null terminate.
+ self_path = std::string(buf, static_cast<size_t>(size));
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+ uint32_t size = 0;
+ PERFETTO_CHECK(_NSGetExecutablePath(nullptr, &size));
+ self_path.resize(size);
+ PERFETTO_CHECK(_NSGetExecutablePath(&self_path[0], &size) == 0);
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ char buf[MAX_PATH];
+ auto len = ::GetModuleFileNameA(nullptr /*current*/, buf, sizeof(buf));
+ self_path = std::string(buf, len);
+#else
+ PERFETTO_FATAL(
+ "GetCurExecutableDir() not implemented on the current platform");
+#endif
+ return self_path;
+}
+
+std::string GetCurExecutableDir() {
+ auto path = GetCurExecutablePath();
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ // Paths in Windows can have both kinds of slashes (mingw vs msvc).
+ path = path.substr(0, path.find_last_of("\\"));
+#endif
+ path = path.substr(0, path.find_last_of("/"));
+ return path;
}
} // namespace base
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc
index c8f6abc..db4c9b9 100644
--- a/src/perfetto_cmd/perfetto_cmd.cc
+++ b/src/perfetto_cmd/perfetto_cmd.cc
@@ -70,7 +70,7 @@
namespace perfetto {
namespace {
-perfetto::PerfettoCmd* g_consumer_cmd;
+perfetto::PerfettoCmd* g_perfetto_cmd;
uint32_t kOnTraceDataTimeoutMs = 3000;
@@ -166,55 +166,69 @@
const char* kStateDir = "/data/misc/perfetto-traces";
+PerfettoCmd::PerfettoCmd() {
+ PERFETTO_DCHECK(!g_perfetto_cmd);
+ g_perfetto_cmd = this;
+}
+
+PerfettoCmd::~PerfettoCmd() {
+ PERFETTO_DCHECK(g_perfetto_cmd == this);
+ g_perfetto_cmd = nullptr;
+}
+
int PerfettoCmd::PrintUsage(const char* argv0) {
- PERFETTO_ELOG(R"(
+ fprintf(stderr, R"(
Usage: %s
- --background -d : Exits immediately and continues tracing in
- background
+ --background -d : Exits immediately and continues in the background.
+ Prints the PID of the bg process. The printed PID
+ can used to gracefully terminate the tracing
+ session by ussuing a `kill -TERM $PRINTED_PID`.
--config -c : /path/to/trace/config/file or - for stdin
--out -o : /path/to/out/trace/file or - for stdout
- --upload : Upload field trace (Android only)
- --dropbox TAG : DEPRECATED: Use --upload instead
- TAG should always be set to 'perfetto'
- --no-guardrails : Ignore guardrails triggered when using --upload
- (for testing).
--txt : Parse config as pbtxt. Not for production use.
Not a stable API.
- --reset-guardrails : Resets the state of the guardails and exits
- (for testing).
--query : Queries the service state and prints it as
human-readable text.
--query-raw : Like --query, but prints raw proto-encoded bytes
of tracing_service_state.proto.
- --save-for-bugreport : If a trace with bugreport_score > 0 is running, it
- saves it into a file. Outputs the path when done.
--help -h
-
-light configuration flags: (only when NOT using -c/--config)
+Light configuration flags: (only when NOT using -c/--config)
--time -t : Trace duration N[s,m,h] (default: 10s)
--buffer -b : Ring buffer size N[mb,gb] (default: 32mb)
- --size -s : Max file size N[mb,gb] (default: in-memory ring-buffer only)
+ --size -s : Max file size N[mb,gb]
+ (default: in-memory ring-buffer only)
--app -a : Android (atrace) app name
- ATRACE_CAT : Record ATRACE_CAT (e.g. wm)
FTRACE_GROUP/FTRACE_NAME : Record ftrace event (e.g. sched/sched_switch)
+ ATRACE_CAT : Record ATRACE_CAT (e.g. wm) (Android only)
-statsd-specific flags:
+Statsd-specific and other Android-only flags:
--alert-id : ID of the alert that triggered this trace.
--config-id : ID of the triggering config.
--config-uid : UID of app which registered the config.
--subscription-id : ID of the subscription that triggered this trace.
+ --upload : Upload trace.
+ --dropbox TAG : DEPRECATED: Use --upload instead
+ TAG should always be set to 'perfetto'.
+ --save-for-bugreport : If a trace with bugreport_score > 0 is running, it
+ saves it into a file. Outputs the path when done.
+ --no-guardrails : Ignore guardrails triggered when using --upload
+ (testing only).
+ --reset-guardrails : Resets the state of the guardails and exits
+ (testing only).
-Detach mode. DISCOURAGED, read https://perfetto.dev/docs/concepts/detached-mode :
+Detach mode. DISCOURAGED, read https://perfetto.dev/docs/concepts/detached-mode
--detach=key : Detach from the tracing session with the given key.
- --attach=key [--stop] : Re-attach to the session (optionally stop tracing once reattached).
- --is_detached=key : Check if the session can be re-attached (0:Yes, 2:No, 1:Error).
+ --attach=key [--stop] : Re-attach to the session (optionally stop tracing
+ once reattached).
+ --is_detached=key : Check if the session can be re-attached.
+ Exit code: 0:Yes, 2:No, 1:Error.
)", /* this comment fixes syntax highlighting in some editors */
- argv0);
+ argv0);
return 1;
}
-int PerfettoCmd::Main(int argc, char** argv) {
+int PerfettoCmd::ParseCmdlineAndMaybeDaemonize(int argc, char** argv) {
#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
umask(0000); // make sure that file creation is not affected by umask.
#endif
@@ -267,16 +281,18 @@
std::string config_file_name;
std::string trace_config_raw;
- bool background = false;
- bool ignore_guardrails = false;
bool parse_as_pbtxt = false;
- bool upload_flag = false;
TraceConfig::StatsdMetadata statsd_metadata;
- RateLimiter limiter;
+ limiter_.reset(new RateLimiter());
ConfigOptions config_options;
bool has_config_options = false;
+ if (argc <= 1) {
+ PrintUsage(argv[0]);
+ return 1;
+ }
+
for (;;) {
int option =
getopt_long(argc, argv, "hc:o:dt:b:s:a:", long_options, nullptr);
@@ -314,7 +330,7 @@
}
if (option == 'd') {
- background = true;
+ background_ = true;
continue;
}
if (option == 't') {
@@ -343,7 +359,7 @@
if (option == OPT_UPLOAD) {
#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
- upload_flag = true;
+ upload_flag_ = true;
continue;
#else
PERFETTO_ELOG("--upload is only supported on Android");
@@ -354,7 +370,7 @@
if (option == OPT_DROPBOX) {
#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
PERFETTO_CHECK(optarg);
- upload_flag = true;
+ upload_flag_ = true;
continue;
#else
PERFETTO_ELOG("--dropbox is only supported on Android");
@@ -368,12 +384,12 @@
}
if (option == OPT_IGNORE_GUARDRAILS) {
- ignore_guardrails = true;
+ ignore_guardrails_ = true;
continue;
}
if (option == OPT_RESET_GUARDRAILS) {
- PERFETTO_CHECK(limiter.ClearState());
+ PERFETTO_CHECK(limiter_->ClearState());
PERFETTO_ILOG("Guardrail state cleared");
return 0;
}
@@ -451,7 +467,7 @@
config_options.categories.push_back(argv[i]);
}
- if (query_service_ && (is_detach() || is_attach() || background)) {
+ if (query_service_ && (is_detach() || is_attach() || background_)) {
PERFETTO_ELOG("--query cannot be combined with any other argument");
return 1;
}
@@ -461,7 +477,7 @@
return 1;
}
- if (is_detach() && background) {
+ if (is_detach() && background_) {
PERFETTO_ELOG("--detach and --background are mutually exclusive");
return 1;
}
@@ -485,7 +501,6 @@
// For this we are just acting on already existing sessions.
trace_config_.reset(new TraceConfig());
- std::vector<std::string> triggers_to_activate;
bool parsed = false;
const bool will_trace = !is_attach() && !query_service_ && !bugreport_;
if (!will_trace) {
@@ -540,7 +555,7 @@
}
if (!trace_config_->incident_report_config().destination_package().empty() &&
- !upload_flag) {
+ !upload_flag_) {
PERFETTO_ELOG(
"Unexpected IncidentReportConfig without --dropbox / --upload.");
return 1;
@@ -549,7 +564,7 @@
if (trace_config_->activate_triggers().empty() &&
trace_config_->incident_report_config().destination_package().empty() &&
!trace_config_->incident_report_config().skip_incidentd() &&
- upload_flag) {
+ upload_flag_) {
PERFETTO_ELOG(
"Missing IncidentReportConfig.destination_package with --dropbox / "
"--upload.");
@@ -559,7 +574,7 @@
// Only save to incidentd if both --upload is set and |skip_incidentd| is
// absent or false.
save_to_incidentd_ =
- upload_flag && !trace_config_->incident_report_config().skip_incidentd();
+ upload_flag_ && !trace_config_->incident_report_config().skip_incidentd();
// Respect the wishes of the config with respect to statsd logging or fall
// back on the presence of the --upload flag if not set.
@@ -571,7 +586,7 @@
statsd_logging_ = false;
break;
case TraceConfig::STATSD_LOGGING_UNSPECIFIED:
- statsd_logging_ = upload_flag;
+ statsd_logging_ = upload_flag_;
break;
}
trace_config_->set_statsd_logging(statsd_logging_
@@ -581,7 +596,7 @@
// Set up the output file. Either --out or --upload are expected, with the
// only exception of --attach. In this case the output file is passed when
// detaching.
- if (!trace_out_path_.empty() && upload_flag) {
+ if (!trace_out_path_.empty() && upload_flag_) {
PERFETTO_ELOG(
"Can't log to a file (--out) and incidentd (--upload) at the same "
"time");
@@ -589,7 +604,7 @@
}
if (!trace_config_->output_path().empty()) {
- if (!trace_out_path_.empty() || upload_flag) {
+ if (!trace_out_path_.empty() || upload_flag_) {
PERFETTO_ELOG(
"Can't pass --out or --upload if output_path is set in the "
"trace config");
@@ -611,7 +626,7 @@
// and activate triggers.
if (!trace_config_->activate_triggers().empty()) {
for (const auto& trigger : trace_config_->activate_triggers()) {
- triggers_to_activate.push_back(trigger);
+ triggers_to_activate_.push_back(trigger);
}
trace_config_.reset(new TraceConfig());
}
@@ -619,15 +634,15 @@
bool open_out_file = true;
if (!will_trace) {
open_out_file = false;
- if (!trace_out_path_.empty() || upload_flag) {
+ if (!trace_out_path_.empty() || upload_flag_) {
PERFETTO_ELOG("Can't pass an --out file (or --upload) with this option");
return 1;
}
- } else if (!triggers_to_activate.empty() ||
+ } else if (!triggers_to_activate_.empty() ||
(trace_config_->write_into_file() &&
!trace_config_->output_path().empty())) {
open_out_file = false;
- } else if (trace_out_path_.empty() && !upload_flag) {
+ } else if (trace_out_path_.empty() && !upload_flag_) {
PERFETTO_ELOG("Either --out or --upload is required");
return 1;
} else if (is_detach() && !trace_config_->write_into_file()) {
@@ -654,43 +669,6 @@
packet_writer_ = CreateFilePacketWriter(trace_out_stream_.get());
}
- if (background) {
- base::Daemonize();
- }
-
- // If we are just activating triggers then we don't need to rate limit,
- // connect as a consumer or run the trace. So bail out after processing all
- // the options.
- if (!triggers_to_activate.empty()) {
- LogUploadEvent(PerfettoStatsdAtom::kTriggerBegin);
- LogTriggerEvents(PerfettoTriggerAtom::kCmdTrigger, triggers_to_activate);
-
- bool finished_with_success = false;
- TriggerProducer producer(
- &task_runner_,
- [this, &finished_with_success](bool success) {
- finished_with_success = success;
- task_runner_.Quit();
- },
- &triggers_to_activate);
- task_runner_.Run();
- if (finished_with_success) {
- LogUploadEvent(PerfettoStatsdAtom::kTriggerSuccess);
- } else {
- LogUploadEvent(PerfettoStatsdAtom::kTriggerFailure);
- LogTriggerEvents(PerfettoTriggerAtom::kCmdTriggerFail,
- triggers_to_activate);
- }
- return finished_with_success ? 0 : 1;
- }
-
- if (query_service_ || bugreport_) {
- consumer_endpoint_ =
- ConsumerIPCClient::Connect(GetConsumerSocket(), this, &task_runner_);
- task_runner_.Run();
- return 1; // We can legitimately get here if the service disconnects.
- }
-
if (trace_config_->compression_type() ==
TraceConfig::COMPRESSION_TYPE_DEFLATE) {
if (packet_writer_) {
@@ -704,11 +682,59 @@
}
}
+ if (save_to_incidentd_ && !ignore_guardrails_ &&
+ (trace_config_->duration_ms() == 0 &&
+ trace_config_->trigger_config().trigger_timeout_ms() == 0)) {
+ PERFETTO_ELOG("Can't trace indefinitely when tracing to Incidentd.");
+ return 1;
+ }
+
+ if (background_) {
+ base::Daemonize();
+ }
+
+ return 0; // Continues in ConnectToServiceAndRun() below.
+}
+
+int PerfettoCmd::ConnectToServiceAndRun() {
+ // If we are just activating triggers then we don't need to rate limit,
+ // connect as a consumer or run the trace. So bail out after processing all
+ // the options.
+ if (!triggers_to_activate_.empty()) {
+ LogUploadEvent(PerfettoStatsdAtom::kTriggerBegin);
+ LogTriggerEvents(PerfettoTriggerAtom::kCmdTrigger, triggers_to_activate_);
+
+ bool finished_with_success = false;
+ TriggerProducer producer(
+ &task_runner_,
+ [this, &finished_with_success](bool success) {
+ finished_with_success = success;
+ task_runner_.Quit();
+ },
+ &triggers_to_activate_);
+ task_runner_.Run();
+ if (finished_with_success) {
+ LogUploadEvent(PerfettoStatsdAtom::kTriggerSuccess);
+ } else {
+ LogUploadEvent(PerfettoStatsdAtom::kTriggerFailure);
+ LogTriggerEvents(PerfettoTriggerAtom::kCmdTriggerFail,
+ triggers_to_activate_);
+ }
+ return finished_with_success ? 0 : 1;
+ } // if (triggers_to_activate_)
+
+ if (query_service_ || bugreport_) {
+ consumer_endpoint_ =
+ ConsumerIPCClient::Connect(GetConsumerSocket(), this, &task_runner_);
+ task_runner_.Run();
+ return 1; // We can legitimately get here if the service disconnects.
+ } // if (query_service || bugreport_)
+
RateLimiter::Args args{};
args.is_user_build = IsUserBuild();
args.is_uploading = save_to_incidentd_;
args.current_time = base::GetWallTimeS();
- args.ignore_guardrails = ignore_guardrails;
+ args.ignore_guardrails = ignore_guardrails_;
args.allow_user_build_tracing = trace_config_->allow_user_build_tracing();
args.unique_session_name = trace_config_->unique_session_name();
args.max_upload_bytes_override =
@@ -717,13 +743,6 @@
if (!args.unique_session_name.empty())
base::MaybeSetThreadName("p-" + args.unique_session_name);
- if (args.is_uploading && !args.ignore_guardrails &&
- (trace_config_->duration_ms() == 0 &&
- trace_config_->trigger_config().trigger_timeout_ms() == 0)) {
- PERFETTO_ELOG("Can't trace indefinitely when tracing to Dropbox.");
- return 1;
- }
-
expected_duration_ms_ = trace_config_->duration_ms();
if (!expected_duration_ms_) {
uint32_t timeout_ms = trace_config_->trigger_config().trigger_timeout_ms();
@@ -740,7 +759,7 @@
LogUploadEvent(PerfettoStatsdAtom::kBackgroundTraceBegin);
}
- auto err_atom = ConvertRateLimiterResponseToAtom(limiter.ShouldTrace(args));
+ auto err_atom = ConvertRateLimiterResponseToAtom(limiter_->ShouldTrace(args));
if (err_atom) {
// TODO(lalitm): remove this once we're ready on server side.
LogUploadEvent(PerfettoStatsdAtom::kHitGuardrails);
@@ -749,8 +768,8 @@
}
#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
- if (!background && !is_detach() && !upload_flag &&
- triggers_to_activate.empty() && !isatty(STDIN_FILENO) &&
+ if (!background_ && !is_detach() && !upload_flag_ &&
+ triggers_to_activate_.empty() && !isatty(STDIN_FILENO) &&
!isatty(STDERR_FILENO)) {
fprintf(stderr,
"Warning: No PTY. CTRL+C won't gracefully stop the trace. If you "
@@ -765,8 +784,9 @@
SetupCtrlCSignalHandler();
task_runner_.Run();
- return limiter.OnTraceDone(args, update_guardrail_state_, bytes_written_) ? 0
- : 1;
+ return limiter_->OnTraceDone(args, update_guardrail_state_, bytes_written_)
+ ? 0
+ : 1;
}
void PerfettoCmd::OnConnect() {
@@ -962,7 +982,7 @@
}
void PerfettoCmd::SetupCtrlCSignalHandler() {
- base::InstallCtrCHandler([] { g_consumer_cmd->SignalCtrlC(); });
+ base::InstallCtrCHandler([] { g_perfetto_cmd->SignalCtrlC(); });
task_runner_.AddFileDescriptorWatch(ctrl_c_evt_.fd(), [this] {
PERFETTO_LOG("SIGINT/SIGTERM received: disabling tracing.");
ctrl_c_evt_.Clear();
@@ -1071,8 +1091,11 @@
}
int PERFETTO_EXPORT_ENTRYPOINT PerfettoCmdMain(int argc, char** argv) {
- g_consumer_cmd = new perfetto::PerfettoCmd();
- return g_consumer_cmd->Main(argc, argv);
+ perfetto::PerfettoCmd cmd;
+ int res = cmd.ParseCmdlineAndMaybeDaemonize(argc, argv);
+ if (res)
+ return res;
+ return cmd.ConnectToServiceAndRun();
}
} // namespace perfetto
diff --git a/src/perfetto_cmd/perfetto_cmd.h b/src/perfetto_cmd/perfetto_cmd.h
index 215c3be..4087f49 100644
--- a/src/perfetto_cmd/perfetto_cmd.h
+++ b/src/perfetto_cmd/perfetto_cmd.h
@@ -36,6 +36,7 @@
namespace perfetto {
class PacketWriter;
+class RateLimiter;
// Directory for local state and temporary files. This is automatically
// created by the system by setting setprop persist.traced.enable=1.
@@ -43,7 +44,14 @@
class PerfettoCmd : public Consumer {
public:
- int Main(int argc, char** argv);
+ PerfettoCmd();
+ ~PerfettoCmd() override;
+
+ // The main() is split in two stages: cmdline parsing and actual interaction
+ // with traced. This is to allow tools like tracebox to avoid spawning the
+ // service for no reason if the cmdline parsing fails.
+ int ParseCmdlineAndMaybeDaemonize(int argc, char** argv);
+ int ConnectToServiceAndRun();
// perfetto::Consumer implementation.
void OnConnect() override;
@@ -86,13 +94,13 @@
base::UnixTaskRunner task_runner_;
+ std::unique_ptr<RateLimiter> limiter_;
std::unique_ptr<perfetto::TracingService::ConsumerEndpoint>
consumer_endpoint_;
std::unique_ptr<TraceConfig> trace_config_;
-
std::unique_ptr<PacketWriter> packet_writer_;
base::ScopedFstream trace_out_stream_;
-
+ std::vector<std::string> triggers_to_activate_;
std::string trace_out_path_;
base::EventFd ctrl_c_evt_;
bool save_to_incidentd_ = false;
@@ -106,6 +114,9 @@
bool query_service_ = false;
bool query_service_output_raw_ = false;
bool bugreport_ = false;
+ bool background_ = false;
+ bool ignore_guardrails_ = false;
+ bool upload_flag_ = false;
std::string uuid_;
// How long we expect to trace for or 0 if the trace is indefinite.
diff --git a/src/traced/probes/ftrace/ftrace_controller.cc b/src/traced/probes/ftrace/ftrace_controller.cc
index 53ac1e5..0193e49 100644
--- a/src/traced/probes/ftrace/ftrace_controller.cc
+++ b/src/traced/probes/ftrace/ftrace_controller.cc
@@ -33,6 +33,7 @@
#include "perfetto/base/time.h"
#include "perfetto/ext/base/file_utils.h"
#include "perfetto/ext/base/metatrace.h"
+#include "perfetto/ext/base/string_utils.h"
#include "perfetto/ext/tracing/core/trace_writer.h"
#include "src/kallsyms/kernel_symbol_map.h"
#include "src/kallsyms/lazy_kernel_symbolizer.h"
@@ -84,15 +85,17 @@
return drain_period_ms;
}
-void WriteToFile(const char* path, const char* str) {
+bool WriteToFile(const char* path, const char* str) {
auto fd = base::OpenFile(path, O_WRONLY);
if (!fd)
- return;
- base::ignore_result(base::WriteAll(*fd, str, strlen(str)));
+ return false;
+ const size_t str_len = strlen(str);
+ return base::WriteAll(*fd, str, str_len) == static_cast<ssize_t>(str_len);
}
-void ClearFile(const char* path) {
+bool ClearFile(const char* path) {
auto fd = base::OpenFile(path, O_WRONLY | O_TRUNC);
+ return !!fd;
}
} // namespace
@@ -100,18 +103,21 @@
// Method of last resort to reset ftrace state.
// We don't know what state the rest of the system and process is so as far
// as possible avoid allocations.
-void HardResetFtraceState() {
- PERFETTO_LOG("Hard resetting ftrace state.");
-
- WriteToFile("/sys/kernel/debug/tracing/tracing_on", "0");
- WriteToFile("/sys/kernel/debug/tracing/buffer_size_kb", "4");
- WriteToFile("/sys/kernel/debug/tracing/events/enable", "0");
- ClearFile("/sys/kernel/debug/tracing/trace");
-
- WriteToFile("/sys/kernel/tracing/tracing_on", "0");
- WriteToFile("/sys/kernel/tracing/buffer_size_kb", "4");
- WriteToFile("/sys/kernel/tracing/events/enable", "0");
- ClearFile("/sys/kernel/tracing/trace");
+bool HardResetFtraceState() {
+ for (const char* const* item = FtraceProcfs::kTracingPaths; *item; ++item) {
+ std::string prefix(*item);
+ PERFETTO_CHECK(base::EndsWith(prefix, "/"));
+ bool res = true;
+ res &= WriteToFile((prefix + "tracing_on").c_str(), "0");
+ res &= WriteToFile((prefix + "buffer_size_kb").c_str(), "4");
+ // We deliberately don't check for this as on some older versions of Android
+ // events/enable was not writable by the shell user.
+ WriteToFile((prefix + "events/enable").c_str(), "0");
+ res &= ClearFile((prefix + "trace").c_str());
+ if (res)
+ return true;
+ }
+ return false;
}
// static
diff --git a/src/traced/probes/ftrace/ftrace_controller.h b/src/traced/probes/ftrace/ftrace_controller.h
index 3e150a3..71250c8 100644
--- a/src/traced/probes/ftrace/ftrace_controller.h
+++ b/src/traced/probes/ftrace/ftrace_controller.h
@@ -45,7 +45,7 @@
struct FtraceStats;
// Method of last resort to reset ftrace state.
-void HardResetFtraceState();
+bool HardResetFtraceState();
// Utility class for controlling ftrace.
class FtraceController {
diff --git a/src/traced/probes/probes.cc b/src/traced/probes/probes.cc
index b02b6d5..3ec4da1 100644
--- a/src/traced/probes/probes.cc
+++ b/src/traced/probes/probes.cc
@@ -38,13 +38,16 @@
OPT_CLEANUP_AFTER_CRASH = 1000,
OPT_VERSION,
OPT_BACKGROUND,
+ OPT_RESET_FTRACE,
};
bool background = false;
+ bool reset_ftrace = false;
static const option long_options[] = {
{"background", no_argument, nullptr, OPT_BACKGROUND},
{"cleanup-after-crash", no_argument, nullptr, OPT_CLEANUP_AFTER_CRASH},
+ {"reset-ftrace", no_argument, nullptr, OPT_RESET_FTRACE},
{"version", no_argument, nullptr, OPT_VERSION},
{nullptr, 0, nullptr, 0}};
@@ -57,17 +60,33 @@
background = true;
break;
case OPT_CLEANUP_AFTER_CRASH:
+ // Used by perfetto.rc in Android.
+ PERFETTO_LOG("Hard resetting ftrace state.");
HardResetFtraceState();
return 0;
+ case OPT_RESET_FTRACE:
+ // This is like --cleanup-after-crash but doesn't quit.
+ reset_ftrace = true;
+ break;
case OPT_VERSION:
printf("%s\n", base::GetVersionString());
return 0;
default:
- PERFETTO_ELOG("Usage: %s [--background|--cleanup-after-crash|--version]", argv[0]);
+ fprintf(
+ stderr,
+ "Usage: %s [--background] [--reset-ftrace] [--cleanup-after-crash] "
+ "[--version]\n",
+ argv[0]);
return 1;
}
}
+ if (reset_ftrace && !HardResetFtraceState()) {
+ PERFETTO_ELOG(
+ "Failed to reset ftrace. Either run this as root or run "
+ "`sudo chown -R $USER /sys/kernel/tracing`");
+ }
+
if (background) {
base::Daemonize();
}
diff --git a/src/traced/service/service.cc b/src/traced/service/service.cc
index 6d9ba8b..1dbde02 100644
--- a/src/traced/service/service.cc
+++ b/src/traced/service/service.cc
@@ -75,7 +75,7 @@
#endif // defined(PERFETTO_SET_SOCKET_PERMISSIONS)
void PrintUsage(const char* prog_name) {
- PERFETTO_ELOG(R"(
+ fprintf(stderr, R"(
Usage: %s [option] ...
Options and arguments
--background : Exits immediately and continues running in the background
@@ -87,12 +87,14 @@
<prod_mode> is the mode bits (e.g. 0660) for chmod the produce socket,
<cons_group> is the group name for chgrp the consumer socket, and
<cons_mode> is the mode bits (e.g. 0660) for chmod the consumer socket.
-Example: %s --set-socket-permissions traced-producer:0660:traced-consumer:0660
+
+Example:
+ %s --set-socket-permissions traced-producer:0660:traced-consumer:0660
starts the service and sets the group ownership of the producer and consumer
sockets to "traced-producer" and "traced-consumer", respectively. Both
- producer and consumer sockets are chmod with 0660 (rw-rw----) mode bits.
+ producer and consumer sockets are chmod with 0660 (rw-rw----) mode bits.
)",
- prog_name, prog_name);
+ prog_name, prog_name);
}
} // namespace