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