1 // Copyright 2023 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/trace_event/etw_interceptor_win.h"
6
7 #include <optional>
8 #include <type_traits>
9
10 #include "base/containers/flat_map.h"
11 #include "base/time/time.h"
12 #include "base/trace_event/trace_event_etw_export_win.h"
13 #include "third_party/abseil-cpp/absl/container/inlined_vector.h"
14 #include "third_party/abseil-cpp/absl/types/variant.h"
15 #include "third_party/perfetto/protos/perfetto/common/interceptor_descriptor.gen.h"
16 #include "third_party/perfetto/protos/perfetto/trace/trace_packet.pbzero.h"
17 #include "third_party/perfetto/protos/perfetto/trace/track_event/track_event.pbzero.h"
18
19 namespace base::trace_event {
20
21 namespace {
22 template <typename T>
23 concept EtwFieldWithDataDescType = EtwFieldBaseType<T> && requires(T t) {
24 { t.GetDataDescCount() } -> std::same_as<uint8_t>;
25 };
26
27 template <typename T, typename = void>
28 struct EventDataDescTraits;
29
30 template <typename T>
31 struct EventDataDescTraits<T, std::enable_if_t<std::is_arithmetic_v<T>>> {
GetAddressbase::trace_event::__anon4f85bbcf0111::EventDataDescTraits32 static const T* GetAddress(const T& value) noexcept {
33 return const_cast<T*>(&value);
34 }
GetSizebase::trace_event::__anon4f85bbcf0111::EventDataDescTraits35 static ULONG GetSize(const T& value) noexcept { return sizeof(value); }
36 };
37
38 template <>
39 struct EventDataDescTraits<std::string> {
GetAddressbase::trace_event::__anon4f85bbcf0111::EventDataDescTraits40 static const char* GetAddress(const std::string& value) noexcept {
41 return value.c_str();
42 }
GetSizebase::trace_event::__anon4f85bbcf0111::EventDataDescTraits43 static ULONG GetSize(const std::string& value) noexcept {
44 return static_cast<ULONG>(value.size() + 1);
45 }
46 };
47
48 class TlmFieldDebugAnnotation final : public TlmFieldBase {
49 public:
50 TlmFieldDebugAnnotation(
51 std::string_view name,
52 perfetto::protos::pbzero::DebugAnnotation_Decoder& annotation);
53 ~TlmFieldDebugAnnotation();
54
55 void FillEventDescriptor(EVENT_DATA_DESCRIPTOR* descriptors) const noexcept;
56
57 uint8_t GetDataDescCount() const noexcept;
58 uint8_t GetInType() const noexcept;
59 uint8_t GetOutType() const noexcept;
60
61 // Copy operations are suppressed. Only declare move operations.
62 TlmFieldDebugAnnotation(TlmFieldDebugAnnotation&&) noexcept;
63 TlmFieldDebugAnnotation& operator=(TlmFieldDebugAnnotation&&) noexcept;
64
65 private:
66 uint8_t data_desc_count_ = 1;
67 uint8_t in_type_ = 2 /* TlgInANSISTRING */;
68 uint8_t out_type_ = 0;
69 absl::variant<std::string, uint64_t, int64_t, bool, double> value_;
70 };
71
TlmFieldDebugAnnotation(std::string_view name,perfetto::protos::pbzero::DebugAnnotation_Decoder & annotation)72 TlmFieldDebugAnnotation::TlmFieldDebugAnnotation(
73 std::string_view name,
74 perfetto::protos::pbzero::DebugAnnotation_Decoder& annotation)
75 : TlmFieldBase(name) {
76 CHECK_NE(Name().data(), nullptr);
77
78 if (annotation.has_bool_value()) {
79 in_type_ = 4 /* TlgInUINT8 */;
80 out_type_ = 3 /* TlgOutBOOLEAN */;
81 value_ = annotation.bool_value();
82 } else if (annotation.has_int_value()) {
83 in_type_ = 9;
84 value_ = annotation.int_value();
85 } else if (annotation.has_uint_value()) {
86 in_type_ = 10;
87 value_ = annotation.uint_value();
88 } else if (annotation.has_string_value()) {
89 in_type_ = 2 /* TlgInANSISTRING */;
90 value_.emplace<std::string>(annotation.string_value().data,
91 annotation.string_value().size);
92 } else if (annotation.has_legacy_json_value()) {
93 in_type_ = 2 /* TlgInANSISTRING */;
94 value_.emplace<std::string>(annotation.legacy_json_value().data,
95 annotation.legacy_json_value().size);
96 } else if (annotation.has_pointer_value()) {
97 in_type_ = 21 /* TlgInINTPTR */;
98 value_ = annotation.pointer_value();
99 } else if (annotation.has_double_value()) {
100 in_type_ = 12 /* TlgInDOUBLE */;
101 value_ = annotation.double_value();
102 }
103 }
104
105 TlmFieldDebugAnnotation::~TlmFieldDebugAnnotation() = default;
106
107 TlmFieldDebugAnnotation::TlmFieldDebugAnnotation(
108 TlmFieldDebugAnnotation&&) noexcept = default;
109 TlmFieldDebugAnnotation& TlmFieldDebugAnnotation::operator=(
110 TlmFieldDebugAnnotation&&) noexcept = default;
111
FillEventDescriptor(EVENT_DATA_DESCRIPTOR * descriptors) const112 void TlmFieldDebugAnnotation::FillEventDescriptor(
113 EVENT_DATA_DESCRIPTOR* descriptors) const noexcept {
114 absl::visit(
115 [&]<typename T>(const T& arg) {
116 using Traits = EventDataDescTraits<T>;
117 EventDataDescCreate(&descriptors[0], Traits::GetAddress(arg),
118 Traits::GetSize(arg));
119 },
120 value_);
121 }
122
GetDataDescCount() const123 uint8_t TlmFieldDebugAnnotation::GetDataDescCount() const noexcept {
124 return data_desc_count_;
125 }
126
GetInType() const127 uint8_t TlmFieldDebugAnnotation::GetInType() const noexcept {
128 return in_type_;
129 }
130
GetOutType() const131 uint8_t TlmFieldDebugAnnotation::GetOutType() const noexcept {
132 return out_type_;
133 }
134
GetDebugAnnotationName(perfetto::TrackEventStateTracker::SequenceState & sequence_state,const perfetto::protos::pbzero::DebugAnnotation_Decoder & annotation)135 std::string_view GetDebugAnnotationName(
136 perfetto::TrackEventStateTracker::SequenceState& sequence_state,
137 const perfetto::protos::pbzero::DebugAnnotation_Decoder& annotation) {
138 protozero::ConstChars name{};
139 if (const auto id = annotation.name_iid()) {
140 const auto& value = sequence_state.debug_annotation_names[id];
141 name.data = value.data();
142 name.size = value.size();
143 } else if (annotation.has_name()) {
144 name.data = annotation.name().data;
145 name.size = annotation.name().size;
146 }
147 return std::string_view(name.data, name.size);
148 }
149 } // namespace
150
151 class MultiEtwPayloadHandler final {
152 public:
MultiEtwPayloadHandler(TlmProvider * provider,std::string_view event_name,const EVENT_DESCRIPTOR & event_descriptor)153 MultiEtwPayloadHandler(TlmProvider* provider,
154 std::string_view event_name,
155 const EVENT_DESCRIPTOR& event_descriptor)
156 : provider_(provider), event_descriptor_(event_descriptor) {
157 is_enabled_ = provider_->IsEnabled(event_descriptor);
158 if (!is_enabled_) {
159 return;
160 }
161 metadata_index_ = provider_->EventBegin(metadata_, event_name);
162 }
163
164 // Ensures that this function cannot be called with temporary objects.
165 template <EtwFieldWithDataDescType T>
166 void WriteField(const T&& value) = delete;
167
168 // Caller needs to ensure that the `value` being passed is not destroyed, till
169 // `EventEnd` is called.
170 template <EtwFieldWithDataDescType T>
WriteField(const T & value)171 void WriteField(const T& value) {
172 if (!is_enabled_) {
173 return;
174 }
175 const int data_desc_count = value.GetDataDescCount();
176 provider_->EventAddField(metadata_, &metadata_index_, value.GetInType(),
177 value.GetOutType(), value.Name());
178 descriptors_.resize(descriptors_.size() +
179 static_cast<size_t>(data_desc_count));
180
181 value.FillEventDescriptor(&descriptors_[descriptors_index_]);
182 descriptors_index_ += data_desc_count;
183 }
184
EventEnd()185 ULONG EventEnd() {
186 if (!is_enabled_) {
187 return 0;
188 }
189 ULONG ret =
190 provider_->EventEnd(metadata_, metadata_index_, &descriptors_[0],
191 descriptors_index_, event_descriptor_);
192 return ret;
193 }
194
195 private:
196 raw_ptr<TlmProvider> provider_;
197 bool is_enabled_ = false;
198 char metadata_[TlmProvider::kMaxEventMetadataSize]{};
199 uint16_t metadata_index_ = 0;
200 static constexpr int kMaxPossibleDescriptors = 6;
201 static constexpr int kMinPossibleDescriptors = 2;
202 uint8_t descriptors_index_ = kMinPossibleDescriptors;
203 absl::InlinedVector<EVENT_DATA_DESCRIPTOR, kMaxPossibleDescriptors>
204 descriptors_{kMinPossibleDescriptors};
205 EVENT_DESCRIPTOR event_descriptor_;
206 };
207
208 class ETWInterceptor::Delegate
209 : public perfetto::TrackEventStateTracker::Delegate {
210 public:
Delegate(perfetto::LockedHandle<ETWInterceptor> locked_self,perfetto::TrackEventStateTracker::SequenceState & sequence_state)211 Delegate(perfetto::LockedHandle<ETWInterceptor> locked_self,
212 perfetto::TrackEventStateTracker::SequenceState& sequence_state)
213 : sequence_state_(sequence_state), locked_self_(std::move(locked_self)) {
214 DCHECK(locked_self_);
215 }
216 ~Delegate() override;
217
218 perfetto::TrackEventStateTracker::SessionState* GetSessionState() override;
219 void OnTrackUpdated(perfetto::TrackEventStateTracker::Track&) override;
220 void OnTrackEvent(
221 const perfetto::TrackEventStateTracker::Track&,
222 const perfetto::TrackEventStateTracker::ParsedTrackEvent&) override;
223
224 private:
225 raw_ref<perfetto::TrackEventStateTracker::SequenceState> sequence_state_;
226 perfetto::LockedHandle<ETWInterceptor> locked_self_;
227 };
228
229 ETWInterceptor::Delegate::~Delegate() = default;
230
231 perfetto::TrackEventStateTracker::SessionState*
GetSessionState()232 ETWInterceptor::Delegate::GetSessionState() {
233 return &locked_self_->session_state_;
234 }
235
OnTrackUpdated(perfetto::TrackEventStateTracker::Track & track)236 void ETWInterceptor::Delegate::OnTrackUpdated(
237 perfetto::TrackEventStateTracker::Track& track) {}
238
OnTrackEvent(const perfetto::TrackEventStateTracker::Track & track,const perfetto::TrackEventStateTracker::ParsedTrackEvent & event)239 void ETWInterceptor::Delegate::OnTrackEvent(
240 const perfetto::TrackEventStateTracker::Track& track,
241 const perfetto::TrackEventStateTracker::ParsedTrackEvent& event) {
242 uint64_t keyword = base::trace_event::CategoryGroupToETWKeyword(
243 std::string_view(event.category.data, event.category.size));
244 const char* phase_string = nullptr;
245 switch (event.track_event.type()) {
246 case perfetto::protos::pbzero::TrackEvent::TYPE_SLICE_BEGIN:
247 phase_string = "Begin";
248 break;
249 case perfetto::protos::pbzero::TrackEvent::TYPE_SLICE_END:
250 phase_string = "End";
251 break;
252 case perfetto::protos::pbzero::TrackEvent::TYPE_INSTANT:
253 phase_string = "Instant";
254 break;
255 }
256 DCHECK_NE(nullptr, phase_string);
257 // TODO(crbug.com/40276149): Consider exporting thread time once
258 // TrackEventStateTracker supports it.
259 if (!event.track_event.has_debug_annotations()) {
260 if (event.track_event.type() ==
261 perfetto::protos::pbzero::TrackEvent::TYPE_SLICE_END) {
262 locked_self_->provider_->WriteEvent(
263 std::string_view(event.name.data, event.name.size),
264 TlmEventDescriptor(0, keyword),
265 TlmMbcsStringField("Phase", phase_string),
266 TlmUInt64Field("Id", track.uuid),
267 TlmUInt64Field(
268 "Timestamp",
269 event.timestamp_ns / base::TimeTicks::kNanosecondsPerMicrosecond),
270 TlmUInt64Field(
271 "Duration",
272 event.duration_ns / base::TimeTicks::kNanosecondsPerMicrosecond));
273 } else {
274 locked_self_->provider_->WriteEvent(
275 std::string_view(event.name.data, event.name.size),
276 TlmEventDescriptor(0, keyword),
277 TlmMbcsStringField("Phase", phase_string),
278 TlmUInt64Field("Id", track.uuid),
279 TlmUInt64Field("Timestamp",
280 event.timestamp_ns /
281 base::TimeTicks::kNanosecondsPerMicrosecond));
282 }
283 } else {
284 const auto event_descriptor = TlmEventDescriptor(0, keyword);
285 const std::string_view event_name(event.name.data, event.name.size);
286
287 MultiEtwPayloadHandler etw_payload_handler(locked_self_->provider_,
288 event_name, event_descriptor);
289 const TlmMbcsStringField phase_event("Phase", phase_string);
290 etw_payload_handler.WriteField(phase_event);
291
292 const TlmUInt64Field timestamp_field(
293 "Timestamp",
294 event.timestamp_ns / base::TimeTicks::kNanosecondsPerMicrosecond);
295 etw_payload_handler.WriteField(timestamp_field);
296
297 const TlmUInt64Field id_field("Id", track.uuid);
298 etw_payload_handler.WriteField(id_field);
299
300 std::optional<TlmUInt64Field> duration_field;
301 if (event.track_event.type() ==
302 perfetto::protos::pbzero::TrackEvent::TYPE_SLICE_END) {
303 duration_field.emplace(
304 "Duration",
305 event.duration_ns / base::TimeTicks::kNanosecondsPerMicrosecond);
306 etw_payload_handler.WriteField(*duration_field);
307 }
308
309 // Add debug annotations.
310 static constexpr int kMaxDebugAnnotations = 2;
311 absl::InlinedVector<TlmFieldDebugAnnotation, kMaxDebugAnnotations>
312 debug_fields;
313 for (auto it = event.track_event.debug_annotations(); it; ++it) {
314 perfetto::protos::pbzero::DebugAnnotation_Decoder annotation(*it);
315 debug_fields.emplace_back(
316 GetDebugAnnotationName(sequence_state_.get(), annotation),
317 annotation);
318 }
319 for (const auto& debug_field : debug_fields) {
320 etw_payload_handler.WriteField(debug_field);
321 }
322 etw_payload_handler.EventEnd();
323 }
324 }
325
ETWInterceptor(TlmProvider * provider)326 ETWInterceptor::ETWInterceptor(TlmProvider* provider) : provider_(provider) {}
327 ETWInterceptor::~ETWInterceptor() = default;
328
Register(TlmProvider * provider)329 void ETWInterceptor::Register(TlmProvider* provider) {
330 perfetto::protos::gen::InterceptorDescriptor desc;
331 desc.set_name("etwexport");
332 perfetto::Interceptor<ETWInterceptor>::Register(desc, provider);
333 }
334
OnTracePacket(InterceptorContext context)335 void ETWInterceptor::OnTracePacket(InterceptorContext context) {
336 auto& tls = context.GetThreadLocalState();
337 perfetto::LockedHandle<ETWInterceptor> locked_self =
338 context.GetInterceptorLocked();
339 if (!locked_self) {
340 return;
341 }
342 Delegate delegate(std::move(locked_self), tls.sequence_state);
343 perfetto::protos::pbzero::TracePacket::Decoder packet(
344 context.packet_data.data, context.packet_data.size);
345 perfetto::TrackEventStateTracker::ProcessTracePacket(
346 delegate, tls.sequence_state, packet);
347 }
348
ThreadLocalState(ThreadLocalStateArgs & args)349 ETWInterceptor::ThreadLocalState::ThreadLocalState(ThreadLocalStateArgs& args) {
350 }
351 ETWInterceptor::ThreadLocalState::~ThreadLocalState() = default;
352
OnSetup(const SetupArgs &)353 void ETWInterceptor::OnSetup(const SetupArgs&) {}
OnStart(const StartArgs &)354 void ETWInterceptor::OnStart(const StartArgs&) {}
OnStop(const StopArgs &)355 void ETWInterceptor::OnStop(const StopArgs&) {}
356
357 } // namespace base::trace_event
358