• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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