Merge "proto_merger: Fix some typos"
diff --git a/Android.bp b/Android.bp
index ad54f8d..92728f9 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1005,6 +1005,7 @@
":perfetto_src_base_version",
":perfetto_src_ipc_client",
":perfetto_src_ipc_common",
+ ":perfetto_src_perfetto_cmd_bugreport_path",
":perfetto_src_perfetto_cmd_perfetto_cmd",
":perfetto_src_perfetto_cmd_protos_cpp_gen",
":perfetto_src_perfetto_cmd_trigger_producer",
@@ -1191,6 +1192,7 @@
":perfetto_src_ipc_perfetto_ipc",
":perfetto_src_kallsyms_kallsyms",
":perfetto_src_kernel_utils_syscall_table",
+ ":perfetto_src_perfetto_cmd_bugreport_path",
":perfetto_src_protozero_filtering_bytecode_common",
":perfetto_src_protozero_filtering_bytecode_generator",
":perfetto_src_protozero_filtering_bytecode_parser",
@@ -1990,6 +1992,7 @@
":perfetto_src_ipc_perfetto_ipc",
":perfetto_src_kallsyms_kallsyms",
":perfetto_src_kernel_utils_syscall_table",
+ ":perfetto_src_perfetto_cmd_bugreport_path",
":perfetto_src_profiling_common_callstack_trie",
":perfetto_src_profiling_common_interner",
":perfetto_src_profiling_common_interning_output",
@@ -8467,6 +8470,11 @@
],
}
+// GN: //src/perfetto_cmd:bugreport_path
+filegroup {
+ name: "perfetto_src_perfetto_cmd_bugreport_path",
+}
+
// GN: //src/perfetto_cmd:gen_cc_config_descriptor
genrule {
name: "perfetto_src_perfetto_cmd_gen_cc_config_descriptor",
@@ -8688,6 +8696,7 @@
name: "perfetto_src_profiling_memory_end_to_end_tests",
srcs: [
"src/profiling/memory/heapprofd_end_to_end_test.cc",
+ "src/profiling/memory/heapprofd_producer_integrationtest.cc",
],
}
@@ -9625,6 +9634,7 @@
"src/trace_processor/importers/proto/active_chrome_processes_tracker_unittest.cc",
"src/trace_processor/importers/proto/heap_graph_tracker_unittest.cc",
"src/trace_processor/importers/proto/heap_profile_tracker_unittest.cc",
+ "src/trace_processor/importers/proto/network_trace_module_unittest.cc",
"src/trace_processor/importers/proto/perf_sample_tracker_unittest.cc",
"src/trace_processor/importers/proto/proto_trace_parser_unittest.cc",
],
@@ -11623,6 +11633,7 @@
":perfetto_src_kallsyms_kallsyms",
":perfetto_src_kallsyms_unittests",
":perfetto_src_kernel_utils_syscall_table",
+ ":perfetto_src_perfetto_cmd_bugreport_path",
":perfetto_src_perfetto_cmd_perfetto_cmd",
":perfetto_src_perfetto_cmd_protos_cpp_gen",
":perfetto_src_perfetto_cmd_trigger_producer",
diff --git a/BUILD b/BUILD
index e5d3c9b..cb6b454 100644
--- a/BUILD
+++ b/BUILD
@@ -906,6 +906,14 @@
],
)
+# GN target: //src/perfetto_cmd:bugreport_path
+perfetto_filegroup(
+ name = "src_perfetto_cmd_bugreport_path",
+ srcs = [
+ "src/perfetto_cmd/bugreport_path.h",
+ ],
+)
+
# GN target: //src/perfetto_cmd:gen_cc_config_descriptor
perfetto_cc_proto_descriptor(
name = "src_perfetto_cmd_gen_cc_config_descriptor",
@@ -4537,6 +4545,7 @@
":include_perfetto_tracing_tracing",
":src_android_stats_android_stats",
":src_android_stats_perfetto_atoms",
+ ":src_perfetto_cmd_bugreport_path",
":src_perfetto_cmd_perfetto_cmd",
":src_perfetto_cmd_trigger_producer",
":src_tracing_common",
diff --git a/include/perfetto/ext/tracing/core/basic_types.h b/include/perfetto/ext/tracing/core/basic_types.h
index ac75b4d..0950fe8 100644
--- a/include/perfetto/ext/tracing/core/basic_types.h
+++ b/include/perfetto/ext/tracing/core/basic_types.h
@@ -76,6 +76,11 @@
constexpr uint32_t kDefaultFlushTimeoutMs = 5000;
+// The special id 0xffff..ffff represents the tracing session with the highest
+// bugreport score. This is used for CloneSession(kBugreportSessionId).
+constexpr TracingSessionID kBugreportSessionId =
+ static_cast<TracingSessionID>(-1);
+
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_TRACING_CORE_BASIC_TYPES_H_
diff --git a/include/perfetto/ext/tracing/core/tracing_service.h b/include/perfetto/ext/tracing/core/tracing_service.h
index 68dc086..82e53f7 100644
--- a/include/perfetto/ext/tracing/core/tracing_service.h
+++ b/include/perfetto/ext/tracing/core/tracing_service.h
@@ -42,9 +42,6 @@
class SharedMemoryArbiter;
class TraceWriter;
-// Exposed for testing.
-std::string GetBugreportPath();
-
// TODO: for the moment this assumes that all the calls happen on the same
// thread/sequence. Not sure this will be the case long term in Chrome.
@@ -192,6 +189,8 @@
// Clones an existing tracing session and attaches to it. The session is
// cloned in read-only mode and can only be used to read a snapshot of an
// existing tracing session. Will invoke Consumer::OnSessionCloned().
+ // If TracingSessionID == kBugreportSessionId (0xff...ff) the session with the
+ // highest bugreport score is cloned (if any exists).
// TODO(primiano): make pure virtual after various 3way patches.
virtual void CloneSession(TracingSessionID);
diff --git a/protos/perfetto/config/chrome/BUILD.gn b/protos/perfetto/config/chrome/BUILD.gn
index 4f44718..4397cfc 100644
--- a/protos/perfetto/config/chrome/BUILD.gn
+++ b/protos/perfetto/config/chrome/BUILD.gn
@@ -15,6 +15,8 @@
import("../../../../gn/perfetto.gni")
import("../../../../gn/proto_library.gni")
-perfetto_proto_library("@TYPE@") {
- sources = [ "chrome_config.proto" ]
+perfetto_proto_library("scenario_descriptor") {
+ proto_generators = [ "descriptor" ]
+ generate_descriptor = "scenario_config.descriptor"
+ sources = [ "scenario_config.proto" ]
}
diff --git a/protos/perfetto/config/perfetto_config.proto b/protos/perfetto/config/perfetto_config.proto
index 03f9d8a..2eb9d1a 100644
--- a/protos/perfetto/config/perfetto_config.proto
+++ b/protos/perfetto/config/perfetto_config.proto
@@ -868,7 +868,7 @@
// Begin of protos/perfetto/config/profiling/heapprofd_config.proto
// Configuration for go/heapprofd.
-// Next id: 27
+// Next id: 28
message HeapprofdConfig {
message ContinuousDumpConfig {
// ms to wait before first dump.
diff --git a/protos/perfetto/config/profiling/heapprofd_config.proto b/protos/perfetto/config/profiling/heapprofd_config.proto
index 2636e06..bb0a700 100644
--- a/protos/perfetto/config/profiling/heapprofd_config.proto
+++ b/protos/perfetto/config/profiling/heapprofd_config.proto
@@ -19,7 +19,7 @@
package perfetto.protos;
// Configuration for go/heapprofd.
-// Next id: 27
+// Next id: 28
message HeapprofdConfig {
message ContinuousDumpConfig {
// ms to wait before first dump.
diff --git a/protos/perfetto/ipc/consumer_port.proto b/protos/perfetto/ipc/consumer_port.proto
index aa7fc93..d5a4025 100644
--- a/protos/perfetto/ipc/consumer_port.proto
+++ b/protos/perfetto/ipc/consumer_port.proto
@@ -270,7 +270,7 @@
// or something failed.
message SaveTraceForBugreportResponse {
// If true, an eligible the trace was saved into a known location (on Android
- // /data/misc/perfetto-traces, see GetBugreportPath()).
+ // /data/misc/perfetto-traces, see GetBugreportTracePath()).
// If false no trace with bugreport_score > 0 was found or an error occurred.
// see |msg| in that case for details about the failure.
optional bool success = 1;
@@ -279,6 +279,8 @@
// Arguments for rpc CloneSession.
message CloneSessionRequest {
+ // The session ID to clone. If session_id == kBugreportSessionId (0xff...ff)
+ // the session with the highest bugreport score is cloned (if any exists).
optional uint64 session_id = 1;
}
diff --git a/protos/perfetto/trace/perfetto/tracing_service_event.proto b/protos/perfetto/trace/perfetto/tracing_service_event.proto
index 04923dc..0573be0 100644
--- a/protos/perfetto/trace/perfetto/tracing_service_event.proto
+++ b/protos/perfetto/trace/perfetto/tracing_service_event.proto
@@ -60,6 +60,8 @@
// the contents are routed onto the bugreport file. This event flags the
// situation explicitly. Traces that contain this marker should be discarded
// by test infrastructures / pipelines.
+ // Deprecated since Android U, where --save-for-bugreport uses
+ // non-destructive cloning.
bool seized_for_bugreport = 6;
}
}
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index 66055fa..3efbb44 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -868,7 +868,7 @@
// Begin of protos/perfetto/config/profiling/heapprofd_config.proto
// Configuration for go/heapprofd.
-// Next id: 27
+// Next id: 28
message HeapprofdConfig {
message ContinuousDumpConfig {
// ms to wait before first dump.
@@ -10372,6 +10372,8 @@
// the contents are routed onto the bugreport file. This event flags the
// situation explicitly. Traces that contain this marker should be discarded
// by test infrastructures / pipelines.
+ // Deprecated since Android U, where --save-for-bugreport uses
+ // non-destructive cloning.
bool seized_for_bugreport = 6;
}
}
diff --git a/src/perfetto_cmd/BUILD.gn b/src/perfetto_cmd/BUILD.gn
index 70c47fc..51486c0 100644
--- a/src/perfetto_cmd/BUILD.gn
+++ b/src/perfetto_cmd/BUILD.gn
@@ -49,6 +49,7 @@
"../../include/perfetto/ext/traced",
]
deps = [
+ ":bugreport_path",
":gen_cc_config_descriptor",
":trigger_producer",
"../../gn:default_deps",
@@ -83,6 +84,15 @@
}
}
+source_set("bugreport_path") {
+ sources = [ "bugreport_path.h" ]
+ deps = [ "../../gn:default_deps" ]
+ public_deps = [
+ "../../include/perfetto/base",
+ "../../include/perfetto/ext/base",
+ ]
+}
+
perfetto_cc_proto_descriptor("gen_cc_config_descriptor") {
descriptor_name = "config.descriptor"
descriptor_target = "../../protos/perfetto/config:descriptor"
diff --git a/src/perfetto_cmd/bugreport_path.h b/src/perfetto_cmd/bugreport_path.h
new file mode 100644
index 0000000..5bc7602
--- /dev/null
+++ b/src/perfetto_cmd/bugreport_path.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2023 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_PERFETTO_CMD_BUGREPORT_PATH_H_
+#define SRC_PERFETTO_CMD_BUGREPORT_PATH_H_
+
+#include <string>
+
+#include "perfetto/base/build_config.h"
+#include "perfetto/ext/base/temp_file.h"
+
+namespace perfetto {
+
+// Expose for testing
+inline std::string GetBugreportTracePath() {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \
+ PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
+ return "/data/misc/perfetto-traces/bugreport/systrace.pftrace";
+#else
+ // Only for tests, SaveTraceForBugreport is not used on other OSes.
+ return base::GetSysTempDir() + "/bugreport.pftrace";
+#endif
+}
+
+} // namespace perfetto
+
+#endif // SRC_PERFETTO_CMD_BUGREPORT_PATH_H_
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc
index 5818c59..a0234b8 100644
--- a/src/perfetto_cmd/perfetto_cmd.cc
+++ b/src/perfetto_cmd/perfetto_cmd.cc
@@ -51,6 +51,7 @@
#include "perfetto/ext/base/getopt.h"
#include "perfetto/ext/base/pipe.h"
#include "perfetto/ext/base/string_view.h"
+#include "perfetto/ext/base/temp_file.h"
#include "perfetto/ext/base/thread_utils.h"
#include "perfetto/ext/base/utils.h"
#include "perfetto/ext/base/uuid.h"
@@ -64,9 +65,11 @@
#include "perfetto/tracing/core/trace_config.h"
#include "perfetto/tracing/core/tracing_service_state.h"
#include "src/android_stats/statsd_logging_helper.h"
+#include "src/perfetto_cmd/bugreport_path.h"
#include "src/perfetto_cmd/config.h"
#include "src/perfetto_cmd/packet_writer.h"
#include "src/perfetto_cmd/pbtxt_to_pb.h"
+#include "src/perfetto_cmd/rate_limiter.h"
#include "src/perfetto_cmd/trigger_producer.h"
#include "protos/perfetto/common/ftrace_descriptor.gen.h"
@@ -78,7 +81,8 @@
perfetto::PerfettoCmd* g_perfetto_cmd;
-uint32_t kOnTraceDataTimeoutMs = 3000;
+const uint32_t kOnTraceDataTimeoutMs = 3000;
+const uint32_t kCloneTimeoutMs = 10000;
class LoggingErrorReporter : public ErrorReporter {
public:
@@ -552,6 +556,13 @@
return 1;
}
+ // --save-for-bugreport is the equivalent of:
+ // --clone kBugreportSessionId -o /data/misc/perfetto-traces/bugreport/...
+ if (bugreport_ && trace_out_path_.empty()) {
+ clone_tsid_ = kBugreportSessionId;
+ trace_out_path_ = GetBugreportTracePath();
+ }
+
// Parse the trace config. It can be either:
// 1) A proto-encoded file/stdin (-c ...).
// 2) A proto-text file/stdin (-c ... --txt).
@@ -561,8 +572,7 @@
trace_config_.reset(new TraceConfig());
bool parsed = false;
- const bool will_trace_or_trigger =
- !is_attach() && !query_service_ && !bugreport_;
+ const bool will_trace_or_trigger = !is_attach() && !query_service_;
if (!will_trace_or_trigger || clone_tsid_) {
if ((!trace_config_raw.empty() || has_config_options)) {
PERFETTO_ELOG("Cannot specify a trace config with this option");
@@ -904,12 +914,12 @@
return finished_with_success ? 0 : 1;
} // if (triggers_to_activate_)
- if (query_service_ || bugreport_) {
+ if (query_service_) {
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();
@@ -997,25 +1007,14 @@
return;
}
- if (bugreport_) {
- consumer_endpoint_->SaveTraceForBugreport(
- [](bool success, const std::string& msg) {
- if (success) {
- PERFETTO_ILOG("Trace saved into %s", msg.c_str());
- exit(0);
- }
- PERFETTO_ELOG("%s", msg.c_str());
- exit(1);
- });
- return;
- }
-
if (is_attach()) {
consumer_endpoint_->Attach(attach_key_);
return;
}
if (clone_tsid_.has_value()) {
+ task_runner_.PostDelayedTask(std::bind(&PerfettoCmd::OnTimeout, this),
+ kCloneTimeoutMs);
consumer_endpoint_->CloneSession(*clone_tsid_);
return;
}
diff --git a/src/perfetto_cmd/perfetto_cmd.h b/src/perfetto_cmd/perfetto_cmd.h
index 842ddcc..bd4a8c4 100644
--- a/src/perfetto_cmd/perfetto_cmd.h
+++ b/src/perfetto_cmd/perfetto_cmd.h
@@ -33,7 +33,6 @@
#include "perfetto/ext/tracing/core/consumer.h"
#include "perfetto/ext/tracing/ipc/consumer_ipc_client.h"
#include "src/android_stats/perfetto_atoms.h"
-#include "src/perfetto_cmd/rate_limiter.h"
namespace perfetto {
diff --git a/src/profiling/memory/BUILD.gn b/src/profiling/memory/BUILD.gn
index 51deba3..817c5b8 100644
--- a/src/profiling/memory/BUILD.gn
+++ b/src/profiling/memory/BUILD.gn
@@ -358,8 +358,12 @@
"../../base",
"../../base:test_support",
"../../trace_processor:lib",
+ "../../tracing/test:test_support",
]
- sources = [ "heapprofd_end_to_end_test.cc" ]
+ sources = [
+ "heapprofd_end_to_end_test.cc",
+ "heapprofd_producer_integrationtest.cc",
+ ]
if (perfetto_build_with_android) {
deps += [ ":heapprofd_client_api" ]
} else {
diff --git a/src/profiling/memory/heapprofd_producer_integrationtest.cc b/src/profiling/memory/heapprofd_producer_integrationtest.cc
new file mode 100644
index 0000000..077a539
--- /dev/null
+++ b/src/profiling/memory/heapprofd_producer_integrationtest.cc
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2022 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_producer.h"
+
+#include "perfetto/base/proc_utils.h"
+#include "perfetto/ext/base/thread_task_runner.h"
+#include "perfetto/ext/tracing/ipc/consumer_ipc_client.h"
+#include "perfetto/ext/tracing/ipc/service_ipc_host.h"
+#include "protos/perfetto/common/data_source_descriptor.gen.h"
+#include "protos/perfetto/config/trace_config.gen.h"
+#include "src/base/test/test_task_runner.h"
+#include "src/base/test/tmp_dir_tree.h"
+#include "src/profiling/memory/client.h"
+#include "src/profiling/memory/unhooked_allocator.h"
+#include "src/tracing/test/mock_consumer.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace profiling {
+namespace {
+
+using ::testing::NiceMock;
+using ::testing::NotNull;
+
+class TracingServiceThread {
+ public:
+ TracingServiceThread(const std::string& producer_socket,
+ const std::string& consumer_socket)
+ : runner_(base::ThreadTaskRunner::CreateAndStart("perfetto.svc")),
+ producer_socket_(producer_socket),
+ consumer_socket_(consumer_socket) {
+ runner_.PostTaskAndWaitForTesting([this]() {
+ svc_ = ServiceIPCHost::CreateInstance(&runner_);
+ bool res =
+ svc_->Start(producer_socket_.c_str(), consumer_socket_.c_str());
+ if (!res) {
+ PERFETTO_FATAL("Failed to start service listening on %s and %s",
+ producer_socket_.c_str(), consumer_socket_.c_str());
+ }
+ });
+ }
+
+ ~TracingServiceThread() {
+ runner_.PostTaskAndWaitForTesting([this]() { svc_.reset(); });
+ }
+
+ const std::string& producer_socket() const { return producer_socket_; }
+ const std::string& consumer_socket() const { return consumer_socket_; }
+
+ private:
+ base::ThreadTaskRunner runner_;
+
+ std::string producer_socket_;
+ std::string consumer_socket_;
+ std::unique_ptr<ServiceIPCHost> svc_;
+};
+
+class HeapprofdThread {
+ public:
+ HeapprofdThread(const std::string& producer_socket,
+ const std::string& heapprofd_socket)
+ : runner_(base::ThreadTaskRunner::CreateAndStart("heapprofd.svc")),
+ producer_socket_(producer_socket),
+ heapprofd_socket_(heapprofd_socket) {
+ runner_.PostTaskAndWaitForTesting([this]() {
+ heapprofd_.reset(new HeapprofdProducer(HeapprofdMode::kCentral, &runner_,
+ /* exit_when_done= */ false));
+
+ heapprofd_->ConnectWithRetries(producer_socket_.c_str());
+ listen_sock_ = base::UnixSocket::Listen(
+ heapprofd_socket_.c_str(), &heapprofd_->socket_delegate(), &runner_,
+ base::SockFamily::kUnix, base::SockType::kStream);
+ EXPECT_THAT(listen_sock_, NotNull());
+ });
+ }
+
+ void Sync() {
+ runner_.PostTaskAndWaitForTesting([]() {});
+ }
+
+ ~HeapprofdThread() {
+ runner_.PostTaskAndWaitForTesting([this]() {
+ listen_sock_.reset();
+ heapprofd_.reset();
+ });
+ }
+
+ const std::string& producer_socket() const { return producer_socket_; }
+ const std::string& heapprofd_socket() const { return heapprofd_socket_; }
+
+ private:
+ base::ThreadTaskRunner runner_;
+
+ std::string producer_socket_;
+ std::string heapprofd_socket_;
+ std::unique_ptr<HeapprofdProducer> heapprofd_;
+ std::unique_ptr<base::UnixSocket> listen_sock_;
+};
+
+TraceConfig MakeTraceConfig() {
+ TraceConfig trace_config;
+ trace_config.add_buffers()->set_size_kb(10 * 1024);
+ trace_config.set_data_source_stop_timeout_ms(10000);
+
+ auto* ds_config = trace_config.add_data_sources()->mutable_config();
+ ds_config->set_name("android.heapprofd");
+ ds_config->set_target_buffer(0);
+
+ protos::gen::HeapprofdConfig heapprofd_config;
+ heapprofd_config.set_sampling_interval_bytes(1);
+ heapprofd_config.add_pid(static_cast<uint64_t>(base::GetProcessId()));
+ heapprofd_config.set_all_heaps(true);
+ heapprofd_config.set_no_startup(true);
+ heapprofd_config.set_no_running(true);
+ ds_config->set_heapprofd_config_raw(heapprofd_config.SerializeAsString());
+ return trace_config;
+}
+
+bool WaitFor(std::function<bool()> predicate, long long timeout_ms = 40000) {
+ long long deadline_ms = base::GetWallTimeMs().count() + timeout_ms;
+ while (base::GetWallTimeMs().count() < deadline_ms) {
+ if (predicate())
+ return true;
+ base::SleepMicroseconds(100 * 1000); // 0.1 s.
+ }
+ return false;
+}
+
+bool WaitForDsRegistered(MockConsumer* mock_consumer,
+ const std::string& ds_name) {
+ return WaitFor([mock_consumer, &ds_name]() {
+ auto dss = mock_consumer->QueryServiceState().data_sources();
+ return std::any_of(dss.begin(), dss.end(),
+ [&](const TracingServiceState::DataSource& ds) {
+ return ds.ds_descriptor().name() == ds_name;
+ });
+ });
+}
+
+// Waits for the heapprofd data source to be registered and starts a trace with
+// it.
+std::unique_ptr<NiceMock<MockConsumer>> StartHeapprofdTrace(
+ const std::string& consumer_socket,
+ base::TestTaskRunner* task_runner) {
+ std::unique_ptr<NiceMock<MockConsumer>> mock_consumer;
+ mock_consumer.reset(new NiceMock<MockConsumer>(task_runner));
+ mock_consumer->Connect(ConsumerIPCClient::Connect(
+ consumer_socket.c_str(), mock_consumer.get(), task_runner));
+
+ if (WaitForDsRegistered(mock_consumer.get(), "android.heapprofd") == false) {
+ ADD_FAILURE();
+ return nullptr;
+ }
+
+ mock_consumer->ObserveEvents(ObservableEvents::TYPE_ALL_DATA_SOURCES_STARTED);
+ mock_consumer->EnableTracing(MakeTraceConfig());
+ mock_consumer->WaitForObservableEvents();
+
+ return mock_consumer;
+}
+
+TEST(HeapprofdProducerIntegrationTest, Restart) {
+ base::TmpDirTree tmpdir_;
+
+ base::TestTaskRunner task_runner;
+
+ tmpdir_.TrackFile("producer.sock");
+ tmpdir_.TrackFile("consumer.sock");
+
+ base::Optional<TracingServiceThread> tracing_service;
+ tracing_service.emplace(tmpdir_.AbsolutePath("producer.sock"),
+ tmpdir_.AbsolutePath("consumer.sock"));
+
+ tmpdir_.TrackFile("heapprofd.sock");
+ HeapprofdThread heapprofd_service(tmpdir_.AbsolutePath("producer.sock"),
+ tmpdir_.AbsolutePath("heapprofd.sock"));
+
+ std::unique_ptr<NiceMock<MockConsumer>> consumer =
+ StartHeapprofdTrace(tmpdir_.AbsolutePath("consumer.sock"), &task_runner);
+ ASSERT_THAT(consumer, NotNull());
+
+ base::Optional<base::UnixSocketRaw> client_sock =
+ perfetto::profiling::Client::ConnectToHeapprofd(
+ tmpdir_.AbsolutePath("heapprofd.sock"));
+ ASSERT_TRUE(client_sock.has_value());
+
+ std::shared_ptr<Client> client =
+ perfetto::profiling::Client::CreateAndHandshake(
+ std::move(client_sock.value()),
+ UnhookedAllocator<perfetto::profiling::Client>(malloc, free));
+
+ // Shutdown tracing service. This should cause HeapprofdProducer::Restart() to
+ // be executed on the heapprofd thread.
+ tracing_service.reset();
+ // Wait for the effects of the tracing service disconnect to propagate to the
+ // heapprofd thread.
+ heapprofd_service.Sync();
+
+ consumer->ForceDisconnect();
+ consumer.reset();
+
+ task_runner.RunUntilIdle();
+
+ // Start tracing service again. Heapprofd should reconnect.
+ ASSERT_EQ(remove(tmpdir_.AbsolutePath("producer.sock").c_str()), 0);
+ ASSERT_EQ(remove(tmpdir_.AbsolutePath("consumer.sock").c_str()), 0);
+ tracing_service.emplace(tmpdir_.AbsolutePath("producer.sock"),
+ tmpdir_.AbsolutePath("consumer.sock"));
+
+ consumer =
+ StartHeapprofdTrace(tmpdir_.AbsolutePath("consumer.sock"), &task_runner);
+ ASSERT_THAT(consumer, NotNull());
+
+ consumer->ForceDisconnect();
+ consumer.reset();
+}
+
+} // namespace
+} // namespace profiling
+} // namespace perfetto
diff --git a/src/trace_processor/importers/proto/BUILD.gn b/src/trace_processor/importers/proto/BUILD.gn
index 47bf6c7..b52254d 100644
--- a/src/trace_processor/importers/proto/BUILD.gn
+++ b/src/trace_processor/importers/proto/BUILD.gn
@@ -236,6 +236,7 @@
"active_chrome_processes_tracker_unittest.cc",
"heap_graph_tracker_unittest.cc",
"heap_profile_tracker_unittest.cc",
+ "network_trace_module_unittest.cc",
"perf_sample_tracker_unittest.cc",
"proto_trace_parser_unittest.cc",
]
diff --git a/src/trace_processor/importers/proto/network_trace_module.cc b/src/trace_processor/importers/proto/network_trace_module.cc
index ab56358..1ea1ca7 100644
--- a/src/trace_processor/importers/proto/network_trace_module.cc
+++ b/src/trace_processor/importers/proto/network_trace_module.cc
@@ -17,7 +17,6 @@
#include "src/trace_processor/importers/proto/network_trace_module.h"
#include "perfetto/ext/base/string_writer.h"
-#include "protos/perfetto/trace/android/network_trace.pbzero.h"
#include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
#include "protos/perfetto/trace/trace_packet.pbzero.h"
#include "src/trace_processor/importers/common/async_track_set_tracker.h"
@@ -61,7 +60,8 @@
net_arg_local_port_(context->storage->InternString("local_port")),
net_arg_remote_port_(context->storage->InternString("remote_port")),
net_ipproto_tcp_(context->storage->InternString("IPPROTO_TCP")),
- net_ipproto_udp_(context->storage->InternString("IPPROTO_UDP")) {
+ net_ipproto_udp_(context->storage->InternString("IPPROTO_UDP")),
+ packet_count_(context->storage->InternString("packet_count")) {
RegisterForField(TracePacket::kNetworkPacketFieldNumber, context);
RegisterForField(TracePacket::kNetworkPacketBundleFieldNumber, context);
}
@@ -132,14 +132,17 @@
case TracePacket::kNetworkPacketFieldNumber:
ParseNetworkPacketEvent(ts, decoder.network_packet());
return;
+ case TracePacket::kNetworkPacketBundleFieldNumber:
+ ParseNetworkPacketBundle(ts, decoder.network_packet_bundle());
+ return;
}
}
-void NetworkTraceModule::ParseNetworkPacketEvent(int64_t ts, ConstBytes blob) {
- using protos::pbzero::NetworkPacketEvent;
- using protos::pbzero::TrafficDirection;
- NetworkPacketEvent::Decoder evt(blob);
-
+void NetworkTraceModule::ParseGenericEvent(
+ int64_t ts,
+ int64_t dur,
+ protos::pbzero::NetworkPacketEvent::Decoder& evt,
+ std::function<void(ArgsTracker::BoundInserter*)> extra_args) {
// Tracks are per interface and per direction.
const char* track_suffix =
evt.direction() == TrafficDirection::DIR_INGRESS ? " Received"
@@ -167,12 +170,11 @@
}
TrackId track_id = context_->async_track_set_tracker->Scoped(
- context_->async_track_set_tracker->InternGlobalTrackSet(name_id), ts, 0);
+ context_->async_track_set_tracker->InternGlobalTrackSet(name_id), ts,
+ dur);
context_->slice_tracker->Scoped(
- ts, track_id, name_id, title_id, 0, [&](ArgsTracker::BoundInserter* i) {
- i->AddArg(net_arg_length_, Variadic::Integer(evt.length()));
-
+ ts, track_id, name_id, title_id, dur, [&](ArgsTracker::BoundInserter* i) {
StringId ip_proto;
if (evt.ip_proto() == kIpprotoTcp) {
ip_proto = net_ipproto_tcp_;
@@ -197,9 +199,30 @@
i->AddArg(net_arg_local_port_, Variadic::Integer(evt.local_port()));
i->AddArg(net_arg_remote_port_, Variadic::Integer(evt.remote_port()));
+ extra_args(i);
});
}
+void NetworkTraceModule::ParseNetworkPacketEvent(int64_t ts, ConstBytes blob) {
+ NetworkPacketEvent::Decoder event(blob);
+ ParseGenericEvent(ts, /*dur=*/0, event, [&](ArgsTracker::BoundInserter* i) {
+ i->AddArg(net_arg_length_, Variadic::Integer(event.length()));
+ });
+}
+
+void NetworkTraceModule::ParseNetworkPacketBundle(int64_t ts, ConstBytes blob) {
+ NetworkPacketBundle::Decoder event(blob);
+ NetworkPacketEvent::Decoder ctx(event.ctx());
+ int64_t dur = static_cast<int64_t>(event.total_duration());
+
+ // Any bundle that makes it through tokenization must be aggregated bundles
+ // with total packets/total length.
+ ParseGenericEvent(ts, dur, ctx, [&](ArgsTracker::BoundInserter* i) {
+ i->AddArg(net_arg_length_, Variadic::UnsignedInteger(event.total_length()));
+ i->AddArg(packet_count_, Variadic::UnsignedInteger(event.total_packets()));
+ });
+}
+
void NetworkTraceModule::PushPacketBufferForSort(int64_t timestamp,
PacketSequenceState* state) {
std::vector<uint8_t> v = packet_buffer_.SerializeAsArray();
diff --git a/src/trace_processor/importers/proto/network_trace_module.h b/src/trace_processor/importers/proto/network_trace_module.h
index 843f055..e83ce7b 100644
--- a/src/trace_processor/importers/proto/network_trace_module.h
+++ b/src/trace_processor/importers/proto/network_trace_module.h
@@ -20,7 +20,9 @@
#include <cstdint>
#include "perfetto/protozero/scattered_heap_buffer.h"
+#include "protos/perfetto/trace/android/network_trace.pbzero.h"
#include "protos/perfetto/trace/trace_packet.pbzero.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
#include "src/trace_processor/importers/common/parser_types.h"
#include "src/trace_processor/importers/proto/proto_importer_module.h"
#include "src/trace_processor/storage/trace_storage.h"
@@ -52,7 +54,14 @@
uint32_t field_id) override;
private:
+ void ParseGenericEvent(
+ int64_t ts,
+ int64_t dur,
+ protos::pbzero::NetworkPacketEvent::Decoder& evt,
+ std::function<void(ArgsTracker::BoundInserter*)> extra_args);
+
void ParseNetworkPacketEvent(int64_t ts, protozero::ConstBytes blob);
+ void ParseNetworkPacketBundle(int64_t ts, protozero::ConstBytes blob);
// Helper to simplify pushing a TracePacket to the sorter. The caller fills in
// the packet buffer and uses this to push for sorting and reset the buffer.
@@ -69,6 +78,7 @@
const StringId net_arg_remote_port_;
const StringId net_ipproto_tcp_;
const StringId net_ipproto_udp_;
+ const StringId packet_count_;
};
} // namespace trace_processor
diff --git a/src/trace_processor/importers/proto/network_trace_module_unittest.cc b/src/trace_processor/importers/proto/network_trace_module_unittest.cc
new file mode 100644
index 0000000..56118c5
--- /dev/null
+++ b/src/trace_processor/importers/proto/network_trace_module_unittest.cc
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2023 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/trace_processor/importers/proto/network_trace_module.h"
+
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/args_translation_table.h"
+#include "src/trace_processor/importers/common/async_track_set_tracker.h"
+#include "src/trace_processor/importers/common/global_args_tracker.h"
+#include "src/trace_processor/importers/common/slice_tracker.h"
+#include "src/trace_processor/importers/common/slice_translation_table.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
+#include "src/trace_processor/importers/proto/proto_trace_parser.h"
+#include "src/trace_processor/importers/proto/proto_trace_reader.h"
+#include "src/trace_processor/sorter/trace_sorter.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+using ::perfetto::protos::pbzero::TrafficDirection;
+
+class NetworkTraceModuleTest : public testing::Test {
+ public:
+ NetworkTraceModuleTest() {
+ context_.storage.reset(new TraceStorage());
+ storage_ = context_.storage.get();
+
+ context_.track_tracker.reset(new TrackTracker(&context_));
+ context_.slice_tracker.reset(new SliceTracker(&context_));
+ context_.args_tracker.reset(new ArgsTracker(&context_));
+ context_.global_args_tracker.reset(new GlobalArgsTracker(storage_));
+ context_.slice_translation_table.reset(new SliceTranslationTable(storage_));
+ context_.args_translation_table.reset(new ArgsTranslationTable(storage_));
+ context_.async_track_set_tracker.reset(new AsyncTrackSetTracker(&context_));
+ context_.sorter.reset(new TraceSorter(
+ &context_, std::make_unique<ProtoTraceParser>(&context_),
+ TraceSorter::SortingMode::kFullSort));
+ }
+
+ util::Status TokenizeAndParse() {
+ context_.chunk_reader.reset(new ProtoTraceReader(&context_));
+
+ trace_->Finalize();
+ std::vector<uint8_t> v = trace_.SerializeAsArray();
+ trace_.Reset();
+
+ auto status = context_.chunk_reader->Parse(
+ TraceBlobView(TraceBlob::CopyFrom(v.data(), v.size())));
+ context_.sorter->ExtractEventsForced();
+ context_.slice_tracker->FlushPendingSlices();
+ context_.args_tracker->Flush();
+ return status;
+ }
+
+ bool HasArg(ArgSetId set_id, base::StringView key, Variadic value) {
+ StringId key_id = storage_->InternString(key);
+ const auto& args = storage_->arg_table();
+ RowMap rm = args.FilterToRowMap({args.arg_set_id().eq(set_id)});
+ bool found = false;
+ for (auto it = rm.IterateRows(); it; it.Next()) {
+ if (args.key()[it.index()] == key_id) {
+ EXPECT_EQ(args.flat_key()[it.index()], key_id);
+ if (storage_->GetArgValue(it.index()) == value) {
+ found = true;
+ break;
+ }
+ }
+ }
+ return found;
+ }
+
+ protected:
+ protozero::HeapBuffered<protos::pbzero::Trace> trace_;
+ TraceProcessorContext context_;
+ TraceStorage* storage_;
+};
+
+TEST_F(NetworkTraceModuleTest, ParseAndFormatPacket) {
+ NetworkTraceModule module(&context_);
+
+ auto* packet = trace_->add_packet();
+ packet->set_timestamp(123);
+
+ auto* event = packet->set_network_packet();
+ event->set_direction(TrafficDirection::DIR_EGRESS);
+ event->set_length(72);
+ event->set_uid(1010);
+ event->set_tag(0x407);
+ event->set_local_port(5100);
+ event->set_remote_port(443);
+ event->set_tcp_flags(0b10010);
+ event->set_ip_proto(6);
+ event->set_interface("wlan");
+
+ ASSERT_TRUE(TokenizeAndParse().ok());
+
+ const auto& slices = storage_->slice_table();
+ ASSERT_EQ(slices.row_count(), 1u);
+ EXPECT_EQ(slices.ts()[0], 123);
+
+ EXPECT_TRUE(HasArg(1u, "packet_length", Variadic::Integer(72)));
+ EXPECT_TRUE(HasArg(1u, "local_port", Variadic::Integer(5100)));
+ EXPECT_TRUE(HasArg(1u, "remote_port", Variadic::Integer(443)));
+ EXPECT_TRUE(HasArg(1u, "packet_transport",
+ Variadic::String(storage_->InternString("IPPROTO_TCP"))));
+ EXPECT_TRUE(HasArg(1u, "socket_tag",
+ Variadic::String(storage_->InternString("0x407"))));
+ EXPECT_TRUE(HasArg(1u, "packet_tcp_flags",
+ Variadic::String(storage_->InternString(".s..a..."))));
+}
+
+TEST_F(NetworkTraceModuleTest, TokenizeAndParsePerPacketBundle) {
+ NetworkTraceModule module(&context_);
+
+ auto* packet = trace_->add_packet();
+ packet->set_timestamp(123);
+
+ protozero::PackedVarInt timestamps;
+ timestamps.Append(0);
+ timestamps.Append(10);
+
+ protozero::PackedVarInt lengths;
+ lengths.Append(72);
+ lengths.Append(100);
+
+ auto* event = packet->set_network_packet_bundle();
+ event->set_packet_timestamps(timestamps);
+ event->set_packet_lengths(lengths);
+
+ auto* ctx = event->set_ctx();
+ ctx->set_uid(456);
+
+ ASSERT_TRUE(TokenizeAndParse().ok());
+
+ const auto& slices = storage_->slice_table();
+ ASSERT_EQ(slices.row_count(), 2u);
+ EXPECT_EQ(slices.ts()[0], 123);
+ EXPECT_EQ(slices.ts()[1], 133);
+
+ EXPECT_TRUE(HasArg(1u, "packet_length", Variadic::Integer(72)));
+ EXPECT_TRUE(HasArg(2u, "packet_length", Variadic::Integer(100)));
+}
+
+TEST_F(NetworkTraceModuleTest, TokenizeAndParseAggregateBundle) {
+ NetworkTraceModule module(&context_);
+
+ auto* packet = trace_->add_packet();
+ packet->set_timestamp(123);
+
+ auto* event = packet->set_network_packet_bundle();
+ event->set_total_packets(2);
+ event->set_total_duration(10);
+ event->set_total_length(172);
+
+ auto* ctx = event->set_ctx();
+ ctx->set_uid(456);
+
+ ASSERT_TRUE(TokenizeAndParse().ok());
+
+ const auto& slices = storage_->slice_table();
+ ASSERT_EQ(slices.row_count(), 1u);
+ EXPECT_EQ(slices.ts()[0], 123);
+ EXPECT_EQ(slices.dur()[0], 10);
+
+ EXPECT_TRUE(HasArg(1u, "packet_length", Variadic::UnsignedInteger(172)));
+ EXPECT_TRUE(HasArg(1u, "packet_count", Variadic::UnsignedInteger(2)));
+}
+
+} // namespace
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/metrics/sql/android/android_startup.sql b/src/trace_processor/metrics/sql/android/android_startup.sql
index cb925ec..8399ddb 100644
--- a/src/trace_processor/metrics/sql/android/android_startup.sql
+++ b/src/trace_processor/metrics/sql/android/android_startup.sql
@@ -305,12 +305,12 @@
FROM (
SELECT 'dex2oat running during launch' AS slow_cause
WHERE
- DUR_OF_PROCESS_RUNNING_CONCURRENT_TO_LAUNCH(launches.startup_id, '*dex2oat64') > 20e6
+ DUR_OF_PROCESS_RUNNING_CONCURRENT_TO_LAUNCH(launches.startup_id, '*dex2oat64') > 0
UNION ALL
SELECT 'installd running during launch' AS slow_cause
WHERE
- DUR_OF_PROCESS_RUNNING_CONCURRENT_TO_LAUNCH(launches.startup_id, '*installd') > 150e6
+ DUR_OF_PROCESS_RUNNING_CONCURRENT_TO_LAUNCH(launches.startup_id, '*installd') > 0
UNION ALL
SELECT 'Main Thread - Time spent in Running state'
@@ -399,21 +399,21 @@
SELECT COUNT(1)
FROM ANDROID_SLICES_FOR_STARTUP_AND_SLICE_NAME(launches.startup_id, 'JIT compiling*')
WHERE thread_name = 'Jit thread pool'
- ) > 40
+ ) > 65
UNION ALL
SELECT 'Broadcast dispatched count'
WHERE COUNT_SLICES_CONCURRENT_TO_LAUNCH(
launches.startup_id,
'Broadcast dispatched*'
- ) > 10
+ ) > 15
UNION ALL
SELECT 'Broadcast received count'
WHERE COUNT_SLICES_CONCURRENT_TO_LAUNCH(
launches.startup_id,
'broadcastReceiveReg*'
- ) > 10
+ ) > 50
UNION ALL
SELECT 'No baseline or cloud profiles'
diff --git a/src/tracing/core/tracing_service_impl.cc b/src/tracing/core/tracing_service_impl.cc
index 5277657..0ee3122 100644
--- a/src/tracing/core/tracing_service_impl.cc
+++ b/src/tracing/core/tracing_service_impl.cc
@@ -102,15 +102,6 @@
namespace perfetto {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \
- PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
-// These are the only SELinux approved dir for trace files that are created
-// directly by traced.
-const char* kTraceDirBasePath = "/data/misc/perfetto-traces/";
-const char* kAndroidProductionBugreportTracePath =
- "/data/misc/perfetto-traces/bugreport/systrace.pftrace";
-#endif
-
namespace {
constexpr int kMaxBuffersPerConsumer = 128;
constexpr uint32_t kDefaultSnapshotsIntervalMs = 10 * 1000;
@@ -241,9 +232,7 @@
return filter_matches || filter_regex_matches;
}
-// Used when:
-// 1. TraceConfig.write_into_file == true and output_path is not empty.
-// 2. Calling SaveTraceForBugreport(), from perfetto --save-for-bugreport.
+// Used when TraceConfig.write_into_file == true and output_path is not empty.
base::ScopedFile CreateTraceFile(const std::string& path, bool overwrite) {
#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \
PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
@@ -251,6 +240,9 @@
// It just improves the actionability of the error when people try to save the
// trace in a location that is not SELinux-allowed (a generic "permission
// denied" vs "don't put it here, put it there").
+ // These are the only SELinux approved dir for trace files that are created
+ // directly by traced.
+ static const char* kTraceDirBasePath = "/data/misc/perfetto-traces/";
if (!base::StartsWith(path, kTraceDirBasePath)) {
PERFETTO_ELOG("Invalid output_path %s. On Android it must be within %s.",
path.c_str(), kTraceDirBasePath);
@@ -271,10 +263,6 @@
return fd;
}
-std::string GetBugreportTmpPath() {
- return GetBugreportPath() + ".tmp";
-}
-
bool ShouldLogEvent(const TraceConfig& cfg) {
switch (cfg.statsd_logging()) {
case TraceConfig::STATSD_LOGGING_ENABLED:
@@ -320,16 +308,6 @@
constexpr uint8_t TracingServiceImpl::kSyncMarker[];
#endif
-std::string GetBugreportPath() {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \
- PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
- return kAndroidProductionBugreportTracePath;
-#else
- // Only for tests, SaveTraceForBugreport is not used on other OSes.
- return base::GetSysTempDir() + "/bugreport.pftrace";
-#endif
-}
-
// static
std::unique_ptr<TracingService> TracingService::CreateInstance(
std::unique_ptr<SharedMemory::Factory> shm_factory,
@@ -1692,11 +1670,6 @@
ReadBuffersIntoFile(tracing_session->id);
}
- if (tracing_session->on_disable_callback_for_bugreport) {
- std::move(tracing_session->on_disable_callback_for_bugreport)();
- tracing_session->on_disable_callback_for_bugreport = nullptr;
- }
-
MaybeLogUploadEvent(tracing_session->config, tracing_session->trace_uuid,
PerfettoStatsdAtom::kTracedNotifyTracingDisabled);
@@ -2078,21 +2051,6 @@
return false;
}
- // If a bugreport request happened and the trace was stolen for that, give
- // an empty trace with a clear signal to the consumer. This deals only with
- // the case of readback-from-IPC. A similar code-path deals with the
- // write_into_file case in MaybeSaveTraceForBugreport().
- if (tracing_session->seized_for_bugreport) {
- std::vector<TracePacket> packets;
- if (!tracing_session->config.builtin_data_sources()
- .disable_service_events()) {
- EmitSeizedForBugreportLifecycleEvent(&packets);
- }
- EmitLifecycleEvents(tracing_session, &packets);
- consumer->consumer_->OnTraceData(std::move(packets), /*has_more=*/false);
- return true;
- }
-
if (IsWaitingForTrigger(tracing_session))
return false;
@@ -2141,8 +2099,7 @@
if (!tracing_session->write_into_file)
return false;
- if (!tracing_session->seized_for_bugreport &&
- IsWaitingForTrigger(tracing_session))
+ if (IsWaitingForTrigger(tracing_session))
return false;
// ReadBuffers() can allocate memory internally, for filtering. By limiting
@@ -2501,25 +2458,22 @@
bool is_long_trace =
(tracing_session->config.write_into_file() &&
tracing_session->config.file_write_period_ms() < kMillisPerDay);
- bool seized_for_bugreport = tracing_session->seized_for_bugreport;
tracing_sessions_.erase(tsid);
tracing_session = nullptr;
UpdateMemoryGuardrail();
PERFETTO_LOG("Tracing session %" PRIu64 " ended, total sessions:%zu", tsid,
tracing_sessions_.size());
-
#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD) && \
PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
- if (notify_traceur && (seized_for_bugreport || is_long_trace)) {
+ if (notify_traceur && is_long_trace) {
PERFETTO_LAZY_LOAD(android_internal::NotifyTraceSessionEnded, notify_fn);
- if (!notify_fn || !notify_fn(seized_for_bugreport))
+ if (!notify_fn || !notify_fn(/*session_stolen=*/false))
PERFETTO_ELOG("Failed to notify Traceur long tracing has ended");
}
#else
base::ignore_result(notify_traceur);
base::ignore_result(is_long_trace);
- base::ignore_result(seized_for_bugreport);
#endif
}
@@ -2983,6 +2937,23 @@
return &it->second;
}
+TracingServiceImpl::TracingSession*
+TracingServiceImpl::FindTracingSessionWithMaxBugreportScore() {
+ TracingSession* max_session = nullptr;
+ for (auto& session_id_and_session : tracing_sessions_) {
+ auto& session = session_id_and_session.second;
+ const int32_t score = session.config.bugreport_score();
+ // Exclude sessions with 0 (or below) score. By default tracing sessions
+ // should NOT be eligible to be attached to bugreports.
+ if (score <= 0 || session.state != TracingSession::STARTED)
+ continue;
+
+ if (!max_session || score > max_session->config.bugreport_score())
+ max_session = &session;
+ }
+ return max_session;
+}
+
ProducerID TracingServiceImpl::GetNextProducerID() {
PERFETTO_DCHECK_THREAD(thread_checker_);
PERFETTO_CHECK(producers_.size() < kMaxProducerID);
@@ -3411,18 +3382,6 @@
SerializeAndAppendPacket(packets, std::move(pair.second));
}
-void TracingServiceImpl::EmitSeizedForBugreportLifecycleEvent(
- std::vector<TracePacket>* packets) {
- protozero::HeapBuffered<protos::pbzero::TracePacket> packet;
- packet->set_timestamp(static_cast<uint64_t>(base::GetBootTimeNs().count()));
- packet->set_trusted_uid(static_cast<int32_t>(uid_));
- packet->set_trusted_packet_sequence_id(kServicePacketSequenceID);
- auto* service_event = packet->set_service_event();
- service_event->AppendVarInt(
- protos::pbzero::TracingServiceEvent::kSeizedForBugreportFieldNumber, 1);
- SerializeAndAppendPacket(packets, packet.SerializeAsArray());
-}
-
void TracingServiceImpl::MaybeEmitReceivedTriggers(
TracingSession* tracing_session,
std::vector<TracePacket>* packets) {
@@ -3445,91 +3404,6 @@
}
}
-bool TracingServiceImpl::MaybeSaveTraceForBugreport(
- std::function<void()> callback) {
- TracingSession* max_session = nullptr;
- TracingSessionID max_tsid = 0;
- for (auto& session_id_and_session : tracing_sessions_) {
- auto& session = session_id_and_session.second;
- const int32_t score = session.config.bugreport_score();
- // Exclude sessions with 0 (or below) score. By default tracing sessions
- // should NOT be eligible to be attached to bugreports.
- if (score <= 0 || session.state != TracingSession::STARTED)
- continue;
-
- // Also don't try to steal long traces with write_into_file if their content
- // has been already partially written into a file, as we would get partial
- // traces on both sides. We can't just copy the original file into the
- // bugreport because the file could be too big (GBs) for bugreports.
- // The only case where it's legit to steal traces with write_into_file, is
- // when the consumer specified a very large write_period_ms (e.g. 24h),
- // meaning that this is effectively a ring-buffer trace. Traceur (the
- // Android System Tracing app), which uses --detach, does this to have a
- // consistent invocation path for long-traces and ring-buffer-mode traces.
- if (session.write_into_file && session.bytes_written_into_file > 0)
- continue;
-
- // If we are already in the process of finalizing another trace for
- // bugreport, don't even start another one, as they would try to write onto
- // the same file.
- if (session.on_disable_callback_for_bugreport)
- return false;
-
- if (!max_session || score > max_session->config.bugreport_score()) {
- max_session = &session;
- max_tsid = session_id_and_session.first;
- }
- }
-
- // No eligible trace found.
- if (!max_session)
- return false;
-
- PERFETTO_LOG("Seizing trace for bugreport. tsid:%" PRIu64
- " state:%d wf:%d score:%d name:\"%s\"",
- max_tsid, max_session->state, !!max_session->write_into_file,
- max_session->config.bugreport_score(),
- max_session->config.unique_session_name().c_str());
-
- auto br_fd = CreateTraceFile(GetBugreportTmpPath(), /*overwrite=*/true);
- if (!br_fd)
- return false;
-
- if (max_session->write_into_file) {
- auto fd = *max_session->write_into_file;
- // If we are stealing a write_into_file session, add a marker that explains
- // why the trace has been stolen rather than creating an empty file. This is
- // only for write_into_file traces. A similar code path deals with the case
- // of reading-back a seized trace from IPC in ReadBuffersIntoConsumer().
- if (!max_session->config.builtin_data_sources().disable_service_events()) {
- std::vector<TracePacket> packets;
- EmitSeizedForBugreportLifecycleEvent(&packets);
- for (auto& packet : packets) {
- char* preamble;
- size_t preamble_size = 0;
- std::tie(preamble, preamble_size) = packet.GetProtoPreamble();
- base::WriteAll(fd, preamble, preamble_size);
- for (const Slice& slice : packet.slices()) {
- base::WriteAll(fd, slice.start, slice.size);
- }
- } // for (packets)
- } // if (!disable_service_events())
- } // if (max_session->write_into_file)
- max_session->write_into_file = std::move(br_fd);
- max_session->on_disable_callback_for_bugreport = std::move(callback);
- max_session->seized_for_bugreport = true;
-
- // Post a task to avoid that early FlushAndDisableTracing() failures invoke
- // the callback before we return. That would re-enter in a weird way the
- // callstack of the calling ConsumerEndpointImpl::SaveTraceForBugreport().
- auto weak_this = weak_ptr_factory_.GetWeakPtr();
- task_runner_->PostTask([weak_this, max_tsid] {
- if (weak_this)
- weak_this->FlushAndDisableTracing(max_tsid);
- });
- return true;
-}
-
void TracingServiceImpl::MaybeLogUploadEvent(const TraceConfig& cfg,
const base::Uuid& uuid,
PerfettoStatsdAtom atom,
@@ -3571,6 +3445,17 @@
void TracingServiceImpl::FlushAndCloneSession(ConsumerEndpointImpl* consumer,
TracingSessionID tsid) {
PERFETTO_DCHECK_THREAD(thread_checker_);
+
+ if (tsid == kBugreportSessionId) {
+ TracingSession* session = FindTracingSessionWithMaxBugreportScore();
+ if (!session) {
+ consumer->consumer_->OnSessionCloned(
+ false, "No tracing sessions eligible for bugreport found");
+ return;
+ }
+ tsid = session->id;
+ }
+
auto weak_this = weak_ptr_factory_.GetWeakPtr();
auto weak_consumer = consumer->GetWeakPtr();
Flush(tsid, 0, [weak_this, tsid, weak_consumer](bool final_flush_outcome) {
@@ -3589,6 +3474,7 @@
bool final_flush_outcome) {
PERFETTO_DLOG("CloneSession(%" PRIu64 ") started, consumer uid: %d", src_tsid,
static_cast<int>(consumer->uid_));
+
TracingSession* src = GetTracingSession(src_tsid);
// The session might be gone by the time we try to clone it.
@@ -3662,6 +3548,11 @@
new protozero::MessageFilter(*src->trace_filter));
}
+ SnapshotLifecyleEvent(
+ cloned_session,
+ protos::pbzero::TracingServiceEvent::kTracingDisabledFieldNumber,
+ true /* snapshot_clocks */);
+
PERFETTO_DLOG("Consumer (uid:%d) cloned tracing session %" PRIu64
" -> %" PRIu64,
static_cast<int>(consumer->uid_), src_tsid, tsid);
@@ -3982,21 +3873,9 @@
void TracingServiceImpl::ConsumerEndpointImpl::SaveTraceForBugreport(
SaveTraceForBugreportCallback consumer_callback) {
- PERFETTO_DCHECK_THREAD(thread_checker_);
- auto on_complete_callback = [consumer_callback] {
- if (rename(GetBugreportTmpPath().c_str(), GetBugreportPath().c_str())) {
- consumer_callback(false, "rename(" + GetBugreportTmpPath() + ", " +
- GetBugreportPath() + ") failed (" +
- strerror(errno) + ")");
- } else {
- consumer_callback(true, GetBugreportPath());
- }
- };
- if (!service_->MaybeSaveTraceForBugreport(std::move(on_complete_callback))) {
- consumer_callback(false,
- "No trace with TraceConfig.bugreport_score > 0 eligible "
- "for bug reporting was found");
- }
+ consumer_callback(false,
+ "SaveTraceForBugreport is deprecated. Use "
+ "CloneSession(kBugreportSessionId) instead.");
}
void TracingServiceImpl::ConsumerEndpointImpl::CloneSession(
diff --git a/src/tracing/core/tracing_service_impl.h b/src/tracing/core/tracing_service_impl.h
index 4800559..02e5639 100644
--- a/src/tracing/core/tracing_service_impl.h
+++ b/src/tracing/core/tracing_service_impl.h
@@ -632,11 +632,6 @@
uint64_t max_file_size_bytes = 0;
uint64_t bytes_written_into_file = 0;
- // Set when using SaveTraceForBugreport(). This callback will be called
- // when the tracing session ends and the data has been saved into the file.
- std::function<void()> on_disable_callback_for_bugreport;
- bool seized_for_bugreport = false;
-
// Periodic task for snapshotting service events (e.g. clocks, sync markers
// etc)
base::PeriodicTask snapshot_periodic_task;
@@ -680,6 +675,10 @@
// session doesn't exists.
TracingSession* GetTracingSession(TracingSessionID);
+ // Returns a pointer to the tracing session that has the highest
+ // TraceConfig.bugreport_score, if any, or nullptr.
+ TracingSession* FindTracingSessionWithMaxBugreportScore();
+
// Returns a pointer to the |tracing_sessions_| entry, matching the given
// uid and detach key, or nullptr if no such session exists.
TracingSession* GetDetachedSession(uid_t, const std::string& key);
@@ -708,12 +707,10 @@
void EmitStats(TracingSession*, std::vector<TracePacket>*);
TraceStats GetTraceStats(TracingSession*);
void EmitLifecycleEvents(TracingSession*, std::vector<TracePacket>*);
- void EmitSeizedForBugreportLifecycleEvent(std::vector<TracePacket>*);
void MaybeEmitUuidAndTraceConfig(TracingSession*, std::vector<TracePacket>*);
void MaybeEmitSystemInfo(TracingSession*, std::vector<TracePacket>*);
void MaybeEmitReceivedTriggers(TracingSession*, std::vector<TracePacket>*);
void MaybeNotifyAllDataSourcesStarted(TracingSession*);
- bool MaybeSaveTraceForBugreport(std::function<void()> callback);
void OnFlushTimeout(TracingSessionID, FlushRequestID);
void OnDisableTracingTimeout(TracingSessionID);
void DisableTracingNotifyConsumerAndFlushFile(TracingSession*);
diff --git a/src/tracing/test/mock_consumer.cc b/src/tracing/test/mock_consumer.cc
index dc44b31..e24ffcc 100644
--- a/src/tracing/test/mock_consumer.cc
+++ b/src/tracing/test/mock_consumer.cc
@@ -39,8 +39,9 @@
task_runner_->RunUntilCheckpoint(checkpoint_name);
}
-void MockConsumer::Connect(TracingService* svc, uid_t uid) {
- service_endpoint_ = svc->ConnectConsumer(this, uid);
+void MockConsumer::Connect(
+ std::unique_ptr<TracingService::ConsumerEndpoint> service_endpoint) {
+ service_endpoint_ = std::move(service_endpoint);
static int i = 0;
auto checkpoint_name = "on_consumer_connect_" + std::to_string(i++);
auto on_connect = task_runner_->CreateCheckpoint(checkpoint_name);
@@ -48,6 +49,14 @@
task_runner_->RunUntilCheckpoint(checkpoint_name);
}
+void MockConsumer::Connect(TracingService* svc, uid_t uid) {
+ Connect(svc->ConnectConsumer(this, uid));
+}
+
+void MockConsumer::ForceDisconnect() {
+ service_endpoint_.reset();
+}
+
void MockConsumer::EnableTracing(const TraceConfig& trace_config,
base::ScopedFile write_into_file) {
service_endpoint_->EnableTracing(trace_config, std::move(write_into_file));
diff --git a/src/tracing/test/mock_consumer.h b/src/tracing/test/mock_consumer.h
index a840349..f18671e 100644
--- a/src/tracing/test/mock_consumer.h
+++ b/src/tracing/test/mock_consumer.h
@@ -47,7 +47,9 @@
explicit MockConsumer(base::TestTaskRunner*);
~MockConsumer() override;
+ void Connect(std::unique_ptr<TracingService::ConsumerEndpoint>);
void Connect(TracingService* svc, uid_t = 0);
+ void ForceDisconnect();
void EnableTracing(const TraceConfig&, base::ScopedFile = base::ScopedFile());
void StartTracing();
void ChangeTraceConfig(const TraceConfig&);
diff --git a/test/BUILD.gn b/test/BUILD.gn
index 768b8ad..6b995ed 100644
--- a/test/BUILD.gn
+++ b/test/BUILD.gn
@@ -38,6 +38,7 @@
"../protos/perfetto/trace/sys_stats:cpp",
"../src/base:base",
"../src/base:test_support",
+ "../src/perfetto_cmd:bugreport_path",
]
if (enable_perfetto_traced_probes) {
deps += [
diff --git a/test/android_integrationtest.cc b/test/android_integrationtest.cc
index 19e894d..9d4e543 100644
--- a/test/android_integrationtest.cc
+++ b/test/android_integrationtest.cc
@@ -63,33 +63,6 @@
using ::testing::Property;
using ::testing::SizeIs;
-// For the SaveForBugreport* tests.
-void SetTraceConfigForBugreportTest(TraceConfig* trace_config) {
- trace_config->add_buffers()->set_size_kb(4096);
- trace_config->set_duration_ms(60000); // Will never hit this.
- trace_config->set_bugreport_score(10);
- auto* ds_config = trace_config->add_data_sources()->mutable_config();
- ds_config->set_name("android.perfetto.FakeProducer");
- ds_config->mutable_for_testing()->set_message_count(3);
- ds_config->mutable_for_testing()->set_message_size(10);
- ds_config->mutable_for_testing()->set_send_batch_on_register(true);
-}
-
-// For the SaveForBugreport* tests.
-static void VerifyBugreportTraceContents() {
- // Read the trace written in the fixed location (/data/misc/perfetto-traces/
- // on Android, /tmp/ on Linux/Mac) and make sure it has the right contents.
- std::string trace_str;
- base::ReadFile(GetBugreportPath(), &trace_str);
- ASSERT_FALSE(trace_str.empty());
- protos::gen::Trace trace;
- ASSERT_TRUE(trace.ParseFromString(trace_str));
- int test_packets = 0;
- for (const auto& p : trace.packet())
- test_packets += p.has_for_testing() ? 1 : 0;
- ASSERT_EQ(test_packets, 3);
-}
-
} // namespace
TEST(PerfettoAndroidIntegrationTest, TestKmemActivity) {
@@ -283,125 +256,6 @@
ASSERT_TRUE(has_battery_packet);
}
-TEST(PerfettoAndroidIntegrationTest, SaveForBugreport) {
- base::TestTaskRunner task_runner;
-
- TestHelper helper(&task_runner);
- helper.StartServiceIfRequired();
- helper.ConnectFakeProducer();
- helper.ConnectConsumer();
- helper.WaitForConsumerConnect();
-
- TraceConfig trace_config;
- SetTraceConfigForBugreportTest(&trace_config);
-
- helper.StartTracing(trace_config);
- helper.WaitForProducerEnabled();
-
- EXPECT_TRUE(helper.SaveTraceForBugreportAndWait());
- helper.WaitForTracingDisabled();
-
- VerifyBugreportTraceContents();
-
- // Now read the trace returned to the consumer via ReadBuffers. This should
- // be always empty because --save-for-bugreport takes it over and makes the
- // buffers unreadable by the consumer (by virtue of force-setting
- // write_into_file, which is incompatible with ReadBuffers()). The only
- // content should be the |seized_for_bugreport| flag.
- helper.ReadData();
- helper.WaitForReadData();
- const auto& packets = helper.full_trace();
- ASSERT_EQ(packets.size(), 1u);
- for (const auto& p : packets) {
- ASSERT_TRUE(p.has_service_event());
- ASSERT_TRUE(p.service_event().seized_for_bugreport());
- }
-}
-
-// Tests that the SaveForBugreport logic works also for traces with
-// write_into_file = true (with a passed file descriptor).
-TEST(PerfettoAndroidIntegrationTest, SaveForBugreport_WriteIntoFile) {
- base::TestTaskRunner task_runner;
-
- TestHelper helper(&task_runner);
- helper.StartServiceIfRequired();
- helper.ConnectFakeProducer();
- helper.ConnectConsumer();
- helper.WaitForConsumerConnect();
-
- TraceConfig trace_config;
- SetTraceConfigForBugreportTest(&trace_config);
- trace_config.set_file_write_period_ms(60000); // Will never hit this.
- trace_config.set_write_into_file(true);
-
- auto pipe_pair = base::Pipe::Create();
- helper.StartTracing(trace_config, std::move(pipe_pair.wr));
- helper.WaitForProducerEnabled();
-
- EXPECT_TRUE(helper.SaveTraceForBugreportAndWait());
- helper.WaitForTracingDisabled();
-
- VerifyBugreportTraceContents();
-
- // Now read the original file descriptor passed in.
- std::string trace_bytes;
- ASSERT_TRUE(base::ReadPlatformHandle(*pipe_pair.rd, &trace_bytes));
- protos::gen::Trace trace;
- ASSERT_TRUE(trace.ParseFromString(trace_bytes));
- ASSERT_EQ(trace.packet().size(), 1u);
- for (const auto& p : trace.packet()) {
- ASSERT_TRUE(p.has_service_event());
- ASSERT_TRUE(p.service_event().seized_for_bugreport());
- }
-}
-
-// Tests that SaveTraceForBugreport() works also if the trace has triggers
-// defined and those triggers have not been hit. This is a regression test for
-// b/188008375 .
-#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
-// Disabled due to b/191940560
-#define MAYBE_SaveForBugreport_Triggers DISABLED_SaveForBugreport_Triggers
-#else
-#define MAYBE_SaveForBugreport_Triggers SaveForBugreport_Triggers
-#endif
-TEST(PerfettoAndroidIntegrationTest, MAYBE_SaveForBugreport_Triggers) {
- base::TestTaskRunner task_runner;
-
- TestHelper helper(&task_runner);
- helper.StartServiceIfRequired();
- helper.ConnectFakeProducer();
- helper.ConnectConsumer();
- helper.WaitForConsumerConnect();
-
- TraceConfig trace_config;
- SetTraceConfigForBugreportTest(&trace_config);
- trace_config.set_duration_ms(0); // set_trigger_timeout_ms is used instead.
- auto* trigger_config = trace_config.mutable_trigger_config();
- trigger_config->set_trigger_timeout_ms(8.64e+7);
- trigger_config->set_trigger_mode(TraceConfig::TriggerConfig::STOP_TRACING);
- auto* trigger = trigger_config->add_triggers();
- trigger->set_name("trigger_name");
- trigger->set_stop_delay_ms(1);
-
- helper.StartTracing(trace_config);
- helper.WaitForProducerEnabled();
-
- EXPECT_TRUE(helper.SaveTraceForBugreportAndWait());
- helper.WaitForTracingDisabled();
-
- VerifyBugreportTraceContents();
-
- // Now read the original trace.
- helper.ReadData();
- helper.WaitForReadData();
- const auto& packets = helper.full_trace();
- ASSERT_EQ(packets.size(), 1u);
- for (const auto& p : packets) {
- ASSERT_TRUE(p.has_service_event());
- ASSERT_TRUE(p.service_event().seized_for_bugreport());
- }
-}
-
} // namespace perfetto
#endif // PERFETTO_OS_ANDROID
diff --git a/test/cmdline_integrationtest.cc b/test/cmdline_integrationtest.cc
index 0747789..cbc3555 100644
--- a/test/cmdline_integrationtest.cc
+++ b/test/cmdline_integrationtest.cc
@@ -27,6 +27,7 @@
#include "perfetto/protozero/scattered_heap_buffer.h"
#include "src/base/test/test_task_runner.h"
#include "src/base/test/utils.h"
+#include "src/perfetto_cmd/bugreport_path.h"
#include "test/gtest_and_gmock.h"
#include "test/test_helper.h"
@@ -66,6 +67,19 @@
return path;
}
+// For the SaveForBugreport* tests.
+TraceConfig CreateTraceConfigForBugreportTest() {
+ TraceConfig trace_config;
+ trace_config.add_buffers()->set_size_kb(4096);
+ trace_config.set_duration_ms(60000); // Will never hit this.
+ trace_config.set_bugreport_score(10);
+ auto* ds_config = trace_config.add_data_sources()->mutable_config();
+ ds_config->set_name("android.perfetto.FakeProducer");
+ ds_config->mutable_for_testing()->set_message_count(3);
+ ds_config->mutable_for_testing()->set_message_size(10);
+ return trace_config;
+}
+
class PerfettoCmdlineTest : public ::testing::Test {
public:
void SetUp() override {
@@ -120,6 +134,68 @@
return Exec("trigger_perfetto", std::move(args), std::move(std_in));
}
+ // This is in common to the 3 TEST_F SaveForBugreport* fixtures, which differ
+ // only in the config, passed here as input.
+ void RunBugreportTest(protos::gen::TraceConfig trace_config,
+ bool check_original_trace = true) {
+ const std::string path = RandomTraceFileName();
+
+ auto perfetto_proc = ExecPerfetto(
+ {
+ "-o",
+ path,
+ "-c",
+ "-",
+ },
+ trace_config.SerializeAsString());
+
+ auto perfetto_br_proc = ExecPerfetto({
+ "--save-for-bugreport",
+ });
+
+ // Start the service and connect a simple fake producer.
+ StartServiceIfRequiredNoNewExecsAfterThis();
+
+ auto* fake_producer = ConnectFakeProducer();
+ ASSERT_TRUE(fake_producer);
+
+ std::thread background_trace([&perfetto_proc]() {
+ std::string stderr_str;
+ ASSERT_EQ(0, perfetto_proc.Run(&stderr_str)) << stderr_str;
+ });
+
+ // Wait for the producer to start, and then write out packets.
+ WaitForProducerEnabled();
+ auto on_data_written = task_runner_.CreateCheckpoint("data_written");
+ fake_producer->ProduceEventBatch(WrapTask(on_data_written));
+ task_runner_.RunUntilCheckpoint("data_written");
+
+ ASSERT_EQ(0, perfetto_br_proc.Run(&stderr_)) << "stderr: " << stderr_;
+ perfetto_proc.SendSigterm();
+ background_trace.join();
+
+ auto check_trace_contents = [](std::string trace_path) {
+ // Read the trace written in the fixed location
+ // (/data/misc/perfetto-traces/ on Android, /tmp/ on Linux/Mac) and make
+ // sure it has the right contents.
+ std::string trace_str;
+ base::ReadFile(trace_path, &trace_str);
+ ASSERT_FALSE(trace_str.empty());
+ protos::gen::Trace trace;
+ ASSERT_TRUE(trace.ParseFromString(trace_str));
+ int test_packets = 0;
+ for (const auto& p : trace.packet())
+ test_packets += p.has_for_testing() ? 1 : 0;
+ ASSERT_EQ(test_packets, 3) << trace_path;
+ };
+
+ // Verify that both the original trace and the cloned bugreport contain
+ // the expected contents.
+ check_trace_contents(GetBugreportTracePath());
+ if (check_original_trace)
+ check_trace_contents(path);
+ }
+
// Tests are allowed to freely use these variables.
std::string stderr_;
base::TestTaskRunner task_runner_;
@@ -760,4 +836,37 @@
}
}
+TEST_F(PerfettoCmdlineTest, SaveForBugreport) {
+ TraceConfig trace_config = CreateTraceConfigForBugreportTest();
+ RunBugreportTest(std::move(trace_config));
+}
+
+TEST_F(PerfettoCmdlineTest, SaveForBugreport_WriteIntoFile) {
+ TraceConfig trace_config = CreateTraceConfigForBugreportTest();
+ trace_config.set_file_write_period_ms(60000); // Will never hit this.
+ trace_config.set_write_into_file(true);
+ RunBugreportTest(std::move(trace_config));
+}
+
+// Tests that SaveTraceForBugreport() works also if the trace has triggers
+// defined and those triggers have not been hit. This is a regression test for
+// b/188008375 .
+#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
+// Disabled due to b/191940560
+#define MAYBE_SaveForBugreport_Triggers DISABLED_SaveForBugreport_Triggers
+#else
+#define MAYBE_SaveForBugreport_Triggers SaveForBugreport_Triggers
+#endif
+TEST_F(PerfettoCmdlineTest, MAYBE_SaveForBugreport_Triggers) {
+ TraceConfig trace_config = CreateTraceConfigForBugreportTest();
+ trace_config.set_duration_ms(0); // set_trigger_timeout_ms is used instead.
+ auto* trigger_config = trace_config.mutable_trigger_config();
+ trigger_config->set_trigger_timeout_ms(8.64e+7);
+ trigger_config->set_trigger_mode(TraceConfig::TriggerConfig::STOP_TRACING);
+ auto* trigger = trigger_config->add_triggers();
+ trigger->set_name("trigger_name");
+ trigger->set_stop_delay_ms(1);
+ RunBugreportTest(std::move(trace_config), /*check_original_trace=*/false);
+}
+
} // namespace perfetto
diff --git a/test/configs/bugreport.cfg b/test/configs/bugreport.cfg
new file mode 100644
index 0000000..d42fd61
--- /dev/null
+++ b/test/configs/bugreport.cfg
@@ -0,0 +1,30 @@
+bugreport_score: 100
+duration_ms: 600000
+
+buffers {
+ size_kb: 32768
+ fill_policy: RING_BUFFER
+}
+
+data_sources {
+ config {
+ name: "linux.ftrace"
+ target_buffer: 0
+ ftrace_config {
+ ftrace_events: "sched/sched_switch"
+ ftrace_events: "power/suspend_resume"
+ ftrace_events: "sched/sched_process_exit"
+ ftrace_events: "sched/sched_process_free"
+ ftrace_events: "task/task_newtask"
+ ftrace_events: "task/task_rename"
+ ftrace_events: "sched/sched_wakeup"
+ }
+ }
+}
+
+data_sources {
+ config {
+ name: "linux.process_stats"
+ target_buffer: 0
+ }
+}
diff --git a/test/test_helper.cc b/test/test_helper.cc
index 2267dc0..cbc3aca 100644
--- a/test/test_helper.cc
+++ b/test/test_helper.cc
@@ -156,18 +156,6 @@
return success;
}
-bool TestHelper::SaveTraceForBugreportAndWait() {
- bool success = false;
- auto checkpoint = CreateCheckpoint("bugreport");
- auto callback = [&success, checkpoint](bool s, const std::string&) {
- success = s;
- checkpoint();
- };
- endpoint_->SaveTraceForBugreport(callback);
- RunUntilCheckpoint("bugreport");
- return success;
-}
-
void TestHelper::CreateProducerProvidedSmb() {
fake_producer_thread_.CreateProducerProvidedSmb();
}
diff --git a/test/test_helper.h b/test/test_helper.h
index fda54b0..c8d1b48 100644
--- a/test/test_helper.h
+++ b/test/test_helper.h
@@ -40,6 +40,8 @@
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
#include "src/tracing/ipc/shared_memory_windows.h"
#else
+#include <signal.h>
+
#include "src/traced/probes/probes_producer.h"
#include "src/tracing/ipc/posix_shared_memory.h"
#endif
@@ -309,7 +311,6 @@
void FreeBuffers();
void DetachConsumer(const std::string& key);
bool AttachConsumer(const std::string& key);
- bool SaveTraceForBugreportAndWait();
void CreateProducerProvidedSmb();
bool IsShmemProvidedByProducer();
void ProduceStartupEventBatch(const protos::gen::TestConfig& config);
@@ -427,6 +428,12 @@
constexpr bool kUseSystemBinaries = true;
#endif
+ auto pass_env = [](const std::string& var, base::Subprocess* proc) {
+ const char* val = getenv(var.c_str());
+ if (val)
+ proc->args.env.push_back(var + "=" + val);
+ };
+
std::vector<std::string>& cmd = subprocess_.args.exec_cmd;
if (kUseSystemBinaries) {
PERFETTO_CHECK(TestHelper::kDefaultMode ==
@@ -442,6 +449,9 @@
subprocess_.args.env.push_back(
std::string("PERFETTO_CONSUMER_SOCK_NAME=") +
TestHelper::GetDefaultModeConsumerSocketName());
+ pass_env("TMPDIR", &subprocess_);
+ pass_env("TMP", &subprocess_);
+ pass_env("TEMP", &subprocess_);
cmd.push_back(base::GetCurExecutableDir() + "/" + argv0);
cmd.insert(cmd.end(), args.begin(), args.end());
}
@@ -479,6 +489,16 @@
sync_pipe_.rd.reset();
}
+ void SendSigterm() {
+#ifdef SIGTERM
+ kill(subprocess_.pid(), SIGTERM);
+#else
+ // This code is never used on Windows tests, not bothering.
+ if (subprocess_.pid()) // Always true, but avoids Wnoreturn compile errors.
+ PERFETTO_FATAL("SendSigterm() not implemented on this platform");
+#endif
+ }
+
private:
base::Subprocess subprocess_;
base::Pipe sync_pipe_;
diff --git a/test/trace_processor/diff_tests/startup/android_startup_attribution_slow.out b/test/trace_processor/diff_tests/startup/android_startup_attribution_slow.out
index 2257cb0..475edf8 100644
--- a/test/trace_processor/diff_tests/startup/android_startup_attribution_slow.out
+++ b/test/trace_processor/diff_tests/startup/android_startup_attribution_slow.out
@@ -32,7 +32,7 @@
dur_ns: 40000000000
dur_ms: 40000
}
- jit_compiled_methods: 41
+ jit_compiled_methods: 71
time_jit_thread_pool_on_cpu {
dur_ns: 20000000000
dur_ms: 20000
diff --git a/test/trace_processor/diff_tests/startup/android_startup_attribution_slow.py b/test/trace_processor/diff_tests/startup/android_startup_attribution_slow.py
index aa81cca..93e652f 100644
--- a/test/trace_processor/diff_tests/startup/android_startup_attribution_slow.py
+++ b/test/trace_processor/diff_tests/startup/android_startup_attribution_slow.py
@@ -123,7 +123,7 @@
ts=to_s(260), pid=APP_PID, tid=SECOND_APP_TID, buf='VerifyClass vp')
trace.add_atrace_end(ts=to_s(280), pid=APP_PID, tid=SECOND_APP_TID)
-for t in range(100, 160, 2):
+for t in range(100, 160, 1):
# JIT compilation slices
trace.add_atrace_begin(
ts=to_s(t), pid=APP_PID, tid=JIT_TID, buf='JIT compiling someting')
diff --git a/test/trace_processor/diff_tests/startup/android_startup_broadcast_multiple.out b/test/trace_processor/diff_tests/startup/android_startup_broadcast_multiple.out
index 8f50a7d..c0b245c 100644
--- a/test/trace_processor/diff_tests/startup/android_startup_broadcast_multiple.out
+++ b/test/trace_processor/diff_tests/startup/android_startup_broadcast_multiple.out
@@ -24,8 +24,8 @@
system_state {
dex2oat_running: false
installd_running: false
- broadcast_dispatched_count: 12
- broadcast_received_count: 11
+ broadcast_dispatched_count: 24
+ broadcast_received_count: 52
installd_dur_ns: 0
dex2oat_dur_ns: 0
}
diff --git a/test/trace_processor/diff_tests/startup/android_startup_broadcast_multiple.py b/test/trace_processor/diff_tests/startup/android_startup_broadcast_multiple.py
index 4b5f5b2..da3fab6 100644
--- a/test/trace_processor/diff_tests/startup/android_startup_broadcast_multiple.py
+++ b/test/trace_processor/diff_tests/startup/android_startup_broadcast_multiple.py
@@ -31,7 +31,7 @@
trace.add_atrace_async_begin(ts=100, tid=2, pid=2, buf='launchingActivity#1')
trace.add_atrace_async_end(ts=200, tid=2, pid=2, buf='launchingActivity#1')
-for t in range(105, 129, 2):
+for t in range(105, 129, 1):
trace.add_atrace_begin(
ts=t,
tid=1,
@@ -39,7 +39,7 @@
buf='Broadcast dispatched from android (2005:system/1000) x')
trace.add_atrace_end(ts=t + 1, tid=1, pid=1)
-for t in range(130, 152, 2):
+for t in range(100, 152, 1):
trace.add_atrace_begin(ts=t, tid=2, pid=2, buf='broadcastReceiveReg: x')
trace.add_atrace_end(ts=t + 1, tid=2, pid=2)
diff --git a/test/trace_processor/diff_tests/startup/android_startup_installd_dex2oat.out b/test/trace_processor/diff_tests/startup/android_startup_installd_dex2oat.out
index 48aa205..f2b6285 100644
--- a/test/trace_processor/diff_tests/startup/android_startup_installd_dex2oat.out
+++ b/test/trace_processor/diff_tests/startup/android_startup_installd_dex2oat.out
@@ -61,6 +61,7 @@
installd_dur_ns: 0
dex2oat_dur_ns: 5
}
+ slow_start_reason: "dex2oat running during launch"
}
startup {
startup_id: 3
@@ -93,6 +94,7 @@
installd_dur_ns: 5
dex2oat_dur_ns: 0
}
+ slow_start_reason: "installd running during launch"
}
startup {
startup_id: 4
@@ -126,5 +128,7 @@
installd_dur_ns: 5
dex2oat_dur_ns: 5
}
+ slow_start_reason: "dex2oat running during launch"
+ slow_start_reason: "installd running during launch"
}
}