• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 #ifndef INCLUDE_PERFETTO_TRACING_EVENT_CONTEXT_H_
18 #define INCLUDE_PERFETTO_TRACING_EVENT_CONTEXT_H_
19 
20 #include "perfetto/protozero/message_handle.h"
21 #include "perfetto/tracing/internal/track_event_internal.h"
22 #include "perfetto/tracing/traced_proto.h"
23 #include "protos/perfetto/trace/trace_packet.pbzero.h"
24 
25 namespace perfetto {
26 namespace protos {
27 namespace pbzero {
28 class DebugAnnotation;
29 }  // namespace pbzero
30 }  // namespace protos
31 
32 namespace internal {
33 class TrackEventInternal;
34 }
35 
36 // Allows adding custom arguments into track events. Example:
37 //
38 //   TRACE_EVENT_BEGIN("category", "Title",
39 //                     [](perfetto::EventContext ctx) {
40 //                       auto* log = ctx.event()->set_log_message();
41 //                       log->set_body_iid(1234);
42 //
43 //                       ctx.AddDebugAnnotation("name", 1234);
44 //                     });
45 //
46 class PERFETTO_EXPORT_COMPONENT EventContext {
47  public:
48   EventContext(EventContext&&) = default;
49 
50   // For Chromium during the transition phase to the client library.
51   // TODO(eseckler): Remove once Chromium has switched to client lib entirely.
52   explicit EventContext(
53       protos::pbzero::TrackEvent* event,
54       internal::TrackEventIncrementalState* incremental_state = nullptr,
55       bool filter_debug_annotations = false)
event_(event)56       : event_(event),
57         incremental_state_(incremental_state),
58         filter_debug_annotations_(filter_debug_annotations) {}
59 
60   ~EventContext();
61 
GetIncrementalState()62   internal::TrackEventIncrementalState* GetIncrementalState() const {
63     return incremental_state_;
64   }
65 
66   // Disclaimer: Experimental method, subject to change.
67   // Exposed publicly to emit some TrackEvent fields in Chromium only in local
68   // tracing. Long-term, we really shouldn't be (ab)using the
69   // filter_debug_annotation setting for this.
70   //
71   // TODO(kraskevich): Come up with a more precise name once we have more than
72   // one usecase.
ShouldFilterDebugAnnotations()73   bool ShouldFilterDebugAnnotations() const {
74     if (tls_state_) {
75       return tls_state_->filter_debug_annotations;
76     }
77     // In Chromium tls_state_ is nullptr, so we need to get this information
78     // from a separate field.
79     return filter_debug_annotations_;
80   }
81 
82   // Get a TrackEvent message to write typed arguments to.
83   //
84   // event() is a template method to allow callers to specify a subclass of
85   // TrackEvent instead. Those subclasses correspond to TrackEvent message with
86   // application-specific extensions. More information in
87   // design-docs/extensions.md.
88   template <typename EventType = protos::pbzero::TrackEvent>
event()89   EventType* event() const {
90     // As the method does downcasting, we check that a target subclass does
91     // not add new fields.
92     static_assert(
93         sizeof(EventType) == sizeof(protos::pbzero::TrackEvent),
94         "Event type must be binary-compatible with protos::pbzero::TrackEvent");
95     return static_cast<EventType*>(event_);
96   }
97 
98   // Convert a raw pointer to protozero message to TracedProto which captures
99   // the reference to this EventContext.
100   template <typename MessageType>
Wrap(MessageType * message)101   TracedProto<MessageType> Wrap(MessageType* message) {
102     static_assert(std::is_base_of<protozero::Message, MessageType>::value,
103                   "TracedProto can be used only with protozero messages");
104 
105     return TracedProto<MessageType>(message, this);
106   }
107 
108   // Add a new `debug_annotation` proto message and populate it from |value|
109   // using perfetto::TracedValue API. Users should generally prefer passing
110   // values directly to TRACE_EVENT (i.e. TRACE_EVENT(..., "arg", value, ...);)
111   // but in rare cases (e.g. when an argument should be written conditionally)
112   // EventContext::AddDebugAnnotation provides an explicit equivalent.
113   template <typename EventNameType, typename T>
AddDebugAnnotation(EventNameType && name,T && value)114   void AddDebugAnnotation(EventNameType&& name, T&& value) {
115     if (tls_state_ && tls_state_->filter_debug_annotations)
116       return;
117     auto annotation = AddDebugAnnotation(std::forward<EventNameType>(name));
118     WriteIntoTracedValue(internal::CreateTracedValueFromProto(annotation, this),
119                          std::forward<T>(value));
120   }
121 
122   // Read arbitrary user data that is associated with the thread-local per
123   // instance state of the track event. `key` must be non-null and unique
124   // per TrackEventTlsStateUserData subclass.
125   TrackEventTlsStateUserData* GetTlsUserData(const void* key);
126 
127   // Set arbitrary user data that is associated with the thread-local per
128   // instance state of the track event. `key` must be non-null and unique
129   // per TrackEventTlsStateUserData subclass.
130   void SetTlsUserData(const void* key,
131                       std::unique_ptr<TrackEventTlsStateUserData> data);
132 
133  private:
134   template <typename, size_t, typename, typename>
135   friend class TrackEventInternedDataIndex;
136   friend class internal::TrackEventInternal;
137 
138   using TracePacketHandle =
139       ::protozero::MessageHandle<protos::pbzero::TracePacket>;
140 
141   EventContext(TraceWriterBase* trace_writer,
142                TracePacketHandle,
143                internal::TrackEventIncrementalState*,
144                internal::TrackEventTlsState*);
145   EventContext(const EventContext&) = delete;
146 
147   protos::pbzero::DebugAnnotation* AddDebugAnnotation(const char* name);
148   protos::pbzero::DebugAnnotation* AddDebugAnnotation(
149       ::perfetto::DynamicString name);
150 
151   TraceWriterBase* trace_writer_ = nullptr;
152   TracePacketHandle trace_packet_;
153   protos::pbzero::TrackEvent* event_;
154   internal::TrackEventIncrementalState* incremental_state_;
155   // TODO(mohitms): Make it const-reference instead of pointer, once we
156   // are certain that it cannot be nullptr. Once we switch to client library in
157   // chrome, we can make that happen.
158   internal::TrackEventTlsState* tls_state_ = nullptr;
159   // TODO(kraskevich): Come up with a more precise name once we have more than
160   // one usecase.
161   // TODO(kraskevich): Remove once Chromium has fully switched to client lib.
162   const bool filter_debug_annotations_ = false;
163 };
164 
165 }  // namespace perfetto
166 
167 #endif  // INCLUDE_PERFETTO_TRACING_EVENT_CONTEXT_H_
168