aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVilas Bhat <vilasbhat@google.com>2024-04-23 13:07:17 -0700
committerVilas Bhat <vilasbhat@google.com>2024-05-15 15:04:55 +0000
commitf1ac3c61433ffebed5b372bd93dc95525e18874a (patch)
treeb4d79307e86d3bc0ab2cc78a4ef775b8b3061bf4
parent6c5bf2420572da8a1c5fdf319ce4df3ed6010689 (diff)
downloadperfetto-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.cc126
-rw-r--r--src/trace_processor/importers/ftrace/ftrace_parser.h5
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 {