service: Add GetTraceStats() to consumer endpoint
The new API allows a consumer to obtain trace and buffer stats about the
current tracing session separately from the actual trace data. Consumers
may use this to show stats such as buffer utilization in a UI.
Also adds a few new stats to TraceBuffer.
Change-Id: I929955b2854e5b84b6d1c0ca82e750c47df77ae1
diff --git a/Android.bp b/Android.bp
index ec143a5..8de77cf 100644
--- a/Android.bp
+++ b/Android.bp
@@ -94,6 +94,7 @@
"src/tracing/core/trace_buffer.cc",
"src/tracing/core/trace_config.cc",
"src/tracing/core/trace_packet.cc",
+ "src/tracing/core/trace_stats.cc",
"src/tracing/core/trace_writer_impl.cc",
"src/tracing/core/tracing_service_impl.cc",
"src/tracing/core/virtual_destructors.cc",
@@ -285,6 +286,7 @@
"src/tracing/core/trace_buffer.cc",
"src/tracing/core/trace_config.cc",
"src/tracing/core/trace_packet.cc",
+ "src/tracing/core/trace_stats.cc",
"src/tracing/core/trace_writer_impl.cc",
"src/tracing/core/tracing_service_impl.cc",
"src/tracing/core/virtual_destructors.cc",
@@ -429,6 +431,7 @@
"src/tracing/core/trace_buffer.cc",
"src/tracing/core/trace_config.cc",
"src/tracing/core/trace_packet.cc",
+ "src/tracing/core/trace_stats.cc",
"src/tracing/core/trace_writer_impl.cc",
"src/tracing/core/tracing_service_impl.cc",
"src/tracing/core/virtual_destructors.cc",
@@ -626,6 +629,7 @@
"src/tracing/core/trace_buffer.cc",
"src/tracing/core/trace_config.cc",
"src/tracing/core/trace_packet.cc",
+ "src/tracing/core/trace_stats.cc",
"src/tracing/core/trace_writer_impl.cc",
"src/tracing/core/tracing_service_impl.cc",
"src/tracing/core/virtual_destructors.cc",
@@ -697,6 +701,7 @@
"protos/perfetto/common/android_log_constants.proto",
"protos/perfetto/common/commit_data_request.proto",
"protos/perfetto/common/sys_stats_counters.proto",
+ "protos/perfetto/common/trace_stats.proto",
],
tools: [
"aprotoc",
@@ -706,6 +711,7 @@
"external/perfetto/protos/perfetto/common/android_log_constants.pb.cc",
"external/perfetto/protos/perfetto/common/commit_data_request.pb.cc",
"external/perfetto/protos/perfetto/common/sys_stats_counters.pb.cc",
+ "external/perfetto/protos/perfetto/common/trace_stats.pb.cc",
],
}
@@ -716,6 +722,7 @@
"protos/perfetto/common/android_log_constants.proto",
"protos/perfetto/common/commit_data_request.proto",
"protos/perfetto/common/sys_stats_counters.proto",
+ "protos/perfetto/common/trace_stats.proto",
],
tools: [
"aprotoc",
@@ -725,6 +732,7 @@
"external/perfetto/protos/perfetto/common/android_log_constants.pb.h",
"external/perfetto/protos/perfetto/common/commit_data_request.pb.h",
"external/perfetto/protos/perfetto/common/sys_stats_counters.pb.h",
+ "external/perfetto/protos/perfetto/common/trace_stats.pb.h",
],
export_include_dirs: [
"protos",
@@ -738,6 +746,7 @@
"protos/perfetto/common/android_log_constants.proto",
"protos/perfetto/common/commit_data_request.proto",
"protos/perfetto/common/sys_stats_counters.proto",
+ "protos/perfetto/common/trace_stats.proto",
],
tools: [
"aprotoc",
@@ -748,6 +757,7 @@
"external/perfetto/protos/perfetto/common/android_log_constants.pbzero.cc",
"external/perfetto/protos/perfetto/common/commit_data_request.pbzero.cc",
"external/perfetto/protos/perfetto/common/sys_stats_counters.pbzero.cc",
+ "external/perfetto/protos/perfetto/common/trace_stats.pbzero.cc",
],
}
@@ -758,6 +768,7 @@
"protos/perfetto/common/android_log_constants.proto",
"protos/perfetto/common/commit_data_request.proto",
"protos/perfetto/common/sys_stats_counters.proto",
+ "protos/perfetto/common/trace_stats.proto",
],
tools: [
"aprotoc",
@@ -768,6 +779,7 @@
"external/perfetto/protos/perfetto/common/android_log_constants.pbzero.h",
"external/perfetto/protos/perfetto/common/commit_data_request.pbzero.h",
"external/perfetto/protos/perfetto/common/sys_stats_counters.pbzero.h",
+ "external/perfetto/protos/perfetto/common/trace_stats.pbzero.h",
],
export_include_dirs: [
"protos",
@@ -1531,7 +1543,6 @@
name: "perfetto_protos_perfetto_trace_minimal_lite_gen",
srcs: [
"protos/perfetto/trace/clock_snapshot.proto",
- "protos/perfetto/trace/trace_stats.proto",
],
tools: [
"aprotoc",
@@ -1539,7 +1550,6 @@
cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos $(in)",
out: [
"external/perfetto/protos/perfetto/trace/clock_snapshot.pb.cc",
- "external/perfetto/protos/perfetto/trace/trace_stats.pb.cc",
],
}
@@ -1548,7 +1558,6 @@
name: "perfetto_protos_perfetto_trace_minimal_lite_gen_headers",
srcs: [
"protos/perfetto/trace/clock_snapshot.proto",
- "protos/perfetto/trace/trace_stats.proto",
],
tools: [
"aprotoc",
@@ -1556,7 +1565,6 @@
cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos $(in)",
out: [
"external/perfetto/protos/perfetto/trace/clock_snapshot.pb.h",
- "external/perfetto/protos/perfetto/trace/trace_stats.pb.h",
],
export_include_dirs: [
"protos",
@@ -1884,7 +1892,6 @@
"protos/perfetto/trace/test_event.proto",
"protos/perfetto/trace/trace.proto",
"protos/perfetto/trace/trace_packet.proto",
- "protos/perfetto/trace/trace_stats.proto",
],
tools: [
"aprotoc",
@@ -1896,7 +1903,6 @@
"external/perfetto/protos/perfetto/trace/test_event.pbzero.cc",
"external/perfetto/protos/perfetto/trace/trace.pbzero.cc",
"external/perfetto/protos/perfetto/trace/trace_packet.pbzero.cc",
- "external/perfetto/protos/perfetto/trace/trace_stats.pbzero.cc",
],
}
@@ -1908,7 +1914,6 @@
"protos/perfetto/trace/test_event.proto",
"protos/perfetto/trace/trace.proto",
"protos/perfetto/trace/trace_packet.proto",
- "protos/perfetto/trace/trace_stats.proto",
],
tools: [
"aprotoc",
@@ -1920,7 +1925,6 @@
"external/perfetto/protos/perfetto/trace/test_event.pbzero.h",
"external/perfetto/protos/perfetto/trace/trace.pbzero.h",
"external/perfetto/protos/perfetto/trace/trace_packet.pbzero.h",
- "external/perfetto/protos/perfetto/trace/trace_stats.pbzero.h",
],
export_include_dirs: [
"protos",
@@ -2349,6 +2353,7 @@
"src/tracing/core/trace_buffer.cc",
"src/tracing/core/trace_config.cc",
"src/tracing/core/trace_packet.cc",
+ "src/tracing/core/trace_stats.cc",
"src/tracing/core/trace_writer_impl.cc",
"src/tracing/core/tracing_service_impl.cc",
"src/tracing/core/virtual_destructors.cc",
@@ -2684,6 +2689,7 @@
"src/tracing/core/trace_config.cc",
"src/tracing/core/trace_packet.cc",
"src/tracing/core/trace_packet_unittest.cc",
+ "src/tracing/core/trace_stats.cc",
"src/tracing/core/trace_writer_for_testing.cc",
"src/tracing/core/trace_writer_impl.cc",
"src/tracing/core/trace_writer_impl_unittest.cc",
diff --git a/include/perfetto/tracing/core/BUILD.gn b/include/perfetto/tracing/core/BUILD.gn
index b09026c..c4e7eed 100644
--- a/include/perfetto/tracing/core/BUILD.gn
+++ b/include/perfetto/tracing/core/BUILD.gn
@@ -30,6 +30,7 @@
"startup_trace_writer.h",
"trace_config.h",
"trace_packet.h",
+ "trace_stats.h",
"trace_writer.h",
"tracing_service.h",
]
diff --git a/include/perfetto/tracing/core/consumer.h b/include/perfetto/tracing/core/consumer.h
index e6a2614..6e5e779 100644
--- a/include/perfetto/tracing/core/consumer.h
+++ b/include/perfetto/tracing/core/consumer.h
@@ -27,6 +27,7 @@
class TraceConfig;
class TracePacket;
+class TraceStats;
class PERFETTO_EXPORT Consumer {
public:
@@ -68,6 +69,10 @@
// Called back by the Service (or transport layer) after invoking
// TracingService::ConsumerEndpoint::Attach().
virtual void OnAttach(bool success, const TraceConfig&) = 0;
+
+ // Called back by the Service (or transport layer) after invoking
+ // TracingService::ConsumerEndpoint::GetTraceStats().
+ virtual void OnTraceStats(bool success, const TraceStats&) = 0;
};
} // namespace perfetto
diff --git a/include/perfetto/tracing/core/trace_stats.h b/include/perfetto/tracing/core/trace_stats.h
new file mode 100644
index 0000000..5d02163
--- /dev/null
+++ b/include/perfetto/tracing/core/trace_stats.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/*******************************************************************************
+ * AUTOGENERATED - DO NOT EDIT
+ *******************************************************************************
+ * This file has been generated from the protobuf message
+ * perfetto/common/trace_stats.proto
+ * by
+ * ../../tools/proto_to_cpp/proto_to_cpp.cc.
+ * If you need to make changes here, change the .proto file and then run
+ * ./tools/gen_tracing_cpp_headers_from_protos
+ */
+
+#ifndef INCLUDE_PERFETTO_TRACING_CORE_TRACE_STATS_H_
+#define INCLUDE_PERFETTO_TRACING_CORE_TRACE_STATS_H_
+
+#include <stdint.h>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "perfetto/base/export.h"
+
+// Forward declarations for protobuf types.
+namespace perfetto {
+namespace protos {
+class TraceStats;
+class TraceStats_BufferStats;
+} // namespace protos
+} // namespace perfetto
+
+namespace perfetto {
+
+class PERFETTO_EXPORT TraceStats {
+ public:
+ class PERFETTO_EXPORT BufferStats {
+ public:
+ BufferStats();
+ ~BufferStats();
+ BufferStats(BufferStats&&) noexcept;
+ BufferStats& operator=(BufferStats&&);
+ BufferStats(const BufferStats&);
+ BufferStats& operator=(const BufferStats&);
+
+ // Conversion methods from/to the corresponding protobuf types.
+ void FromProto(const perfetto::protos::TraceStats_BufferStats&);
+ void ToProto(perfetto::protos::TraceStats_BufferStats*) const;
+
+ uint64_t buffer_size() const { return buffer_size_; }
+ void set_buffer_size(uint64_t value) { buffer_size_ = value; }
+
+ uint64_t bytes_written() const { return bytes_written_; }
+ void set_bytes_written(uint64_t value) { bytes_written_ = value; }
+
+ uint64_t bytes_overwritten() const { return bytes_overwritten_; }
+ void set_bytes_overwritten(uint64_t value) { bytes_overwritten_ = value; }
+
+ uint64_t bytes_read() const { return bytes_read_; }
+ void set_bytes_read(uint64_t value) { bytes_read_ = value; }
+
+ uint64_t padding_bytes_written() const { return padding_bytes_written_; }
+ void set_padding_bytes_written(uint64_t value) {
+ padding_bytes_written_ = value;
+ }
+
+ uint64_t padding_bytes_cleared() const { return padding_bytes_cleared_; }
+ void set_padding_bytes_cleared(uint64_t value) {
+ padding_bytes_cleared_ = value;
+ }
+
+ uint64_t chunks_written() const { return chunks_written_; }
+ void set_chunks_written(uint64_t value) { chunks_written_ = value; }
+
+ uint64_t chunks_rewritten() const { return chunks_rewritten_; }
+ void set_chunks_rewritten(uint64_t value) { chunks_rewritten_ = value; }
+
+ uint64_t chunks_overwritten() const { return chunks_overwritten_; }
+ void set_chunks_overwritten(uint64_t value) { chunks_overwritten_ = value; }
+
+ uint64_t chunks_read() const { return chunks_read_; }
+ void set_chunks_read(uint64_t value) { chunks_read_ = value; }
+
+ uint64_t chunks_committed_out_of_order() const {
+ return chunks_committed_out_of_order_;
+ }
+ void set_chunks_committed_out_of_order(uint64_t value) {
+ chunks_committed_out_of_order_ = value;
+ }
+
+ uint64_t write_wrap_count() const { return write_wrap_count_; }
+ void set_write_wrap_count(uint64_t value) { write_wrap_count_ = value; }
+
+ uint64_t patches_succeeded() const { return patches_succeeded_; }
+ void set_patches_succeeded(uint64_t value) { patches_succeeded_ = value; }
+
+ uint64_t patches_failed() const { return patches_failed_; }
+ void set_patches_failed(uint64_t value) { patches_failed_ = value; }
+
+ uint64_t readaheads_succeeded() const { return readaheads_succeeded_; }
+ void set_readaheads_succeeded(uint64_t value) {
+ readaheads_succeeded_ = value;
+ }
+
+ uint64_t readaheads_failed() const { return readaheads_failed_; }
+ void set_readaheads_failed(uint64_t value) { readaheads_failed_ = value; }
+
+ uint64_t abi_violations() const { return abi_violations_; }
+ void set_abi_violations(uint64_t value) { abi_violations_ = value; }
+
+ private:
+ uint64_t buffer_size_ = {};
+ uint64_t bytes_written_ = {};
+ uint64_t bytes_overwritten_ = {};
+ uint64_t bytes_read_ = {};
+ uint64_t padding_bytes_written_ = {};
+ uint64_t padding_bytes_cleared_ = {};
+ uint64_t chunks_written_ = {};
+ uint64_t chunks_rewritten_ = {};
+ uint64_t chunks_overwritten_ = {};
+ uint64_t chunks_read_ = {};
+ uint64_t chunks_committed_out_of_order_ = {};
+ uint64_t write_wrap_count_ = {};
+ uint64_t patches_succeeded_ = {};
+ uint64_t patches_failed_ = {};
+ uint64_t readaheads_succeeded_ = {};
+ uint64_t readaheads_failed_ = {};
+ uint64_t abi_violations_ = {};
+
+ // Allows to preserve unknown protobuf fields for compatibility
+ // with future versions of .proto files.
+ std::string unknown_fields_;
+ };
+
+ TraceStats();
+ ~TraceStats();
+ TraceStats(TraceStats&&) noexcept;
+ TraceStats& operator=(TraceStats&&);
+ TraceStats(const TraceStats&);
+ TraceStats& operator=(const TraceStats&);
+
+ // Conversion methods from/to the corresponding protobuf types.
+ void FromProto(const perfetto::protos::TraceStats&);
+ void ToProto(perfetto::protos::TraceStats*) const;
+
+ int buffer_stats_size() const {
+ return static_cast<int>(buffer_stats_.size());
+ }
+ const std::vector<BufferStats>& buffer_stats() const { return buffer_stats_; }
+ BufferStats* add_buffer_stats() {
+ buffer_stats_.emplace_back();
+ return &buffer_stats_.back();
+ }
+
+ uint32_t producers_connected() const { return producers_connected_; }
+ void set_producers_connected(uint32_t value) { producers_connected_ = value; }
+
+ uint64_t producers_seen() const { return producers_seen_; }
+ void set_producers_seen(uint64_t value) { producers_seen_ = value; }
+
+ uint32_t data_sources_registered() const { return data_sources_registered_; }
+ void set_data_sources_registered(uint32_t value) {
+ data_sources_registered_ = value;
+ }
+
+ uint64_t data_sources_seen() const { return data_sources_seen_; }
+ void set_data_sources_seen(uint64_t value) { data_sources_seen_ = value; }
+
+ uint32_t tracing_sessions() const { return tracing_sessions_; }
+ void set_tracing_sessions(uint32_t value) { tracing_sessions_ = value; }
+
+ uint32_t total_buffers() const { return total_buffers_; }
+ void set_total_buffers(uint32_t value) { total_buffers_ = value; }
+
+ private:
+ std::vector<BufferStats> buffer_stats_;
+ uint32_t producers_connected_ = {};
+ uint64_t producers_seen_ = {};
+ uint32_t data_sources_registered_ = {};
+ uint64_t data_sources_seen_ = {};
+ uint32_t tracing_sessions_ = {};
+ uint32_t total_buffers_ = {};
+
+ // Allows to preserve unknown protobuf fields for compatibility
+ // with future versions of .proto files.
+ std::string unknown_fields_;
+};
+
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_TRACING_CORE_TRACE_STATS_H_
diff --git a/include/perfetto/tracing/core/tracing_service.h b/include/perfetto/tracing/core/tracing_service.h
index c4f369e..b80f2a8 100644
--- a/include/perfetto/tracing/core/tracing_service.h
+++ b/include/perfetto/tracing/core/tracing_service.h
@@ -167,6 +167,9 @@
// Will call OnAttach().
virtual void Attach(const std::string& key) = 0;
+
+ // Will call OnTraceStats().
+ virtual void GetTraceStats() = 0;
}; // class ConsumerEndpoint.
// Implemented in src/core/tracing_service_impl.cc .
diff --git a/protos/perfetto/common/BUILD.gn b/protos/perfetto/common/BUILD.gn
index e2b22b8..fb34919 100644
--- a/protos/perfetto/common/BUILD.gn
+++ b/protos/perfetto/common/BUILD.gn
@@ -20,6 +20,7 @@
"commit_data_request.proto",
"android_log_constants.proto",
"sys_stats_counters.proto",
+ "trace_stats.proto",
]
# Proto messages that are required by the IPC service definitions but have also
diff --git a/protos/perfetto/trace/trace_stats.proto b/protos/perfetto/common/trace_stats.proto
similarity index 63%
rename from protos/perfetto/trace/trace_stats.proto
rename to protos/perfetto/common/trace_stats.proto
index cd69e2b..5993e3e 100644
--- a/protos/perfetto/trace/trace_stats.proto
+++ b/protos/perfetto/common/trace_stats.proto
@@ -23,11 +23,45 @@
message TraceStats {
// From TraceBuffer::Stats.
//
- // NEXT ID: 12
+ // Next id: 18.
message BufferStats {
- // Num. bytes written into the circular buffer.
+ // Size of the circular buffer in bytes.
+ optional uint64 buffer_size = 12;
+
+ // Num. bytes written into the circular buffer, including chunk headers.
optional uint64 bytes_written = 1;
+ // Num. bytes overwritten before they have been read (i.e. loss of data).
+ optional uint64 bytes_overwritten = 13;
+
+ // Total size of chunks that were fully read from the circular buffer by the
+ // consumer. This may not be equal to |bytes_written| either in the middle
+ // of tracing, or if |chunks_overwritten| is non-zero. Note that this is the
+ // size of the chunks read from the buffer, including chunk headers, which
+ // will be different from the total size of packets returned to the
+ // consumer.
+ //
+ // The current utilization of the trace buffer (mid-tracing) can be obtained
+ // by subtracting |bytes_read| and |bytes_overwritten| from |bytes_written|,
+ // adding the difference of |padding_bytes_written| and
+ // |padding_bytes_cleared|, and comparing this sum to the |buffer_size|.
+ // Note that this represents the total size of buffered data in the buffer,
+ // yet this data may be spread non-contiguously through the buffer and may
+ // be overridden before the utilization reaches 100%.
+ optional uint64 bytes_read = 14;
+
+ // Num. bytes that were allocated as padding between chunks in the circular
+ // buffer.
+ optional uint64 padding_bytes_written = 15;
+
+ // Num. of padding bytes that were removed from the circular buffer when
+ // they were overwritten.
+ //
+ // The difference between |padding_bytes_written| and
+ // |padding_bytes_cleared| denotes the total size of padding currently
+ // present in the buffer.
+ optional uint64 padding_bytes_cleared = 16;
+
// Num. chunks (!= packets) written into the buffer.
optional uint64 chunks_written = 2;
@@ -38,6 +72,11 @@
// Num. chunks overwritten before they have been read (i.e. loss of data).
optional uint64 chunks_overwritten = 3;
+ // Num. chunks (!= packets) that were fully read from the circular buffer by
+ // the consumer. This may not be equal to |chunks_written| either in the
+ // middle of tracing, or if |chunks_overwritten| is non-zero.
+ optional uint64 chunks_read = 17;
+
// Num. chunks that were committed out of order.
optional uint64 chunks_committed_out_of_order = 11;
@@ -81,7 +120,7 @@
// Num. data sources registered for all trace sessions.
optional uint32 data_sources_registered = 4;
- // Num. data sources ever seen for all trace sessions since startupb
+ // Num. data sources ever seen for all trace sessions since startup.
optional uint64 data_sources_seen = 5;
// Num. concurrently active tracing sessions.
diff --git a/protos/perfetto/ipc/consumer_port.proto b/protos/perfetto/ipc/consumer_port.proto
index 944090e..6e7affc 100644
--- a/protos/perfetto/ipc/consumer_port.proto
+++ b/protos/perfetto/ipc/consumer_port.proto
@@ -17,6 +17,7 @@
syntax = "proto2";
option optimize_for = LITE_RUNTIME;
+import "perfetto/common/trace_stats.proto";
import "perfetto/config/trace_config.proto";
package perfetto.protos;
@@ -85,6 +86,10 @@
// in the standard non-detached case.
rpc Attach(AttachRequest) returns (AttachResponse) {}
+ // Allows the consumer to obtain statistics about the current tracing session,
+ // such as buffer usage stats. Intended for debugging or UI use.
+ rpc GetTraceStats(GetTraceStatsRequest) returns (GetTraceStatsResponse) {}
+
// TODO rpc ListDataSources(), for the UI.
}
@@ -161,12 +166,22 @@
message DetachRequest {
optional string key = 1;
}
+
message DetachResponse {}
// Arguments for rpc Attach.
message AttachRequest {
optional string key = 1;
}
+
message AttachResponse {
optional protos.TraceConfig trace_config = 1;
}
+
+// Arguments for rpc GetTraceStats.
+
+message GetTraceStatsRequest {}
+
+message GetTraceStatsResponse {
+ optional TraceStats trace_stats = 1;
+}
diff --git a/protos/perfetto/trace/BUILD.gn b/protos/perfetto/trace/BUILD.gn
index ad3a35b..3dbef21 100644
--- a/protos/perfetto/trace/BUILD.gn
+++ b/protos/perfetto/trace/BUILD.gn
@@ -18,10 +18,7 @@
# Common protos used by both the ":minimal_lite" target (for the service) and
# the generic ":lite" target
-proto_sources_minimal = [
- "clock_snapshot.proto",
- "trace_stats.proto",
-]
+proto_sources_minimal = [ "clock_snapshot.proto" ]
proto_sources_trusted = [ "trusted_packet.proto" ]
diff --git a/protos/perfetto/trace/trace_packet.proto b/protos/perfetto/trace/trace_packet.proto
index 6a118d1..9a25982 100644
--- a/protos/perfetto/trace/trace_packet.proto
+++ b/protos/perfetto/trace/trace_packet.proto
@@ -17,6 +17,7 @@
syntax = "proto2";
option optimize_for = LITE_RUNTIME;
+import "perfetto/common/trace_stats.proto";
import "perfetto/config/trace_config.proto";
import "perfetto/trace/android/android_log.proto";
import "perfetto/trace/chrome/chrome_trace_event.proto";
@@ -30,7 +31,6 @@
import "perfetto/trace/ps/process_tree.proto";
import "perfetto/trace/sys_stats/sys_stats.proto";
import "perfetto/trace/test_event.proto";
-import "perfetto/trace/trace_stats.proto";
package perfetto.protos;
diff --git a/protos/perfetto/trace/trusted_packet.proto b/protos/perfetto/trace/trusted_packet.proto
index 758b3ca..84c6a52 100644
--- a/protos/perfetto/trace/trusted_packet.proto
+++ b/protos/perfetto/trace/trusted_packet.proto
@@ -26,9 +26,9 @@
syntax = "proto3";
option optimize_for = LITE_RUNTIME;
+import "perfetto/common/trace_stats.proto";
import "perfetto/config/trace_config.proto";
import "perfetto/trace/clock_snapshot.proto";
-import "perfetto/trace/trace_stats.proto";
package perfetto.protos;
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc
index ede5c5b..bf890b7 100644
--- a/src/perfetto_cmd/perfetto_cmd.cc
+++ b/src/perfetto_cmd/perfetto_cmd.cc
@@ -706,6 +706,11 @@
}
}
+void PerfettoCmd::OnTraceStats(bool /*success*/,
+ const TraceStats& /*trace_config*/) {
+ // TODO(eseckler): Support GetTraceStats().
+}
+
int __attribute__((visibility("default")))
PerfettoCmdMain(int argc, char** argv) {
g_consumer_cmd = new perfetto::PerfettoCmd();
diff --git a/src/perfetto_cmd/perfetto_cmd.h b/src/perfetto_cmd/perfetto_cmd.h
index 943f204..8c6fbc7 100644
--- a/src/perfetto_cmd/perfetto_cmd.h
+++ b/src/perfetto_cmd/perfetto_cmd.h
@@ -60,6 +60,7 @@
void OnTraceData(std::vector<TracePacket>, bool has_more) override;
void OnDetach(bool) override;
void OnAttach(bool, const TraceConfig&) override;
+ void OnTraceStats(bool, const TraceStats&) override;
void SignalCtrlC() { ctrl_c_evt_.Notify(); }
diff --git a/src/tracing/BUILD.gn b/src/tracing/BUILD.gn
index ae49d44..4cf124e 100644
--- a/src/tracing/BUILD.gn
+++ b/src/tracing/BUILD.gn
@@ -61,6 +61,7 @@
"core/trace_buffer.h",
"core/trace_config.cc",
"core/trace_packet.cc",
+ "core/trace_stats.cc",
"core/trace_writer_impl.cc",
"core/trace_writer_impl.h",
"core/tracing_service_impl.cc",
diff --git a/src/tracing/api_impl/consumer_api.cc b/src/tracing/api_impl/consumer_api.cc
index 438f00d..544926e 100644
--- a/src/tracing/api_impl/consumer_api.cc
+++ b/src/tracing/api_impl/consumer_api.cc
@@ -91,6 +91,7 @@
void OnTraceData(std::vector<TracePacket>, bool has_more) override;
void OnDetach(bool) override;
void OnAttach(bool, const TraceConfig&) override;
+ void OnTraceStats(bool, const TraceStats&) override;
private:
TracingSession(const TracingSession&) = delete;
@@ -235,6 +236,11 @@
PERFETTO_DCHECK(false); // Should never be called, Attach() is not used here.
}
+void TracingSession::OnTraceStats(bool, const TraceStats&) {
+ // Should never be called, GetTraceStats() is not used here.
+ PERFETTO_DCHECK(false);
+}
+
void TracingSession::DestroyConnection() {
// Destroys the connection in a separate task. This is to avoid destroying
// the IPC connection directly from within the IPC callback.
diff --git a/src/tracing/core/service_impl_unittest.cc b/src/tracing/core/service_impl_unittest.cc
index 2a23b15..1cf8ff6 100644
--- a/src/tracing/core/service_impl_unittest.cc
+++ b/src/tracing/core/service_impl_unittest.cc
@@ -1374,4 +1374,33 @@
consumer->WaitForTracingDisabled(5000);
}
+TEST_F(TracingServiceImplTest, GetTraceStats) {
+ std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
+ consumer->Connect(svc.get());
+
+ consumer->GetTraceStats();
+ consumer->WaitForTraceStats(false);
+
+ std::unique_ptr<MockProducer> producer = CreateMockProducer();
+ producer->Connect(svc.get(), "mock_producer");
+ producer->RegisterDataSource("data_source");
+
+ TraceConfig trace_config;
+ trace_config.add_buffers()->set_size_kb(128);
+ auto* ds_config = trace_config.add_data_sources()->mutable_config();
+ ds_config->set_name("data_source");
+
+ consumer->EnableTracing(trace_config);
+ producer->WaitForTracingSetup();
+ producer->WaitForDataSourceSetup("data_source");
+ producer->WaitForDataSourceStart("data_source");
+
+ consumer->GetTraceStats();
+ consumer->WaitForTraceStats(true);
+
+ consumer->DisableTracing();
+ producer->WaitForDataSourceStop("data_source");
+ consumer->WaitForTracingDisabled();
+}
+
} // namespace perfetto
diff --git a/src/tracing/core/trace_buffer.cc b/src/tracing/core/trace_buffer.cc
index 4d990e1..5dfc62b 100644
--- a/src/tracing/core/trace_buffer.cc
+++ b/src/tracing/core/trace_buffer.cc
@@ -90,6 +90,7 @@
return false;
}
size_ = size;
+ stats_.set_buffer_size(size);
max_chunk_size_ = std::min(size, ChunkRecord::kMaxSize);
wptr_ = begin();
index_.clear();
@@ -115,7 +116,7 @@
const size_t record_size =
base::AlignUp<sizeof(ChunkRecord)>(size + sizeof(ChunkRecord));
if (PERFETTO_UNLIKELY(record_size > max_chunk_size_)) {
- stats_.abi_violations++;
+ stats_.set_abi_violations(stats_.abi_violations() + 1);
PERFETTO_DCHECK(suppress_sanity_dchecks_for_testing_);
return;
}
@@ -162,7 +163,7 @@
prev->size != record_size ||
prev->num_fragments > num_fragments ||
(prev->flags & chunk_flags) != prev->flags)) {
- stats_.abi_violations++;
+ stats_.set_abi_violations(stats_.abi_violations() + 1);
PERFETTO_DCHECK(suppress_sanity_dchecks_for_testing_);
return;
}
@@ -179,7 +180,7 @@
const auto subsequent_it = index_.find(subsequent_key);
if (subsequent_it != index_.end() &&
subsequent_it->second.num_fragments_read > 0) {
- stats_.abi_violations++;
+ stats_.set_abi_violations(stats_.abi_violations() + 1);
PERFETTO_DCHECK(suppress_sanity_dchecks_for_testing_);
return;
}
@@ -217,7 +218,7 @@
wptr - begin() + record_size, record_size);
WriteChunkRecord(wptr, record, src, size);
TRACE_BUFFER_DLOG("Chunk raw: %s", HexDump(wptr, record_size).c_str());
- stats_.chunks_rewritten++;
+ stats_.set_chunks_rewritten(stats_.chunks_rewritten() + 1);
return;
}
@@ -229,7 +230,7 @@
PERFETTO_DCHECK(res <= cached_size_to_end);
AddPaddingRecord(cached_size_to_end);
wptr_ = begin();
- stats_.write_wrap_count++;
+ stats_.set_write_wrap_count(stats_.write_wrap_count() + 1);
PERFETTO_DCHECK(size_to_end() >= record_size);
}
@@ -256,8 +257,8 @@
size_t padding_size = DeleteNextChunksFor(record_size);
// Now first insert the new chunk. At the end, if necessary, add the padding.
- stats_.chunks_written++;
- stats_.bytes_written += size;
+ stats_.set_chunks_written(stats_.chunks_written() + 1);
+ stats_.set_bytes_written(stats_.bytes_written() + record_size);
auto it_and_inserted = index_.emplace(
key, ChunkMeta(GetChunkRecordAt(wptr_), num_fragments, chunk_complete,
chunk_flags, producer_uid_trusted));
@@ -270,7 +271,7 @@
if (wptr_ >= end()) {
PERFETTO_DCHECK(padding_size == 0);
wptr_ = begin();
- stats_.write_wrap_count++;
+ stats_.set_write_wrap_count(stats_.write_wrap_count() + 1);
}
DcheckIsAlignedAndWithinBounds(wptr_);
@@ -293,7 +294,8 @@
if (chunk_id - last_chunk_id < kMaxChunkID / 2) {
last_chunk_id = chunk_id;
} else {
- stats_.chunks_committed_out_of_order++;
+ stats_.set_chunks_committed_out_of_order(
+ stats_.chunks_committed_out_of_order() + 1);
}
if (padding_size)
@@ -335,8 +337,11 @@
bool removed = false;
if (PERFETTO_LIKELY(it != index_.end())) {
const ChunkMeta& meta = it->second;
- if (PERFETTO_UNLIKELY(meta.num_fragments_read < meta.num_fragments))
- stats_.chunks_overwritten++;
+ if (PERFETTO_UNLIKELY(meta.num_fragments_read < meta.num_fragments)) {
+ stats_.set_chunks_overwritten(stats_.chunks_overwritten() + 1);
+ stats_.set_bytes_overwritten(stats_.bytes_overwritten() +
+ next_chunk.size);
+ }
index_.erase(it);
removed = true;
}
@@ -346,6 +351,9 @@
next_chunk_ptr - begin(),
next_chunk_ptr - begin() + next_chunk.size, removed);
PERFETTO_DCHECK(removed);
+ } else {
+ stats_.set_padding_bytes_cleared(stats_.padding_bytes_cleared() +
+ next_chunk.size);
}
next_chunk_ptr += next_chunk.size;
@@ -367,6 +375,7 @@
TRACE_BUFFER_DLOG("AddPaddingRecord @ [%lu - %lu] %zu", wptr_ - begin(),
wptr_ - begin() + size, size);
WriteChunkRecord(wptr_, record, nullptr, size - sizeof(ChunkRecord));
+ stats_.set_padding_bytes_written(stats_.padding_bytes_written() + size);
// |wptr_| is deliberately not advanced when writing a padding record.
}
@@ -379,7 +388,7 @@
ChunkMeta::Key key(producer_id, writer_id, chunk_id);
auto it = index_.find(key);
if (it == index_.end()) {
- stats_.patches_failed++;
+ stats_.set_patches_failed(stats_.patches_failed() + 1);
return false;
}
ChunkMeta& chunk_meta = it->second;
@@ -409,7 +418,7 @@
ptr > chunk_end - Patch::kSize) {
// Either the IPC was so slow and in the meantime the writer managed to
// wrap over |chunk_id| or the producer sent a malicious IPC.
- stats_.patches_failed++;
+ stats_.set_patches_failed(stats_.patches_failed() + 1);
return false;
}
@@ -425,7 +434,7 @@
"Chunk raw (after patch): %s",
HexDump(chunk_begin, chunk_meta.chunk_record->size).c_str());
- stats_.patches_succeeded += patches_size;
+ stats_.set_patches_succeeded(stats_.patches_succeeded() + patches_size);
if (!other_patches_pending) {
chunk_meta.flags &= ~kChunkNeedsPatching;
chunk_meta.chunk_record->flags = chunk_meta.flags;
@@ -617,7 +626,7 @@
// In extremely rare cases (producer bugged / malicious) the chunk might
// contain an invalid fragment. In such case we don't want to stall the
// sequence but just skip the chunk and move on.
- stats_.abi_violations++;
+ stats_.set_abi_violations(stats_.abi_violations() + 1);
PERFETTO_DCHECK(suppress_sanity_dchecks_for_testing_);
break;
}
@@ -625,7 +634,7 @@
PERFETTO_DCHECK(action == kTryReadAhead);
ReadAheadResult ra_res = ReadAhead(packet);
if (ra_res == ReadAheadResult::kSucceededReturnSlices) {
- stats_.readaheads_succeeded++;
+ stats_.set_readaheads_succeeded(stats_.readaheads_succeeded() + 1);
*producer_uid = trusted_uid;
return true;
}
@@ -633,7 +642,7 @@
if (ra_res == ReadAheadResult::kFailedMoveToNextSequence) {
// readahead didn't find a contigous packet sequence. We'll try again
// on the next ReadPacket() call.
- stats_.readaheads_failed++;
+ stats_.set_readaheads_failed(stats_.readaheads_failed() + 1);
// TODO(primiano): optimization: this MoveToEnd() is the reason why
// MoveNext() (that is called in the outer for(;;MoveNext)) needs to
@@ -723,7 +732,7 @@
PERFETTO_DCHECK(read_iter_.cur == it.cur);
if (PERFETTO_UNLIKELY(packet_corruption)) {
- stats_.abi_violations++;
+ stats_.set_abi_violations(stats_.abi_violations() + 1);
PERFETTO_DCHECK(suppress_sanity_dchecks_for_testing_);
*packet = TracePacket(); // clear.
return ReadAheadResult::kFailedStayOnSameSequence;
@@ -749,7 +758,7 @@
packet_begin >= record_end)) {
// The producer has a bug or is malicious and did declare that the chunk
// contains more packets beyond its boundaries.
- stats_.abi_violations++;
+ stats_.set_abi_violations(stats_.abi_violations() + 1);
PERFETTO_DCHECK(suppress_sanity_dchecks_for_testing_);
return false;
}
@@ -767,18 +776,30 @@
const uint8_t* next_packet = packet_data + packet_size;
if (PERFETTO_UNLIKELY(next_packet <= packet_begin ||
next_packet > record_end)) {
- stats_.abi_violations++;
+ stats_.set_abi_violations(stats_.abi_violations() + 1);
PERFETTO_DCHECK(suppress_sanity_dchecks_for_testing_);
chunk_meta->cur_fragment_offset = 0;
chunk_meta->num_fragments_read = chunk_meta->num_fragments;
+ if (PERFETTO_LIKELY(chunk_meta->is_complete)) {
+ stats_.set_chunks_read(stats_.chunks_read() + 1);
+ stats_.set_bytes_read(stats_.bytes_read() +
+ chunk_meta->chunk_record->size);
+ }
return false;
}
chunk_meta->cur_fragment_offset =
static_cast<uint16_t>(next_packet - packets_begin);
chunk_meta->num_fragments_read++;
+ if (PERFETTO_UNLIKELY(chunk_meta->num_fragments_read ==
+ chunk_meta->num_fragments &&
+ chunk_meta->is_complete)) {
+ stats_.set_chunks_read(stats_.chunks_read() + 1);
+ stats_.set_bytes_read(stats_.bytes_read() + chunk_meta->chunk_record->size);
+ }
+
if (PERFETTO_UNLIKELY(packet_size == 0)) {
- stats_.abi_violations++;
+ stats_.set_abi_violations(stats_.abi_violations() + 1);
PERFETTO_DCHECK(suppress_sanity_dchecks_for_testing_);
return false;
}
diff --git a/src/tracing/core/trace_buffer.h b/src/tracing/core/trace_buffer.h
index 7c33f13..8b17e1c 100644
--- a/src/tracing/core/trace_buffer.h
+++ b/src/tracing/core/trace_buffer.h
@@ -29,6 +29,7 @@
#include "perfetto/base/paged_memory.h"
#include "perfetto/tracing/core/basic_types.h"
#include "perfetto/tracing/core/slice.h"
+#include "perfetto/tracing/core/trace_stats.h"
namespace perfetto {
@@ -128,22 +129,6 @@
public:
static const size_t InlineChunkHeaderSize; // For test/fake_packet.{cc,h}.
- // Maintain these fields consistent with trace_stats.proto. See comments in
- // the .proto for the semantic of these fields.
- struct Stats {
- uint64_t bytes_written = 0;
- uint64_t chunks_written = 0;
- uint64_t chunks_rewritten = 0;
- uint64_t chunks_overwritten = 0;
- uint64_t chunks_committed_out_of_order = 0;
- uint64_t write_wrap_count = 0;
- uint64_t patches_succeeded = 0;
- uint64_t patches_failed = 0;
- uint64_t readaheads_succeeded = 0;
- uint64_t readaheads_failed = 0;
- uint64_t abi_violations = 0;
- };
-
// Argument for out-of-band patches applied through TryPatchChunkContents().
struct Patch {
// From SharedMemoryABI::kPacketHeaderSize.
@@ -232,7 +217,7 @@
// P1, P5, P7, P4 (P4 cannot come after P5)
bool ReadNextTracePacket(TracePacket*, uid_t* producer_uid);
- const Stats& stats() const { return stats_; }
+ const TraceStats::BufferStats& stats() const { return stats_; }
size_t size() const { return size_; }
private:
@@ -559,7 +544,7 @@
std::map<std::pair<ProducerID, WriterID>, ChunkID> last_chunk_id_written_;
// Statistics about buffer usage.
- Stats stats_;
+ TraceStats::BufferStats stats_;
#if PERFETTO_DCHECK_IS_ON()
bool changed_since_last_read_ = false;
diff --git a/src/tracing/core/trace_buffer_unittest.cc b/src/tracing/core/trace_buffer_unittest.cc
index 9f1c7a8..d6eef84 100644
--- a/src/tracing/core/trace_buffer_unittest.cc
+++ b/src/tracing/core/trace_buffer_unittest.cc
@@ -159,6 +159,14 @@
trace_buffer()->BeginRead();
ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(42, seed)));
ASSERT_THAT(ReadPacket(), IsEmpty());
+ EXPECT_EQ(chunk_id + 1u, trace_buffer()->stats().chunks_written());
+ EXPECT_EQ(trace_buffer()->stats().chunks_written(),
+ trace_buffer()->stats().chunks_read());
+ EXPECT_LT(0u, trace_buffer()->stats().bytes_written());
+ EXPECT_EQ(trace_buffer()->stats().bytes_written(),
+ trace_buffer()->stats().bytes_read());
+ EXPECT_EQ(0u, trace_buffer()->stats().padding_bytes_written());
+ EXPECT_EQ(0u, trace_buffer()->stats().padding_bytes_cleared());
}
}
@@ -250,6 +258,21 @@
ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(2048 - 16, 'e')));
ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(512 - 16, 'f')));
ASSERT_THAT(ReadPacket(), IsEmpty());
+
+ EXPECT_EQ(6u, trace_buffer()->stats().chunks_written());
+ EXPECT_EQ(3u, trace_buffer()->stats().chunks_overwritten());
+ EXPECT_EQ(3u, trace_buffer()->stats().chunks_read());
+ EXPECT_EQ(4480u, trace_buffer()->stats().bytes_written());
+ EXPECT_EQ(896u, trace_buffer()->stats().bytes_overwritten());
+ EXPECT_EQ(3584u, trace_buffer()->stats().bytes_read());
+ EXPECT_EQ(512u, trace_buffer()->stats().padding_bytes_written());
+ EXPECT_EQ(0u, trace_buffer()->stats().padding_bytes_cleared());
+
+ // Adding another chunk should clear some of the padding.
+ ASSERT_EQ(128u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(6))
+ .AddPacket(128 - 16, 'g')
+ .CopyIntoTraceBuffer());
+ EXPECT_EQ(384u, trace_buffer()->stats().padding_bytes_cleared());
}
// Like ReadWrite_Padding, but this time the padding introduced is the minimum
@@ -488,7 +511,7 @@
CreateChunk(ProducerID(1), WriterID(1), ChunkID(2))
.AddPacket(30, 'c')
.CopyIntoTraceBuffer();
- EXPECT_EQ(0u, trace_buffer()->stats().chunks_committed_out_of_order);
+ EXPECT_EQ(0u, trace_buffer()->stats().chunks_committed_out_of_order());
trace_buffer()->BeginRead();
ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(10, 'a')));
ASSERT_THAT(ReadPacket(), IsEmpty());
@@ -496,7 +519,7 @@
CreateChunk(ProducerID(1), WriterID(1), ChunkID(1))
.AddPacket(20, 'b')
.CopyIntoTraceBuffer();
- EXPECT_EQ(1u, trace_buffer()->stats().chunks_committed_out_of_order);
+ EXPECT_EQ(1u, trace_buffer()->stats().chunks_committed_out_of_order());
trace_buffer()->BeginRead();
ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(20, 'b')));
ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(30, 'c')));
@@ -1285,7 +1308,7 @@
.AddPacket(100, 'b')
.PadTo(512)
.CopyIntoTraceBuffer(/*chunk_complete=*/false);
- EXPECT_EQ(0u, trace_buffer()->stats().chunks_rewritten);
+ EXPECT_EQ(0u, trace_buffer()->stats().chunks_rewritten());
CreateChunk(ProducerID(1), WriterID(1), ChunkID(0))
.AddPacket(100, 'a')
.AddPacket(100, 'b')
@@ -1294,7 +1317,7 @@
.PadTo(512)
.CopyIntoTraceBuffer();
trace_buffer()->BeginRead();
- EXPECT_EQ(1u, trace_buffer()->stats().chunks_rewritten);
+ EXPECT_EQ(1u, trace_buffer()->stats().chunks_rewritten());
ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(100, 'a')));
ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(100, 'b')));
ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(100, 'c')));
diff --git a/src/tracing/core/trace_stats.cc b/src/tracing/core/trace_stats.cc
new file mode 100644
index 0000000..4350995
--- /dev/null
+++ b/src/tracing/core/trace_stats.cc
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/*******************************************************************************
+ * AUTOGENERATED - DO NOT EDIT
+ *******************************************************************************
+ * This file has been generated from the protobuf message
+ * perfetto/common/trace_stats.proto
+ * by
+ * ../../tools/proto_to_cpp/proto_to_cpp.cc.
+ * If you need to make changes here, change the .proto file and then run
+ * ./tools/gen_tracing_cpp_headers_from_protos
+ */
+
+#include "perfetto/tracing/core/trace_stats.h"
+
+#include "perfetto/common/trace_stats.pb.h"
+
+namespace perfetto {
+
+TraceStats::TraceStats() = default;
+TraceStats::~TraceStats() = default;
+TraceStats::TraceStats(const TraceStats&) = default;
+TraceStats& TraceStats::operator=(const TraceStats&) = default;
+TraceStats::TraceStats(TraceStats&&) noexcept = default;
+TraceStats& TraceStats::operator=(TraceStats&&) = default;
+
+void TraceStats::FromProto(const perfetto::protos::TraceStats& proto) {
+ buffer_stats_.clear();
+ for (const auto& field : proto.buffer_stats()) {
+ buffer_stats_.emplace_back();
+ buffer_stats_.back().FromProto(field);
+ }
+
+ static_assert(
+ sizeof(producers_connected_) == sizeof(proto.producers_connected()),
+ "size mismatch");
+ producers_connected_ =
+ static_cast<decltype(producers_connected_)>(proto.producers_connected());
+
+ static_assert(sizeof(producers_seen_) == sizeof(proto.producers_seen()),
+ "size mismatch");
+ producers_seen_ =
+ static_cast<decltype(producers_seen_)>(proto.producers_seen());
+
+ static_assert(sizeof(data_sources_registered_) ==
+ sizeof(proto.data_sources_registered()),
+ "size mismatch");
+ data_sources_registered_ = static_cast<decltype(data_sources_registered_)>(
+ proto.data_sources_registered());
+
+ static_assert(sizeof(data_sources_seen_) == sizeof(proto.data_sources_seen()),
+ "size mismatch");
+ data_sources_seen_ =
+ static_cast<decltype(data_sources_seen_)>(proto.data_sources_seen());
+
+ static_assert(sizeof(tracing_sessions_) == sizeof(proto.tracing_sessions()),
+ "size mismatch");
+ tracing_sessions_ =
+ static_cast<decltype(tracing_sessions_)>(proto.tracing_sessions());
+
+ static_assert(sizeof(total_buffers_) == sizeof(proto.total_buffers()),
+ "size mismatch");
+ total_buffers_ = static_cast<decltype(total_buffers_)>(proto.total_buffers());
+ unknown_fields_ = proto.unknown_fields();
+}
+
+void TraceStats::ToProto(perfetto::protos::TraceStats* proto) const {
+ proto->Clear();
+
+ for (const auto& it : buffer_stats_) {
+ auto* entry = proto->add_buffer_stats();
+ it.ToProto(entry);
+ }
+
+ static_assert(
+ sizeof(producers_connected_) == sizeof(proto->producers_connected()),
+ "size mismatch");
+ proto->set_producers_connected(
+ static_cast<decltype(proto->producers_connected())>(
+ producers_connected_));
+
+ static_assert(sizeof(producers_seen_) == sizeof(proto->producers_seen()),
+ "size mismatch");
+ proto->set_producers_seen(
+ static_cast<decltype(proto->producers_seen())>(producers_seen_));
+
+ static_assert(sizeof(data_sources_registered_) ==
+ sizeof(proto->data_sources_registered()),
+ "size mismatch");
+ proto->set_data_sources_registered(
+ static_cast<decltype(proto->data_sources_registered())>(
+ data_sources_registered_));
+
+ static_assert(
+ sizeof(data_sources_seen_) == sizeof(proto->data_sources_seen()),
+ "size mismatch");
+ proto->set_data_sources_seen(
+ static_cast<decltype(proto->data_sources_seen())>(data_sources_seen_));
+
+ static_assert(sizeof(tracing_sessions_) == sizeof(proto->tracing_sessions()),
+ "size mismatch");
+ proto->set_tracing_sessions(
+ static_cast<decltype(proto->tracing_sessions())>(tracing_sessions_));
+
+ static_assert(sizeof(total_buffers_) == sizeof(proto->total_buffers()),
+ "size mismatch");
+ proto->set_total_buffers(
+ static_cast<decltype(proto->total_buffers())>(total_buffers_));
+ *(proto->mutable_unknown_fields()) = unknown_fields_;
+}
+
+TraceStats::BufferStats::BufferStats() = default;
+TraceStats::BufferStats::~BufferStats() = default;
+TraceStats::BufferStats::BufferStats(const TraceStats::BufferStats&) = default;
+TraceStats::BufferStats& TraceStats::BufferStats::operator=(
+ const TraceStats::BufferStats&) = default;
+TraceStats::BufferStats::BufferStats(TraceStats::BufferStats&&) noexcept =
+ default;
+TraceStats::BufferStats& TraceStats::BufferStats::operator=(
+ TraceStats::BufferStats&&) = default;
+
+void TraceStats::BufferStats::FromProto(
+ const perfetto::protos::TraceStats_BufferStats& proto) {
+ static_assert(sizeof(buffer_size_) == sizeof(proto.buffer_size()),
+ "size mismatch");
+ buffer_size_ = static_cast<decltype(buffer_size_)>(proto.buffer_size());
+
+ static_assert(sizeof(bytes_written_) == sizeof(proto.bytes_written()),
+ "size mismatch");
+ bytes_written_ = static_cast<decltype(bytes_written_)>(proto.bytes_written());
+
+ static_assert(sizeof(bytes_overwritten_) == sizeof(proto.bytes_overwritten()),
+ "size mismatch");
+ bytes_overwritten_ =
+ static_cast<decltype(bytes_overwritten_)>(proto.bytes_overwritten());
+
+ static_assert(sizeof(bytes_read_) == sizeof(proto.bytes_read()),
+ "size mismatch");
+ bytes_read_ = static_cast<decltype(bytes_read_)>(proto.bytes_read());
+
+ static_assert(
+ sizeof(padding_bytes_written_) == sizeof(proto.padding_bytes_written()),
+ "size mismatch");
+ padding_bytes_written_ = static_cast<decltype(padding_bytes_written_)>(
+ proto.padding_bytes_written());
+
+ static_assert(
+ sizeof(padding_bytes_cleared_) == sizeof(proto.padding_bytes_cleared()),
+ "size mismatch");
+ padding_bytes_cleared_ = static_cast<decltype(padding_bytes_cleared_)>(
+ proto.padding_bytes_cleared());
+
+ static_assert(sizeof(chunks_written_) == sizeof(proto.chunks_written()),
+ "size mismatch");
+ chunks_written_ =
+ static_cast<decltype(chunks_written_)>(proto.chunks_written());
+
+ static_assert(sizeof(chunks_rewritten_) == sizeof(proto.chunks_rewritten()),
+ "size mismatch");
+ chunks_rewritten_ =
+ static_cast<decltype(chunks_rewritten_)>(proto.chunks_rewritten());
+
+ static_assert(
+ sizeof(chunks_overwritten_) == sizeof(proto.chunks_overwritten()),
+ "size mismatch");
+ chunks_overwritten_ =
+ static_cast<decltype(chunks_overwritten_)>(proto.chunks_overwritten());
+
+ static_assert(sizeof(chunks_read_) == sizeof(proto.chunks_read()),
+ "size mismatch");
+ chunks_read_ = static_cast<decltype(chunks_read_)>(proto.chunks_read());
+
+ static_assert(sizeof(chunks_committed_out_of_order_) ==
+ sizeof(proto.chunks_committed_out_of_order()),
+ "size mismatch");
+ chunks_committed_out_of_order_ =
+ static_cast<decltype(chunks_committed_out_of_order_)>(
+ proto.chunks_committed_out_of_order());
+
+ static_assert(sizeof(write_wrap_count_) == sizeof(proto.write_wrap_count()),
+ "size mismatch");
+ write_wrap_count_ =
+ static_cast<decltype(write_wrap_count_)>(proto.write_wrap_count());
+
+ static_assert(sizeof(patches_succeeded_) == sizeof(proto.patches_succeeded()),
+ "size mismatch");
+ patches_succeeded_ =
+ static_cast<decltype(patches_succeeded_)>(proto.patches_succeeded());
+
+ static_assert(sizeof(patches_failed_) == sizeof(proto.patches_failed()),
+ "size mismatch");
+ patches_failed_ =
+ static_cast<decltype(patches_failed_)>(proto.patches_failed());
+
+ static_assert(
+ sizeof(readaheads_succeeded_) == sizeof(proto.readaheads_succeeded()),
+ "size mismatch");
+ readaheads_succeeded_ = static_cast<decltype(readaheads_succeeded_)>(
+ proto.readaheads_succeeded());
+
+ static_assert(sizeof(readaheads_failed_) == sizeof(proto.readaheads_failed()),
+ "size mismatch");
+ readaheads_failed_ =
+ static_cast<decltype(readaheads_failed_)>(proto.readaheads_failed());
+
+ static_assert(sizeof(abi_violations_) == sizeof(proto.abi_violations()),
+ "size mismatch");
+ abi_violations_ =
+ static_cast<decltype(abi_violations_)>(proto.abi_violations());
+ unknown_fields_ = proto.unknown_fields();
+}
+
+void TraceStats::BufferStats::ToProto(
+ perfetto::protos::TraceStats_BufferStats* proto) const {
+ proto->Clear();
+
+ static_assert(sizeof(buffer_size_) == sizeof(proto->buffer_size()),
+ "size mismatch");
+ proto->set_buffer_size(
+ static_cast<decltype(proto->buffer_size())>(buffer_size_));
+
+ static_assert(sizeof(bytes_written_) == sizeof(proto->bytes_written()),
+ "size mismatch");
+ proto->set_bytes_written(
+ static_cast<decltype(proto->bytes_written())>(bytes_written_));
+
+ static_assert(
+ sizeof(bytes_overwritten_) == sizeof(proto->bytes_overwritten()),
+ "size mismatch");
+ proto->set_bytes_overwritten(
+ static_cast<decltype(proto->bytes_overwritten())>(bytes_overwritten_));
+
+ static_assert(sizeof(bytes_read_) == sizeof(proto->bytes_read()),
+ "size mismatch");
+ proto->set_bytes_read(
+ static_cast<decltype(proto->bytes_read())>(bytes_read_));
+
+ static_assert(
+ sizeof(padding_bytes_written_) == sizeof(proto->padding_bytes_written()),
+ "size mismatch");
+ proto->set_padding_bytes_written(
+ static_cast<decltype(proto->padding_bytes_written())>(
+ padding_bytes_written_));
+
+ static_assert(
+ sizeof(padding_bytes_cleared_) == sizeof(proto->padding_bytes_cleared()),
+ "size mismatch");
+ proto->set_padding_bytes_cleared(
+ static_cast<decltype(proto->padding_bytes_cleared())>(
+ padding_bytes_cleared_));
+
+ static_assert(sizeof(chunks_written_) == sizeof(proto->chunks_written()),
+ "size mismatch");
+ proto->set_chunks_written(
+ static_cast<decltype(proto->chunks_written())>(chunks_written_));
+
+ static_assert(sizeof(chunks_rewritten_) == sizeof(proto->chunks_rewritten()),
+ "size mismatch");
+ proto->set_chunks_rewritten(
+ static_cast<decltype(proto->chunks_rewritten())>(chunks_rewritten_));
+
+ static_assert(
+ sizeof(chunks_overwritten_) == sizeof(proto->chunks_overwritten()),
+ "size mismatch");
+ proto->set_chunks_overwritten(
+ static_cast<decltype(proto->chunks_overwritten())>(chunks_overwritten_));
+
+ static_assert(sizeof(chunks_read_) == sizeof(proto->chunks_read()),
+ "size mismatch");
+ proto->set_chunks_read(
+ static_cast<decltype(proto->chunks_read())>(chunks_read_));
+
+ static_assert(sizeof(chunks_committed_out_of_order_) ==
+ sizeof(proto->chunks_committed_out_of_order()),
+ "size mismatch");
+ proto->set_chunks_committed_out_of_order(
+ static_cast<decltype(proto->chunks_committed_out_of_order())>(
+ chunks_committed_out_of_order_));
+
+ static_assert(sizeof(write_wrap_count_) == sizeof(proto->write_wrap_count()),
+ "size mismatch");
+ proto->set_write_wrap_count(
+ static_cast<decltype(proto->write_wrap_count())>(write_wrap_count_));
+
+ static_assert(
+ sizeof(patches_succeeded_) == sizeof(proto->patches_succeeded()),
+ "size mismatch");
+ proto->set_patches_succeeded(
+ static_cast<decltype(proto->patches_succeeded())>(patches_succeeded_));
+
+ static_assert(sizeof(patches_failed_) == sizeof(proto->patches_failed()),
+ "size mismatch");
+ proto->set_patches_failed(
+ static_cast<decltype(proto->patches_failed())>(patches_failed_));
+
+ static_assert(
+ sizeof(readaheads_succeeded_) == sizeof(proto->readaheads_succeeded()),
+ "size mismatch");
+ proto->set_readaheads_succeeded(
+ static_cast<decltype(proto->readaheads_succeeded())>(
+ readaheads_succeeded_));
+
+ static_assert(
+ sizeof(readaheads_failed_) == sizeof(proto->readaheads_failed()),
+ "size mismatch");
+ proto->set_readaheads_failed(
+ static_cast<decltype(proto->readaheads_failed())>(readaheads_failed_));
+
+ static_assert(sizeof(abi_violations_) == sizeof(proto->abi_violations()),
+ "size mismatch");
+ proto->set_abi_violations(
+ static_cast<decltype(proto->abi_violations())>(abi_violations_));
+ *(proto->mutable_unknown_fields()) = unknown_fields_;
+}
+
+} // namespace perfetto
diff --git a/src/tracing/core/tracing_service_impl.cc b/src/tracing/core/tracing_service_impl.cc
index 9eb44d8..4a4be28 100644
--- a/src/tracing/core/tracing_service_impl.cc
+++ b/src/tracing/core/tracing_service_impl.cc
@@ -1598,15 +1598,23 @@
packet.set_trusted_uid(static_cast<int32_t>(uid_));
protos::TraceStats* trace_stats = packet.mutable_trace_stats();
- trace_stats->set_producers_connected(
- static_cast<uint32_t>(producers_.size()));
- trace_stats->set_producers_seen(last_producer_id_);
- trace_stats->set_data_sources_registered(
+ GetTraceStats(tracing_session).ToProto(trace_stats);
+ Slice slice = Slice::Allocate(static_cast<size_t>(packet.ByteSize()));
+ PERFETTO_CHECK(packet.SerializeWithCachedSizesToArray(slice.own_data()));
+ packets->emplace_back();
+ packets->back().AddSlice(std::move(slice));
+}
+
+TraceStats TracingServiceImpl::GetTraceStats(TracingSession* tracing_session) {
+ TraceStats trace_stats;
+ trace_stats.set_producers_connected(static_cast<uint32_t>(producers_.size()));
+ trace_stats.set_producers_seen(last_producer_id_);
+ trace_stats.set_data_sources_registered(
static_cast<uint32_t>(data_sources_.size()));
- trace_stats->set_data_sources_seen(last_data_source_instance_id_);
- trace_stats->set_tracing_sessions(
+ trace_stats.set_data_sources_seen(last_data_source_instance_id_);
+ trace_stats.set_tracing_sessions(
static_cast<uint32_t>(tracing_sessions_.size()));
- trace_stats->set_total_buffers(static_cast<uint32_t>(buffers_.size()));
+ trace_stats.set_total_buffers(static_cast<uint32_t>(buffers_.size()));
for (BufferID buf_id : tracing_session->buffers_index) {
TraceBuffer* buf = GetBufferByID(buf_id);
@@ -1614,25 +1622,9 @@
PERFETTO_DFATAL("Buffer not found.");
continue;
}
- auto* buf_stats_proto = trace_stats->add_buffer_stats();
- const TraceBuffer::Stats& buf_stats = buf->stats();
- buf_stats_proto->set_bytes_written(buf_stats.bytes_written);
- buf_stats_proto->set_chunks_written(buf_stats.chunks_written);
- buf_stats_proto->set_chunks_rewritten(buf_stats.chunks_rewritten);
- buf_stats_proto->set_chunks_overwritten(buf_stats.chunks_overwritten);
- buf_stats_proto->set_chunks_committed_out_of_order(
- buf_stats.chunks_committed_out_of_order);
- buf_stats_proto->set_write_wrap_count(buf_stats.write_wrap_count);
- buf_stats_proto->set_patches_succeeded(buf_stats.patches_succeeded);
- buf_stats_proto->set_patches_failed(buf_stats.patches_failed);
- buf_stats_proto->set_readaheads_succeeded(buf_stats.readaheads_succeeded);
- buf_stats_proto->set_readaheads_failed(buf_stats.readaheads_failed);
- buf_stats_proto->set_abi_violations(buf_stats.abi_violations);
+ *trace_stats.add_buffer_stats() = buf->stats();
} // for (buf in session).
- Slice slice = Slice::Allocate(static_cast<size_t>(packet.ByteSize()));
- PERFETTO_CHECK(packet.SerializeWithCachedSizesToArray(slice.own_data()));
- packets->emplace_back();
- packets->back().AddSlice(std::move(slice));
+ return trace_stats;
}
void TracingServiceImpl::MaybeEmitTraceConfig(
@@ -1762,6 +1754,22 @@
});
}
+void TracingServiceImpl::ConsumerEndpointImpl::GetTraceStats() {
+ PERFETTO_DCHECK_THREAD(thread_checker_);
+ bool success = false;
+ TraceStats stats;
+ TracingSession* session = service_->GetTracingSession(tracing_session_id_);
+ if (session) {
+ success = true;
+ stats = service_->GetTraceStats(session);
+ }
+ auto weak_this = GetWeakPtr();
+ task_runner_->PostTask([weak_this, success, stats] {
+ if (weak_this)
+ weak_this->consumer_->OnTraceStats(success, stats);
+ });
+}
+
base::WeakPtr<TracingServiceImpl::ConsumerEndpointImpl>
TracingServiceImpl::ConsumerEndpointImpl::GetWeakPtr() {
PERFETTO_DCHECK_THREAD(thread_checker_);
diff --git a/src/tracing/core/tracing_service_impl.h b/src/tracing/core/tracing_service_impl.h
index f16aaf9..7822b58 100644
--- a/src/tracing/core/tracing_service_impl.h
+++ b/src/tracing/core/tracing_service_impl.h
@@ -32,6 +32,7 @@
#include "perfetto/tracing/core/data_source_descriptor.h"
#include "perfetto/tracing/core/shared_memory_abi.h"
#include "perfetto/tracing/core/trace_config.h"
+#include "perfetto/tracing/core/trace_stats.h"
#include "perfetto/tracing/core/tracing_service.h"
#include "src/tracing/core/id_allocator.h"
@@ -162,6 +163,7 @@
void Flush(uint32_t timeout_ms, FlushCallback) override;
void Detach(const std::string& key) override;
void Attach(const std::string& key) override;
+ void GetTraceStats() override;
private:
friend class TracingServiceImpl;
@@ -377,6 +379,7 @@
void SnapshotSyncMarker(std::vector<TracePacket>*);
void SnapshotClocks(std::vector<TracePacket>*);
void SnapshotStats(TracingSession*, std::vector<TracePacket>*);
+ TraceStats GetTraceStats(TracingSession* tracing_session);
void MaybeEmitTraceConfig(TracingSession*, std::vector<TracePacket>*);
void OnFlushTimeout(TracingSessionID, FlushRequestID);
void OnDisableTracingTimeout(TracingSessionID);
diff --git a/src/tracing/ipc/consumer/consumer_ipc_client_impl.cc b/src/tracing/ipc/consumer/consumer_ipc_client_impl.cc
index a37bacd..f7db5a6 100644
--- a/src/tracing/ipc/consumer/consumer_ipc_client_impl.cc
+++ b/src/tracing/ipc/consumer/consumer_ipc_client_impl.cc
@@ -23,6 +23,7 @@
#include "perfetto/ipc/client.h"
#include "perfetto/tracing/core/consumer.h"
#include "perfetto/tracing/core/trace_config.h"
+#include "perfetto/tracing/core/trace_stats.h"
// TODO(fmayer): Add a test to check to what happens when ConsumerIPCClientImpl
// gets destroyed w.r.t. the Consumer pointer. Also think to lifetime of the
@@ -253,4 +254,29 @@
}
}
+void ConsumerIPCClientImpl::GetTraceStats() {
+ if (!connected_) {
+ PERFETTO_DLOG("Cannot GetTraceStats(), not connected to tracing service");
+ return;
+ }
+
+ protos::GetTraceStatsRequest req;
+ ipc::Deferred<protos::GetTraceStatsResponse> async_response;
+ auto weak_this = weak_ptr_factory_.GetWeakPtr();
+
+ async_response.Bind(
+ [weak_this](ipc::AsyncResult<protos::GetTraceStatsResponse> response) {
+ if (!weak_this)
+ return;
+ TraceStats trace_stats;
+ if (!response) {
+ weak_this->consumer_->OnTraceStats(/*success=*/false, trace_stats);
+ return;
+ }
+ trace_stats.FromProto(response->trace_stats());
+ weak_this->consumer_->OnTraceStats(/*success=*/true, trace_stats);
+ });
+ consumer_port_.GetTraceStats(req, std::move(async_response));
+}
+
} // namespace perfetto
diff --git a/src/tracing/ipc/consumer/consumer_ipc_client_impl.h b/src/tracing/ipc/consumer/consumer_ipc_client_impl.h
index a41b200..0210dc3 100644
--- a/src/tracing/ipc/consumer/consumer_ipc_client_impl.h
+++ b/src/tracing/ipc/consumer/consumer_ipc_client_impl.h
@@ -67,6 +67,7 @@
void Flush(uint32_t timeout_ms, FlushCallback) override;
void Detach(const std::string& key) override;
void Attach(const std::string& key) override;
+ void GetTraceStats() override;
// ipc::ServiceProxy::EventListener implementation.
// These methods are invoked by the IPC layer, which knows nothing about
diff --git a/src/tracing/ipc/service/consumer_ipc_service.cc b/src/tracing/ipc/service/consumer_ipc_service.cc
index 125b0da..5eade92 100644
--- a/src/tracing/ipc/service/consumer_ipc_service.cc
+++ b/src/tracing/ipc/service/consumer_ipc_service.cc
@@ -27,6 +27,7 @@
#include "perfetto/tracing/core/slice.h"
#include "perfetto/tracing/core/trace_config.h"
#include "perfetto/tracing/core/trace_packet.h"
+#include "perfetto/tracing/core/trace_stats.h"
#include "perfetto/tracing/core/tracing_service.h"
namespace perfetto {
@@ -137,6 +138,15 @@
remote_consumer->service_endpoint->Attach(req.key());
}
+// Called by the IPC layer.
+void ConsumerIPCService::GetTraceStats(const protos::GetTraceStatsRequest&,
+ DeferredGetTraceStatsResponse resp) {
+ // OnTraceStats() will resolve the |get_trace_stats_response|.
+ RemoteConsumer* remote_consumer = GetConsumerForCurrentRequest();
+ remote_consumer->get_trace_stats_response = std::move(resp);
+ remote_consumer->service_endpoint->GetTraceStats();
+}
+
// Called by the service in response to a service_endpoint->Flush() request.
void ConsumerIPCService::OnFlushCallback(
bool success,
@@ -247,4 +257,15 @@
std::move(attach_response).Resolve(std::move(response));
}
+void ConsumerIPCService::RemoteConsumer::OnTraceStats(bool success,
+ const TraceStats& stats) {
+ if (!success) {
+ std::move(get_trace_stats_response).Reject();
+ return;
+ }
+ auto response = ipc::AsyncResult<protos::GetTraceStatsResponse>::Create();
+ stats.ToProto(response->mutable_trace_stats());
+ std::move(get_trace_stats_response).Resolve(std::move(response));
+}
+
} // namespace perfetto
diff --git a/src/tracing/ipc/service/consumer_ipc_service.h b/src/tracing/ipc/service/consumer_ipc_service.h
index a3fa030..6a3782b 100644
--- a/src/tracing/ipc/service/consumer_ipc_service.h
+++ b/src/tracing/ipc/service/consumer_ipc_service.h
@@ -57,6 +57,8 @@
void Flush(const protos::FlushRequest&, DeferredFlushResponse) override;
void Detach(const protos::DetachRequest&, DeferredDetachResponse) override;
void Attach(const protos::AttachRequest&, DeferredAttachResponse) override;
+ void GetTraceStats(const protos::GetTraceStatsRequest&,
+ DeferredGetTraceStatsResponse) override;
void OnClientDisconnected() override;
private:
@@ -76,6 +78,7 @@
void OnTraceData(std::vector<TracePacket>, bool has_more) override;
void OnDetach(bool) override;
void OnAttach(bool, const TraceConfig&) override;
+ void OnTraceStats(bool, const TraceStats&) override;
// The interface obtained from the core service business logic through
// TracingService::ConnectConsumer(this). This allows to invoke methods for
@@ -96,6 +99,9 @@
// As above, but for the Attach() case.
DeferredAttachResponse attach_response;
+
+ // As above, but for GetTraceStats().
+ DeferredGetTraceStatsResponse get_trace_stats_response;
};
// This has to be a container that doesn't invalidate iterators.
diff --git a/src/tracing/test/mock_consumer.cc b/src/tracing/test/mock_consumer.cc
index 3822ebe..b92b274 100644
--- a/src/tracing/test/mock_consumer.cc
+++ b/src/tracing/test/mock_consumer.cc
@@ -17,6 +17,7 @@
#include "src/tracing/test/mock_consumer.h"
#include "perfetto/tracing/core/trace_config.h"
+#include "perfetto/tracing/core/trace_stats.h"
#include "src/base/test/test_task_runner.h"
using ::testing::_;
@@ -114,4 +115,27 @@
return decoded_packets;
}
+void MockConsumer::GetTraceStats() {
+ service_endpoint_->GetTraceStats();
+}
+
+void MockConsumer::WaitForTraceStats(bool success) {
+ static int i = 0;
+ auto checkpoint_name = "on_trace_stats_" + std::to_string(i++);
+ auto on_trace_stats = task_runner_->CreateCheckpoint(checkpoint_name);
+ auto result_callback = [on_trace_stats](bool, const TraceStats&) {
+ on_trace_stats();
+ };
+ if (success) {
+ EXPECT_CALL(*this,
+ OnTraceStats(true, testing::Property(&TraceStats::total_buffers,
+ testing::Gt(0))))
+ .WillOnce(Invoke(result_callback));
+ } else {
+ EXPECT_CALL(*this, OnTraceStats(false, _))
+ .WillOnce(Invoke(result_callback));
+ }
+ task_runner_->RunUntilCheckpoint(checkpoint_name);
+}
+
} // namespace perfetto
diff --git a/src/tracing/test/mock_consumer.h b/src/tracing/test/mock_consumer.h
index e19729d..184e8da 100644
--- a/src/tracing/test/mock_consumer.h
+++ b/src/tracing/test/mock_consumer.h
@@ -54,6 +54,8 @@
void WaitForTracingDisabled(uint32_t timeout_ms = 3000);
FlushRequest Flush(uint32_t timeout_ms = 10000);
std::vector<protos::TracePacket> ReadBuffers();
+ void GetTraceStats();
+ void WaitForTraceStats(bool success);
TracingService::ConsumerEndpoint* endpoint() {
return service_endpoint_.get();
@@ -67,6 +69,7 @@
void(std::vector<TracePacket>* /*packets*/, bool /*has_more*/));
MOCK_METHOD1(OnDetach, void(bool));
MOCK_METHOD2(OnAttach, void(bool, const TraceConfig&));
+ MOCK_METHOD2(OnTraceStats, void(bool, const TraceStats&));
// gtest doesn't support move-only types. This wrapper is here jut to pass
// a pointer to the vector (rather than the vector itself) to the mock method.
diff --git a/src/tracing/test/tracing_integration_test.cc b/src/tracing/test/tracing_integration_test.cc
index 5478dcd..fdac291 100644
--- a/src/tracing/test/tracing_integration_test.cc
+++ b/src/tracing/test/tracing_integration_test.cc
@@ -26,6 +26,7 @@
#include "perfetto/tracing/core/producer.h"
#include "perfetto/tracing/core/trace_config.h"
#include "perfetto/tracing/core/trace_packet.h"
+#include "perfetto/tracing/core/trace_stats.h"
#include "perfetto/tracing/core/trace_writer.h"
#include "perfetto/tracing/ipc/consumer_ipc_client.h"
#include "perfetto/tracing/ipc/producer_ipc_client.h"
@@ -78,6 +79,7 @@
MOCK_METHOD2(OnTracePackets, void(std::vector<TracePacket>*, bool));
MOCK_METHOD1(OnDetach, void(bool));
MOCK_METHOD2(OnAttach, void(bool, const TraceConfig&));
+ MOCK_METHOD2(OnTraceStats, void(bool, const TraceStats&));
// Workaround, gmock doesn't support yet move-only types, passing a pointer.
void OnTraceData(std::vector<TracePacket> packets, bool has_more) {
diff --git a/test/test_helper.cc b/test/test_helper.cc
index 46572e8..afdd8eb 100644
--- a/test/test_helper.cc
+++ b/test/test_helper.cc
@@ -175,6 +175,8 @@
std::move(on_attach_callback_)(success);
}
+void TestHelper::OnTraceStats(bool, const TraceStats&) {}
+
// static
const char* TestHelper::GetConsumerSocketName() {
return TEST_CONSUMER_SOCK_NAME;
diff --git a/test/test_helper.h b/test/test_helper.h
index a5a1301..f2fe7dd 100644
--- a/test/test_helper.h
+++ b/test/test_helper.h
@@ -43,6 +43,7 @@
void OnTraceData(std::vector<TracePacket> packets, bool has_more) override;
void OnDetach(bool) override;
void OnAttach(bool, const TraceConfig&) override;
+ void OnTraceStats(bool, const TraceStats&) override;
void StartServiceIfRequired();
FakeProducer* ConnectFakeProducer();
diff --git a/tools/gen_tracing_cpp_headers_from_protos b/tools/gen_tracing_cpp_headers_from_protos
index ce1cbe4..8c14b6f 100755
--- a/tools/gen_tracing_cpp_headers_from_protos
+++ b/tools/gen_tracing_cpp_headers_from_protos
@@ -21,6 +21,7 @@
'perfetto/common/android_log_constants.proto',
'perfetto/common/commit_data_request.proto',
'perfetto/common/sys_stats_counters.proto',
+ 'perfetto/common/trace_stats.proto',
'perfetto/config/android/android_log_config.proto',
'perfetto/config/chrome/chrome_config.proto',
'perfetto/config/data_source_config.proto',