1 /*
2 etw* Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "src/trace_processor/importers/etw/etw_parser.h"
18
19 #include "perfetto/ext/base/string_view.h"
20 #include "src/trace_processor/importers/common/parser_types.h"
21 #include "src/trace_processor/importers/common/process_tracker.h"
22 #include "src/trace_processor/importers/common/sched_event_tracker.h"
23 #include "src/trace_processor/importers/common/thread_state_tracker.h"
24 #include "src/trace_processor/storage/trace_storage.h"
25
26 #include "protos/perfetto/trace/etw/etw.pbzero.h"
27 #include "protos/perfetto/trace/etw/etw_event.pbzero.h"
28
29 namespace perfetto {
30 namespace trace_processor {
31
32 namespace {
33
34 using protozero::ConstBytes;
35
36 } // namespace
EtwParser(TraceProcessorContext * context)37 EtwParser::EtwParser(TraceProcessorContext* context) : context_(context) {}
38
ParseEtwEvent(uint32_t cpu,int64_t ts,const TracePacketData & data)39 util::Status EtwParser::ParseEtwEvent(uint32_t cpu,
40 int64_t ts,
41 const TracePacketData& data) {
42 using protos::pbzero::EtwTraceEvent;
43 const TraceBlobView& event = data.packet;
44 protos::pbzero::EtwTraceEvent::Decoder decoder(event.data(), event.length());
45
46 if (decoder.has_c_switch()) {
47 ParseCswitch(ts, cpu, decoder.c_switch());
48 }
49
50 if (decoder.has_ready_thread()) {
51 ParseReadyThread(ts, decoder.ready_thread());
52 }
53
54 return util::OkStatus();
55 }
56
ParseCswitch(int64_t timestamp,uint32_t cpu,ConstBytes blob)57 void EtwParser::ParseCswitch(int64_t timestamp, uint32_t cpu, ConstBytes blob) {
58 protos::pbzero::CSwitchEtwEvent::Decoder cs(blob.data, blob.size);
59 PushSchedSwitch(cpu, timestamp, cs.old_thread_id(), cs.old_thread_state(),
60 cs.new_thread_id(), cs.new_thread_priority());
61 }
62
ParseReadyThread(int64_t timestamp,ConstBytes blob)63 void EtwParser::ParseReadyThread(int64_t timestamp, ConstBytes blob) {
64 protos::pbzero::ReadyThreadEtwEvent::Decoder rt(blob.data, blob.size);
65 UniqueTid utid =
66 context_->process_tracker->GetOrCreateThread(rt.t_thread_id());
67 ThreadStateTracker::GetOrCreate(context_)->PushWakingEvent(timestamp, utid,
68 utid);
69 }
70
PushSchedSwitch(uint32_t cpu,int64_t ts,uint32_t prev_tid,int64_t prev_state,uint32_t next_tid,int32_t next_prio)71 void EtwParser::PushSchedSwitch(uint32_t cpu,
72 int64_t ts,
73 uint32_t prev_tid,
74 int64_t prev_state,
75 uint32_t next_tid,
76 int32_t next_prio) {
77 // At this stage all events should be globally timestamp ordered.
78 if (!context_->sched_event_tracker->UpdateEventTrackerTimestamp(
79 ts, "etw_cswitch", stats::sched_switch_out_of_order)) {
80 return;
81 }
82
83 UniqueTid next_utid = context_->process_tracker->GetOrCreateThread(next_tid);
84
85 // First use this data to close the previous slice.
86 bool prev_pid_match_prev_next_pid = false;
87 auto* pending_sched = sched_event_state_.GetPendingSchedInfoForCpu(cpu);
88 uint32_t pending_slice_idx = pending_sched->pending_slice_storage_idx;
89 StringId prev_state_string_id = TaskStateToStringId(prev_state);
90 if (prev_state_string_id == kNullStringId) {
91 context_->storage->IncrementStats(stats::task_state_invalid);
92 }
93 if (pending_slice_idx < std::numeric_limits<uint32_t>::max()) {
94 prev_pid_match_prev_next_pid = prev_tid == pending_sched->last_pid;
95 if (PERFETTO_LIKELY(prev_pid_match_prev_next_pid)) {
96 context_->sched_event_tracker->ClosePendingSlice(pending_slice_idx, ts,
97 prev_state_string_id);
98 } else {
99 // If the pids are not consistent, make a note of this.
100 context_->storage->IncrementStats(stats::mismatched_sched_switch_tids);
101 }
102 }
103
104 auto new_slice_idx = context_->sched_event_tracker->AddStartSlice(
105 cpu, ts, next_utid, next_prio);
106
107 // Finally, update the info for the next sched switch on this CPU.
108 pending_sched->pending_slice_storage_idx = new_slice_idx;
109 pending_sched->last_pid = next_tid;
110 pending_sched->last_utid = next_utid;
111 pending_sched->last_prio = next_prio;
112
113 UniqueTid prev_utid = context_->process_tracker->GetOrCreateThread(prev_tid);
114
115 // Update the ThreadState table.
116 ThreadStateTracker::GetOrCreate(context_)->PushSchedSwitchEvent(
117 ts, cpu, prev_utid, prev_state_string_id, next_utid);
118 }
119
TaskStateToStringId(int64_t task_state_int)120 StringId EtwParser::TaskStateToStringId(int64_t task_state_int) {
121 const auto state = static_cast<uint8_t>(task_state_int);
122 // Mapping for the different Etw states with their string description.
123 std::map<uint8_t, base::StringView> etw_states_map = {
124 {0x00, "Initialized"}, // INITIALIZED
125 {0x01, "R"}, // READY
126 {0x02, "Running"}, // RUNNING
127 {0x03, "Stand By"}, // STANDBY
128 {0x04, "T"}, // TERMINATED
129 {0x05, "Waiting"}, // WAITING
130 {0x06, "Transition"}, // TRANSITION
131 {0x07, "Deferred Ready"}, // DEFERRED_READY
132 };
133
134 return etw_states_map.find(state) != etw_states_map.end()
135 ? context_->storage->InternString(etw_states_map[state])
136 : kNullStringId;
137 }
138
139 } // namespace trace_processor
140 } // namespace perfetto
141