diff options
author | Vilas Bhat <vilasbhat@google.com> | 2024-04-23 13:07:17 -0700 |
---|---|---|
committer | Vilas Bhat <vilasbhat@google.com> | 2024-05-15 15:04:55 +0000 |
commit | f1ac3c61433ffebed5b372bd93dc95525e18874a (patch) | |
tree | b4d79307e86d3bc0ab2cc78a4ef775b8b3061bf4 | |
parent | 6c5bf2420572da8a1c5fdf319ce4df3ed6010689 (diff) | |
download | perfetto-f1ac3c61433ffebed5b372bd93dc95525e18874a.tar.gz |
tp: Parsing device_pm_callback_[start|end] ftrace events
This patch does the following:
- Parses `device_pm_callback_[start|end]` ftrace events to capture
information about the device, associated driver, type of callback etc.
- Creates perfetto slices to visualize device PM callbacks executed
during a suspend / resume cycle and adds them to the "Suspend / Resume
Latency" UI track.
- The display name format for the slices is:
"<driver name> <device name> <event type>[:<callback phase>]"
A 60s trace with ~9 suspend/resume cycles was used to measure the load
times on the UI (via the `perfetto.AsyncSlices` plugin). The size of the
trace was 41MB.
Load times (averaged across 3 runs)
With this change: 51 ms
Without this change: 28 ms
The increase in ftrace_event table entries processed for the "Suspend /
Resume Latency" track correspond to `device_pm_callback_[start|end]`
events.
```
$ select COUNT(*) from ftrace_event where name like "device_pm_callback%";
91622
$ select COUNT(*) from ftrace_event where name like "suspend_resume%";
502
```
Bug: 330333710
Change-Id: I873efd72a3db271075d5507e03b8b298422348a3
-rw-r--r-- | src/trace_processor/importers/ftrace/ftrace_parser.cc | 126 | ||||
-rw-r--r-- | src/trace_processor/importers/ftrace/ftrace_parser.h | 5 |
2 files changed, 131 insertions, 0 deletions
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.cc b/src/trace_processor/importers/ftrace/ftrace_parser.cc index 136763b97..406f30199 100644 --- a/src/trace_processor/importers/ftrace/ftrace_parser.cc +++ b/src/trace_processor/importers/ftrace/ftrace_parser.cc @@ -232,6 +232,75 @@ enum RpmStatus { RPM_SUSPENDED, RPM_SUSPENDING, }; + +// Obtain the string corresponding to the event code (`event` field) in the +// `device_pm_callback_start` tracepoint. +std::string GetDpmCallbackEventString(int64_t event) { + // This mapping order is obtained directly from the Linux kernel code. + switch (event) { + case 0x2: + return "suspend"; + case 0x10: + return "resume"; + case 0x1: + return "freeze"; + case 0x8: + return "quiesce"; + case 0x4: + return "hibernate"; + case 0x20: + return "thaw"; + case 0x40: + return "restore"; + case 0x80: + return "recover"; + default: + return "(unknown PM event)"; + } +} + +bool StrStartsWith(const std::string& str, const std::string& prefix) { + return str.size() >= prefix.size() && + str.compare(0, prefix.size(), prefix) == 0; +} + +// Constructs the display string for device PM callback slices. +// +// Format: "<driver name> <device name> <event type>[:<callback phase>]" +// +// Note: The optional `<callback phase>` is extracted from the `pm_ops` field +// of the `device_pm_callback_start` tracepoint. +std::string ConstructDpmCallbackSliceName(const std::string& driver_name, + const std::string& device_name, + const std::string& pm_ops, + const std::string& event_type) { + std::string slice_name_base = + driver_name + " " + device_name + " " + event_type; + + // The Linux kernel has a limitation where the `pm_ops` field in the + // tracepoint is left empty if the phase is either prepare/complete. + if (pm_ops == "") { + if (event_type == "suspend") + return slice_name_base + ":prepare"; + else if (event_type == "resume") + return slice_name_base + ":complete"; + } + + // Extract callback phase (if present) for slice display name. + // + // The `pm_ops` string may contain both callback phase and callback type, but + // only phase is needed. A prefix match is used due to potential absence of + // either/both phase or type in `pm_ops`. + const std::vector<std::string> valid_phases = {"early", "late", "noirq"}; + for (const std::string& valid_phase : valid_phases) { + if (StrStartsWith(pm_ops, valid_phase)) { + return slice_name_base + ":" + valid_phase; + } + } + + return slice_name_base; +} + } // namespace FtraceParser::FtraceParser(TraceProcessorContext* context) @@ -1169,6 +1238,14 @@ base::Status FtraceParser::ParseFtraceEvent(uint32_t cpu, ParseGoogleIrmEvent(ts, fld_bytes); break; } + case FtraceEvent::kDevicePmCallbackStartFieldNumber: { + ParseDevicePmCallbackStart(ts, fld_bytes); + break; + } + case FtraceEvent::kDevicePmCallbackEndFieldNumber: { + ParseDevicePmCallbackEnd(ts, fld_bytes); + break; + } default: break; } @@ -3378,6 +3455,55 @@ void FtraceParser::ParseRpmStatus(int64_t ts, protozero::ConstBytes blob) { devices_with_active_rpm_slice_.insert(device_name); } +// Parses `device_pm_callback_start` events and begins corresponding slices in +// the suspend / resume latency UI track. +void FtraceParser::ParseDevicePmCallbackStart(int64_t ts, + protozero::ConstBytes blob) { + protos::pbzero::DevicePmCallbackStartFtraceEvent::Decoder dpm_event( + blob.data, blob.size); + + // Device here refers to anything managed by a Linux kernel driver. + std::string device_name = dpm_event.device().ToStdString(); + int64_t cookie; + + if (suspend_resume_cookie_map_.Find(device_name) == nullptr) { + cookie = static_cast<int64_t>(suspend_resume_cookie_map_.size()); + suspend_resume_cookie_map_[device_name] = cookie; + } else { + cookie = suspend_resume_cookie_map_[device_name]; + } + + std::string slice_name = ConstructDpmCallbackSliceName( + dpm_event.driver().ToStdString(), device_name, + dpm_event.pm_ops().ToStdString(), + GetDpmCallbackEventString(dpm_event.event())); + StringId slice_name_id = context_->storage->InternString(slice_name.c_str()); + + auto async_track = context_->async_track_set_tracker->InternGlobalTrackSet( + suspend_resume_name_id_); + TrackId track_id = + context_->async_track_set_tracker->Begin(async_track, cookie); + context_->slice_tracker->Begin(ts, track_id, suspend_resume_name_id_, + slice_name_id); +} + +// Parses `device_pm_callback_end` events and ends corresponding slices in the +// suspend / resume latency UI track. +void FtraceParser::ParseDevicePmCallbackEnd(int64_t ts, + protozero::ConstBytes blob) { + protos::pbzero::DevicePmCallbackEndFtraceEvent::Decoder dpm_event(blob.data, + blob.size); + + // Device here refers to anything managed by a Linux kernel driver. + std::string device_name = dpm_event.device().ToStdString(); + + auto async_track = context_->async_track_set_tracker->InternGlobalTrackSet( + suspend_resume_name_id_); + TrackId track_id = context_->async_track_set_tracker->End( + async_track, suspend_resume_cookie_map_[device_name]); + context_->slice_tracker->End(ts, track_id); +} + void FtraceParser::ParsePanelWriteGeneric(int64_t timestamp, uint32_t pid, ConstBytes blob) { diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.h b/src/trace_processor/importers/ftrace/ftrace_parser.h index 9cf034ab6..d8bafde94 100644 --- a/src/trace_processor/importers/ftrace/ftrace_parser.h +++ b/src/trace_processor/importers/ftrace/ftrace_parser.h @@ -297,6 +297,8 @@ class FtraceParser { protozero::ConstBytes); StringId GetRpmStatusStringId(int32_t rpm_status_val); void ParseRpmStatus(int64_t ts, protozero::ConstBytes); + void ParseDevicePmCallbackStart(int64_t ts, protozero::ConstBytes); + void ParseDevicePmCallbackEnd(int64_t ts, protozero::ConstBytes); void ParsePanelWriteGeneric(int64_t timestamp, uint32_t pid, protozero::ConstBytes); @@ -475,6 +477,9 @@ class FtraceParser { // // For `suspend_resume` ftrace events, the key for this map is derived from // the `val` field in the trace object. + // + // For `device_pm_callback_[start|end]` ftrace events, the key for this map is + // derived from the device name. base::FlatHashMap<std::string, int64_t> suspend_resume_cookie_map_; struct PairHash { |