ftrace: Add new "linux.frozen_ftrace" data source (#1334)
Add new "linux.frozen_ftrace" data source for reading the remaining
previous boot data from the ftrace persistent ring buffer. Note that
this data source does not actively record any trace data for the current
boot. Thus you must not mix this with other data sources in the same
session.
This new data source requires 1 parameter;
- instance_name: Specifies an instance keeping the previous boot data.
Change-Id: Ic1f61f6141d019c569b6f67425f331632c1985b0
diff --git a/Android.bp b/Android.bp
index 97c2e31..3a7496c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1418,6 +1418,7 @@
"protos/perfetto/config/chrome/v8_config.proto",
"protos/perfetto/config/data_source_config.proto",
"protos/perfetto/config/etw/etw_config.proto",
+ "protos/perfetto/config/ftrace/frozen_ftrace_config.proto",
"protos/perfetto/config/ftrace/ftrace_config.proto",
"protos/perfetto/config/gpu/gpu_counter_config.proto",
"protos/perfetto/config/gpu/gpu_renderstages_config.proto",
@@ -1485,6 +1486,7 @@
"protos/perfetto/config/chrome/v8_config.proto",
"protos/perfetto/config/data_source_config.proto",
"protos/perfetto/config/etw/etw_config.proto",
+ "protos/perfetto/config/ftrace/frozen_ftrace_config.proto",
"protos/perfetto/config/ftrace/ftrace_config.proto",
"protos/perfetto/config/gpu/gpu_counter_config.proto",
"protos/perfetto/config/gpu/gpu_renderstages_config.proto",
@@ -2932,6 +2934,7 @@
"protos/perfetto/config/chrome/v8_config.proto",
"protos/perfetto/config/data_source_config.proto",
"protos/perfetto/config/etw/etw_config.proto",
+ "protos/perfetto/config/ftrace/frozen_ftrace_config.proto",
"protos/perfetto/config/ftrace/ftrace_config.proto",
"protos/perfetto/config/gpu/gpu_counter_config.proto",
"protos/perfetto/config/gpu/gpu_renderstages_config.proto",
@@ -3932,6 +3935,7 @@
"protos/perfetto/config/chrome/v8_config.proto",
"protos/perfetto/config/data_source_config.proto",
"protos/perfetto/config/etw/etw_config.proto",
+ "protos/perfetto/config/ftrace/frozen_ftrace_config.proto",
"protos/perfetto/config/ftrace/ftrace_config.proto",
"protos/perfetto/config/gpu/gpu_counter_config.proto",
"protos/perfetto/config/gpu/gpu_renderstages_config.proto",
@@ -3966,6 +3970,7 @@
filegroup {
name: "perfetto_protos_perfetto_config_ftrace_cpp",
srcs: [
+ "protos/perfetto/config/ftrace/frozen_ftrace_config.proto",
"protos/perfetto/config/ftrace/ftrace_config.proto",
],
}
@@ -3982,6 +3987,7 @@
],
cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_cppgen_plugin) --plugin_out=wrapper_namespace=gen:$(genDir)/external/perfetto/ $(locations :perfetto_protos_perfetto_config_ftrace_cpp)",
out: [
+ "external/perfetto/protos/perfetto/config/ftrace/frozen_ftrace_config.gen.cc",
"external/perfetto/protos/perfetto/config/ftrace/ftrace_config.gen.cc",
],
}
@@ -3998,6 +4004,7 @@
],
cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_cppgen_plugin) --plugin_out=wrapper_namespace=gen:$(genDir)/external/perfetto/ $(locations :perfetto_protos_perfetto_config_ftrace_cpp)",
out: [
+ "external/perfetto/protos/perfetto/config/ftrace/frozen_ftrace_config.gen.h",
"external/perfetto/protos/perfetto/config/ftrace/ftrace_config.gen.h",
],
export_include_dirs: [
@@ -4010,6 +4017,7 @@
filegroup {
name: "perfetto_protos_perfetto_config_ftrace_lite",
srcs: [
+ "protos/perfetto/config/ftrace/frozen_ftrace_config.proto",
"protos/perfetto/config/ftrace/ftrace_config.proto",
],
}
@@ -4025,6 +4033,7 @@
],
cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(locations :perfetto_protos_perfetto_config_ftrace_lite)",
out: [
+ "external/perfetto/protos/perfetto/config/ftrace/frozen_ftrace_config.pb.cc",
"external/perfetto/protos/perfetto/config/ftrace/ftrace_config.pb.cc",
],
}
@@ -4040,6 +4049,7 @@
],
cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(locations :perfetto_protos_perfetto_config_ftrace_lite)",
out: [
+ "external/perfetto/protos/perfetto/config/ftrace/frozen_ftrace_config.pb.h",
"external/perfetto/protos/perfetto/config/ftrace/ftrace_config.pb.h",
],
export_include_dirs: [
@@ -4052,6 +4062,7 @@
filegroup {
name: "perfetto_protos_perfetto_config_ftrace_zero",
srcs: [
+ "protos/perfetto/config/ftrace/frozen_ftrace_config.proto",
"protos/perfetto/config/ftrace/ftrace_config.proto",
],
}
@@ -4068,6 +4079,7 @@
],
cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location protozero_plugin) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/ $(locations :perfetto_protos_perfetto_config_ftrace_zero)",
out: [
+ "external/perfetto/protos/perfetto/config/ftrace/frozen_ftrace_config.pbzero.cc",
"external/perfetto/protos/perfetto/config/ftrace/ftrace_config.pbzero.cc",
],
}
@@ -4084,6 +4096,7 @@
],
cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location protozero_plugin) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/ $(locations :perfetto_protos_perfetto_config_ftrace_zero)",
out: [
+ "external/perfetto/protos/perfetto/config/ftrace/frozen_ftrace_config.pbzero.h",
"external/perfetto/protos/perfetto/config/ftrace/ftrace_config.pbzero.h",
],
export_include_dirs: [
@@ -7402,6 +7415,7 @@
"protos/perfetto/config/chrome/v8_config.proto",
"protos/perfetto/config/data_source_config.proto",
"protos/perfetto/config/etw/etw_config.proto",
+ "protos/perfetto/config/ftrace/frozen_ftrace_config.proto",
"protos/perfetto/config/ftrace/ftrace_config.proto",
"protos/perfetto/config/gpu/gpu_counter_config.proto",
"protos/perfetto/config/gpu/gpu_renderstages_config.proto",
@@ -15417,6 +15431,7 @@
"src/traced/probes/ftrace/cpu_stats_parser.cc",
"src/traced/probes/ftrace/event_info.cc",
"src/traced/probes/ftrace/event_info_constants.cc",
+ "src/traced/probes/ftrace/frozen_ftrace_data_source.cc",
"src/traced/probes/ftrace/ftrace_config_muxer.cc",
"src/traced/probes/ftrace/ftrace_config_utils.cc",
"src/traced/probes/ftrace/ftrace_controller.cc",
@@ -16184,6 +16199,7 @@
"protos/perfetto/config/chrome/v8_config.proto",
"protos/perfetto/config/data_source_config.proto",
"protos/perfetto/config/etw/etw_config.proto",
+ "protos/perfetto/config/ftrace/frozen_ftrace_config.proto",
"protos/perfetto/config/ftrace/ftrace_config.proto",
"protos/perfetto/config/gpu/gpu_counter_config.proto",
"protos/perfetto/config/gpu/gpu_renderstages_config.proto",
diff --git a/BUILD b/BUILD
index a53c1a2..d63c1dc 100644
--- a/BUILD
+++ b/BUILD
@@ -4145,6 +4145,8 @@
"src/traced/probes/ftrace/event_info.h",
"src/traced/probes/ftrace/event_info_constants.cc",
"src/traced/probes/ftrace/event_info_constants.h",
+ "src/traced/probes/ftrace/frozen_ftrace_data_source.cc",
+ "src/traced/probes/ftrace/frozen_ftrace_data_source.h",
"src/traced/probes/ftrace/ftrace_config_muxer.cc",
"src/traced/probes/ftrace/ftrace_config_muxer.h",
"src/traced/probes/ftrace/ftrace_config_utils.cc",
@@ -5347,6 +5349,7 @@
perfetto_proto_library(
name = "protos_perfetto_config_ftrace_protos",
srcs = [
+ "protos/perfetto/config/ftrace/frozen_ftrace_config.proto",
"protos/perfetto/config/ftrace/ftrace_config.proto",
],
visibility = [
diff --git a/docs/data-sources/previous-boot-trace.md b/docs/data-sources/previous-boot-trace.md
new file mode 100644
index 0000000..3e818e3
--- /dev/null
+++ b/docs/data-sources/previous-boot-trace.md
@@ -0,0 +1,71 @@
+# Tracing across reboot
+
+_This data source is supported only on the linux-based systems._
+
+The "linux.frozen_ftrace" data source is used for reading the ftrace
+trace data recorded in the previous boot on the persistent ring buffer.
+
+This data source allows you to dump the last seconds of the ftrace
+trace data in the previous boot time, for analyzing the system crash
+reason from the ftrace trace log.
+
+Therefore, this is expected that the user ran the another perfetto
+trace session in background on the special persistent ring buffer.
+
+### Creating a persistent ring buffer
+
+You have to set up a ftrace persistent ring buffer via the kernel
+cmdline. If you need a 20MiB persistent ring buffer, you need to
+add following kernel options to the kernel cmdline when boot.
+
+```
+reserve_mem=20M:2M:trace trace_instance=boot_mapped^traceoff@trace
+```
+
+This creates a `boot_mapped` ftrace instance on a reserved memory area,
+which will preseve the data and be attatched again in the next boot.
+(Note: this is not 100% sure if the kernel configuration has been
+ changed or kernel address mapping is changed by KASLR.)
+
+### Use the persistent ring buffer
+
+Normally, perfetto will record the ftrace data in the top level instance
+instead of the sub-instances. Thus you need to specify `instance_name:`
+option to your trace config. Also, you need to run the trace session as
+long-time backend session. What you need are;
+
+- Specify `RING_BUFFER` fill_policy to all buffers which receives ftrace
+ data source.
+- Specify `instance_name: "boot_mapped"` to the ftrace data source.
+ (NOTE: Split the `atrace` data source from this data source, since
+ atrace related events can not be used for this instance.)
+- Do not specify `duration_ms:`.
+
+And run the perfetto command with `--background` option.
+
+Once you have done it, prepare for a crash.
+
+### Read out the data after crash
+
+After the system crash, you will see the `boot_mapped` instance, which
+should keep the trace data recorded in the last seconds.
+
+Run the perfetto with `"linux.frozen_ftrace"` data source like;
+
+```
+buffers {
+ size_kb: 65536
+ fill_policy: DISCARD
+}
+
+data_sources {
+ config {
+ name: "linux.frozen_ftrace"
+ frozen_ftrace_config {
+ instance_name: "boot_mapped"
+ }
+ }
+}
+
+duration_ms: 5000
+```
\ No newline at end of file
diff --git a/docs/toc.md b/docs/toc.md
index 896d531..95ad16f 100644
--- a/docs/toc.md
+++ b/docs/toc.md
@@ -33,6 +33,8 @@
* [Atrace instrumentation](data-sources/atrace.md)
* [Android log (logcat)](data-sources/android-log.md)
* [Android Janks](data-sources/frametimeline.md)
+ * [Linux system](#)
+ * [Tracing across reboot](data-sources/previous-boot-trace.md)
* [App Instrumentation](#)
* [Tracing SDK](instrumentation/tracing-sdk.md)
diff --git a/include/perfetto/public/protos/config/data_source_config.pzc.h b/include/perfetto/public/protos/config/data_source_config.pzc.h
index 7abf887..4071602 100644
--- a/include/perfetto/public/protos/config/data_source_config.pzc.h
+++ b/include/perfetto/public/protos/config/data_source_config.pzc.h
@@ -37,6 +37,7 @@
PERFETTO_PB_MSG_DECL(perfetto_protos_ChromiumHistogramSamplesConfig);
PERFETTO_PB_MSG_DECL(perfetto_protos_ChromiumSystemMetricsConfig);
PERFETTO_PB_MSG_DECL(perfetto_protos_EtwConfig);
+PERFETTO_PB_MSG_DECL(perfetto_protos_FrozenFtraceConfig);
PERFETTO_PB_MSG_DECL(perfetto_protos_FtraceConfig);
PERFETTO_PB_MSG_DECL(perfetto_protos_GpuCounterConfig);
PERFETTO_PB_MSG_DECL(perfetto_protos_GpuRenderStagesConfig);
@@ -219,6 +220,11 @@
119);
PERFETTO_PB_FIELD(perfetto_protos_DataSourceConfig,
MSG,
+ perfetto_protos_FrozenFtraceConfig,
+ frozen_ftrace_config,
+ 136);
+PERFETTO_PB_FIELD(perfetto_protos_DataSourceConfig,
+ MSG,
perfetto_protos_ChromeConfig,
chrome_config,
101);
diff --git a/protos/perfetto/config/data_source_config.proto b/protos/perfetto/config/data_source_config.proto
index 89d6f7a..0d4b33e 100644
--- a/protos/perfetto/config/data_source_config.proto
+++ b/protos/perfetto/config/data_source_config.proto
@@ -38,6 +38,7 @@
import "protos/perfetto/config/etw/etw_config.proto";
import "protos/perfetto/config/chrome/system_metrics.proto";
import "protos/perfetto/config/ftrace/ftrace_config.proto";
+import "protos/perfetto/config/ftrace/frozen_ftrace_config.proto";
import "protos/perfetto/config/gpu/gpu_counter_config.proto";
import "protos/perfetto/config/gpu/vulkan_memory_config.proto";
import "protos/perfetto/config/gpu/gpu_renderstages_config.proto";
@@ -56,7 +57,7 @@
import "protos/perfetto/config/chrome/histogram_samples.proto";
// The configuration that is passed to each data source when starting tracing.
-// Next id: 136
+// Next id: 137
message DataSourceConfig {
enum SessionInitiator {
SESSION_INITIATOR_UNSPECIFIED = 0;
@@ -178,6 +179,8 @@
optional StatsdTracingConfig statsd_tracing_config = 117 [lazy = true];
// Data source name: linux.system_info
optional SystemInfoConfig system_info_config = 119;
+ // Data source name: linux.frozen_ftrace
+ optional FrozenFtraceConfig frozen_ftrace_config = 136 [lazy = true];
// Chrome is special as it doesn't use the perfetto IPC layer. We want to
// avoid proto serialization and de-serialization there because that would
diff --git a/protos/perfetto/config/ftrace/BUILD.gn b/protos/perfetto/config/ftrace/BUILD.gn
index 057a0b4..095b35b 100644
--- a/protos/perfetto/config/ftrace/BUILD.gn
+++ b/protos/perfetto/config/ftrace/BUILD.gn
@@ -16,5 +16,8 @@
import("../../../../gn/proto_library.gni")
perfetto_proto_library("@TYPE@") {
- sources = [ "ftrace_config.proto" ]
+ sources = [
+ "frozen_ftrace_config.proto",
+ "ftrace_config.proto",
+ ]
}
diff --git a/protos/perfetto/config/ftrace/frozen_ftrace_config.proto b/protos/perfetto/config/ftrace/frozen_ftrace_config.proto
new file mode 100644
index 0000000..286006f
--- /dev/null
+++ b/protos/perfetto/config/ftrace/frozen_ftrace_config.proto
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2025 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.
+ */
+
+syntax = "proto2";
+
+package perfetto.protos;
+
+message FrozenFtraceConfig {
+ // The instance name which stores the previous boot ftrace data.
+ required string instance_name = 1;
+}
\ No newline at end of file
diff --git a/protos/perfetto/config/perfetto_config.proto b/protos/perfetto/config/perfetto_config.proto
index 4de7700..4d80eba 100644
--- a/protos/perfetto/config/perfetto_config.proto
+++ b/protos/perfetto/config/perfetto_config.proto
@@ -996,6 +996,14 @@
}
// End of protos/perfetto/config/etw/etw_config.proto
+// Begin of protos/perfetto/config/ftrace/frozen_ftrace_config.proto
+
+message FrozenFtraceConfig {
+ // The instance name which stores the previous boot ftrace data.
+ required string instance_name = 1;
+}
+// End of protos/perfetto/config/ftrace/frozen_ftrace_config.proto
+
// Begin of protos/perfetto/config/ftrace/ftrace_config.proto
// Next id: 33
@@ -3776,7 +3784,7 @@
// Begin of protos/perfetto/config/data_source_config.proto
// The configuration that is passed to each data source when starting tracing.
-// Next id: 136
+// Next id: 137
message DataSourceConfig {
enum SessionInitiator {
SESSION_INITIATOR_UNSPECIFIED = 0;
@@ -3898,6 +3906,8 @@
optional StatsdTracingConfig statsd_tracing_config = 117 [lazy = true];
// Data source name: linux.system_info
optional SystemInfoConfig system_info_config = 119;
+ // Data source name: linux.frozen_ftrace
+ optional FrozenFtraceConfig frozen_ftrace_config = 136 [lazy = true];
// Chrome is special as it doesn't use the perfetto IPC layer. We want to
// avoid proto serialization and de-serialization there because that would
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index 2457965..e0c12bb 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -996,6 +996,14 @@
}
// End of protos/perfetto/config/etw/etw_config.proto
+// Begin of protos/perfetto/config/ftrace/frozen_ftrace_config.proto
+
+message FrozenFtraceConfig {
+ // The instance name which stores the previous boot ftrace data.
+ required string instance_name = 1;
+}
+// End of protos/perfetto/config/ftrace/frozen_ftrace_config.proto
+
// Begin of protos/perfetto/config/ftrace/ftrace_config.proto
// Next id: 33
@@ -3776,7 +3784,7 @@
// Begin of protos/perfetto/config/data_source_config.proto
// The configuration that is passed to each data source when starting tracing.
-// Next id: 136
+// Next id: 137
message DataSourceConfig {
enum SessionInitiator {
SESSION_INITIATOR_UNSPECIFIED = 0;
@@ -3898,6 +3906,8 @@
optional StatsdTracingConfig statsd_tracing_config = 117 [lazy = true];
// Data source name: linux.system_info
optional SystemInfoConfig system_info_config = 119;
+ // Data source name: linux.frozen_ftrace
+ optional FrozenFtraceConfig frozen_ftrace_config = 136 [lazy = true];
// Chrome is special as it doesn't use the perfetto IPC layer. We want to
// avoid proto serialization and de-serialization there because that would
diff --git a/src/traced/probes/ftrace/BUILD.gn b/src/traced/probes/ftrace/BUILD.gn
index 9e19b8a..b1cb7b8 100644
--- a/src/traced/probes/ftrace/BUILD.gn
+++ b/src/traced/probes/ftrace/BUILD.gn
@@ -139,6 +139,8 @@
"event_info.h",
"event_info_constants.cc",
"event_info_constants.h",
+ "frozen_ftrace_data_source.cc",
+ "frozen_ftrace_data_source.h",
"ftrace_config_muxer.cc",
"ftrace_config_muxer.h",
"ftrace_config_utils.cc",
diff --git a/src/traced/probes/ftrace/cpu_reader.cc b/src/traced/probes/ftrace/cpu_reader.cc
index 359a053..bc438d4 100644
--- a/src/traced/probes/ftrace/cpu_reader.cc
+++ b/src/traced/probes/ftrace/cpu_reader.cc
@@ -149,6 +149,15 @@
}
}
+void SetParseErrorOne(
+ base::FlatSet<protos::pbzero::FtraceParseStatus>* parse_errors,
+ size_t cpu,
+ FtraceParseStatus status) {
+ PERFETTO_DPLOG("[cpu%zu]: unexpected ftrace read error: %s", cpu,
+ protos::pbzero::FtraceParseStatus_Name(status));
+ parse_errors->insert(status);
+}
+
void WriteAndSetParseError(CpuReader::Bundler* bundler,
base::FlatSet<FtraceParseStatus>* stat,
uint64_t timestamp,
@@ -1136,4 +1145,60 @@
compact_buf->sched_waking().common_flags().Append(common_flags);
}
+size_t CpuReader::ReadFrozen(
+ ParsingBuffers* parsing_bufs,
+ size_t max_pages,
+ const FtraceDataSourceConfig* parsing_config,
+ FtraceMetadata* metadata,
+ base::FlatSet<protos::pbzero::FtraceParseStatus>* parse_errors,
+ TraceWriter* trace_writer) {
+ PERFETTO_CHECK(max_pages > 0);
+ // Limit the max read page under the buffer size.
+ max_pages = std::min(parsing_bufs->ftrace_data_buf_pages(), max_pages);
+
+ uint8_t* parsing_buf = parsing_bufs->ftrace_data_buf();
+ const uint32_t sys_page_size = base::GetSysPageSize();
+
+ // Read the pages into |parsing_buf|.
+ size_t pages_read = 0;
+ for (; pages_read < max_pages;) {
+ uint8_t* curr_page = parsing_buf + (pages_read * sys_page_size);
+ ssize_t res = PERFETTO_EINTR(read(*trace_fd_, curr_page, sys_page_size));
+ if (res < 0) {
+ // Expected:
+ // * EAGAIN: no data (since we're in non-blocking mode).
+ if (errno != EAGAIN)
+ SetParseErrorOne(
+ parse_errors, cpu_,
+ FtraceParseStatus::FTRACE_STATUS_UNEXPECTED_READ_ERROR);
+ break;
+ }
+ if (res != static_cast<ssize_t>(sys_page_size)) {
+ // For the frozen trace buffer, it should return page size. If not,
+ // this should stop reading at that point.
+ SetParseErrorOne(parse_errors, cpu_,
+ FtraceParseStatus::FTRACE_STATUS_PARTIAL_PAGE_READ);
+ break;
+ }
+ pages_read += 1;
+ }
+
+ if (pages_read == 0)
+ return pages_read;
+
+ // Inputs that we will throw away since we only need a subset of what
+ // FtraceDataSource does.
+ uint64_t bundle_end_timestamp = 0;
+
+ // Convert events and serialise the protos. We don't handle the failure
+ // here, because appropriate errors are recorded in |parsing_errors|.
+ ProcessPagesForDataSource(trace_writer, metadata, cpu_, parsing_config,
+ parse_errors, &bundle_end_timestamp, parsing_buf,
+ pages_read, parsing_bufs->compact_sched_buf(),
+ table_, symbolizer_, ftrace_clock_snapshot_,
+ ftrace_clock_);
+
+ return pages_read;
+}
+
} // namespace perfetto
diff --git a/src/traced/probes/ftrace/cpu_reader.h b/src/traced/probes/ftrace/cpu_reader.h
index 08f21bb..dee0c5a 100644
--- a/src/traced/probes/ftrace/cpu_reader.h
+++ b/src/traced/probes/ftrace/cpu_reader.h
@@ -212,6 +212,16 @@
size_t max_pages,
const std::set<FtraceDataSource*>& started_data_sources);
+ // Niche version of ReadCycle for FrozenFtraceDataSource, assumes a stopped
+ // tracefs instance. Don't add new callers.
+ size_t ReadFrozen(
+ ParsingBuffers* parsing_bufs,
+ size_t max_pages,
+ const FtraceDataSourceConfig* parsing_config,
+ FtraceMetadata* metadata,
+ base::FlatSet<protos::pbzero::FtraceParseStatus>* parse_errors,
+ TraceWriter* trace_writer);
+
template <typename T>
static bool ReadAndAdvance(const uint8_t** ptr, const uint8_t* end, T* out) {
if (*ptr > end - sizeof(T))
diff --git a/src/traced/probes/ftrace/cpu_reader_unittest.cc b/src/traced/probes/ftrace/cpu_reader_unittest.cc
index df1b067..c5e10d7 100644
--- a/src/traced/probes/ftrace/cpu_reader_unittest.cc
+++ b/src/traced/probes/ftrace/cpu_reader_unittest.cc
@@ -21,6 +21,7 @@
#include <sys/syscall.h>
#include "perfetto/base/build_config.h"
+#include "perfetto/ext/base/file_utils.h"
#include "perfetto/ext/base/utils.h"
#include "perfetto/protozero/proto_utils.h"
#include "perfetto/protozero/scattered_heap_buffer.h"
@@ -3859,5 +3860,79 @@
// (skip verifying last switch)
}
+// Tests that |ReadFrozen()| reads the specified file and parse it correctly.
+TEST(CpuReaderTest, FrozenPageRead) {
+ auto page_ok = PageFromXxd(g_switch_page);
+ auto page_loss = PageFromXxd(g_switch_page_lost_events);
+
+ // build test buffer with 8 pages
+ size_t page_sz = base::GetSysPageSize();
+ base::TempFile test_pages = base::TempFile::CreateUnlinked();
+ base::WriteAll(test_pages.fd(), page_ok.get(), page_sz);
+ base::WriteAll(test_pages.fd(), page_ok.get(), page_sz);
+ base::WriteAll(test_pages.fd(), page_ok.get(), page_sz);
+ base::WriteAll(test_pages.fd(), page_loss.get(), page_sz);
+ base::WriteAll(test_pages.fd(), page_loss.get(), page_sz);
+ base::WriteAll(test_pages.fd(), page_ok.get(), page_sz);
+ base::WriteAll(test_pages.fd(), page_ok.get(), page_sz);
+ base::WriteAll(test_pages.fd(), page_ok.get(), page_sz);
+ ASSERT_NE(lseek(test_pages.fd(), 0, SEEK_SET), -1);
+
+ ProtoTranslationTable* table = GetTable("synthetic");
+ CpuReader cpu_reader(0, test_pages.ReleaseFD(), table,
+ /*symbolizer=*/nullptr,
+ protos::pbzero::FTRACE_CLOCK_UNSPECIFIED,
+ /*ftrace_clock_snapshot=*/nullptr);
+
+ // build cfg requesting ftrace/print
+ FtraceMetadata metadata{};
+ FtraceDataSourceConfig ftrace_cfg = EmptyConfig();
+ ftrace_cfg.event_filter.AddEnabledEvent(
+ table->EventToFtraceId(GroupAndName("sched", "sched_switch")));
+
+ // invoke ReadFrozen
+ TraceWriterForTesting trace_writer;
+ base::FlatSet<protos::pbzero::FtraceParseStatus> parse_errors;
+ CpuReader::ParsingBuffers parsing_mem;
+ parsing_mem.AllocateIfNeeded();
+
+ // read 2 pages because max_pages limits it.
+ size_t pages_read = cpu_reader.ReadFrozen(
+ &parsing_mem, 2, &ftrace_cfg, &metadata, &parse_errors, &trace_writer);
+ EXPECT_EQ(2u, pages_read);
+ EXPECT_EQ(0u, parse_errors.size());
+
+ // first 2 pages has 2 events without any data lost.
+ auto first_packets = trace_writer.GetAllTracePackets();
+ ASSERT_EQ(1u, first_packets.size());
+
+ EXPECT_FALSE(first_packets[0].ftrace_events().lost_events());
+ EXPECT_EQ(2u, first_packets[0].ftrace_events().event().size());
+
+ // read remaining pages.
+ pages_read = cpu_reader.ReadFrozen(&parsing_mem, 6, &ftrace_cfg, &metadata,
+ &parse_errors, &trace_writer);
+ EXPECT_EQ(6u, pages_read);
+ EXPECT_EQ(0u, parse_errors.size());
+
+ // Each packet should contain the parsed contents of a contiguous run of pages
+ // without data loss.
+ // So we should get three packets (each page has 1 event):
+ // [2 events (previous ReadFrozen)] [1 events] [1 event] [4 events].
+ auto packets = trace_writer.GetAllTracePackets();
+ ASSERT_EQ(4u, packets.size());
+ EXPECT_FALSE(packets[0].ftrace_events().lost_events());
+ EXPECT_EQ(2u, packets[0].ftrace_events().event().size());
+
+ EXPECT_FALSE(packets[1].ftrace_events().lost_events());
+ EXPECT_EQ(1u, packets[1].ftrace_events().event().size());
+
+ EXPECT_TRUE(packets[2].ftrace_events().lost_events());
+ EXPECT_EQ(1u, packets[2].ftrace_events().event().size());
+
+ EXPECT_TRUE(packets[3].ftrace_events().lost_events());
+ EXPECT_EQ(4u, packets[3].ftrace_events().event().size());
+}
+
} // namespace
} // namespace perfetto
diff --git a/src/traced/probes/ftrace/frozen_ftrace_data_source.cc b/src/traced/probes/ftrace/frozen_ftrace_data_source.cc
new file mode 100644
index 0000000..6ce044d
--- /dev/null
+++ b/src/traced/probes/ftrace/frozen_ftrace_data_source.cc
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2025 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/traced/probes/ftrace/frozen_ftrace_data_source.h"
+
+#include <memory>
+
+#include "perfetto/base/task_runner.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "perfetto/ext/tracing/core/trace_writer.h"
+#include "perfetto/tracing/core/data_source_config.h"
+#include "src/traced/probes/ftrace/compact_sched.h"
+#include "src/traced/probes/ftrace/cpu_stats_parser.h"
+#include "src/traced/probes/ftrace/event_info.h"
+#include "src/traced/probes/ftrace/ftrace_config_muxer.h"
+#include "src/traced/probes/ftrace/ftrace_procfs.h"
+#include "src/traced/probes/ftrace/proto_translation_table.h"
+
+#include "protos/perfetto/trace/ftrace/ftrace_stats.pbzero.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+
+namespace perfetto {
+
+// static
+const ProbesDataSource::Descriptor FrozenFtraceDataSource::descriptor = {
+ /*name*/ "linux.frozen_ftrace",
+ /*flags*/ Descriptor::kFlagsNone,
+ /*fill_descriptor_func*/ nullptr,
+};
+
+FrozenFtraceDataSource::~FrozenFtraceDataSource() = default;
+
+FrozenFtraceDataSource::FrozenFtraceDataSource(
+ base::TaskRunner* task_runner,
+ const DataSourceConfig& ds_config,
+ TracingSessionID session_id,
+ std::unique_ptr<TraceWriter> writer)
+ : ProbesDataSource(session_id, &descriptor),
+ task_runner_(task_runner),
+ writer_(std::move(writer)),
+ weak_factory_(this) {
+ // This should check the required parameters.
+ ds_config_.ParseFromString(ds_config.frozen_ftrace_config_raw());
+}
+
+void FrozenFtraceDataSource::Start() {
+ parsing_mem_.AllocateIfNeeded();
+
+ std::string raw_instance_name = ds_config_.instance_name();
+ if (base::Contains(raw_instance_name, '/') ||
+ base::StartsWith(raw_instance_name, "..")) {
+ PERFETTO_ELOG("instance name '%s' is invalid.", raw_instance_name.c_str());
+ return;
+ }
+
+ tracefs_ = FtraceProcfs::CreateGuessingMountPoint("instances/" +
+ raw_instance_name + "/");
+ if (!tracefs_)
+ return;
+
+ translation_table_ = ProtoTranslationTable::Create(
+ tracefs_.get(), GetStaticEventInfo(), GetStaticCommonFieldsInfo());
+ if (!translation_table_) {
+ PERFETTO_ELOG("Failed to create translation table.");
+ return;
+ }
+
+ // Assumes the same core count as currently. If not, the previous boot
+ // data is cleared because of the failure of buffer metadata validation.
+ size_t num_cpus = tracefs_->NumberOfCpus();
+
+ // This assumes that the clock is CLOCK_BOOTTIME, but anyway the clock
+ // is not matter because this is not a live trace. We already don't
+ // know the absolute time in the previous boot.
+ const auto ftrace_clock =
+ protos::pbzero::FtraceClock::FTRACE_CLOCK_UNSPECIFIED;
+
+ // To avoid reading pages more than expected, record remaining pages.
+ size_t initial_page_quota = tracefs_->GetCpuBufferSizeInPages();
+
+ PERFETTO_CHECK(cpu_readers_.empty());
+ cpu_readers_.reserve(num_cpus);
+ for (size_t cpu = 0; cpu < num_cpus; cpu++) {
+ cpu_readers_.emplace_back(cpu, tracefs_->OpenPipeForCpu(cpu),
+ translation_table_.get(),
+ /*symbolizer=*/nullptr, ftrace_clock,
+ /*ftrace_clock_snapshot=*/nullptr);
+
+ cpu_page_quota_.push_back(initial_page_quota);
+ }
+ if (cpu_readers_.empty())
+ return;
+
+ // Enable all events in the translation table because the previous
+ // boot trace data may record any events.
+ EventFilter event_filter;
+ for (const auto& event : translation_table_->events()) {
+ event_filter.AddEnabledEvent(event.ftrace_event_id);
+ }
+
+ parsing_config_ =
+ std::unique_ptr<FtraceDataSourceConfig>(new FtraceDataSourceConfig(
+ /*event_filter=*/std::move(event_filter),
+ /*syscall_filter=*/EventFilter{},
+ /*compact_sched_in=*/CompactSchedConfig{false},
+ /*print_filter=*/std::nullopt,
+ /*atrace_apps=*/{},
+ /*atrace_categories=*/{},
+ /*atrace_categories_sdk_optout=*/{},
+ /*symbolize_ksyms=*/false,
+ /*buffer_percent=*/0u,
+ /*syscalls_returning_fd=*/{},
+ /*kprobes=*/
+ base::FlatHashMap<uint32_t, protos::pbzero::KprobeEvent::KprobeType>{
+ 0},
+ /*debug_ftrace_abi=*/false,
+ /*write_generic_evt_descriptors=*/false));
+
+ // For serialising pre-existing ftrace data, emit a special packet so that
+ // trace_processor doesn't filter out data before start-of-trace.
+ auto stats_packet = writer_->NewTracePacket();
+ auto* stats = stats_packet->set_ftrace_stats();
+ stats->set_phase(protos::pbzero::FtraceStats::Phase::START_OF_TRACE);
+ stats->set_preserve_ftrace_buffer(true);
+
+ // Start the reader tasks, which will self-repost until the existing raw
+ // buffer pages have been parsed. The work is split into tasks to let other
+ // ipc/tasks to run inbetween.
+ task_runner_->PostTask([weak_this = weak_factory_.GetWeakPtr()] {
+ if (weak_this)
+ weak_this->ReadTask();
+ });
+}
+
+void FrozenFtraceDataSource::ReadTask() {
+ bool all_cpus_done = true;
+ for (size_t i = 0; i < cpu_readers_.size(); i++) {
+ size_t max_pages =
+ std::min<size_t>(kFrozenFtraceMaxReadPages, cpu_page_quota_[i]);
+ if (max_pages == 0)
+ continue;
+
+ size_t pages_read = cpu_readers_[i].ReadFrozen(
+ &parsing_mem_, max_pages, parsing_config_.get(), &metadata_,
+ &parse_errors_, writer_.get());
+ PERFETTO_DCHECK(pages_read <= max_pages);
+
+ if (pages_read != 0) {
+ all_cpus_done = false;
+ }
+ cpu_page_quota_[i] -= pages_read;
+ }
+
+ // More work to do, repost the task at the end of the queue.
+ if (!all_cpus_done) {
+ task_runner_->PostTask([weak_this = weak_factory_.GetWeakPtr()] {
+ if (weak_this)
+ weak_this->ReadTask();
+ });
+ return;
+ }
+
+ // Finished. Write the end of trace packet.
+ {
+ FtraceStats stats_after{};
+ DumpAllCpuStats(tracefs_.get(), &stats_after);
+ auto after_packet = writer_->NewTracePacket();
+ auto out = after_packet->set_ftrace_stats();
+ out->set_phase(protos::pbzero::FtraceStats::Phase::END_OF_TRACE);
+ stats_after.Write(out);
+ for (auto error : parse_errors_) {
+ out->add_ftrace_parse_errors(error);
+ }
+ }
+}
+
+void FrozenFtraceDataSource::Flush(FlushRequestID,
+ std::function<void()> callback) {
+ writer_->Flush(std::move(callback));
+}
+
+} // namespace perfetto
diff --git a/src/traced/probes/ftrace/frozen_ftrace_data_source.h b/src/traced/probes/ftrace/frozen_ftrace_data_source.h
new file mode 100644
index 0000000..d1d94cb
--- /dev/null
+++ b/src/traced/probes/ftrace/frozen_ftrace_data_source.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2025 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_TRACED_PROBES_FTRACE_FROZEN_FTRACE_DATA_SOURCE_H_
+#define SRC_TRACED_PROBES_FTRACE_FROZEN_FTRACE_DATA_SOURCE_H_
+
+#include <functional>
+#include <map>
+#include <memory>
+
+#include "perfetto/ext/base/utils.h"
+#include "perfetto/ext/base/weak_ptr.h"
+#include "perfetto/ext/tracing/core/basic_types.h"
+#include "perfetto/ext/tracing/core/trace_writer.h"
+#include "perfetto/tracing/core/forward_decls.h"
+#include "src/traced/probes/ftrace/cpu_reader.h"
+#include "src/traced/probes/ftrace/ftrace_procfs.h"
+#include "src/traced/probes/ftrace/ftrace_stats.h"
+#include "src/traced/probes/probes_data_source.h"
+
+#include "protos/perfetto/config/ftrace/frozen_ftrace_config.gen.h"
+
+namespace perfetto {
+struct FtraceDataSourceConfig;
+class ProtoTranslationTable;
+
+namespace base {
+class TaskRunner;
+}
+
+// Consumes the contents of a stopped tracefs instance, converting them to
+// perfetto ftrace protos (same as FtraceDataSource). Does not reactivate the
+// instance or write to any other control files within the tracefs instance (but
+// the buffer contents do get consumed).
+class FrozenFtraceDataSource : public ProbesDataSource {
+ public:
+ static const ProbesDataSource::Descriptor descriptor;
+
+ FrozenFtraceDataSource(base::TaskRunner* task_runner,
+ const DataSourceConfig& ds_config,
+ TracingSessionID session_id,
+ std::unique_ptr<TraceWriter> writer);
+ ~FrozenFtraceDataSource() override;
+
+ // ProbeDataSource implementation.
+ void Start() override;
+ void Flush(FlushRequestID, std::function<void()> callback) override;
+
+ base::WeakPtr<FrozenFtraceDataSource> GetWeakPtr() const {
+ return weak_factory_.GetWeakPtr();
+ }
+
+ private:
+ void ReadTask();
+
+ // This is the maximum number of pages reading at once from
+ // per-cpu buffer. To prevent blocking other services, keep
+ // it small enough.
+ static constexpr size_t kFrozenFtraceMaxReadPages = 32;
+
+ base::TaskRunner* const task_runner_;
+ std::unique_ptr<TraceWriter> writer_;
+
+ protos::gen::FrozenFtraceConfig ds_config_;
+
+ std::unique_ptr<FtraceProcfs> tracefs_;
+ std::unique_ptr<ProtoTranslationTable> translation_table_;
+ std::unique_ptr<FtraceDataSourceConfig> parsing_config_;
+ CpuReader::ParsingBuffers parsing_mem_;
+ std::vector<CpuReader> cpu_readers_;
+
+ std::vector<size_t> cpu_page_quota_;
+ // Storing parsed metadata (e.g. pid)
+ FtraceMetadata metadata_;
+
+ base::FlatSet<protos::pbzero::FtraceParseStatus> parse_errors_;
+
+ base::WeakPtrFactory<FrozenFtraceDataSource> weak_factory_; // Keep last.
+};
+
+} // namespace perfetto
+
+#endif // SRC_TRACED_PROBES_FTRACE_FROZEN_FTRACE_DATA_SOURCE_H_
diff --git a/src/traced/probes/ftrace/ftrace_procfs.cc b/src/traced/probes/ftrace/ftrace_procfs.cc
index 90eb913..fd1f7a5 100644
--- a/src/traced/probes/ftrace/ftrace_procfs.cc
+++ b/src/traced/probes/ftrace/ftrace_procfs.cc
@@ -463,6 +463,30 @@
return WriteNumberToFile(path, pages * (base::GetSysPageSize() / 1024ul));
}
+// This returns the rounded up pages of the cpu buffer size.
+// In case of any error, this returns 1.
+size_t FtraceProcfs::GetCpuBufferSizeInPages() {
+ std::string path = root_ + "buffer_size_kb";
+ auto str = ReadFileIntoString(path);
+
+ if (str.size() == 0) {
+ PERFETTO_ELOG("Failed to read per-cpu buffer size.");
+ return 1;
+ }
+
+ // For the root instance, before starting tracing, the buffer_size_kb
+ // returns something like "7 (expanded: 1408)". We also cut off the
+ // last newline('\n').
+ std::size_t found = str.find_first_not_of("0123456789");
+ if (found != std::string::npos) {
+ str.resize(found);
+ }
+
+ uint32_t page_in_kb = base::GetSysPageSize() / 1024ul;
+ std::optional<uint32_t> size_kb = base::StringToUInt32(str);
+ return (size_kb.value_or(1) + page_in_kb - 1) / page_in_kb;
+}
+
bool FtraceProcfs::GetTracingOn() {
std::string path = root_ + "tracing_on";
char tracing_on = ReadOneCharFromFile(path);
diff --git a/src/traced/probes/ftrace/ftrace_procfs.h b/src/traced/probes/ftrace/ftrace_procfs.h
index 2a0593f..6482f07 100644
--- a/src/traced/probes/ftrace/ftrace_procfs.h
+++ b/src/traced/probes/ftrace/ftrace_procfs.h
@@ -135,6 +135,9 @@
// by the number of CPUs.
bool SetCpuBufferSizeInPages(size_t pages);
+ // Returns the current per-cpu buffer size in pages.
+ size_t GetCpuBufferSizeInPages();
+
// Returns the number of CPUs.
// This will match the number of tracing/per_cpu/cpuXX directories.
size_t virtual NumberOfCpus() const;
diff --git a/src/traced/probes/ftrace/ftrace_procfs_unittest.cc b/src/traced/probes/ftrace/ftrace_procfs_unittest.cc
index 8fb96af..9eee276 100644
--- a/src/traced/probes/ftrace/ftrace_procfs_unittest.cc
+++ b/src/traced/probes/ftrace/ftrace_procfs_unittest.cc
@@ -15,6 +15,7 @@
*/
#include "src/traced/probes/ftrace/ftrace_procfs.h"
+#include "perfetto/ext/base/utils.h"
#include "test/gtest_and_gmock.h"
@@ -100,5 +101,47 @@
EXPECT_THAT(ftrace.AvailableClocks(), IsEmpty());
}
+TEST(FtraceProcfsTest, ReadBufferSizeInPages) {
+ MockFtraceProcfs ftrace;
+ uint32_t page_in_kb = base::GetSysPageSize() / 1024ul;
+
+ // Boundary checks
+ EXPECT_CALL(ftrace, ReadFileIntoString("/root/buffer_size_kb"))
+ .WillOnce(Return(std::to_string(page_in_kb) + "\n"));
+ EXPECT_THAT(ftrace.GetCpuBufferSizeInPages(), 1);
+
+ EXPECT_CALL(ftrace, ReadFileIntoString("/root/buffer_size_kb"))
+ .WillOnce(Return(std::to_string(page_in_kb - 1) + "\n"));
+ EXPECT_THAT(ftrace.GetCpuBufferSizeInPages(), 1);
+
+ EXPECT_CALL(ftrace, ReadFileIntoString("/root/buffer_size_kb"))
+ .WillOnce(Return(std::to_string(page_in_kb + 1) + "\n"));
+ EXPECT_THAT(ftrace.GetCpuBufferSizeInPages(), 2);
+
+ EXPECT_CALL(ftrace, ReadFileIntoString("/root/buffer_size_kb"))
+ .WillOnce(Return(std::to_string(2 * page_in_kb) + "\n"));
+ EXPECT_THAT(ftrace.GetCpuBufferSizeInPages(), 2);
+
+ EXPECT_CALL(ftrace, ReadFileIntoString("/root/buffer_size_kb"))
+ .WillOnce(Return(std::to_string(2 * page_in_kb + 1) + "\n"));
+ EXPECT_THAT(ftrace.GetCpuBufferSizeInPages(), 3);
+
+ // Read before setup buffer size.
+ EXPECT_CALL(ftrace, ReadFileIntoString("/root/buffer_size_kb"))
+ .WillOnce(
+ Return(std::to_string(2 * page_in_kb - 1) + " (expanded: 1408)\n"));
+ EXPECT_THAT(ftrace.GetCpuBufferSizeInPages(), 2);
+
+ // Failed to read file (e.g. permission error)
+ EXPECT_CALL(ftrace, ReadFileIntoString("/root/buffer_size_kb"))
+ .WillOnce(Return(""));
+ EXPECT_THAT(ftrace.GetCpuBufferSizeInPages(), 1);
+
+ // Wrong string
+ EXPECT_CALL(ftrace, ReadFileIntoString("/root/buffer_size_kb"))
+ .WillOnce(Return("\n\n\n\n"));
+ EXPECT_THAT(ftrace.GetCpuBufferSizeInPages(), 1);
+}
+
} // namespace
} // namespace perfetto
diff --git a/src/traced/probes/probes_producer.cc b/src/traced/probes/probes_producer.cc
index 1b8cc86..4e16df2 100644
--- a/src/traced/probes/probes_producer.cc
+++ b/src/traced/probes/probes_producer.cc
@@ -36,6 +36,7 @@
#include "src/traced/probes/android_log/android_log_data_source.h"
#include "src/traced/probes/android_system_property/android_system_property_data_source.h"
#include "src/traced/probes/filesystem/inode_file_data_source.h"
+#include "src/traced/probes/ftrace/frozen_ftrace_data_source.h"
#include "src/traced/probes/ftrace/ftrace_data_source.h"
#include "src/traced/probes/initial_display_state/initial_display_state_data_source.h"
#include "src/traced/probes/metatrace/metatrace_data_source.h"
@@ -302,6 +303,17 @@
endpoint_->CreateTraceWriter(buffer_id, BufferExhaustedPolicy::kStall));
}
+template <>
+std::unique_ptr<ProbesDataSource>
+ProbesProducer::CreateDSInstance<FrozenFtraceDataSource>(
+ TracingSessionID session_id,
+ const DataSourceConfig& config) {
+ auto buffer_id = static_cast<BufferID>(config.target_buffer());
+ return std::make_unique<FrozenFtraceDataSource>(
+ task_runner_, config, session_id,
+ endpoint_->CreateTraceWriter(buffer_id, BufferExhaustedPolicy::kStall));
+}
+
// Another anonymous namespace. This cannot be moved into the anonymous
// namespace on top (it would fail to compile), because the CreateDSInstance
// methods need to be fully declared before.
@@ -326,6 +338,7 @@
Ds<AndroidLogDataSource>(),
Ds<AndroidPowerDataSource>(),
Ds<AndroidSystemPropertyDataSource>(),
+ Ds<FrozenFtraceDataSource>(),
Ds<FtraceDataSource>(),
Ds<InitialDisplayStateDataSource>(),
Ds<InodeFileDataSource>(),