• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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_INTERNAL_TRACK_EVENT_LEGACY_H_
18 #define INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_LEGACY_H_
19 
20 #include "perfetto/base/build_config.h"
21 #include "perfetto/tracing/event_context.h"
22 #include "perfetto/tracing/track.h"
23 #include "protos/perfetto/trace/track_event/track_event.pbzero.h"
24 
25 #ifndef PERFETTO_ENABLE_LEGACY_TRACE_EVENTS
26 #define PERFETTO_ENABLE_LEGACY_TRACE_EVENTS 0
27 #endif
28 
29 // ----------------------------------------------------------------------------
30 // Constants.
31 // ----------------------------------------------------------------------------
32 
33 namespace perfetto {
34 namespace legacy {
35 
36 enum TraceEventFlag {
37   kTraceEventFlagNone = 0,
38   kTraceEventFlagCopy = 1u << 0,
39   kTraceEventFlagHasId = 1u << 1,
40   kTraceEventFlagScopeOffset = 1u << 2,
41   kTraceEventFlagScopeExtra = 1u << 3,
42   kTraceEventFlagExplicitTimestamp = 1u << 4,
43   kTraceEventFlagAsyncTTS = 1u << 5,
44   kTraceEventFlagBindToEnclosing = 1u << 6,
45   kTraceEventFlagFlowIn = 1u << 7,
46   kTraceEventFlagFlowOut = 1u << 8,
47   kTraceEventFlagHasContextId = 1u << 9,
48   kTraceEventFlagHasProcessId = 1u << 10,
49   kTraceEventFlagHasLocalId = 1u << 11,
50   kTraceEventFlagHasGlobalId = 1u << 12,
51   // TODO(eseckler): Remove once we have native support for typed proto events
52   // in TRACE_EVENT macros.
53   kTraceEventFlagTypedProtoArgs = 1u << 15,
54   kTraceEventFlagJavaStringLiterals = 1u << 16,
55 };
56 
57 enum PerfettoLegacyCurrentThreadId { kCurrentThreadId };
58 
59 // The following user-provided adaptors are used to serialize user-defined
60 // thread id and time types into track events. For full compatibility, the user
61 // should also define the following macros appropriately:
62 //
63 //   #define TRACE_TIME_TICKS_NOW() ...
64 //   #define TRACE_TIME_NOW() ...
65 
66 // User-provided function to convert an abstract thread id into a thread track.
67 template <typename T>
68 ThreadTrack ConvertThreadId(const T&);
69 
70 // Built-in implementation for events referring to the current thread.
71 template <>
72 ThreadTrack PERFETTO_EXPORT_COMPONENT
73 ConvertThreadId(const PerfettoLegacyCurrentThreadId&);
74 
75 }  // namespace legacy
76 }  // namespace perfetto
77 
78 #if PERFETTO_ENABLE_LEGACY_TRACE_EVENTS
79 // The following constants are defined in the global namespace, since they were
80 // originally implemented as macros.
81 
82 // Event phases.
83 static constexpr char TRACE_EVENT_PHASE_BEGIN = 'B';
84 static constexpr char TRACE_EVENT_PHASE_END = 'E';
85 static constexpr char TRACE_EVENT_PHASE_COMPLETE = 'X';
86 static constexpr char TRACE_EVENT_PHASE_INSTANT = 'I';
87 static constexpr char TRACE_EVENT_PHASE_ASYNC_BEGIN = 'S';
88 static constexpr char TRACE_EVENT_PHASE_ASYNC_STEP_INTO = 'T';
89 static constexpr char TRACE_EVENT_PHASE_ASYNC_STEP_PAST = 'p';
90 static constexpr char TRACE_EVENT_PHASE_ASYNC_END = 'F';
91 static constexpr char TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN = 'b';
92 static constexpr char TRACE_EVENT_PHASE_NESTABLE_ASYNC_END = 'e';
93 static constexpr char TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT = 'n';
94 static constexpr char TRACE_EVENT_PHASE_FLOW_BEGIN = 's';
95 static constexpr char TRACE_EVENT_PHASE_FLOW_STEP = 't';
96 static constexpr char TRACE_EVENT_PHASE_FLOW_END = 'f';
97 static constexpr char TRACE_EVENT_PHASE_METADATA = 'M';
98 static constexpr char TRACE_EVENT_PHASE_COUNTER = 'C';
99 static constexpr char TRACE_EVENT_PHASE_SAMPLE = 'P';
100 static constexpr char TRACE_EVENT_PHASE_CREATE_OBJECT = 'N';
101 static constexpr char TRACE_EVENT_PHASE_SNAPSHOT_OBJECT = 'O';
102 static constexpr char TRACE_EVENT_PHASE_DELETE_OBJECT = 'D';
103 static constexpr char TRACE_EVENT_PHASE_MEMORY_DUMP = 'v';
104 static constexpr char TRACE_EVENT_PHASE_MARK = 'R';
105 static constexpr char TRACE_EVENT_PHASE_CLOCK_SYNC = 'c';
106 
107 // Flags for changing the behavior of TRACE_EVENT_API_ADD_TRACE_EVENT.
108 static constexpr uint32_t TRACE_EVENT_FLAG_NONE =
109     perfetto::legacy::kTraceEventFlagNone;
110 static constexpr uint32_t TRACE_EVENT_FLAG_COPY =
111     perfetto::legacy::kTraceEventFlagCopy;
112 static constexpr uint32_t TRACE_EVENT_FLAG_HAS_ID =
113     perfetto::legacy::kTraceEventFlagHasId;
114 static constexpr uint32_t TRACE_EVENT_FLAG_SCOPE_OFFSET =
115     perfetto::legacy::kTraceEventFlagScopeOffset;
116 static constexpr uint32_t TRACE_EVENT_FLAG_SCOPE_EXTRA =
117     perfetto::legacy::kTraceEventFlagScopeExtra;
118 static constexpr uint32_t TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP =
119     perfetto::legacy::kTraceEventFlagExplicitTimestamp;
120 static constexpr uint32_t TRACE_EVENT_FLAG_ASYNC_TTS =
121     perfetto::legacy::kTraceEventFlagAsyncTTS;
122 static constexpr uint32_t TRACE_EVENT_FLAG_BIND_TO_ENCLOSING =
123     perfetto::legacy::kTraceEventFlagBindToEnclosing;
124 static constexpr uint32_t TRACE_EVENT_FLAG_FLOW_IN =
125     perfetto::legacy::kTraceEventFlagFlowIn;
126 static constexpr uint32_t TRACE_EVENT_FLAG_FLOW_OUT =
127     perfetto::legacy::kTraceEventFlagFlowOut;
128 static constexpr uint32_t TRACE_EVENT_FLAG_HAS_CONTEXT_ID =
129     perfetto::legacy::kTraceEventFlagHasContextId;
130 static constexpr uint32_t TRACE_EVENT_FLAG_HAS_PROCESS_ID =
131     perfetto::legacy::kTraceEventFlagHasProcessId;
132 static constexpr uint32_t TRACE_EVENT_FLAG_HAS_LOCAL_ID =
133     perfetto::legacy::kTraceEventFlagHasLocalId;
134 static constexpr uint32_t TRACE_EVENT_FLAG_HAS_GLOBAL_ID =
135     perfetto::legacy::kTraceEventFlagHasGlobalId;
136 static constexpr uint32_t TRACE_EVENT_FLAG_TYPED_PROTO_ARGS =
137     perfetto::legacy::kTraceEventFlagTypedProtoArgs;
138 static constexpr uint32_t TRACE_EVENT_FLAG_JAVA_STRING_LITERALS =
139     perfetto::legacy::kTraceEventFlagJavaStringLiterals;
140 
141 static constexpr uint32_t TRACE_EVENT_FLAG_SCOPE_MASK =
142     TRACE_EVENT_FLAG_SCOPE_OFFSET | TRACE_EVENT_FLAG_SCOPE_EXTRA;
143 
144 // Type values for identifying types in the TraceValue union.
145 static constexpr uint8_t TRACE_VALUE_TYPE_BOOL = 1;
146 static constexpr uint8_t TRACE_VALUE_TYPE_UINT = 2;
147 static constexpr uint8_t TRACE_VALUE_TYPE_INT = 3;
148 static constexpr uint8_t TRACE_VALUE_TYPE_DOUBLE = 4;
149 static constexpr uint8_t TRACE_VALUE_TYPE_POINTER = 5;
150 static constexpr uint8_t TRACE_VALUE_TYPE_STRING = 6;
151 static constexpr uint8_t TRACE_VALUE_TYPE_COPY_STRING = 7;
152 static constexpr uint8_t TRACE_VALUE_TYPE_CONVERTABLE = 8;
153 static constexpr uint8_t TRACE_VALUE_TYPE_PROTO = 9;
154 
155 // Enum reflecting the scope of an INSTANT event. Must fit within
156 // TRACE_EVENT_FLAG_SCOPE_MASK.
157 static constexpr uint8_t TRACE_EVENT_SCOPE_GLOBAL = 0u << 2;
158 static constexpr uint8_t TRACE_EVENT_SCOPE_PROCESS = 1u << 2;
159 static constexpr uint8_t TRACE_EVENT_SCOPE_THREAD = 2u << 2;
160 
161 static constexpr char TRACE_EVENT_SCOPE_NAME_GLOBAL = 'g';
162 static constexpr char TRACE_EVENT_SCOPE_NAME_PROCESS = 'p';
163 static constexpr char TRACE_EVENT_SCOPE_NAME_THREAD = 't';
164 
165 #define TRACE_EVENT_API_CURRENT_THREAD_ID ::perfetto::legacy::kCurrentThreadId
166 
167 #endif  // PERFETTO_ENABLE_LEGACY_TRACE_EVENTS
168 
169 namespace perfetto {
170 namespace internal {
171 
172 // LegacyTraceId encapsulates an ID that can either be an integer or pointer.
173 class PERFETTO_EXPORT_COMPONENT LegacyTraceId {
174  public:
175   // Can be combined with WithScope.
176   class LocalId {
177    public:
LocalId(const void * raw_id)178     explicit LocalId(const void* raw_id)
179         : raw_id_(static_cast<uint64_t>(reinterpret_cast<uintptr_t>(raw_id))) {}
LocalId(uint64_t raw_id)180     explicit LocalId(uint64_t raw_id) : raw_id_(raw_id) {}
raw_id()181     uint64_t raw_id() const { return raw_id_; }
182 
183    private:
184     uint64_t raw_id_;
185   };
186 
187   // Can be combined with WithScope.
188   class GlobalId {
189    public:
GlobalId(uint64_t raw_id)190     explicit GlobalId(uint64_t raw_id) : raw_id_(raw_id) {}
raw_id()191     uint64_t raw_id() const { return raw_id_; }
192 
193    private:
194     uint64_t raw_id_;
195   };
196 
197   class WithScope {
198    public:
WithScope(const char * scope,uint64_t raw_id)199     WithScope(const char* scope, uint64_t raw_id)
200         : scope_(scope), raw_id_(raw_id) {}
WithScope(const char * scope,LocalId local_id)201     WithScope(const char* scope, LocalId local_id)
202         : scope_(scope), raw_id_(local_id.raw_id()) {
203       id_flags_ = legacy::kTraceEventFlagHasLocalId;
204     }
WithScope(const char * scope,GlobalId global_id)205     WithScope(const char* scope, GlobalId global_id)
206         : scope_(scope), raw_id_(global_id.raw_id()) {
207       id_flags_ = legacy::kTraceEventFlagHasGlobalId;
208     }
WithScope(const char * scope,uint64_t prefix,uint64_t raw_id)209     WithScope(const char* scope, uint64_t prefix, uint64_t raw_id)
210         : scope_(scope), has_prefix_(true), prefix_(prefix), raw_id_(raw_id) {}
WithScope(const char * scope,uint64_t prefix,GlobalId global_id)211     WithScope(const char* scope, uint64_t prefix, GlobalId global_id)
212         : scope_(scope),
213           has_prefix_(true),
214           prefix_(prefix),
215           raw_id_(global_id.raw_id()) {
216       id_flags_ = legacy::kTraceEventFlagHasGlobalId;
217     }
raw_id()218     uint64_t raw_id() const { return raw_id_; }
scope()219     const char* scope() const { return scope_; }
has_prefix()220     bool has_prefix() const { return has_prefix_; }
prefix()221     uint64_t prefix() const { return prefix_; }
id_flags()222     uint32_t id_flags() const { return id_flags_; }
223 
224    private:
225     const char* scope_ = nullptr;
226     bool has_prefix_ = false;
227     uint64_t prefix_;
228     uint64_t raw_id_;
229     uint32_t id_flags_ = legacy::kTraceEventFlagHasId;
230   };
231 
LegacyTraceId(const void * raw_id)232   explicit LegacyTraceId(const void* raw_id)
233       : raw_id_(static_cast<uint64_t>(reinterpret_cast<uintptr_t>(raw_id))) {
234     id_flags_ = legacy::kTraceEventFlagHasLocalId;
235   }
LegacyTraceId(uint64_t raw_id)236   explicit LegacyTraceId(uint64_t raw_id) : raw_id_(raw_id) {}
LegacyTraceId(uint32_t raw_id)237   explicit LegacyTraceId(uint32_t raw_id) : raw_id_(raw_id) {}
LegacyTraceId(uint16_t raw_id)238   explicit LegacyTraceId(uint16_t raw_id) : raw_id_(raw_id) {}
LegacyTraceId(uint8_t raw_id)239   explicit LegacyTraceId(uint8_t raw_id) : raw_id_(raw_id) {}
LegacyTraceId(int64_t raw_id)240   explicit LegacyTraceId(int64_t raw_id)
241       : raw_id_(static_cast<uint64_t>(raw_id)) {}
LegacyTraceId(int32_t raw_id)242   explicit LegacyTraceId(int32_t raw_id)
243       : raw_id_(static_cast<uint64_t>(raw_id)) {}
LegacyTraceId(int16_t raw_id)244   explicit LegacyTraceId(int16_t raw_id)
245       : raw_id_(static_cast<uint64_t>(raw_id)) {}
LegacyTraceId(int8_t raw_id)246   explicit LegacyTraceId(int8_t raw_id)
247       : raw_id_(static_cast<uint64_t>(raw_id)) {}
248 // Different platforms disagree on which integer types are same and which
249 // are different. E.g. on Mac size_t is considered a different type from
250 // uint64_t even though it has the same size and signedness.
251 // Below we add overloads for those types that are known to cause ambiguity.
252 #if PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
LegacyTraceId(size_t raw_id)253   explicit LegacyTraceId(size_t raw_id) : raw_id_(raw_id) {}
LegacyTraceId(intptr_t raw_id)254   explicit LegacyTraceId(intptr_t raw_id)
255       : raw_id_(static_cast<uint64_t>(raw_id)) {}
256 #elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
LegacyTraceId(unsigned long raw_id)257   explicit LegacyTraceId(unsigned long raw_id) : raw_id_(raw_id) {}
258 #endif
LegacyTraceId(LocalId raw_id)259   explicit LegacyTraceId(LocalId raw_id) : raw_id_(raw_id.raw_id()) {
260     id_flags_ = legacy::kTraceEventFlagHasLocalId;
261   }
LegacyTraceId(GlobalId raw_id)262   explicit LegacyTraceId(GlobalId raw_id) : raw_id_(raw_id.raw_id()) {
263     id_flags_ = legacy::kTraceEventFlagHasGlobalId;
264   }
LegacyTraceId(WithScope scoped_id)265   explicit LegacyTraceId(WithScope scoped_id)
266       : scope_(scoped_id.scope()),
267         has_prefix_(scoped_id.has_prefix()),
268         prefix_(scoped_id.prefix()),
269         raw_id_(scoped_id.raw_id()),
270         id_flags_(scoped_id.id_flags()) {}
271 
raw_id()272   uint64_t raw_id() const { return raw_id_; }
scope()273   const char* scope() const { return scope_; }
has_prefix()274   bool has_prefix() const { return has_prefix_; }
prefix()275   uint64_t prefix() const { return prefix_; }
id_flags()276   uint32_t id_flags() const { return id_flags_; }
277 
278   void Write(protos::pbzero::TrackEvent::LegacyEvent*,
279              uint32_t event_flags) const;
280 
281  private:
282   const char* scope_ = nullptr;
283   bool has_prefix_ = false;
284   uint64_t prefix_;
285   uint64_t raw_id_;
286   uint32_t id_flags_ = legacy::kTraceEventFlagHasId;
287 };
288 
289 #if PERFETTO_ENABLE_LEGACY_TRACE_EVENTS
290 template <typename T>
IsEqual(T x,T y)291 bool IsEqual(T x, T y) {
292   return x == y;
293 }
294 
295 template <typename T, typename U>
IsEqual(T,U)296 bool IsEqual(T, U) {
297   return false;
298 }
299 
300 class PERFETTO_EXPORT_COMPONENT TrackEventLegacy {
301  public:
PhaseToType(char phase)302   static constexpr protos::pbzero::TrackEvent::Type PhaseToType(char phase) {
303     // clang-format off
304     return (phase == TRACE_EVENT_PHASE_BEGIN) ?
305                protos::pbzero::TrackEvent::TYPE_SLICE_BEGIN :
306            (phase == TRACE_EVENT_PHASE_END) ?
307                protos::pbzero::TrackEvent::TYPE_SLICE_END :
308            (phase == TRACE_EVENT_PHASE_INSTANT) ?
309                protos::pbzero::TrackEvent::TYPE_INSTANT :
310            protos::pbzero::TrackEvent::TYPE_UNSPECIFIED;
311     // clang-format on
312   }
313 
314   // Reduce binary size overhead by outlining most of the code for writing a
315   // legacy trace event.
316   template <typename... Args>
WriteLegacyEvent(EventContext ctx,char phase,uint32_t flags,Args &&...args)317   static void WriteLegacyEvent(EventContext ctx,
318                                char phase,
319                                uint32_t flags,
320                                Args&&... args) PERFETTO_NO_INLINE {
321     PERFETTO_DCHECK(!(flags & TRACE_EVENT_FLAG_HAS_PROCESS_ID));
322     AddDebugAnnotations(&ctx, std::forward<Args>(args)...);
323     if (NeedLegacyFlags(phase, flags)) {
324       auto legacy_event = ctx.event()->set_legacy_event();
325       SetLegacyFlags(legacy_event, phase, flags);
326     }
327   }
328 
329   template <typename ThreadIdType, typename... Args>
WriteLegacyEventWithIdAndTid(EventContext ctx,char phase,uint32_t flags,const LegacyTraceId & id,const ThreadIdType & thread_id,Args &&...args)330   static void WriteLegacyEventWithIdAndTid(EventContext ctx,
331                                            char phase,
332                                            uint32_t flags,
333                                            const LegacyTraceId& id,
334                                            const ThreadIdType& thread_id,
335                                            Args&&... args) PERFETTO_NO_INLINE {
336     //
337     // Overrides to consider:
338     //
339     // 1. If we have an id, we need to write {unscoped,local,global}_id and/or
340     //    bind_id.
341     // 2. If we have a thread id, we need to write track_uuid() or
342     //    {pid,tid}_override if the id represents another process.  The
343     //    conversion from |thread_id| happens in embedder code since the type is
344     //    embedder-specified.
345     // 3. If we have a timestamp, we need to write a different timestamp in the
346     //    trace packet itself and make sure TrackEvent won't write one
347     //    internally. This is already done at the call site.
348     //
349     PERFETTO_DCHECK(PhaseToType(phase) ==
350                         protos::pbzero::TrackEvent::TYPE_UNSPECIFIED ||
351                     !(flags & TRACE_EVENT_FLAG_HAS_PROCESS_ID));
352     flags |= id.id_flags();
353     AddDebugAnnotations(&ctx, std::forward<Args>(args)...);
354     if (NeedLegacyFlags(phase, flags)) {
355       auto legacy_event = ctx.event()->set_legacy_event();
356       SetLegacyFlags(legacy_event, phase, flags);
357       if (id.id_flags())
358         id.Write(legacy_event, flags);
359       if (flags & TRACE_EVENT_FLAG_HAS_PROCESS_ID) {
360         // The thread identifier actually represents a process id. Let's set an
361         // override for it.
362         int32_t pid_override =
363             static_cast<int32_t>(legacy::ConvertThreadId(thread_id).tid);
364         legacy_event->set_pid_override(pid_override);
365         legacy_event->set_tid_override(-1);
366       } else {
367         // Only synchronous phases are supported for other threads. These phases
368         // are supported in TrackEvent types and receive a track_uuid
369         // association via TrackEventDataSource::TraceForCategoryImpl().
370         PERFETTO_DCHECK(PhaseToType(phase) !=
371                             protos::pbzero::TrackEvent::TYPE_UNSPECIFIED ||
372                         IsEqual(thread_id, TRACE_EVENT_API_CURRENT_THREAD_ID) ||
373                         legacy::ConvertThreadId(thread_id).tid ==
374                             ThreadTrack::Current().tid);
375       }
376     }
377   }
378 
379   // No arguments.
AddDebugAnnotations(EventContext *)380   static void AddDebugAnnotations(EventContext*) {}
381 
382   // N number of debug arguments.
383   template <typename ArgNameType, typename ArgType, typename... OtherArgs>
AddDebugAnnotations(EventContext * ctx,ArgNameType && arg_name,ArgType && arg_value,OtherArgs &&...more_args)384   static void AddDebugAnnotations(EventContext* ctx,
385                                   ArgNameType&& arg_name,
386                                   ArgType&& arg_value,
387                                   OtherArgs&&... more_args) {
388     TrackEventInternal::AddDebugAnnotation(ctx,
389                                            std::forward<ArgNameType>(arg_name),
390                                            std::forward<ArgType>(arg_value));
391     AddDebugAnnotations(ctx, std::forward<OtherArgs>(more_args)...);
392   }
393 
394  private:
NeedLegacyFlags(char phase,uint32_t flags)395   static bool NeedLegacyFlags(char phase, uint32_t flags) {
396     if (PhaseToType(phase) == protos::pbzero::TrackEvent::TYPE_UNSPECIFIED)
397       return true;
398     // TODO(skyostil): Implement/deprecate:
399     // - TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP
400     // - TRACE_EVENT_FLAG_HAS_CONTEXT_ID
401     // - TRACE_EVENT_FLAG_TYPED_PROTO_ARGS
402     // - TRACE_EVENT_FLAG_JAVA_STRING_LITERALS
403     return flags &
404            (TRACE_EVENT_FLAG_HAS_ID | TRACE_EVENT_FLAG_HAS_LOCAL_ID |
405             TRACE_EVENT_FLAG_HAS_GLOBAL_ID | TRACE_EVENT_FLAG_ASYNC_TTS |
406             TRACE_EVENT_FLAG_BIND_TO_ENCLOSING | TRACE_EVENT_FLAG_FLOW_IN |
407             TRACE_EVENT_FLAG_FLOW_OUT | TRACE_EVENT_FLAG_HAS_PROCESS_ID);
408   }
409 
SetLegacyFlags(protos::pbzero::TrackEvent::LegacyEvent * legacy_event,char phase,uint32_t flags)410   static void SetLegacyFlags(
411       protos::pbzero::TrackEvent::LegacyEvent* legacy_event,
412       char phase,
413       uint32_t flags) {
414     if (PhaseToType(phase) == protos::pbzero::TrackEvent::TYPE_UNSPECIFIED)
415       legacy_event->set_phase(phase);
416     if (flags & TRACE_EVENT_FLAG_ASYNC_TTS)
417       legacy_event->set_use_async_tts(true);
418     if (flags & TRACE_EVENT_FLAG_BIND_TO_ENCLOSING)
419       legacy_event->set_bind_to_enclosing(true);
420 
421     const auto kFlowIn = TRACE_EVENT_FLAG_FLOW_IN;
422     const auto kFlowOut = TRACE_EVENT_FLAG_FLOW_OUT;
423     const auto kFlowInOut = kFlowIn | kFlowOut;
424     if ((flags & kFlowInOut) == kFlowInOut) {
425       legacy_event->set_flow_direction(
426           protos::pbzero::TrackEvent::LegacyEvent::FLOW_INOUT);
427     } else if (flags & kFlowIn) {
428       legacy_event->set_flow_direction(
429           protos::pbzero::TrackEvent::LegacyEvent::FLOW_IN);
430     } else if (flags & kFlowOut) {
431       legacy_event->set_flow_direction(
432           protos::pbzero::TrackEvent::LegacyEvent::FLOW_OUT);
433     }
434   }
435 };
436 #endif  // PERFETTO_ENABLE_LEGACY_TRACE_EVENTS
437 
438 // Legacy macros allow argument values to be nullptr and convert them to the
439 // "NULL" string. The following function helps mimic this behavior: it forwards
440 // all types of arguments apart from a nullptr string as is, and in case of a
441 // nullptr returns "NULL".
442 template <typename T>
PossiblyNull(T && value)443 inline T PossiblyNull(T&& value) {
444   return std::forward<T>(value);
445 }
446 
PossiblyNull(const char * name)447 inline const char* PossiblyNull(const char* name) {
448   return name ? name : "NULL";
449 }
450 
PossiblyNull(char * name)451 inline const char* PossiblyNull(char* name) {
452   return name ? name : "NULL";
453 }
454 
455 }  // namespace internal
456 }  // namespace perfetto
457 
458 #endif  // INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_LEGACY_H_
459