xref: /aosp_15_r20/external/perfetto/src/trace_processor/importers/etw/etw_parser.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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