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 #include <cstdint>
19 #include <limits>
20
21 #include "perfetto/base/status.h"
22 #include "perfetto/ext/base/string_view.h"
23 #include "perfetto/protozero/field.h"
24 #include "perfetto/trace_processor/status.h"
25 #include "perfetto/trace_processor/trace_blob_view.h"
26 #include "src/trace_processor/importers/common/parser_types.h"
27 #include "src/trace_processor/importers/common/process_tracker.h"
28 #include "src/trace_processor/importers/common/sched_event_tracker.h"
29 #include "src/trace_processor/importers/common/thread_state_tracker.h"
30 #include "src/trace_processor/storage/stats.h"
31 #include "src/trace_processor/storage/trace_storage.h"
32
33 #include "protos/perfetto/trace/etw/etw.pbzero.h"
34 #include "protos/perfetto/trace/etw/etw_event.pbzero.h"
35 #include "src/trace_processor/types/trace_processor_context.h"
36
37 namespace perfetto {
38 namespace trace_processor {
39
40 namespace {
41
42 using protozero::ConstBytes;
43
44 } // namespace
EtwParser(TraceProcessorContext * context)45 EtwParser::EtwParser(TraceProcessorContext* context) : context_(context) {}
46
ParseEtwEvent(uint32_t cpu,int64_t ts,const TracePacketData & data)47 base::Status EtwParser::ParseEtwEvent(uint32_t cpu,
48 int64_t ts,
49 const TracePacketData& data) {
50 using protos::pbzero::EtwTraceEvent;
51 const TraceBlobView& event = data.packet;
52 protos::pbzero::EtwTraceEvent::Decoder decoder(event.data(), event.length());
53
54 if (decoder.has_c_switch()) {
55 ParseCswitch(ts, cpu, decoder.c_switch());
56 }
57
58 if (decoder.has_ready_thread()) {
59 ParseReadyThread(ts, decoder.ready_thread());
60 }
61
62 return base::OkStatus();
63 }
64
ParseCswitch(int64_t timestamp,uint32_t cpu,ConstBytes blob)65 void EtwParser::ParseCswitch(int64_t timestamp, uint32_t cpu, ConstBytes blob) {
66 protos::pbzero::CSwitchEtwEvent::Decoder cs(blob);
67 PushSchedSwitch(cpu, timestamp, cs.old_thread_id(), cs.old_thread_state(),
68 cs.new_thread_id(), cs.new_thread_priority());
69 }
70
ParseReadyThread(int64_t timestamp,ConstBytes blob)71 void EtwParser::ParseReadyThread(int64_t timestamp, ConstBytes blob) {
72 protos::pbzero::ReadyThreadEtwEvent::Decoder rt(blob);
73 UniqueTid utid =
74 context_->process_tracker->GetOrCreateThread(rt.t_thread_id());
75 ThreadStateTracker::GetOrCreate(context_)->PushWakingEvent(timestamp, utid,
76 utid);
77 }
78
PushSchedSwitch(uint32_t cpu,int64_t ts,uint32_t prev_tid,int64_t prev_state,uint32_t next_tid,int32_t next_prio)79 void EtwParser::PushSchedSwitch(uint32_t cpu,
80 int64_t ts,
81 uint32_t prev_tid,
82 int64_t prev_state,
83 uint32_t next_tid,
84 int32_t next_prio) {
85 UniqueTid next_utid = context_->process_tracker->GetOrCreateThread(next_tid);
86
87 // First use this data to close the previous slice.
88 bool prev_pid_match_prev_next_pid = false;
89 auto* pending_sched = sched_event_state_.GetPendingSchedInfoForCpu(cpu);
90 uint32_t pending_slice_idx = pending_sched->pending_slice_storage_idx;
91 StringId prev_state_string_id = TaskStateToStringId(prev_state);
92 if (prev_state_string_id == kNullStringId) {
93 context_->storage->IncrementStats(stats::task_state_invalid);
94 }
95 if (pending_slice_idx < std::numeric_limits<uint32_t>::max()) {
96 prev_pid_match_prev_next_pid = prev_tid == pending_sched->last_pid;
97 if (PERFETTO_LIKELY(prev_pid_match_prev_next_pid)) {
98 context_->sched_event_tracker->ClosePendingSlice(pending_slice_idx, ts,
99 prev_state_string_id);
100 } else {
101 // If the pids are not consistent, make a note of this.
102 context_->storage->IncrementStats(stats::mismatched_sched_switch_tids);
103 }
104 }
105
106 auto new_slice_idx = context_->sched_event_tracker->AddStartSlice(
107 cpu, ts, next_utid, next_prio);
108
109 // Finally, update the info for the next sched switch on this CPU.
110 pending_sched->pending_slice_storage_idx = new_slice_idx;
111 pending_sched->last_pid = next_tid;
112 pending_sched->last_utid = next_utid;
113 pending_sched->last_prio = next_prio;
114
115 UniqueTid prev_utid = context_->process_tracker->GetOrCreateThread(prev_tid);
116
117 // Update the ThreadState table.
118 ThreadStateTracker::GetOrCreate(context_)->PushSchedSwitchEvent(
119 ts, cpu, prev_utid, prev_state_string_id, next_utid);
120 }
121
TaskStateToStringId(int64_t task_state_int)122 StringId EtwParser::TaskStateToStringId(int64_t task_state_int) {
123 const auto state = static_cast<uint8_t>(task_state_int);
124 // Mapping for the different Etw states with their string description.
125 std::map<uint8_t, base::StringView> etw_states_map = {
126 {0x00, "Initialized"}, // INITIALIZED
127 {0x01, "R"}, // READY
128 {0x02, "Running"}, // RUNNING
129 {0x03, "Stand By"}, // STANDBY
130 {0x04, "T"}, // TERMINATED
131 {0x05, "Waiting"}, // WAITING
132 {0x06, "Transition"}, // TRANSITION
133 {0x07, "Deferred Ready"}, // DEFERRED_READY
134 };
135
136 return etw_states_map.find(state) != etw_states_map.end()
137 ? context_->storage->InternString(etw_states_map[state])
138 : kNullStringId;
139 }
140
141 } // namespace trace_processor
142 } // namespace perfetto
143