Merge "Support writing untyped annotations into typed contexts."
diff --git a/Android.bp b/Android.bp
index de0d6e9..70e2297 100644
--- a/Android.bp
+++ b/Android.bp
@@ -83,6 +83,7 @@
":perfetto_src_profiling_common_profiler_guardrails",
":perfetto_src_profiling_common_unwind_support",
":perfetto_src_profiling_memory_daemon",
+ ":perfetto_src_profiling_memory_heapprofd_main",
":perfetto_src_profiling_memory_ring_buffer",
":perfetto_src_profiling_memory_scoped_spinlock",
":perfetto_src_profiling_memory_wire_protocol",
@@ -1760,6 +1761,7 @@
":perfetto_src_trace_processor_tables_tables",
":perfetto_src_trace_processor_types_types",
":perfetto_src_trace_processor_util_descriptors",
+ ":perfetto_src_trace_processor_util_gzip",
":perfetto_src_trace_processor_util_interned_message_view",
":perfetto_src_trace_processor_util_proto_to_args_parser",
":perfetto_src_trace_processor_util_protozero_to_text",
@@ -7287,6 +7289,14 @@
],
}
+// GN: //src/profiling/memory:heapprofd_main
+filegroup {
+ name: "perfetto_src_profiling_memory_heapprofd_main",
+ srcs: [
+ "src/profiling/memory/heapprofd.cc",
+ ],
+}
+
// GN: //src/profiling/memory:malloc_interceptor_bionic_hooks
filegroup {
name: "perfetto_src_profiling_memory_malloc_interceptor_bionic_hooks",
@@ -8152,7 +8162,6 @@
"src/trace_processor/forwarding_trace_parser.cc",
"src/trace_processor/importers/default_modules.cc",
"src/trace_processor/importers/ftrace/ftrace_module.cc",
- "src/trace_processor/importers/gzip/gzip_utils.cc",
"src/trace_processor/importers/json/json_utils.cc",
"src/trace_processor/importers/ninja/ninja_log_parser.cc",
"src/trace_processor/importers/proto/async_track_set_tracker.cc",
@@ -8261,6 +8270,14 @@
],
}
+// GN: //src/trace_processor/util:gzip
+filegroup {
+ name: "perfetto_src_trace_processor_util_gzip",
+ srcs: [
+ "src/trace_processor/util/gzip_utils.cc",
+ ],
+}
+
// GN: //src/trace_processor/util:interned_message_view
filegroup {
name: "perfetto_src_trace_processor_util_interned_message_view",
@@ -9299,6 +9316,7 @@
":perfetto_src_trace_processor_types_unittests",
":perfetto_src_trace_processor_unittests",
":perfetto_src_trace_processor_util_descriptors",
+ ":perfetto_src_trace_processor_util_gzip",
":perfetto_src_trace_processor_util_interned_message_view",
":perfetto_src_trace_processor_util_proto_to_args_parser",
":perfetto_src_trace_processor_util_protozero_to_text",
@@ -9589,6 +9607,7 @@
":perfetto_src_trace_processor_tables_tables",
":perfetto_src_trace_processor_types_types",
":perfetto_src_trace_processor_util_descriptors",
+ ":perfetto_src_trace_processor_util_gzip",
":perfetto_src_trace_processor_util_interned_message_view",
":perfetto_src_trace_processor_util_proto_to_args_parser",
":perfetto_src_trace_processor_util_protozero_to_text",
@@ -9740,6 +9759,7 @@
":perfetto_src_trace_processor_tables_tables",
":perfetto_src_trace_processor_types_types",
":perfetto_src_trace_processor_util_descriptors",
+ ":perfetto_src_trace_processor_util_gzip",
":perfetto_src_trace_processor_util_interned_message_view",
":perfetto_src_trace_processor_util_proto_to_args_parser",
":perfetto_src_trace_processor_util_protozero_to_text",
diff --git a/BUILD b/BUILD
index 45b742f..23d5d19 100644
--- a/BUILD
+++ b/BUILD
@@ -1148,6 +1148,15 @@
],
)
+# GN target: //src/trace_processor/util:gzip
+filegroup(
+ name = "src_trace_processor_util_gzip",
+ srcs = [
+ "src/trace_processor/util/gzip_utils.cc",
+ "src/trace_processor/util/gzip_utils.h",
+ ],
+)
+
# GN target: //src/trace_processor/util:interned_message_view
filegroup(
name = "src_trace_processor_util_interned_message_view",
@@ -1337,8 +1346,6 @@
"src/trace_processor/importers/ftrace/ftrace_module.h",
"src/trace_processor/importers/fuchsia/fuchsia_record.h",
"src/trace_processor/importers/fuchsia/fuchsia_trace_utils.h",
- "src/trace_processor/importers/gzip/gzip_utils.cc",
- "src/trace_processor/importers/gzip/gzip_utils.h",
"src/trace_processor/importers/json/json_utils.cc",
"src/trace_processor/importers/json/json_utils.h",
"src/trace_processor/importers/ninja/ninja_log_parser.cc",
@@ -3572,6 +3579,7 @@
":src_trace_processor_tables_tables",
":src_trace_processor_types_types",
":src_trace_processor_util_descriptors",
+ ":src_trace_processor_util_gzip",
":src_trace_processor_util_interned_message_view",
":src_trace_processor_util_proto_to_args_parser",
":src_trace_processor_util_protozero_to_text",
@@ -3673,6 +3681,7 @@
":src_trace_processor_tables_tables",
":src_trace_processor_types_types",
":src_trace_processor_util_descriptors",
+ ":src_trace_processor_util_gzip",
":src_trace_processor_util_interned_message_view",
":src_trace_processor_util_proto_to_args_parser",
":src_trace_processor_util_protozero_to_text",
@@ -3854,6 +3863,7 @@
":src_trace_processor_tables_tables",
":src_trace_processor_types_types",
":src_trace_processor_util_descriptors",
+ ":src_trace_processor_util_gzip",
":src_trace_processor_util_interned_message_view",
":src_trace_processor_util_proto_to_args_parser",
":src_trace_processor_util_protozero_to_text",
diff --git a/BUILD.gn b/BUILD.gn
index 6f6cc7d..0fab609 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -124,6 +124,7 @@
# compile-time checks for the CI.
if (perfetto_build_standalone) {
all_targets += [
+ "src/tracebox",
"test/configs",
# For syntax-checking the proto.
diff --git a/gn/BUILD.gn b/gn/BUILD.gn
index 1f799ad..7fc0cdd 100644
--- a/gn/BUILD.gn
+++ b/gn/BUILD.gn
@@ -79,6 +79,8 @@
"PERFETTO_TP_JSON=$enable_perfetto_trace_processor_json",
"PERFETTO_LOCAL_SYMBOLIZER=$perfetto_local_symbolizer",
"PERFETTO_ZLIB=$enable_perfetto_zlib",
+ "PERFETTO_TRACED_PERF=$enable_perfetto_traced_perf",
+ "PERFETTO_HEAPPROFD=$enable_perfetto_heapprofd",
]
rel_out_path = rebase_path(gen_header_path, "$root_build_dir")
diff --git a/include/perfetto/base/build_configs/android_tree/perfetto_build_flags.h b/include/perfetto/base/build_configs/android_tree/perfetto_build_flags.h
index f0179c6..d68f557 100644
--- a/include/perfetto/base/build_configs/android_tree/perfetto_build_flags.h
+++ b/include/perfetto/base/build_configs/android_tree/perfetto_build_flags.h
@@ -38,6 +38,8 @@
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_JSON() (0)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_LOCAL_SYMBOLIZER() (PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_LINUX() || PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MAC() ||PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WIN())
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_ZLIB() (1)
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TRACED_PERF() (1)
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_HEAPPROFD() (1)
// clang-format on
#endif // GEN_BUILD_CONFIG_PERFETTO_BUILD_FLAGS_H_
diff --git a/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h b/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h
index 6745b3f..dad653c 100644
--- a/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h
+++ b/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h
@@ -38,6 +38,8 @@
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_JSON() (1)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_LOCAL_SYMBOLIZER() (PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_LINUX() || PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MAC() ||PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WIN())
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_ZLIB() (1)
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TRACED_PERF() (0)
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_HEAPPROFD() (0)
// clang-format on
#endif // GEN_BUILD_CONFIG_PERFETTO_BUILD_FLAGS_H_
diff --git a/include/perfetto/ext/base/subprocess.h b/include/perfetto/ext/base/subprocess.h
index 77b2dfd..80adbdc 100644
--- a/include/perfetto/ext/base/subprocess.h
+++ b/include/perfetto/ext/base/subprocess.h
@@ -30,6 +30,7 @@
#include "perfetto/base/platform_handle.h"
#include "perfetto/base/proc_utils.h"
#include "perfetto/ext/base/event_fd.h"
+#include "perfetto/ext/base/optional.h"
#include "perfetto/ext/base/pipe.h"
#include "perfetto/ext/base/scoped_file.h"
@@ -133,6 +134,13 @@
// just before the exec() call, but after having closed all fds % stdin/o/e.
// This is for synchronization barriers in tests.
std::function<void()> posix_entrypoint_for_testing;
+
+ // When set, will will move the process to the given process group. If set
+ // and zero, it will create a new process group. Effectively this calls
+ // setpgid(0 /*self_pid*/, posix_proc_group_id).
+ // This can be used to avoid that subprocesses receive CTRL-C from the
+ // terminal, while still living in the same session.
+ base::Optional<pid_t> posix_proc_group_id{};
#endif
// If non-empty, replaces the environment passed to exec().
diff --git a/include/perfetto/ext/base/utils.h b/include/perfetto/ext/base/utils.h
index af39b37..729225f 100644
--- a/include/perfetto/ext/base/utils.h
+++ b/include/perfetto/ext/base/utils.h
@@ -124,6 +124,13 @@
// Child: redirects stdio onto /dev/null and chdirs into .
void Daemonize();
+// Returns the path of the current executable, e.g. /foo/bar/exe.
+std::string GetCurExecutablePath();
+
+// Returns the directory where the current executable lives in, e.g. /foo/bar.
+// This is independent of cwd().
+std::string GetCurExecutableDir();
+
} // namespace base
} // namespace perfetto
diff --git a/include/perfetto/ext/tracing/core/tracing_service.h b/include/perfetto/ext/tracing/core/tracing_service.h
index 20382a1..c26d949 100644
--- a/include/perfetto/ext/tracing/core/tracing_service.h
+++ b/include/perfetto/ext/tracing/core/tracing_service.h
@@ -326,7 +326,8 @@
ProducerSMBScrapingMode smb_scraping_mode =
ProducerSMBScrapingMode::kDefault,
size_t shared_memory_page_size_hint_bytes = 0,
- std::unique_ptr<SharedMemory> shm = nullptr) = 0;
+ std::unique_ptr<SharedMemory> shm = nullptr,
+ const std::string& sdk_version = {}) = 0;
// Connects a Consumer instance and obtains a ConsumerEndpoint, which is
// essentially a 1:1 channel between one Consumer and the Service.
diff --git a/protos/perfetto/common/tracing_service_state.proto b/protos/perfetto/common/tracing_service_state.proto
index 119cea8..1ef4c56 100644
--- a/protos/perfetto/common/tracing_service_state.proto
+++ b/protos/perfetto/common/tracing_service_state.proto
@@ -34,6 +34,12 @@
// Unix uid of the remote process.
optional int32 uid = 3;
+
+ // The version of the client library used by the producer.
+ // This is a human readable string with and its format varies depending on
+ // the build system and the repo (standalone vs AOSP).
+ // This is intended for human debugging only.
+ optional string sdk_version = 4;
}
// Describes a data source registered by a producer. Data sources are listed
@@ -57,4 +63,10 @@
// Number of tracing sessions in the started state. Always <= num_sessions.
optional int32 num_sessions_started = 4;
+
+ // The version of traced (the same returned by `traced --version`).
+ // This is a human readable string with and its format varies depending on
+ // the build system and the repo (standalone vs AOSP).
+ // This is intended for human debugging only.
+ optional string tracing_service_version = 5;
}
diff --git a/protos/perfetto/config/perfetto_config.proto b/protos/perfetto/config/perfetto_config.proto
index 4c6e882..c8b55e3 100644
--- a/protos/perfetto/config/perfetto_config.proto
+++ b/protos/perfetto/config/perfetto_config.proto
@@ -197,6 +197,12 @@
// Unix uid of the remote process.
optional int32 uid = 3;
+
+ // The version of the client library used by the producer.
+ // This is a human readable string with and its format varies depending on
+ // the build system and the repo (standalone vs AOSP).
+ // This is intended for human debugging only.
+ optional string sdk_version = 4;
}
// Describes a data source registered by a producer. Data sources are listed
@@ -220,6 +226,12 @@
// Number of tracing sessions in the started state. Always <= num_sessions.
optional int32 num_sessions_started = 4;
+
+ // The version of traced (the same returned by `traced --version`).
+ // This is a human readable string with and its format varies depending on
+ // the build system and the repo (standalone vs AOSP).
+ // This is intended for human debugging only.
+ optional string tracing_service_version = 5;
}
// End of protos/perfetto/common/tracing_service_state.proto
diff --git a/protos/perfetto/ipc/producer_port.proto b/protos/perfetto/ipc/producer_port.proto
index ac1b015..9be72ea 100644
--- a/protos/perfetto/ipc/producer_port.proto
+++ b/protos/perfetto/ipc/producer_port.proto
@@ -153,6 +153,16 @@
// |using_shmem_provided_by_producer| in InitializeConnectionResponse.
optional bool producer_provided_shmem = 6;
+ // ---------------------------------------------------
+ // All fields below have been introduced in Android S.
+ // ---------------------------------------------------
+
+ // The version of the client library used by the producer.
+ // This is a human readable string with and its format varies depending on
+ // the build system that is used to build the code and the repo (standalone
+ // vs AOSP). This is intended for human debugging only.
+ optional string sdk_version = 8;
+
// On Windows, when producer_provided_shmem = true, the client creates a named
// SHM region and passes the name (an unguessable token) back to the service.
// Introduced in v13.
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index 043b682..4ee4df6 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -197,6 +197,12 @@
// Unix uid of the remote process.
optional int32 uid = 3;
+
+ // The version of the client library used by the producer.
+ // This is a human readable string with and its format varies depending on
+ // the build system and the repo (standalone vs AOSP).
+ // This is intended for human debugging only.
+ optional string sdk_version = 4;
}
// Describes a data source registered by a producer. Data sources are listed
@@ -220,6 +226,12 @@
// Number of tracing sessions in the started state. Always <= num_sessions.
optional int32 num_sessions_started = 4;
+
+ // The version of traced (the same returned by `traced --version`).
+ // This is a human readable string with and its format varies depending on
+ // the build system and the repo (standalone vs AOSP).
+ // This is intended for human debugging only.
+ optional string tracing_service_version = 5;
}
// End of protos/perfetto/common/tracing_service_state.proto
@@ -8326,6 +8338,12 @@
// Ticks per second - sysconf(_SC_CLK_TCK).
optional int64 hz = 3;
+
+ // The version of traced (the same returned by `traced --version`).
+ // This is a human readable string with and its format varies depending on
+ // the build system and the repo (standalone vs AOSP).
+ // This is intended for human debugging only.
+ optional string tracing_service_version = 4;
}
// End of protos/perfetto/trace/system_info.proto
diff --git a/protos/perfetto/trace/system_info.proto b/protos/perfetto/trace/system_info.proto
index 90a7a57..ddce4ca 100644
--- a/protos/perfetto/trace/system_info.proto
+++ b/protos/perfetto/trace/system_info.proto
@@ -31,4 +31,10 @@
// Ticks per second - sysconf(_SC_CLK_TCK).
optional int64 hz = 3;
+
+ // The version of traced (the same returned by `traced --version`).
+ // This is a human readable string with and its format varies depending on
+ // the build system and the repo (standalone vs AOSP).
+ // This is intended for human debugging only.
+ optional string tracing_service_version = 4;
}
diff --git a/protos/third_party/chromium/chrome_track_event.proto b/protos/third_party/chromium/chrome_track_event.proto
index 813b174..54e11ce 100644
--- a/protos/third_party/chromium/chrome_track_event.proto
+++ b/protos/third_party/chromium/chrome_track_event.proto
@@ -28,6 +28,9 @@
message ChromeTaskAnnotator {
optional uint32 ipc_hash = 1;
+ // The delay in microseconds that was specified, if any, when this task was
+ // posted. This is only valid for delayed tasks.
+ optional uint64 task_delay_us = 2;
}
message ChromeBrowserContext {
@@ -132,9 +135,16 @@
optional string mark = 4;
}
+// These IDs are generated at compile time and differ for each chrome version.
+// IDs are stable on for a given chrome version but are changing when resources
+// are added or removed to chrome.
+message ResourceBundle {
+ optional uint32 resource_id = 1;
+}
+
message ChromeTrackEvent {
// Extension range for Chrome: 1000-1999
- // Next ID: 1012
+ // Next ID: 1017
extend TrackEvent {
optional ChromeAppState chrome_app_state = 1000;
@@ -162,5 +172,9 @@
optional FrameTreeNodeInfo frame_tree_node_info = 1010;
optional ChromeHashedPerformanceMark chrome_hashed_performance_mark = 1011;
+
+ // reserved 1012 to 1015.
+
+ optional ResourceBundle resource_bundle = 1016;
}
}
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..c6fe500 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();
@@ -1037,6 +1057,7 @@
printf(" id: %d\n", producer.id());
printf(" name: \"%s\" \n", producer.name().c_str());
printf(" uid: %d \n", producer.uid());
+ printf(" sdk_version: \"%s\" \n", producer.sdk_version().c_str());
printf("}\n");
}
@@ -1048,6 +1069,8 @@
printf(" }\n");
printf("}\n");
}
+ printf("tracing_service_version: \"%s\"\n",
+ svc_state.tracing_service_version().c_str());
printf("num_sessions: %d\n", svc_state.num_sessions());
printf("num_sessions_started: %d\n", svc_state.num_sessions_started());
}
@@ -1071,8 +1094,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/profiling/memory/BUILD.gn b/src/profiling/memory/BUILD.gn
index f0e350f..804f70e 100644
--- a/src/profiling/memory/BUILD.gn
+++ b/src/profiling/memory/BUILD.gn
@@ -21,6 +21,14 @@
# The Android heap profiling daemon.
executable("heapprofd") {
deps = [
+ ":heapprofd_main",
+ "../../../gn:default_deps",
+ ]
+ sources = [ "main.cc" ]
+}
+
+source_set("heapprofd_main") {
+ deps = [
"../../../gn:default_deps",
"../../../protos/perfetto/trace:zero",
"../../../src/base",
@@ -29,7 +37,10 @@
"../../../src/profiling/memory:wire_protocol",
"../../../src/tracing/ipc/producer",
]
- sources = [ "main.cc" ]
+ sources = [
+ "heapprofd.cc",
+ "heapprofd.h",
+ ]
}
# This library gets loaded into (and executes in) arbitrary android processes.
diff --git a/src/profiling/memory/heapprofd.cc b/src/profiling/memory/heapprofd.cc
new file mode 100644
index 0000000..d3a8cae
--- /dev/null
+++ b/src/profiling/memory/heapprofd.cc
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/profiling/memory/heapprofd.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <array>
+#include <memory>
+#include <vector>
+
+#include <signal.h>
+
+#include "perfetto/ext/base/event_fd.h"
+#include "perfetto/ext/base/getopt.h"
+#include "perfetto/ext/base/scoped_file.h"
+#include "perfetto/ext/base/unix_socket.h"
+#include "perfetto/ext/base/watchdog.h"
+#include "perfetto/ext/tracing/ipc/default_socket.h"
+#include "src/profiling/memory/heapprofd_producer.h"
+#include "src/profiling/memory/java_hprof_producer.h"
+#include "src/profiling/memory/wire_protocol.h"
+
+#include "perfetto/ext/base/unix_task_runner.h"
+
+// TODO(rsavitski): the task runner watchdog spawns a thread (normally for
+// tracking cpu/mem usage) that we don't strictly need.
+
+namespace perfetto {
+namespace profiling {
+namespace {
+
+int StartCentralHeapprofd();
+
+int GetListeningSocket() {
+ const char* sock_fd = getenv(kHeapprofdSocketEnvVar);
+ if (sock_fd == nullptr)
+ PERFETTO_FATAL("Did not inherit socket from init.");
+ char* end;
+ int raw_fd = static_cast<int>(strtol(sock_fd, &end, 10));
+ if (*end != '\0')
+ PERFETTO_FATAL("Invalid %s. Expected decimal integer.",
+ kHeapprofdSocketEnvVar);
+ return raw_fd;
+}
+
+base::EventFd* g_dump_evt = nullptr;
+
+int StartCentralHeapprofd() {
+ // We set this up before launching any threads, so we do not have to use a
+ // std::atomic for g_dump_evt.
+ g_dump_evt = new base::EventFd();
+
+ base::UnixTaskRunner task_runner;
+ base::Watchdog::GetInstance()->Start(); // crash on exceedingly long tasks
+ HeapprofdProducer producer(HeapprofdMode::kCentral, &task_runner,
+ /* exit_when_done= */ false);
+
+ int listening_raw_socket = GetListeningSocket();
+ auto listening_socket = base::UnixSocket::Listen(
+ base::ScopedFile(listening_raw_socket), &producer.socket_delegate(),
+ &task_runner, base::SockFamily::kUnix, base::SockType::kStream);
+
+ struct sigaction action = {};
+ action.sa_handler = [](int) { g_dump_evt->Notify(); };
+ // Allow to trigger a full dump by sending SIGUSR1 to heapprofd.
+ // This will allow manually deciding when to dump on userdebug.
+ PERFETTO_CHECK(sigaction(SIGUSR1, &action, nullptr) == 0);
+ task_runner.AddFileDescriptorWatch(g_dump_evt->fd(), [&producer] {
+ g_dump_evt->Clear();
+ producer.DumpAll();
+ });
+ producer.ConnectWithRetries(GetProducerSocket());
+ // TODO(fmayer): Create one producer that manages both heapprofd and Java
+ // producers, so we do not have two connections to traced.
+ JavaHprofProducer java_producer(&task_runner);
+ java_producer.ConnectWithRetries(GetProducerSocket());
+ task_runner.Run();
+ return 0;
+}
+
+} // namespace
+
+int HeapprofdMain(int argc, char** argv) {
+ bool cleanup_crash = false;
+
+ enum { kCleanupCrash = 256, kTargetPid, kTargetCmd, kInheritFd };
+ static option long_options[] = {
+ {"cleanup-after-crash", no_argument, nullptr, kCleanupCrash},
+ {nullptr, 0, nullptr, 0}};
+ int c;
+ while ((c = getopt_long(argc, argv, "", long_options, nullptr)) != -1) {
+ switch (c) {
+ case kCleanupCrash:
+ cleanup_crash = true;
+ break;
+ }
+ }
+
+ if (cleanup_crash) {
+ PERFETTO_LOG(
+ "Recovering from crash: unsetting heapprofd system properties. "
+ "Expect SELinux denials for unrelated properties.");
+ SystemProperties::ResetHeapprofdProperties();
+ PERFETTO_LOG(
+ "Finished unsetting heapprofd system properties. "
+ "SELinux denials about properties are unexpected after "
+ "this point.");
+ return 0;
+ }
+
+ // start as a central daemon.
+ return StartCentralHeapprofd();
+}
+
+} // namespace profiling
+} // namespace perfetto
diff --git a/src/profiling/memory/heapprofd.h b/src/profiling/memory/heapprofd.h
new file mode 100644
index 0000000..e405585
--- /dev/null
+++ b/src/profiling/memory/heapprofd.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_PROFILING_MEMORY_HEAPPROFD_H_
+#define SRC_PROFILING_MEMORY_HEAPPROFD_H_
+
+namespace perfetto {
+namespace profiling {
+
+int HeapprofdMain(int argc, char** argv);
+
+} // namespace profiling
+} // namespace perfetto
+
+#endif // SRC_PROFILING_MEMORY_HEAPPROFD_H_
diff --git a/src/profiling/memory/main.cc b/src/profiling/memory/main.cc
index f0bb47b..078ca86 100644
--- a/src/profiling/memory/main.cc
+++ b/src/profiling/memory/main.cc
@@ -14,117 +14,7 @@
* limitations under the License.
*/
-#include <stdlib.h>
-#include <unistd.h>
-#include <array>
-#include <memory>
-#include <vector>
-
-#include <signal.h>
-
-#include "perfetto/ext/base/event_fd.h"
-#include "perfetto/ext/base/getopt.h"
-#include "perfetto/ext/base/scoped_file.h"
-#include "perfetto/ext/base/unix_socket.h"
-#include "perfetto/ext/base/watchdog.h"
-#include "perfetto/ext/tracing/ipc/default_socket.h"
-#include "src/profiling/memory/heapprofd_producer.h"
-#include "src/profiling/memory/java_hprof_producer.h"
-#include "src/profiling/memory/wire_protocol.h"
-
-#include "perfetto/ext/base/unix_task_runner.h"
-
-// TODO(rsavitski): the task runner watchdog spawns a thread (normally for
-// tracking cpu/mem usage) that we don't strictly need.
-
-namespace perfetto {
-namespace profiling {
-namespace {
-
-int StartCentralHeapprofd();
-
-int GetListeningSocket() {
- const char* sock_fd = getenv(kHeapprofdSocketEnvVar);
- if (sock_fd == nullptr)
- PERFETTO_FATAL("Did not inherit socket from init.");
- char* end;
- int raw_fd = static_cast<int>(strtol(sock_fd, &end, 10));
- if (*end != '\0')
- PERFETTO_FATAL("Invalid %s. Expected decimal integer.",
- kHeapprofdSocketEnvVar);
- return raw_fd;
-}
-
-base::EventFd* g_dump_evt = nullptr;
-
-int HeapprofdMain(int argc, char** argv) {
- bool cleanup_crash = false;
-
- enum { kCleanupCrash = 256, kTargetPid, kTargetCmd, kInheritFd };
- static option long_options[] = {
- {"cleanup-after-crash", no_argument, nullptr, kCleanupCrash},
- {nullptr, 0, nullptr, 0}};
- int c;
- while ((c = getopt_long(argc, argv, "", long_options, nullptr)) != -1) {
- switch (c) {
- case kCleanupCrash:
- cleanup_crash = true;
- break;
- }
- }
-
- if (cleanup_crash) {
- PERFETTO_LOG(
- "Recovering from crash: unsetting heapprofd system properties. "
- "Expect SELinux denials for unrelated properties.");
- SystemProperties::ResetHeapprofdProperties();
- PERFETTO_LOG(
- "Finished unsetting heapprofd system properties. "
- "SELinux denials about properties are unexpected after "
- "this point.");
- return 0;
- }
-
- // start as a central daemon.
- return StartCentralHeapprofd();
-}
-
-int StartCentralHeapprofd() {
- // We set this up before launching any threads, so we do not have to use a
- // std::atomic for g_dump_evt.
- g_dump_evt = new base::EventFd();
-
- base::UnixTaskRunner task_runner;
- base::Watchdog::GetInstance()->Start(); // crash on exceedingly long tasks
- HeapprofdProducer producer(HeapprofdMode::kCentral, &task_runner,
- /* exit_when_done= */ false);
-
- int listening_raw_socket = GetListeningSocket();
- auto listening_socket = base::UnixSocket::Listen(
- base::ScopedFile(listening_raw_socket), &producer.socket_delegate(),
- &task_runner, base::SockFamily::kUnix, base::SockType::kStream);
-
- struct sigaction action = {};
- action.sa_handler = [](int) { g_dump_evt->Notify(); };
- // Allow to trigger a full dump by sending SIGUSR1 to heapprofd.
- // This will allow manually deciding when to dump on userdebug.
- PERFETTO_CHECK(sigaction(SIGUSR1, &action, nullptr) == 0);
- task_runner.AddFileDescriptorWatch(g_dump_evt->fd(), [&producer] {
- g_dump_evt->Clear();
- producer.DumpAll();
- });
- producer.ConnectWithRetries(GetProducerSocket());
- // TODO(fmayer): Create one producer that manages both heapprofd and Java
- // producers, so we do not have two connections to traced.
- JavaHprofProducer java_producer(&task_runner);
- java_producer.ConnectWithRetries(GetProducerSocket());
- task_runner.Run();
- return 0;
-}
-
-} // namespace
-} // namespace profiling
-} // namespace perfetto
+#include "src/profiling/memory/heapprofd.h"
int main(int argc, char** argv) {
return perfetto::profiling::HeapprofdMain(argc, argv);
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index f07f3fb..f2358ac 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -76,8 +76,6 @@
"importers/ftrace/ftrace_module.h",
"importers/fuchsia/fuchsia_record.h",
"importers/fuchsia/fuchsia_trace_utils.h",
- "importers/gzip/gzip_utils.cc",
- "importers/gzip/gzip_utils.h",
"importers/json/json_utils.cc",
"importers/json/json_utils.h",
"importers/ninja/ninja_log_parser.cc",
@@ -154,6 +152,7 @@
"types",
"util",
"util:descriptors",
+ "util:gzip",
"util:interned_message_view",
"util:proto_to_args_parser",
]
@@ -180,11 +179,6 @@
if (enable_perfetto_trace_processor_json) {
deps += [ "../../gn:jsoncpp" ]
}
-
- # gzip_utils optionally depends on zlib.
- if (enable_perfetto_zlib) {
- deps += [ "../../gn:zlib" ]
- }
}
source_set("storage_full") {
@@ -272,6 +266,7 @@
"tables",
"types",
"util",
+ "util:gzip",
]
if (enable_perfetto_trace_processor_json) {
deps += [ "../../gn:jsoncpp" ]
@@ -344,6 +339,7 @@
"tables",
"types",
"util",
+ "util:gzip",
"util:protozero_to_text",
]
public_deps = [
diff --git a/src/trace_processor/importers/gzip/gzip_trace_parser.cc b/src/trace_processor/importers/gzip/gzip_trace_parser.cc
index bc42db3..cf8416e 100644
--- a/src/trace_processor/importers/gzip/gzip_trace_parser.cc
+++ b/src/trace_processor/importers/gzip/gzip_trace_parser.cc
@@ -22,6 +22,7 @@
#include "perfetto/ext/base/string_utils.h"
#include "perfetto/ext/base/string_view.h"
#include "src/trace_processor/forwarding_trace_parser.h"
+#include "src/trace_processor/util/gzip_utils.h"
#include "src/trace_processor/util/status_macros.h"
namespace perfetto {
@@ -29,7 +30,7 @@
namespace {
-using ResultCode = GzipDecompressor::ResultCode;
+using ResultCode = util::GzipDecompressor::ResultCode;
} // namespace
diff --git a/src/trace_processor/importers/gzip/gzip_trace_parser.h b/src/trace_processor/importers/gzip/gzip_trace_parser.h
index 82b4cd9..051284a 100644
--- a/src/trace_processor/importers/gzip/gzip_trace_parser.h
+++ b/src/trace_processor/importers/gzip/gzip_trace_parser.h
@@ -18,7 +18,7 @@
#define SRC_TRACE_PROCESSOR_IMPORTERS_GZIP_GZIP_TRACE_PARSER_H_
#include "src/trace_processor/importers/common/chunked_trace_reader.h"
-#include "src/trace_processor/importers/gzip/gzip_utils.h"
+#include "src/trace_processor/util/gzip_utils.h"
namespace perfetto {
namespace trace_processor {
@@ -41,7 +41,7 @@
private:
TraceProcessorContext* const context_;
- GzipDecompressor decompressor_;
+ util::GzipDecompressor decompressor_;
std::unique_ptr<ChunkedTraceReader> inner_;
std::unique_ptr<uint8_t[]> buffer_;
diff --git a/src/trace_processor/importers/proto/proto_trace_reader.cc b/src/trace_processor/importers/proto/proto_trace_reader.cc
index ee03db3..be5f1ff 100644
--- a/src/trace_processor/importers/proto/proto_trace_reader.cc
+++ b/src/trace_processor/importers/proto/proto_trace_reader.cc
@@ -30,7 +30,6 @@
#include "src/trace_processor/importers/common/event_tracker.h"
#include "src/trace_processor/importers/common/track_tracker.h"
#include "src/trace_processor/importers/ftrace/ftrace_module.h"
-#include "src/trace_processor/importers/gzip/gzip_utils.h"
#include "src/trace_processor/importers/proto/metadata_tracker.h"
#include "src/trace_processor/importers/proto/packet_sequence_state.h"
#include "src/trace_processor/importers/proto/proto_incremental_state.h"
@@ -38,6 +37,7 @@
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/trace_sorter.h"
#include "src/trace_processor/util/descriptors.h"
+#include "src/trace_processor/util/gzip_utils.h"
#include "protos/perfetto/common/builtin_clock.pbzero.h"
#include "protos/perfetto/config/trace_config.pbzero.h"
diff --git a/src/trace_processor/importers/proto/proto_trace_tokenizer.cc b/src/trace_processor/importers/proto/proto_trace_tokenizer.cc
index fdc7bcd..9bf23df 100644
--- a/src/trace_processor/importers/proto/proto_trace_tokenizer.cc
+++ b/src/trace_processor/importers/proto/proto_trace_tokenizer.cc
@@ -25,7 +25,7 @@
util::Status ProtoTraceTokenizer::Decompress(TraceBlobView input,
TraceBlobView* output) {
- PERFETTO_DCHECK(gzip::IsGzipSupported());
+ PERFETTO_DCHECK(util::IsGzipSupported());
uint8_t out[4096];
@@ -36,7 +36,7 @@
decompressor_.Reset();
decompressor_.SetInput(input.data(), input.length());
- using ResultCode = GzipDecompressor::ResultCode;
+ using ResultCode = util::GzipDecompressor::ResultCode;
for (auto ret = ResultCode::kOk; ret != ResultCode::kEof;) {
auto res = decompressor_.Decompress(out, base::ArraySize(out));
ret = res.ret;
diff --git a/src/trace_processor/importers/proto/proto_trace_tokenizer.h b/src/trace_processor/importers/proto/proto_trace_tokenizer.h
index 484c674..350cb34 100644
--- a/src/trace_processor/importers/proto/proto_trace_tokenizer.h
+++ b/src/trace_processor/importers/proto/proto_trace_tokenizer.h
@@ -21,7 +21,7 @@
#include "perfetto/protozero/proto_utils.h"
#include "perfetto/trace_processor/status.h"
-#include "src/trace_processor/importers/gzip/gzip_utils.h"
+#include "src/trace_processor/util/gzip_utils.h"
#include "src/trace_processor/util/status_macros.h"
#include "src/trace_processor/util/trace_blob_view.h"
@@ -143,7 +143,7 @@
protos::pbzero::TracePacket::Decoder decoder(packet.data(),
packet.length());
if (decoder.has_compressed_packets()) {
- if (!gzip::IsGzipSupported()) {
+ if (!util::IsGzipSupported()) {
return util::Status(
"Cannot decode compressed packets. Zlib not enabled");
}
@@ -185,7 +185,7 @@
std::vector<uint8_t> partial_buf_;
// Allows support for compressed trace packets.
- GzipDecompressor decompressor_;
+ util::GzipDecompressor decompressor_;
};
} // namespace trace_processor
diff --git a/src/trace_processor/read_trace.cc b/src/trace_processor/read_trace.cc
index 8f5b6f0..4a9d8fd 100644
--- a/src/trace_processor/read_trace.cc
+++ b/src/trace_processor/read_trace.cc
@@ -25,8 +25,8 @@
#include "src/trace_processor/forwarding_trace_parser.h"
#include "src/trace_processor/importers/gzip/gzip_trace_parser.h"
-#include "src/trace_processor/importers/gzip/gzip_utils.h"
#include "src/trace_processor/importers/proto/proto_trace_tokenizer.h"
+#include "src/trace_processor/util/gzip_utils.h"
#include "src/trace_processor/util/status_macros.h"
#include "protos/perfetto/trace/trace.pbzero.h"
@@ -210,7 +210,7 @@
PERFETTO_CHECK(type == TraceType::kProtoTraceType);
protos::pbzero::Trace::Decoder decoder(data, size);
- GzipDecompressor decompressor;
+ util::GzipDecompressor decompressor;
if (size > 0 && !decoder.packet()) {
return util::ErrStatus("Trace does not contain valid packets");
}
@@ -226,7 +226,7 @@
decompressor.Reset();
decompressor.SetInput(bytes.data, bytes.size);
- using ResultCode = GzipDecompressor::ResultCode;
+ using ResultCode = util::GzipDecompressor::ResultCode;
uint8_t out[4096];
for (auto ret = ResultCode::kOk; ret != ResultCode::kEof;) {
auto res = decompressor.Decompress(out, base::ArraySize(out));
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 5418f55..d03072d 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -693,7 +693,7 @@
context_.systrace_trace_parser.reset(new SystraceTraceParser(&context_));
- if (gzip::IsGzipSupported())
+ if (util::IsGzipSupported())
context_.gzip_trace_parser.reset(new GzipTraceParser(&context_));
if (json::IsJsonSupported()) {
diff --git a/src/trace_processor/util/BUILD.gn b/src/trace_processor/util/BUILD.gn
index 31925e8..055cc81 100644
--- a/src/trace_processor/util/BUILD.gn
+++ b/src/trace_processor/util/BUILD.gn
@@ -29,6 +29,22 @@
]
}
+source_set("gzip") {
+ sources = [
+ "gzip_utils.cc",
+ "gzip_utils.h",
+ ]
+ deps = [
+ "../../../gn:default_deps",
+ "../../../include/perfetto/base",
+ ]
+
+ # gzip_utils optionally depends on zlib.
+ if (enable_perfetto_zlib) {
+ deps += [ "../../../gn:zlib" ]
+ }
+}
+
source_set("protozero_to_text") {
sources = [
"protozero_to_text.cc",
diff --git a/src/trace_processor/importers/gzip/gzip_utils.cc b/src/trace_processor/util/gzip_utils.cc
similarity index 96%
rename from src/trace_processor/importers/gzip/gzip_utils.cc
rename to src/trace_processor/util/gzip_utils.cc
index 8a30c7f..79cf1c0 100644
--- a/src/trace_processor/importers/gzip/gzip_utils.cc
+++ b/src/trace_processor/util/gzip_utils.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "src/trace_processor/importers/gzip/gzip_utils.h"
+#include "src/trace_processor/util/gzip_utils.h"
// For bazel build.
#include "perfetto/base/build_config.h"
@@ -28,7 +28,7 @@
namespace perfetto {
namespace trace_processor {
-namespace gzip {
+namespace util {
bool IsGzipSupported() {
#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
@@ -38,8 +38,6 @@
#endif
}
-} // namespace gzip
-
#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
GzipDecompressor::GzipDecompressor() : z_stream_(new z_stream()) {
z_stream_->zalloc = Z_NULL;
@@ -108,5 +106,6 @@
#endif
}
+} // namespace util
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/importers/gzip/gzip_utils.h b/src/trace_processor/util/gzip_utils.h
similarity index 88%
rename from src/trace_processor/importers/gzip/gzip_utils.h
rename to src/trace_processor/util/gzip_utils.h
index 624363f..189cfbd 100644
--- a/src/trace_processor/importers/gzip/gzip_utils.h
+++ b/src/trace_processor/util/gzip_utils.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_GZIP_GZIP_UTILS_H_
-#define SRC_TRACE_PROCESSOR_IMPORTERS_GZIP_GZIP_UTILS_H_
+#ifndef SRC_TRACE_PROCESSOR_UTIL_GZIP_UTILS_H_
+#define SRC_TRACE_PROCESSOR_UTIL_GZIP_UTILS_H_
#include <memory>
@@ -23,15 +23,12 @@
namespace perfetto {
namespace trace_processor {
-
-namespace gzip {
+namespace util {
// Returns whether gzip related functioanlity is supported with the current
// build flags.
bool IsGzipSupported();
-} // namespace gzip
-
class GzipDecompressor {
public:
enum class ResultCode {
@@ -66,7 +63,8 @@
std::unique_ptr<z_stream_s> z_stream_;
};
+} // namespace util
} // namespace trace_processor
} // namespace perfetto
-#endif // SRC_TRACE_PROCESSOR_IMPORTERS_GZIP_GZIP_UTILS_H_
+#endif // SRC_TRACE_PROCESSOR_UTIL_GZIP_UTILS_H_
diff --git a/src/tracebox/BUILD.gn b/src/tracebox/BUILD.gn
new file mode 100644
index 0000000..647ad5e
--- /dev/null
+++ b/src/tracebox/BUILD.gn
@@ -0,0 +1,29 @@
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("../../gn/perfetto.gni")
+
+assert(perfetto_build_standalone)
+
+executable("tracebox") {
+ deps = [
+ "../../gn:default_deps",
+ "../base",
+ "../perfetto_cmd",
+ "../perfetto_cmd:trigger_perfetto_cmd",
+ "../traced/probes",
+ "../traced/service",
+ ]
+ sources = [ "tracebox.cc" ]
+}
diff --git a/src/tracebox/tracebox.cc b/src/tracebox/tracebox.cc
new file mode 100644
index 0000000..f99aaa8
--- /dev/null
+++ b/src/tracebox/tracebox.cc
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/base/pipe.h"
+#include "perfetto/ext/base/subprocess.h"
+#include "perfetto/ext/base/utils.h"
+#include "perfetto/ext/traced/traced.h"
+#include "src/perfetto_cmd/perfetto_cmd.h"
+
+#include <stdio.h>
+
+#include <tuple>
+
+namespace perfetto {
+namespace {
+
+struct Applet {
+ using MainFunction = int (*)(int /*argc*/, char** /*argv*/);
+ const char* name;
+ MainFunction entrypoint;
+};
+
+const Applet g_applets[]{
+ {"traced", ServiceMain},
+ {"traced_probes", ProbesMain},
+ {"perfetto", PerfettoCmdMain},
+ {"trigger_perfetto", TriggerPerfettoMain},
+};
+
+void PrintUsage() {
+ printf(R"(Welcome to Perfetto tracing!
+
+Tracebox is a bundle containing all the tracing services and the perfetto
+cmdline client in one binary. It can be used either to spawn manually the
+various subprocess or in "autostart" mode, which will take care of starting
+and tearing down the services for you.
+
+Usage in autostart mode:
+ tracebox -t 10s -o trace_file.perfetto-trace sched/sched_switch
+ See tracebox --help for more options.
+
+Usage in manual mode:
+ tracebox applet_name [args ...] (e.g. ./tracebox traced --help)
+ Applets:)");
+
+ for (const Applet& applet : g_applets)
+ printf(" %s", applet.name);
+
+ printf(R"(
+
+See also:
+ * https://perfetto.dev/docs/
+ * The config editor in the record page of https://ui.perfetto.dev/
+)");
+}
+
+int TraceboxMain(int argc, char** argv) {
+ // Manual mode: if either the 1st argument (argv[1]) or the exe name (argv[0])
+ // match the name of an applet, directly invoke that without further
+ // modifications.
+
+ // Extract the file name from argv[0].
+ char* slash = strrchr(argv[0], '/');
+ char* argv0 = slash ? slash + 1 : argv[0];
+
+ for (const Applet& applet : g_applets) {
+ if (!strcmp(argv0, applet.name))
+ return applet.entrypoint(argc, argv);
+ if (argc > 1 && !strcmp(argv[1], applet.name))
+ return applet.entrypoint(argc - 1, &argv[1]);
+ }
+
+ // If no matching applet is found, switch to the autostart mode. In this mode
+ // we make tracebox behave like the cmdline client (without needing to prefix
+ // it with "perfetto"), but will also start traced and traced_probes.
+ // As part of this we also use a different namespace for the producer/consumer
+ // sockets, to avoid clashing with the system daemon.
+
+ if (argc <= 1) {
+ PrintUsage();
+ return 1;
+ }
+
+ auto pid_str = std::to_string(static_cast<uint64_t>(base::GetProcessId()));
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+ // Use an unlinked abstract domain socket on Linux/Android.
+ std::string consumer_socket = "@traced-c-" + pid_str;
+ std::string producer_socket = "@traced-p-" + pid_str;
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+ std::string consumer_socket = "/tmp/traced-c-" + pid_str;
+ std::string producer_socket = "/tmp/traced-p-" + pid_str;
+#else
+ PERFETTO_FATAL("The autostart mode is not supported on this platform");
+#endif
+
+ // If the caller has set the PERFETTO_*_SOCK_NAME, respect those.
+ const char* env;
+ if ((env = getenv("PERFETTO_CONSUMER_SOCK_NAME")))
+ consumer_socket = env;
+ if ((env = getenv("PERFETTO_PRODUCER_SOCK_NAME")))
+ consumer_socket = env;
+
+ base::SetEnv("PERFETTO_CONSUMER_SOCK_NAME", consumer_socket);
+ base::SetEnv("PERFETTO_PRODUCER_SOCK_NAME", producer_socket);
+
+ PerfettoCmd perfetto_cmd;
+
+ // If the cmdline parsing fails, stop here, no need to spawn services.
+ // It will daemonize if --background. In that case the subprocesses will be
+ // spawned by the damonized cmdline client, which is what we want so killing
+ // the backgrounded cmdline client will also kill the other services, as they
+ // will live in the same background session.
+ int res = perfetto_cmd.ParseCmdlineAndMaybeDaemonize(argc, argv);
+ if (res)
+ return res;
+
+ std::string self_path = base::GetCurExecutablePath();
+ base::Subprocess traced({self_path, "traced"});
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ // |traced_sync_pipe| is used to synchronize with traced socket creation.
+ // traced will write "1" and close the FD when the IPC socket is listening
+ // (or traced crashed).
+ base::Pipe traced_sync_pipe = base::Pipe::Create();
+ int wr_fd = *traced_sync_pipe.wr;
+ base::SetEnv("TRACED_NOTIFY_FD", std::to_string(wr_fd));
+ traced.args.preserve_fds.emplace_back(wr_fd);
+ // Create a new process group so CTRL-C is delivered only to the cmdline
+ // process (the tracebox one) and not to traced. traced will still exit once
+ // the main process exits, but this allows graceful stopping of the trace
+ // without abruptedly killing traced{,probes} when hitting CTRL+C.
+ traced.args.posix_proc_group_id = 0; // 0 = start a new process group.
+#endif
+ traced.Start();
+
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ traced_sync_pipe.wr.reset();
+
+ std::string traced_notify_msg;
+ base::ReadPlatformHandle(*traced_sync_pipe.rd, &traced_notify_msg);
+ if (traced_notify_msg != "1")
+ PERFETTO_FATAL("The tracing service failed unexpectedly. Check the logs");
+#endif
+
+ base::Subprocess traced_probes(
+ {self_path, "traced_probes", "--reset-ftrace"});
+ // Put traced_probes in the same process group as traced. Same reason (CTRL+C)
+ // but it's not worth creating a new group.
+ traced_probes.args.posix_proc_group_id = traced.pid();
+ traced_probes.Start();
+
+ perfetto_cmd.ConnectToServiceAndRun();
+ return 0;
+}
+
+} // namespace
+} // namespace perfetto
+
+int main(int argc, char** argv) {
+ return perfetto::TraceboxMain(argc, argv);
+}
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..f0a6c4a 100644
--- a/src/traced/service/service.cc
+++ b/src/traced/service/service.cc
@@ -17,6 +17,7 @@
#include <stdio.h>
#include <algorithm>
+#include "perfetto/ext/base/file_utils.h"
#include "perfetto/ext/base/getopt.h"
#include "perfetto/ext/base/string_utils.h"
#include "perfetto/ext/base/unix_task_runner.h"
@@ -75,7 +76,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 +88,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
@@ -203,6 +206,16 @@
base::kWatchdogDefaultCpuWindow);
watchdog->Start();
+ // If the TRACED_NOTIFY_FD env var is set, write 1 and close the FD. This is
+ // so tools can synchronize with the point where the IPC socket has been
+ // opened, without having to poll. This is used for //src/tracebox.
+ const char* env_notif = getenv("TRACED_NOTIFY_FD");
+ if (env_notif) {
+ int notif_fd = atoi(env_notif);
+ PERFETTO_CHECK(base::WriteAll(notif_fd, "1", 1) == 1);
+ PERFETTO_CHECK(base::CloseFile(notif_fd) == 0);
+ }
+
PERFETTO_ILOG("Started traced, listening on %s %s", GetProducerSocket(),
GetConsumerSocket());
task_runner.Run();
diff --git a/src/tracing/core/tracing_service_impl.cc b/src/tracing/core/tracing_service_impl.cc
index 7084dec..63839b0 100644
--- a/src/tracing/core/tracing_service_impl.cc
+++ b/src/tracing/core/tracing_service_impl.cc
@@ -57,6 +57,7 @@
#include "perfetto/ext/base/metatrace.h"
#include "perfetto/ext/base/string_utils.h"
#include "perfetto/ext/base/utils.h"
+#include "perfetto/ext/base/version.h"
#include "perfetto/ext/base/watchdog.h"
#include "perfetto/ext/tracing/core/basic_types.h"
#include "perfetto/ext/tracing/core/consumer.h"
@@ -332,7 +333,8 @@
bool in_process,
ProducerSMBScrapingMode smb_scraping_mode,
size_t shared_memory_page_size_hint_bytes,
- std::unique_ptr<SharedMemory> shm) {
+ std::unique_ptr<SharedMemory> shm,
+ const std::string& sdk_version) {
PERFETTO_DCHECK_THREAD(thread_checker_);
if (lockdown_mode_ && uid != base::GetCurrentUserId()) {
@@ -361,8 +363,8 @@
}
std::unique_ptr<ProducerEndpointImpl> endpoint(new ProducerEndpointImpl(
- id, uid, this, task_runner_, producer, producer_name, in_process,
- smb_scraping_enabled));
+ id, uid, this, task_runner_, producer, producer_name, sdk_version,
+ in_process, smb_scraping_enabled));
auto it_and_inserted = producers_.emplace(id, endpoint.get());
PERFETTO_DCHECK(it_and_inserted.second);
endpoint->shmem_size_hint_bytes_ = shared_memory_size_hint_bytes;
@@ -3039,7 +3041,7 @@
tracing_session->did_emit_system_info = true;
protozero::HeapBuffered<protos::pbzero::TracePacket> packet;
auto* info = packet->set_system_info();
- base::ignore_result(info); // For PERFETTO_OS_WIN.
+ info->set_tracing_service_version(base::GetVersionString());
#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) && \
!PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
struct utsname uname_info;
@@ -3479,6 +3481,7 @@
TracingServiceState svc_state;
const auto& sessions = service_->tracing_sessions_;
+ svc_state.set_tracing_service_version(base::GetVersionString());
svc_state.set_num_sessions(static_cast<int>(sessions.size()));
int num_started = 0;
@@ -3490,6 +3493,7 @@
auto* producer = svc_state.add_producers();
producer->set_id(static_cast<int>(kv.first));
producer->set_name(kv.second->name_);
+ producer->set_sdk_version(kv.second->sdk_version_);
producer->set_uid(static_cast<int32_t>(producer->uid()));
}
@@ -3547,6 +3551,7 @@
base::TaskRunner* task_runner,
Producer* producer,
const std::string& producer_name,
+ const std::string& sdk_version,
bool in_process,
bool smb_scraping_enabled)
: id_(id),
@@ -3555,6 +3560,7 @@
task_runner_(task_runner),
producer_(producer),
name_(producer_name),
+ sdk_version_(sdk_version),
in_process_(in_process),
smb_scraping_enabled_(smb_scraping_enabled),
weak_ptr_factory_(this) {}
diff --git a/src/tracing/core/tracing_service_impl.h b/src/tracing/core/tracing_service_impl.h
index 27b172c..401aaa1 100644
--- a/src/tracing/core/tracing_service_impl.h
+++ b/src/tracing/core/tracing_service_impl.h
@@ -87,6 +87,7 @@
base::TaskRunner*,
Producer*,
const std::string& producer_name,
+ const std::string& sdk_version,
bool in_process,
bool smb_scraping_enabled);
~ProducerEndpointImpl() override;
@@ -154,6 +155,7 @@
size_t shmem_page_size_hint_bytes_ = 0;
bool is_shmem_provided_by_producer_ = false;
const std::string name_;
+ std::string sdk_version_;
bool in_process_;
bool smb_scraping_enabled_;
@@ -291,7 +293,8 @@
ProducerSMBScrapingMode smb_scraping_mode =
ProducerSMBScrapingMode::kDefault,
size_t shared_memory_page_size_hint_bytes = 0,
- std::unique_ptr<SharedMemory> shm = nullptr) override;
+ std::unique_ptr<SharedMemory> shm = nullptr,
+ const std::string& sdk_version = {}) override;
std::unique_ptr<TracingService::ConsumerEndpoint> ConnectConsumer(
Consumer*,
diff --git a/src/tracing/ipc/producer/producer_ipc_client_impl.cc b/src/tracing/ipc/producer/producer_ipc_client_impl.cc
index 694a3a9..97b622c 100644
--- a/src/tracing/ipc/producer/producer_ipc_client_impl.cc
+++ b/src/tracing/ipc/producer/producer_ipc_client_impl.cc
@@ -21,6 +21,7 @@
#include "perfetto/base/logging.h"
#include "perfetto/base/task_runner.h"
+#include "perfetto/ext/base/version.h"
#include "perfetto/ext/ipc/client.h"
#include "perfetto/ext/tracing/core/commit_data_request.h"
#include "perfetto/ext/tracing/core/producer.h"
@@ -179,6 +180,7 @@
req.set_build_flags(
protos::gen::InitializeConnectionRequest::BUILD_FLAGS_DCHECKS_OFF);
#endif
+ req.set_sdk_version(base::GetVersionString());
producer_port_.InitializeConnection(req, std::move(on_init), shm_fd);
// Create the back channel to receive commands from the Service.
diff --git a/src/tracing/ipc/service/producer_ipc_service.cc b/src/tracing/ipc/service/producer_ipc_service.cc
index dc74374..71abd8c 100644
--- a/src/tracing/ipc/service/producer_ipc_service.cc
+++ b/src/tracing/ipc/service/producer_ipc_service.cc
@@ -131,7 +131,8 @@
producer.get(), client_info.uid(), req.producer_name(),
req.shared_memory_size_hint_bytes(),
/*in_process=*/false, smb_scraping_mode,
- req.shared_memory_page_size_hint_bytes(), std::move(shmem));
+ req.shared_memory_page_size_hint_bytes(), std::move(shmem),
+ req.sdk_version());
// Could happen if the service has too many producers connected.
if (!producer->service_endpoint) {
diff --git a/test/end_to_end_integrationtest.cc b/test/end_to_end_integrationtest.cc
index 64a7c80..af54f08 100644
--- a/test/end_to_end_integrationtest.cc
+++ b/test/end_to_end_integrationtest.cc
@@ -30,6 +30,7 @@
#include "perfetto/ext/base/string_utils.h"
#include "perfetto/ext/base/subprocess.h"
#include "perfetto/ext/base/temp_file.h"
+#include "perfetto/ext/base/utils.h"
#include "perfetto/ext/ipc/basic_types.h"
#include "perfetto/ext/traced/traced.h"
#include "perfetto/ext/tracing/core/commit_data_request.h"
diff --git a/tools/gen_bazel b/tools/gen_bazel
index f4e3650..a8b7309 100755
--- a/tools/gen_bazel
+++ b/tools/gen_bazel
@@ -43,6 +43,8 @@
'enable_perfetto_watchdog=true',
'monolithic_binaries=true',
'target_os="linux"',
+ 'enable_perfetto_heapprofd=false',
+ 'enable_perfetto_traced_perf=false',
])
# Default targets to translate to the blueprint file.
diff --git a/tools/record_android_trace b/tools/record_android_trace
index 7874638..5de8a4e 100755
--- a/tools/record_android_trace
+++ b/tools/record_android_trace
@@ -1,14 +1,29 @@
#!/usr/bin/env python3
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
import atexit
import argparse
import datetime
+import hashlib
import http.server
import os
import shutil
import socketserver
import subprocess
import sys
+import tempfile
import time
import webbrowser
@@ -18,6 +33,24 @@
# PATH. It's fine if it doesn't exist so this script can be copied elsewhere.
HERMETIC_ADB_PATH = ROOT_DIR + '/buildtools/android_sdk/platform-tools/adb'
+# For downloading and sideloading tracebox on older devices, or when the
+# --sideload argument is passed.
+TRACEBOX_BASE_URL = 'https://storage.googleapis.com/perfetto/'
+TRACEBOX_SHA1S = {
+ 'android-arm': '6e9dfee326468fc6858c6d95e00be110efd187a3', # v15.0.248
+ 'android-arm64': 'ca2d4a02511f73dac32a2ae49964f3e5cd59d252', # v15.0.248
+ 'android-x86': '3fdbc9246412e460d0a373140c114cff858b9b7c', # v15.0.248
+ 'android-x64': 'be85f6f4a2d014d425246b85941c137c28d158cc', # v15.0.248
+}
+
+# Translates the Android ro.product.cpu.abi into the GN's target_cpu.
+ABI_TO_ARCH = {
+ 'armeabi-v7a': 'arm',
+ 'arm64-v8a': 'arm64',
+ 'x86': 'x86',
+ 'x86_64': 'x64',
+}
+
devnull = open(os.devnull, 'rb')
adb_path = None
procs = []
@@ -54,7 +87,7 @@
default_out_dir = os.path.expanduser(default_out_dir_str)
examples = '\n'.join([
- ANSI.BOLD + 'Examples' + ANSI.END, ' -t 10s -b 32mb sched gfx wm',
+ ANSI.BOLD + 'Examples' + ANSI.END, ' -t 10s -b 32mb sched gfx wm -a*',
' -t 5s sched/sched_switch raw_syscalls/sys_enter raw_syscalls/sys_exit',
' -c /path/to/full-textual-trace.config', '',
ANSI.BOLD + 'Long traces' + ANSI.END,
@@ -71,6 +104,16 @@
help = 'Don\'t open in the browser'
parser.add_argument('-n', '--no-open', action='store_true', help=help)
+ help = 'Force the use of the sideloaded binaries rather than system daemons'
+ parser.add_argument('--sideload', action='store_true', help=help)
+
+ help = ('Sideload the the given binary rather than downloading it. ' +
+ 'Implies --sideload')
+ parser.add_argument('--sideload-path', default=None, help=help)
+
+ help = 'Don\'t run `adb root` run as user (only when sideloading)'
+ parser.add_argument('-u', '--user', action='store_true', help=help)
+
grp = parser.add_argument_group(
'Short options: (only when not using -c/--config)')
@@ -80,11 +123,12 @@
help = 'Ring buffer size N[mb,gb] (default: 32mb)'
grp.add_argument('-b', '--buffer', default='32mb', help=help)
- help = 'Android (atrace) app names (can be specified multiple times)'
+ help = ('Android (atrace) app names. Can be specified multiple times.\n-a*' +
+ 'for all apps (without space between a and * or bash will expand it)')
grp.add_argument(
'-a',
'--app',
- metavar='Atrace apps',
+ metavar='com.myapp',
action='append',
default=[],
help=help)
@@ -107,10 +151,7 @@
help = 'Can be generated with https://ui.perfetto.dev/#!/record'
grp.add_argument('-c', '--config', default=None, help=help)
args = parser.parse_args()
-
- tstamp = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M')
- fname = '%s.pftrace' % tstamp
- device_file = '/data/misc/perfetto-traces/' + fname
+ args.sideload = args.sideload or args.sideload_path is not None
find_adb()
@@ -137,13 +178,51 @@
'Did you mean to pass -c / --config ?'), ANSI.RED)
sys.exit(1)
- cmd = ['perfetto', '--background', '--txt', '-o', device_file]
+ perfetto_cmd = 'perfetto'
+ device_dir = '/data/misc/perfetto-traces/'
+
+ # Check the version of android. If too old (< Q) sideload tracebox. Also use
+ # use /data/local/tmp as /data/misc/perfetto-traces was introduced only later.
+ probe_cmd = 'getprop ro.build.version.sdk; getprop ro.product.cpu.abi; whoami'
+ probe = adb('shell', probe_cmd, stdout=subprocess.PIPE)
+ lines = probe.communicate()[0].decode().strip().split('\n')
+ lines = [x.strip() for x in lines] # To strip \r(s) on Windows.
+ if probe.returncode != 0:
+ prt('ADB connection failed', ANSI.RED)
+ sys.exit(1)
+ api_level = int(lines[0])
+ abi = lines[1]
+ arch = ABI_TO_ARCH.get(abi)
+ if arch is None:
+ prt('Unsupported ABI: ' + abi)
+ sys.exit(1)
+ shell_user = lines[2]
+ if api_level < 29 or args.sideload: # 29: Android Q.
+ tracebox_bin = args.sideload_path or download_tracebox_if_needed(arch)
+ perfetto_cmd = '/data/local/tmp/tracebox'
+ exit_code = adb('push', '--sync', tracebox_bin, perfetto_cmd).wait()
+ exit_code |= adb('shell', 'chmod 755 ' + perfetto_cmd).wait()
+ if exit_code != 0:
+ prt('ADB push failed', ANSI.RED)
+ sys.exit(1)
+ device_dir = '/data/local/tmp/'
+ if shell_user != 'root' and not args.user:
+ # Run as root if possible as that will give access to more tracing
+ # capabilities. Non-root still works, but some ftrace events might not be
+ # available.
+ adb('root').wait()
+
+ tstamp = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M')
+ fname = '%s-%s.pftrace' % (tstamp, os.urandom(3).hex())
+ device_file = device_dir + fname
+
+ cmd = [perfetto_cmd, '--background', '--txt', '-o', device_file]
if args.config is not None:
cmd += ['-c', '-']
else:
cmd += ['-t', args.time, '-b', args.buffer]
for app in args.app:
- cmd += ['--app', app]
+ cmd += ['--app', '\'' + app + '\'']
cmd += args.events
# Perfetto will error out with a proper message if both a config file and
@@ -235,7 +314,7 @@
socketserver.TCPServer.allow_reuse_address = True
with socketserver.TCPServer(('127.0.0.1', PORT), HttpHandler) as httpd:
webbrowser.open_new_tab(
- 'https://ui.perfetto.dev/#!/?url=http://127.0.0.1:%d/%s' %
+ 'https://ui.perfetto.dev/#!/?url=http://127.0.0.1:%d/%s' %
(PORT, fname))
while httpd.__dict__.get('last_request') != '/' + fname:
httpd.handle_request()
@@ -258,5 +337,32 @@
p.kill()
+def check_hash(file_name, sha_value):
+ with open(file_name, 'rb') as fd:
+ file_hash = hashlib.sha1(fd.read()).hexdigest()
+ return file_hash == sha_value
+
+
+def download_tracebox_if_needed(arch):
+ sha_value = TRACEBOX_SHA1S.get('android-' + arch)
+ if sha_value is None:
+ prt('Unsupported architecture ' + arch)
+ sys.exit(1)
+ file_name = 'tracebox-android-' + arch + '-' + sha_value
+ local_file = os.path.join(tempfile.gettempdir(), file_name)
+ if os.path.exists(local_file):
+ if not check_hash(local_file, sha_value):
+ os.remove(local_file)
+ else:
+ return local_file
+ url = TRACEBOX_BASE_URL + file_name
+ prt('Downloading %s' % url)
+ subprocess.check_call(['curl', '-L', '-#', '-o', local_file, url])
+ if not check_hash(local_file, sha_value):
+ os.remove(local_file)
+ raise ValueError('Invalid SHA1 for %s' % local_file)
+ return local_file
+
+
if __name__ == '__main__':
sys.exit(main())
diff --git a/tools/update_tracebox b/tools/update_tracebox
new file mode 100755
index 0000000..0836b25
--- /dev/null
+++ b/tools/update_tracebox
@@ -0,0 +1,76 @@
+#!/bin/bash
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e
+
+# cd into the repo root so all commands are independent of the cwd.
+cd -P ${BASH_SOURCE[0]%/*}/..
+
+GN_ARGS="$GN_ARGS" # Allow to expand GN_ARGS from env for ccache.
+
+set -u
+
+DIR=out/tmp_tracebox
+mkdir -p $DIR
+
+function cleanup {
+ rm -rf "$DIR"
+ echo "Deleted temp working directory $DIR"
+}
+
+#trap cleanup EXIT
+
+function is_mac {
+ ! test -d /proc
+ return $?
+}
+
+VERSION="$(tools/write_version_header.py --stdout)"
+
+# Allow overriding the build targets via the cmdline (for testing).
+if [ "$#" -gt 0 ]; then
+ COMBOS="$@"
+else
+ COMBOS="android-arm android-arm64 android-x86 android-x64"
+fi
+
+for COMBO in $COMBOS; do
+ IFS=- read PLATFORM ARCH <<< "$COMBO"
+ echo "Building for $COMBO"
+ rm -rf $DIR
+ mkdir -p $DIR
+ GN_ARGS="$GN_ARGS is_debug=false monolithic_binaries = true"
+ GN_ARGS="$GN_ARGS target_os = \"$PLATFORM\" target_cpu = \"$ARCH\""
+ set -x
+ tools/gn gen $DIR --args="$GN_ARGS"
+ tools/ninja -C $DIR tracebox
+ set +x
+ BINARY=$DIR/stripped/tracebox
+if which shasum; then
+ NEW_SHA=$(shasum $BINARY | cut -f1 -d' ') # Mac OS
+else
+ NEW_SHA=$(sha1sum $BINARY | cut -f1 -d' ') # Linux
+fi
+ FULL_NAME=tracebox-$COMBO-$NEW_SHA
+
+ set -x
+ gsutil cp -n -a public-read $BINARY gs://perfetto/$FULL_NAME
+ sed -e \
+ "s/'$COMBO': '.*/'$COMBO': '$NEW_SHA', # $VERSION/" \
+ -i .tmp tools/record_android_trace
+ set +x
+ rm -f tools/record_android_trace.tmp
+ echo ""
+done