Merge "Allow embedders to set custom process IDs"
diff --git a/CHANGELOG b/CHANGELOG
index 526a152..8f2d129 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -3,6 +3,9 @@
* Added prebuilts for mac-arm64.
* Removed merged trace and config protos from Bazel. Embedder should
instead depend on the non-merged proto targets.
+ * Added FtraceConfig.disable_generic_events. If set, the ftrace data source
+ will not emit events for which it doesn't have a compile-time proto
+ message.
Trace Processor:
* Added prebuilts for mac-arm64.
*
diff --git a/docs/images/cpu-profile-diamond.png b/docs/images/cpu-profile-diamond.png
new file mode 100644
index 0000000..97bd44b
--- /dev/null
+++ b/docs/images/cpu-profile-diamond.png
Binary files differ
diff --git a/docs/images/cpu-profile-flame.png b/docs/images/cpu-profile-flame.png
new file mode 100644
index 0000000..b5da446
--- /dev/null
+++ b/docs/images/cpu-profile-flame.png
Binary files differ
diff --git a/docs/images/enable-profile-flame-graph.png b/docs/images/enable-profile-flame-graph.png
new file mode 100644
index 0000000..642ce73
--- /dev/null
+++ b/docs/images/enable-profile-flame-graph.png
Binary files differ
diff --git a/docs/quickstart/callstack-sampling.md b/docs/quickstart/callstack-sampling.md
new file mode 100644
index 0000000..d2bf266
--- /dev/null
+++ b/docs/quickstart/callstack-sampling.md
@@ -0,0 +1,138 @@
+# Quickstart: Callstack sampling on Android
+
+## Prerequisites
+
+* [ADB](https://developer.android.com/studio/command-line/adb) installed.
+* A device running Android R+.
+* Either a debuggable (`userdebug`/`eng`) Android image, or the apps to be
+ profiled need to be
+ [marked as profileable or debuggable](https://developer.android.com/guide/topics/manifest/profileable-element)
+ in their manifests.
+
+## Capture a CPU profile
+
+### Linux or macOS
+
+Make sure `adb` is installed and in your `PATH`.
+
+```bash
+adb devices -l
+```
+
+If more than one device or emulator is reported you must select one upfront as
+follows:
+
+```bash
+export ANDROID_SERIAL=SER123456
+```
+
+Download `cpu_profile` (if you don't have a Perfetto checkout):
+
+```bash
+curl -LO https://raw.githubusercontent.com/google/perfetto/master/tools/cpu_profile
+chmod +x cpu_profile
+```
+
+Then, start profiling. For example, to profile the processes `com.android.foo`
+and `com.android.bar`, use:
+
+```bash
+./cpu_profile -n "com.android.foo,com.android.bar"
+```
+
+By default, profiling runs until manually terminated manually. To set a specific
+duration for recording (e.g. 30 seconds), use:
+
+```bash
+./cpu_profile -n "com.android.foo,com.android.bar" -d 30000
+```
+
+To change how frequently stack samples are recorded (e.g. 120 samples per
+second), set the `-f` argument:
+
+```bash
+./cpu_profile -n "com.android.foo,com.android.bar" -f 120
+```
+
+You can also pass in parts of the names of the processes you want to profile by
+enabling `--partial-matching/-p`. This matches processes that are already
+running when profiling is started. For instance, to profile the processes
+`com.android.foo` and `com.android.bar`, run:
+
+```bash
+./cpu_profile -n "foo,bar" -p
+```
+
+You can also pass in a custom [Perfetto config](/docs/concepts/config.md), which
+overrides all of the options above, using the `-c` argument:
+
+```bash
+./cpu_profile -c "path/to/perfetto.config"
+```
+
+To change where profiles are output, use the `-o` argument:
+
+```bash
+./cpu_profile -n "com.android.foo,com.android.bar" -o "path/to/output/directory"
+```
+
+### Windows
+
+Make sure that the downloaded `adb.exe` is in the `PATH`.
+
+```bash
+set PATH=%PATH%;%USERPROFILE%\Downloads\platform-tools
+
+adb devices -l
+```
+
+If more than one device or emulator is reported you must select one upfront as
+follows:
+
+```bash
+set ANDROID_SERIAL=SER123456
+```
+
+Download the
+[`cpu_profile`](https://raw.githubusercontent.com/google/perfetto/master/tools/cpu_profile)
+script. Then, start profiling. For example, to profile the processes
+`com.android.foo` and `com.android.bar`, use:
+
+```bash
+python3 /path/to/cpu_profile -n "com.android.foo,com.android.bar"
+```
+
+Please see the [Linux or maxOS section](#linux-or-macos) for more examples.
+
+## Symbolization
+
+You may need to symbolize the collected profiles if they are missing symbols.
+See [this](/docs/data-sources/native-heap-profiler#symbolize-your-profile) for
+more details on how to do this.
+
+For example, to profile and symbolize the profiles for the process
+`com.android.foo`, run:
+
+```bash
+PERFETTO_SYMBOLIZER_MODE=index PERFETTO_BINARY_PATH=path/to/directory/with/symbols/ ./cpu_profile -n "com.android.foo"
+```
+
+## View profile
+
+NOTE: Visualizing callstacks in the Perfetto UI is currently disabled behind a
+flag. Please [enable it](/docs/images/enable-profile-flame-graph.png) before
+proceeding further.
+
+Upload the `raw-trace` or `symbolized-trace` file from the output directory to
+the [Perfetto UI](https://ui.perfetto.dev) and click on one of the diamond
+markers in the UI track named "Perf Samples" for the processes that you selected
+for profiling. Each diamond marker represents a profile with stack samples
+recorded from the beginning of the trace up until that diamond marker on the
+timeline.
+
+![Profile Diamond](/docs/images/cpu-profile-diamond.png)
+![Native Flamegraph](/docs/images/cpu-profile-flame.png)
+
+`cpu_profile` will also write separate profiles for each process that it
+profiled in the output directory, and those can be visualized using
+[`pprof`](https://github.com/google/pprof).
diff --git a/docs/toc.md b/docs/toc.md
index 6ddd809..0142029 100644
--- a/docs/toc.md
+++ b/docs/toc.md
@@ -6,6 +6,7 @@
* [SQL analysis and metrics](quickstart/trace-analysis.md)
* [Trace conversion](quickstart/traceconv.md)
* [Heap profiling](quickstart/heap-profiling.md)
+ * [Callstack sampling on Android](quickstart/callstack-sampling.md)
* [Case studies](#)
* [Android boot tracing](case-studies/android-boot-tracing.md)
diff --git a/protos/perfetto/config/ftrace/ftrace_config.proto b/protos/perfetto/config/ftrace/ftrace_config.proto
index 7a38d74..917ac68 100644
--- a/protos/perfetto/config/ftrace/ftrace_config.proto
+++ b/protos/perfetto/config/ftrace/ftrace_config.proto
@@ -64,4 +64,13 @@
// TODO(kaleshsingh): implement the logic behind this. Right now this flag
// does nothing.
optional bool throttle_rss_stat = 15;
+
+ // If true, avoid enabling events that aren't statically known by
+ // traced_probes. Otherwise, the default is to emit such events as
+ // GenericFtraceEvent protos.
+ // Prefer to keep this flag at its default. This was added for Android
+ // tracing, where atrace categories and/or atrace HAL requested events can
+ // expand to events that aren't of interest to the tracing user.
+ // Introduced in: Android T.
+ optional bool disable_generic_events = 16;
}
diff --git a/protos/perfetto/config/perfetto_config.proto b/protos/perfetto/config/perfetto_config.proto
index b36ee1d..f20da0d 100644
--- a/protos/perfetto/config/perfetto_config.proto
+++ b/protos/perfetto/config/perfetto_config.proto
@@ -484,6 +484,15 @@
// TODO(kaleshsingh): implement the logic behind this. Right now this flag
// does nothing.
optional bool throttle_rss_stat = 15;
+
+ // If true, avoid enabling events that aren't statically known by
+ // traced_probes. Otherwise, the default is to emit such events as
+ // GenericFtraceEvent protos.
+ // Prefer to keep this flag at its default. This was added for Android
+ // tracing, where atrace categories and/or atrace HAL requested events can
+ // expand to events that aren't of interest to the tracing user.
+ // Introduced in: Android T.
+ optional bool disable_generic_events = 16;
}
// End of protos/perfetto/config/ftrace/ftrace_config.proto
diff --git a/protos/perfetto/trace/ftrace/ftrace_stats.proto b/protos/perfetto/trace/ftrace/ftrace_stats.proto
index 11b7cbc..9f26fa3 100644
--- a/protos/perfetto/trace/ftrace/ftrace_stats.proto
+++ b/protos/perfetto/trace/ftrace/ftrace_stats.proto
@@ -88,6 +88,8 @@
// Ftrace events requested by the config but not present on device.
repeated string unknown_ftrace_events = 6;
- // Ftrace events requested by the config, present, which we failed to enable.
+ // Ftrace events requested by the config and present on device, but which we
+ // failed to enable due to permissions, or due to a conflicting option
+ // (currently FtraceConfig.disable_generic_events).
repeated string failed_ftrace_events = 7;
}
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index ea46bd2..406a6cc 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -484,6 +484,15 @@
// TODO(kaleshsingh): implement the logic behind this. Right now this flag
// does nothing.
optional bool throttle_rss_stat = 15;
+
+ // If true, avoid enabling events that aren't statically known by
+ // traced_probes. Otherwise, the default is to emit such events as
+ // GenericFtraceEvent protos.
+ // Prefer to keep this flag at its default. This was added for Android
+ // tracing, where atrace categories and/or atrace HAL requested events can
+ // expand to events that aren't of interest to the tracing user.
+ // Introduced in: Android T.
+ optional bool disable_generic_events = 16;
}
// End of protos/perfetto/config/ftrace/ftrace_config.proto
@@ -6524,7 +6533,9 @@
// Ftrace events requested by the config but not present on device.
repeated string unknown_ftrace_events = 6;
- // Ftrace events requested by the config, present, which we failed to enable.
+ // Ftrace events requested by the config and present on device, but which we
+ // failed to enable due to permissions, or due to a conflicting option
+ // (currently FtraceConfig.disable_generic_events).
repeated string failed_ftrace_events = 7;
}
diff --git a/src/trace_processor/importers/common/flow_tracker.cc b/src/trace_processor/importers/common/flow_tracker.cc
index 6e7dd2f..e9ba05b 100644
--- a/src/trace_processor/importers/common/flow_tracker.cc
+++ b/src/trace_processor/importers/common/flow_tracker.cc
@@ -46,7 +46,11 @@
context_->storage->IncrementStats(stats::flow_no_enclosing_slice);
return;
}
- auto it_and_ins = flow_to_slice_map_.Insert(flow_id, open_slice_id.value());
+ Begin(open_slice_id.value(), flow_id);
+}
+
+void FlowTracker::Begin(SliceId slice_id, FlowId flow_id) {
+ auto it_and_ins = flow_to_slice_map_.Insert(flow_id, slice_id);
if (!it_and_ins.second) {
context_->storage->IncrementStats(stats::flow_duplicate_id);
return;
@@ -60,14 +64,18 @@
context_->storage->IncrementStats(stats::flow_no_enclosing_slice);
return;
}
+ Step(open_slice_id.value(), flow_id);
+}
+
+void FlowTracker::Step(SliceId slice_id, FlowId flow_id) {
auto* it = flow_to_slice_map_.Find(flow_id);
if (!it) {
context_->storage->IncrementStats(stats::flow_step_without_start);
return;
}
SliceId slice_out_id = *it;
- InsertFlow(flow_id, slice_out_id, open_slice_id.value());
- *it = open_slice_id.value();
+ InsertFlow(flow_id, slice_out_id, slice_id);
+ *it = slice_id;
}
void FlowTracker::End(TrackId track_id,
@@ -84,6 +92,10 @@
context_->storage->IncrementStats(stats::flow_no_enclosing_slice);
return;
}
+ End(open_slice_id.value(), flow_id, close_flow);
+}
+
+void FlowTracker::End(SliceId slice_id, FlowId flow_id, bool close_flow) {
auto* it = flow_to_slice_map_.Find(flow_id);
if (!it) {
context_->storage->IncrementStats(stats::flow_end_without_start);
@@ -92,7 +104,7 @@
SliceId slice_out_id = *it;
if (close_flow)
flow_to_slice_map_.Erase(flow_id);
- InsertFlow(flow_id, slice_out_id, open_slice_id.value());
+ InsertFlow(flow_id, slice_out_id, slice_id);
}
bool FlowTracker::IsActive(FlowId flow_id) const {
diff --git a/src/trace_processor/importers/common/flow_tracker.h b/src/trace_processor/importers/common/flow_tracker.h
index 5def9b6..1e635d8 100644
--- a/src/trace_processor/importers/common/flow_tracker.h
+++ b/src/trace_processor/importers/common/flow_tracker.h
@@ -32,14 +32,23 @@
class FlowTracker {
public:
explicit FlowTracker(TraceProcessorContext*);
- virtual ~FlowTracker();
+ ~FlowTracker();
void InsertFlow(SliceId slice_out_id, SliceId slice_in_id);
- // These methods assume you have created a FlowId via GetFlowIdForV1Event.
- // If you don't have a v1 event you should use the InsertFlow method above.
- virtual void Begin(TrackId track_id, FlowId flow_id);
- virtual void Step(TrackId track_id, FlowId flow_id);
+ // These methods track flow ids associated with slices and create flows as
+ // needed.
+ // If you don't have flow ids associated with slices, you should use the
+ // InsertFlow method above.
+ void Begin(SliceId slice_id, FlowId flow_id);
+ void Step(SliceId slice_id, FlowId flow_id);
+ void End(SliceId track_id, FlowId flow_id, bool close_flow);
+
+ // These methods assume you have created a FlowId via GetFlowIdForV1Event and
+ // tie the flow id to the currently open slice on a given track. If you don't
+ // have a v1 event you should use the methods above.
+ void Begin(TrackId track_id, FlowId flow_id);
+ void Step(TrackId track_id, FlowId flow_id);
// When |bind_enclosing_slice| is true we will connect the flow to the
// currently open slice on the track, when false we will connect the flow to
@@ -47,10 +56,10 @@
// When |close_flow| is true it will mark this as the singular end of the
// flow, however if there are multiple end points this should be set to
// false. Both parameters are only needed for v1 flow events support
- virtual void End(TrackId track_id,
- FlowId flow_id,
- bool bind_enclosing_slice,
- bool close_flow);
+ void End(TrackId track_id,
+ FlowId flow_id,
+ bool bind_enclosing_slice,
+ bool close_flow);
bool IsActive(FlowId flow_id) const;
diff --git a/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc b/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
index 581ca37..304a481 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
+++ b/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
@@ -217,19 +217,6 @@
std::function<SliceId()> inserter));
};
-class MockFlowTracker : public FlowTracker {
- public:
- MockFlowTracker(TraceProcessorContext* context) : FlowTracker(context) {}
-
- MOCK_METHOD2(Begin, void(TrackId track_id, FlowId flow_id));
- MOCK_METHOD2(Step, void(TrackId track_id, FlowId flow_id));
- MOCK_METHOD4(End,
- void(TrackId track_id,
- FlowId flow_id,
- bool bind_enclosing,
- bool close_flow));
-};
-
class ProtoTraceParserTest : public ::testing::Test {
public:
ProtoTraceParserTest() {
@@ -249,10 +236,9 @@
context_.process_tracker.reset(process_);
slice_ = new MockSliceTracker(&context_);
context_.slice_tracker.reset(slice_);
- flow_ = new MockFlowTracker(&context_);
- context_.flow_tracker.reset(flow_);
clock_ = new ClockTracker(&context_);
context_.clock_tracker.reset(clock_);
+ context_.flow_tracker.reset(new FlowTracker(&context_));
context_.sorter.reset(new TraceSorter(&context_, CreateParser(),
TraceSorter::SortingMode::kFullSort));
context_.descriptor_pool_.reset(new DescriptorPool());
@@ -305,7 +291,6 @@
MockSchedEventTracker* sched_;
MockProcessTracker* process_;
MockSliceTracker* slice_;
- MockFlowTracker* flow_;
ClockTracker* clock_;
TraceStorage* storage_;
};
@@ -1135,12 +1120,6 @@
.WillOnce(DoAll(IgnoreResult(InvokeArgument<3>()),
InvokeArgument<2>(&inserter), Return(SliceId(0u))));
- EXPECT_CALL(*flow_, Begin(_, _));
-
- EXPECT_CALL(*flow_, Step(_, _));
-
- EXPECT_CALL(*flow_, End(_, _, false, false));
-
EXPECT_CALL(*event_, PushCounter(1010000, testing::DoubleEq(2005000),
thread_time_track));
EXPECT_CALL(*event_, PushCounter(1010000, testing::DoubleEq(3020),
diff --git a/src/trace_processor/importers/proto/track_event_parser.cc b/src/trace_processor/importers/proto/track_event_parser.cc
index 136c5de..f264d27 100644
--- a/src/trace_processor/importers/proto/track_event_parser.cc
+++ b/src/trace_processor/importers/proto/track_event_parser.cc
@@ -653,7 +653,7 @@
[this](BoundInserter* inserter) { ParseTrackEventArgs(inserter); });
if (opt_slice_id.has_value()) {
- MaybeParseFlowEvents();
+ MaybeParseFlowEvents(opt_slice_id.value());
}
return util::OkStatus();
@@ -684,6 +684,7 @@
thread_slices->mutable_thread_instruction_delta()->Set(
*maybe_row, *event_data_->thread_instruction_count - *tic);
}
+ MaybeParseFlowEvents(opt_slice_id.value());
}
return util::OkStatus();
@@ -713,7 +714,7 @@
[this](BoundInserter* inserter) { ParseTrackEventArgs(inserter); });
if (opt_slice_id.has_value()) {
- MaybeParseFlowEvents();
+ MaybeParseFlowEvents(opt_slice_id.value());
}
return util::OkStatus();
@@ -754,16 +755,16 @@
return util::OkStatus();
}
- void MaybeParseTrackEventFlows() {
+ void MaybeParseTrackEventFlows(SliceId slice_id) {
if (event_.has_flow_ids()) {
auto it = event_.flow_ids();
for (; it; ++it) {
FlowId flow_id = *it;
if (!context_->flow_tracker->IsActive(flow_id)) {
- context_->flow_tracker->Begin(track_id_, flow_id);
+ context_->flow_tracker->Begin(slice_id, flow_id);
continue;
}
- context_->flow_tracker->Step(track_id_, flow_id);
+ context_->flow_tracker->Step(slice_id, flow_id);
}
}
if (event_.has_terminating_flow_ids()) {
@@ -775,14 +776,13 @@
// active already.
continue;
}
- context_->flow_tracker->End(track_id_, flow_id,
- /* bind_enclosing_slice = */ true,
+ context_->flow_tracker->End(slice_id, flow_id,
/* close_flow = */ true);
}
}
}
- void MaybeParseFlowEventV2() {
+ void MaybeParseFlowEventV2(SliceId slice_id) {
if (!legacy_event_.has_bind_id()) {
return;
}
@@ -794,14 +794,13 @@
auto bind_id = legacy_event_.bind_id();
switch (legacy_event_.flow_direction()) {
case LegacyEvent::FLOW_OUT:
- context_->flow_tracker->Begin(track_id_, bind_id);
+ context_->flow_tracker->Begin(slice_id, bind_id);
break;
case LegacyEvent::FLOW_INOUT:
- context_->flow_tracker->Step(track_id_, bind_id);
+ context_->flow_tracker->Step(slice_id, bind_id);
break;
case LegacyEvent::FLOW_IN:
- context_->flow_tracker->End(track_id_, bind_id,
- /* bind_enclosing_slice = */ true,
+ context_->flow_tracker->End(slice_id, bind_id,
/* close_flow = */ false);
break;
default:
@@ -809,9 +808,9 @@
}
}
- void MaybeParseFlowEvents() {
- MaybeParseFlowEventV2();
- MaybeParseTrackEventFlows();
+ void MaybeParseFlowEvents(SliceId slice_id) {
+ MaybeParseFlowEventV2(slice_id);
+ MaybeParseTrackEventFlows(slice_id);
}
util::Status ParseThreadInstantEvent(char phase) {
@@ -850,7 +849,7 @@
if (!opt_slice_id.has_value()) {
return util::OkStatus();
}
- MaybeParseFlowEvents();
+ MaybeParseFlowEvents(opt_slice_id.value());
return util::OkStatus();
}
@@ -872,7 +871,7 @@
if (!opt_slice_id.has_value()) {
return util::OkStatus();
}
- MaybeParseFlowEvents();
+ MaybeParseFlowEvents(opt_slice_id.value());
// For the time being, we only create vtrack slice rows if we need to
// store thread timestamps/counters.
if (legacy_event_.use_async_tts()) {
@@ -895,7 +894,10 @@
auto opt_slice_id = context_->slice_tracker->End(
ts_, track_id_, category_id_, name_id_,
[this](BoundInserter* inserter) { ParseTrackEventArgs(inserter); });
- if (legacy_event_.use_async_tts() && opt_slice_id.has_value()) {
+ if (!opt_slice_id.has_value())
+ return util::OkStatus();
+ MaybeParseFlowEvents(opt_slice_id.value());
+ if (legacy_event_.use_async_tts()) {
auto* vtrack_slices = storage_->mutable_virtual_track_slices();
int64_t tts =
event_data_->thread_timestamp ? *event_data_->thread_timestamp : 0;
@@ -940,7 +942,7 @@
if (!opt_slice_id.has_value()) {
return util::OkStatus();
}
- MaybeParseFlowEvents();
+ MaybeParseFlowEvents(opt_slice_id.value());
if (legacy_event_.use_async_tts()) {
auto* vtrack_slices = storage_->mutable_virtual_track_slices();
PERFETTO_DCHECK(!vtrack_slices->slice_count() ||
diff --git a/src/traced/probes/ftrace/cpu_reader.cc b/src/traced/probes/ftrace/cpu_reader.cc
index 7867ff6..16e8e3d 100644
--- a/src/traced/probes/ftrace/cpu_reader.cc
+++ b/src/traced/probes/ftrace/cpu_reader.cc
@@ -674,7 +674,6 @@
for (const Field& field : info.fields) {
auto generic_field = nested->BeginNestedMessage<protozero::Message>(
GenericFtraceEvent::kFieldFieldNumber);
- // TODO(hjd): Avoid outputting field names every time.
generic_field->AppendString(GenericFtraceEvent::Field::kNameFieldNumber,
field.ftrace_name);
success &= ParseField(field, start, end, table, generic_field, metadata);
diff --git a/src/traced/probes/ftrace/ftrace_config_muxer.cc b/src/traced/probes/ftrace/ftrace_config_muxer.cc
index ba48c44..6d3a546 100644
--- a/src/traced/probes/ftrace/ftrace_config_muxer.cc
+++ b/src/traced/probes/ftrace/ftrace_config_muxer.cc
@@ -26,11 +26,12 @@
#include "perfetto/base/compiler.h"
#include "perfetto/ext/base/utils.h"
-#include "protos/perfetto/trace/ftrace/sched.pbzero.h"
#include "src/traced/probes/ftrace/atrace_wrapper.h"
#include "src/traced/probes/ftrace/compact_sched.h"
#include "src/traced/probes/ftrace/ftrace_stats.h"
+#include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
+
namespace perfetto {
namespace {
@@ -544,10 +545,20 @@
errors->unknown_ftrace_events.push_back(group_and_name.ToString());
continue;
}
+ // Niche option to skip events that are in the config, but don't have a
+ // dedicated proto for the event in perfetto. Otherwise such events will be
+ // encoded as GenericFtraceEvent.
+ if (request.disable_generic_events() &&
+ event->proto_field_id ==
+ protos::pbzero::FtraceEvent::kGenericFieldNumber) {
+ if (errors)
+ errors->failed_ftrace_events.push_back(group_and_name.ToString());
+ continue;
+ }
// Note: ftrace events are always implicitly enabled (and don't have an
// "enable" file). So they aren't tracked by the central event filter (but
- // still need to be added to the per data source event filter to retain the
- // events during parsing).
+ // still need to be added to the per data source event filter to retain
+ // the events during parsing).
if (current_state_.ftrace_events.IsEventEnabled(event->ftrace_event_id) ||
std::string("ftrace") == event->group) {
filter.AddEnabledEvent(event->ftrace_event_id);
diff --git a/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc b/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc
index af01bd9..4012763 100644
--- a/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc
+++ b/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc
@@ -37,10 +37,15 @@
using testing::NiceMock;
using testing::Not;
using testing::Return;
+using testing::UnorderedElementsAre;
namespace perfetto {
namespace {
+constexpr int kFakeSchedSwitchEventId = 1;
+constexpr int kCgroupMkdirEventId = 12;
+constexpr int kFakePrintEventId = 20;
+
class MockFtraceProcfs : public FtraceProcfs {
public:
MockFtraceProcfs() : FtraceProcfs("/root/") {
@@ -60,6 +65,9 @@
MOCK_CONST_METHOD0(NumberOfCpus, size_t());
MOCK_CONST_METHOD1(GetEventNamesForGroup,
const std::set<std::string>(const std::string& path));
+ MOCK_CONST_METHOD2(ReadEventFormat,
+ std::string(const std::string& group,
+ const std::string& name));
};
struct MockRunAtrace {
@@ -115,17 +123,13 @@
InvalidCompactSchedEventFormatForTesting()));
}
- static constexpr int kFakeSchedSwitchEventId = 1;
- static constexpr int kCgroupMkdirEventId = 12;
- static constexpr int kFakePrintEventId = 20;
-
std::unique_ptr<ProtoTranslationTable> CreateFakeTable(
CompactSchedEventFormat compact_format =
InvalidCompactSchedEventFormatForTesting()) {
std::vector<Field> common_fields;
std::vector<Event> events;
{
- Event event;
+ Event event = {};
event.name = "sched_switch";
event.group = "sched";
event.ftrace_event_id = kFakeSchedSwitchEventId;
@@ -133,7 +137,7 @@
}
{
- Event event;
+ Event event = {};
event.name = "sched_wakeup";
event.group = "sched";
event.ftrace_event_id = 10;
@@ -141,7 +145,7 @@
}
{
- Event event;
+ Event event = {};
event.name = "sched_new";
event.group = "sched";
event.ftrace_event_id = 11;
@@ -149,7 +153,7 @@
}
{
- Event event;
+ Event event = {};
event.name = "cgroup_mkdir";
event.group = "cgroup";
event.ftrace_event_id = kCgroupMkdirEventId;
@@ -157,7 +161,7 @@
}
{
- Event event;
+ Event event = {};
event.name = "mm_vmscan_direct_reclaim_begin";
event.group = "vmscan";
event.ftrace_event_id = 13;
@@ -165,7 +169,7 @@
}
{
- Event event;
+ Event event = {};
event.name = "lowmemory_kill";
event.group = "lowmemorykiller";
event.ftrace_event_id = 14;
@@ -173,7 +177,7 @@
}
{
- Event event;
+ Event event = {};
event.name = "print";
event.group = "ftrace";
event.ftrace_event_id = kFakePrintEventId;
@@ -430,14 +434,12 @@
const FtraceDataSourceConfig* ds_config = model.GetDataSourceConfig(id);
ASSERT_TRUE(ds_config);
- ASSERT_THAT(
- ds_config->event_filter.GetEnabledEvents(),
- ElementsAreArray({FtraceConfigMuxerTest::kFakeSchedSwitchEventId}));
+ ASSERT_THAT(ds_config->event_filter.GetEnabledEvents(),
+ ElementsAreArray({kFakeSchedSwitchEventId}));
const EventFilter* central_filter = model.GetCentralEventFilterForTesting();
- ASSERT_THAT(
- central_filter->GetEnabledEvents(),
- ElementsAreArray({FtraceConfigMuxerTest::kFakeSchedSwitchEventId}));
+ ASSERT_THAT(central_filter->GetEnabledEvents(),
+ ElementsAreArray({kFakeSchedSwitchEventId}));
ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&ftrace));
EXPECT_CALL(ftrace, NumberOfCpus()).Times(AnyNumber());
@@ -492,13 +494,13 @@
const FtraceDataSourceConfig* ds_config = model.GetDataSourceConfig(id);
ASSERT_TRUE(ds_config);
EXPECT_THAT(ds_config->event_filter.GetEnabledEvents(),
- Contains(FtraceConfigMuxerTest::kFakeSchedSwitchEventId));
+ Contains(kFakeSchedSwitchEventId));
EXPECT_THAT(ds_config->event_filter.GetEnabledEvents(),
- Contains(FtraceConfigMuxerTest::kFakePrintEventId));
+ Contains(kFakePrintEventId));
const EventFilter* central_filter = model.GetCentralEventFilterForTesting();
EXPECT_THAT(central_filter->GetEnabledEvents(),
- Contains(FtraceConfigMuxerTest::kFakeSchedSwitchEventId));
+ Contains(kFakeSchedSwitchEventId));
EXPECT_CALL(
atrace,
@@ -535,7 +537,7 @@
const FtraceDataSourceConfig* ds_config = model.GetDataSourceConfig(id);
ASSERT_TRUE(ds_config);
ASSERT_THAT(ds_config->event_filter.GetEnabledEvents(),
- Contains(FtraceConfigMuxerTest::kFakePrintEventId));
+ Contains(kFakePrintEventId));
EXPECT_CALL(
atrace,
@@ -915,15 +917,15 @@
const FtraceDataSourceConfig* ds_config = model.GetDataSourceConfig(id);
ASSERT_TRUE(ds_config);
EXPECT_THAT(ds_config->event_filter.GetEnabledEvents(),
- Contains(FtraceConfigMuxerTest::kFakeSchedSwitchEventId));
+ Contains(kFakeSchedSwitchEventId));
EXPECT_THAT(ds_config->event_filter.GetEnabledEvents(),
- Contains(FtraceConfigMuxerTest::kCgroupMkdirEventId));
+ Contains(kCgroupMkdirEventId));
const EventFilter* central_filter = model.GetCentralEventFilterForTesting();
EXPECT_THAT(central_filter->GetEnabledEvents(),
- Contains(FtraceConfigMuxerTest::kFakeSchedSwitchEventId));
+ Contains(kFakeSchedSwitchEventId));
EXPECT_THAT(central_filter->GetEnabledEvents(),
- Contains(FtraceConfigMuxerTest::kCgroupMkdirEventId));
+ Contains(kCgroupMkdirEventId));
EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "0"));
EXPECT_CALL(ftrace, WriteToFile("/root/buffer_size_kb", "4"));
@@ -964,7 +966,7 @@
const FtraceDataSourceConfig* ds_config = model.GetDataSourceConfig(id);
ASSERT_TRUE(ds_config);
EXPECT_THAT(ds_config->event_filter.GetEnabledEvents(),
- Contains(FtraceConfigMuxerTest::kFakeSchedSwitchEventId));
+ Contains(kFakeSchedSwitchEventId));
EXPECT_TRUE(ds_config->compact_sched.enabled);
}
{
@@ -973,7 +975,7 @@
const FtraceDataSourceConfig* ds_config = model.GetDataSourceConfig(id);
ASSERT_TRUE(ds_config);
EXPECT_THAT(ds_config->event_filter.GetEnabledEvents(),
- Contains(FtraceConfigMuxerTest::kFakeSchedSwitchEventId));
+ Contains(kFakeSchedSwitchEventId));
EXPECT_FALSE(ds_config->compact_sched.enabled);
}
}
@@ -995,9 +997,55 @@
const FtraceDataSourceConfig* ds_config = model.GetDataSourceConfig(id);
ASSERT_TRUE(ds_config);
EXPECT_THAT(ds_config->event_filter.GetEnabledEvents(),
- Contains(FtraceConfigMuxerTest::kFakeSchedSwitchEventId));
+ Contains(kFakeSchedSwitchEventId));
EXPECT_FALSE(ds_config->compact_sched.enabled);
}
+TEST_F(FtraceConfigMuxerTest, SkipGenericEventsOption) {
+ NiceMock<MockFtraceProcfs> ftrace;
+ FtraceConfigMuxer model(&ftrace, table_.get(), {});
+
+ static constexpr int kFtraceGenericEventId = 42;
+ ON_CALL(table_procfs_, ReadEventFormat("sched", "generic"))
+ .WillByDefault(Return(R"(name: generic
+ID: 42
+format:
+ field:int common_pid; offset:0; size:4; signed:1;
+
+ field:u32 field_a; offset:4; size:4; signed:0;
+ field:int field_b; offset:8; size:4; signed:1;
+
+print fmt: "unused")"));
+
+ // Data source asking for one known and one generic event.
+ FtraceConfig config_default =
+ CreateFtraceConfig({"sched/sched_switch", "sched/generic"});
+
+ // As above, but with an option to suppress generic events.
+ FtraceConfig config_with_disable =
+ CreateFtraceConfig({"sched/sched_switch", "sched/generic"});
+ config_with_disable.set_disable_generic_events(true);
+
+ {
+ FtraceConfigId id = model.SetupConfig(config_default);
+ ASSERT_TRUE(id);
+ const FtraceDataSourceConfig* ds_config = model.GetDataSourceConfig(id);
+ ASSERT_TRUE(ds_config);
+ // Both events enabled for the data source by default.
+ EXPECT_THAT(
+ ds_config->event_filter.GetEnabledEvents(),
+ UnorderedElementsAre(kFakeSchedSwitchEventId, kFtraceGenericEventId));
+ }
+ {
+ FtraceConfigId id = model.SetupConfig(config_with_disable);
+ ASSERT_TRUE(id);
+ const FtraceDataSourceConfig* ds_config = model.GetDataSourceConfig(id);
+ ASSERT_TRUE(ds_config);
+ // Only the statically known event is enabled.
+ EXPECT_THAT(ds_config->event_filter.GetEnabledEvents(),
+ UnorderedElementsAre(kFakeSchedSwitchEventId));
+ }
+}
+
} // namespace
} // namespace perfetto
diff --git a/test/trace_processor/track_event/flow_events_proto_v1.textproto b/test/trace_processor/track_event/flow_events_proto_v1.textproto
index 19c1265..1ed6906 100644
--- a/test/trace_processor/track_event/flow_events_proto_v1.textproto
+++ b/test/trace_processor/track_event/flow_events_proto_v1.textproto
@@ -139,4 +139,4 @@
unscoped_id: 331
}
}
-}
\ No newline at end of file
+}
diff --git a/test/trace_processor/track_event/flow_events_track_event.out b/test/trace_processor/track_event/flow_events_track_event.out
index 895f2a1..d44237e 100644
--- a/test/trace_processor/track_event/flow_events_track_event.out
+++ b/test/trace_processor/track_event/flow_events_track_event.out
@@ -4,4 +4,5 @@
"FlowSlice1Start2Start","FlowSlice2End"
"FlowSlice3Begin","FlowSlice3End4Begin"
"FlowSlice3End4Begin","FlowSlice4Step"
-"FlowSlice4Step","FlowSlice4End"
+"FlowSlice4Step","FlowSlice4Step2_FlowIdOnAsyncEndEvent"
+"FlowSlice4Step2_FlowIdOnAsyncEndEvent","FlowSlice4End"
diff --git a/test/trace_processor/track_event/flow_events_track_event.textproto b/test/trace_processor/track_event/flow_events_track_event.textproto
index af20150..60d9336 100644
--- a/test/trace_processor/track_event/flow_events_track_event.textproto
+++ b/test/trace_processor/track_event/flow_events_track_event.textproto
@@ -28,8 +28,8 @@
track_event {
name: "FlowSlice1Start"
categories: "test"
- track_uuid: 1,
- flow_ids: 1,
+ track_uuid: 1
+ flow_ids: 1
legacy_event {
duration_us: 10
phase: 88 # 'X'
@@ -42,8 +42,8 @@
track_event {
name: "FlowSlice1End"
categories: "test"
- track_uuid: 2,
- terminating_flow_ids: 1,
+ track_uuid: 2
+ terminating_flow_ids: 1
legacy_event {
duration_us: 10
phase: 88 # 'X'
@@ -56,9 +56,9 @@
track_event {
name: "FlowSlice1Start2Start"
categories: "test"
- track_uuid: 1,
- flow_ids: 1,
- flow_ids: 2,
+ track_uuid: 1
+ flow_ids: 1
+ flow_ids: 2
legacy_event {
duration_us: 10
phase: 88 # 'X'
@@ -71,8 +71,8 @@
track_event {
name: "FlowSlice1End"
categories: "test"
- track_uuid: 2,
- flow_ids: 1,
+ track_uuid: 2
+ flow_ids: 1
legacy_event {
duration_us: 10
phase: 88 # 'X'
@@ -86,8 +86,8 @@
track_event {
name: "FlowSlice3Begin"
categories: "test"
- track_uuid: 2,
- flow_ids: 3,
+ track_uuid: 2
+ flow_ids: 3
legacy_event {
phase: 73 # 'I'
}
@@ -99,8 +99,8 @@
track_event {
name: "FlowSlice2End"
categories: "test"
- track_uuid: 2,
- flow_ids: 2,
+ track_uuid: 2
+ flow_ids: 2
legacy_event {
duration_us: 10
phase: 88 # 'X'
@@ -121,9 +121,9 @@
track_event {
name: "FlowSlice3End4Begin"
categories: "test"
- track_uuid: 11,
- terminating_flow_ids: 3,
- flow_ids: 4,
+ track_uuid: 11
+ terminating_flow_ids: 3
+ flow_ids: 4
type: 1, # 'TYPE_SLICE_BEGIN'
}
}
@@ -133,8 +133,8 @@
track_event {
name: "FlowSlice4Step"
categories: "test"
- track_uuid: 11,
- flow_ids: 4,
+ track_uuid: 11
+ flow_ids: 4
type: 3, # 'TYPE_SLICE_INSTANT'
}
}
@@ -144,8 +144,50 @@
track_event {
name: "FlowSlice3End4Begin"
categories: "test"
- track_uuid: 11,
- type: 2, # 'TYPE_SLICE_END'
+ track_uuid: 11
+ type: 2 # 'TYPE_SLICE_END'
+ }
+}
+packet {
+ timestamp: 63000
+ trusted_packet_sequence_id: 1
+ track_event {
+ name: "FlowSlice4Step2_FlowIdOnAsyncEndEvent"
+ categories: "test"
+ track_uuid: 11
+ type: 1 # 'TYPE_SLICE_BEGIN'
+ }
+}
+packet {
+ timestamp: 64000
+ trusted_packet_sequence_id: 1
+ track_event {
+ name: "FlowSlice4Step2_FlowIdOnAsyncEndEvent"
+ categories: "test"
+ track_uuid: 11
+ flow_ids: 4
+ type: 2 # 'TYPE_SLICE_END'
+ }
+}
+packet {
+ timestamp: 65000
+ trusted_packet_sequence_id: 1
+ track_event {
+ name: "FlowSlice4Step2_FlowIdOnEndEvent"
+ categories: "test"
+ track_uuid: 2
+ type: 1 # 'TYPE_SLICE_BEGIN'
+ }
+}
+packet {
+ timestamp: 66000
+ trusted_packet_sequence_id: 1
+ track_event {
+ name: "FlowSlice4Step2_FlowIdOnEndEvent"
+ categories: "test"
+ track_uuid: 2
+ flow_ids: 4
+ type: 2 # 'TYPE_SLICE_END'
}
}
packet {
@@ -161,13 +203,13 @@
}
}
packet {
- timestamp: 65000
+ timestamp: 67000
trusted_packet_sequence_id: 1
track_event {
name: "FlowSlice4End"
categories: "test"
- track_uuid: 13,
- terminating_flow_ids: 4,
+ track_uuid: 13
+ terminating_flow_ids: 4
legacy_event {
phase: 105 # 'i'
instant_event_scope: 2 # SCOPE_PROCESS