blob: 5eb904b5ffb80a25276afaf7f20548caffdc6642 [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_TRACE_EVENT_TRACE_LOG_H_
#define BASE_TRACE_EVENT_TRACE_LOG_H_
#include <stddef.h>
#include <stdint.h>
#include <atomic>
#include <map>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include "base/base_export.h"
#include "base/containers/stack.h"
#include "base/gtest_prod_util.h"
#include "base/memory/scoped_refptr.h"
#include "base/no_destructor.h"
#include "base/task/single_thread_task_runner.h"
#include "base/threading/platform_thread.h"
#include "base/time/time_override.h"
#include "base/trace_event/category_registry.h"
#include "base/trace_event/memory_dump_provider.h"
#include "base/trace_event/trace_config.h"
#include "base/trace_event/trace_event_impl.h"
#include "build/build_config.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
#include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h"
namespace perfetto {
namespace trace_processor {
class TraceProcessorStorage;
} // namespace trace_processor
} // namespace perfetto
#endif // BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
namespace base {
class RefCountedString;
namespace tracing {
class PerfettoPlatform;
} // namespace tracing
namespace trace_event {
struct TraceCategory;
class TraceBuffer;
class TraceBufferChunk;
class TraceEvent;
class TraceEventMemoryOverhead;
class JsonStringOutputWriter;
struct BASE_EXPORT TraceLogStatus {
TraceLogStatus();
~TraceLogStatus();
uint32_t event_capacity;
uint32_t event_count;
};
class BASE_EXPORT TraceLog :
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
public perfetto::TrackEventSessionObserver,
#endif // BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
public MemoryDumpProvider {
public:
class ThreadLocalEventBuffer;
// Argument passed to TraceLog::SetEnabled.
enum Mode : uint8_t {
// Enables normal tracing (recording trace events in the trace buffer).
// This is the only tracing mode supported now.
// TODO(khokhlov): Clean up all uses of tracing mode and remove this enum
// completely.
RECORDING_MODE = 1 << 0,
};
static TraceLog* GetInstance();
TraceLog(const TraceLog&) = delete;
TraceLog& operator=(const TraceLog&) = delete;
// Retrieves a copy (for thread-safety) of the current TraceConfig.
TraceConfig GetCurrentTraceConfig() const;
// Initializes the thread-local event buffer, if not already initialized and
// if the current thread supports that (has a message loop).
void InitializeThreadLocalEventBufferIfSupported();
// See TraceConfig comments for details on how to control which categories
// will be traced. Only RECORDING_MODE is supported.
void SetEnabled(const TraceConfig& trace_config, uint8_t modes_to_enable);
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// Enable tracing using a customized Perfetto trace config. This allows, for
// example, enabling additional data sources and enabling protobuf output
// instead of the legacy JSON trace format.
void SetEnabled(const TraceConfig& trace_config,
const perfetto::TraceConfig& perfetto_config);
#endif
// Disables tracing for all categories. Only RECORDING_MODE is supported.
void SetDisabled();
void SetDisabled(uint8_t modes_to_disable);
// Returns true if TraceLog is enabled on recording mode.
// Note: Returns false even if FILTERING_MODE is enabled.
bool IsEnabled() {
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// In SDK build we return true as soon as the datasource has been set up and
// we know the config. This doesn't necessarily mean that the tracing has
// already started.
// Note that TrackEvent::IsEnabled() can be true even earlier, before the
// OnSetup call, so we can't guarantee that we know the config by the time
// TrackEvent::IsEnabled() is true.
AutoLock lock(track_event_lock_);
return track_event_enabled_;
#else // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
AutoLock lock(lock_);
return enabled_;
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
}
// The number of times we have begun recording traces. If tracing is off,
// returns -1. If tracing is on, then it returns the number of times we have
// recorded a trace. By watching for this number to increment, you can
// passively discover when a new trace has begun. This is then used to
// implement the TRACE_EVENT_IS_NEW_TRACE() primitive.
int GetNumTracesRecorded();
// Enabled state listeners give a callback when tracing is enabled or
// disabled. This can be used to tie into other library's tracing systems
// on-demand.
class BASE_EXPORT EnabledStateObserver {
public:
virtual ~EnabledStateObserver() = default;
// Called just after the tracing system becomes enabled, outside of the
// |lock_|. TraceLog::IsEnabled() is true at this point.
virtual void OnTraceLogEnabled() = 0;
// Called just after the tracing system disables, outside of the |lock_|.
// TraceLog::IsEnabled() is false at this point.
virtual void OnTraceLogDisabled() = 0;
};
// Adds an observer. Cannot be called from within the observer callback.
void AddEnabledStateObserver(EnabledStateObserver* listener);
// Removes an observer. Cannot be called from within the observer callback.
void RemoveEnabledStateObserver(EnabledStateObserver* listener);
// Adds an observer that is owned by TraceLog. This is useful for agents that
// implement tracing feature that needs to stay alive as long as TraceLog
// does.
void AddOwnedEnabledStateObserver(
std::unique_ptr<EnabledStateObserver> listener);
bool HasEnabledStateObserver(EnabledStateObserver* listener) const;
// Asynchronous enabled state listeners. When tracing is enabled or disabled,
// for each observer, a task for invoking its appropriate callback is posted
// to the thread from which AddAsyncEnabledStateObserver() was called. This
// allows the observer to be safely destroyed, provided that it happens on the
// same thread that invoked AddAsyncEnabledStateObserver().
class BASE_EXPORT AsyncEnabledStateObserver {
public:
virtual ~AsyncEnabledStateObserver() = default;
// Posted just after the tracing system becomes enabled, outside |lock_|.
// TraceLog::IsEnabled() is true at this point.
virtual void OnTraceLogEnabled() = 0;
// Posted just after the tracing system becomes disabled, outside |lock_|.
// TraceLog::IsEnabled() is false at this point.
virtual void OnTraceLogDisabled() = 0;
};
// TODO(oysteine): This API originally needed to use WeakPtrs as the observer
// list was copied under the global trace lock, but iterated over outside of
// that lock so that observers could add tracing. The list is now protected by
// its own lock, so this can be changed to a raw ptr.
void AddAsyncEnabledStateObserver(
WeakPtr<AsyncEnabledStateObserver> listener);
void RemoveAsyncEnabledStateObserver(AsyncEnabledStateObserver* listener);
bool HasAsyncEnabledStateObserver(AsyncEnabledStateObserver* listener) const;
// Observers that are notified when incremental state is cleared. This only
// happens when tracing using the perfetto backend.
class BASE_EXPORT IncrementalStateObserver {
public:
virtual ~IncrementalStateObserver() = default;
// Called just after the tracing system has cleared incremental state, while
// a tracing session is active.
virtual void OnIncrementalStateCleared() = 0;
};
// Adds an observer. Cannot be called from within the observer callback.
void AddIncrementalStateObserver(IncrementalStateObserver* listener);
// Removes an observer. Cannot be called from within the observer callback.
void RemoveIncrementalStateObserver(IncrementalStateObserver* listener);
TraceLogStatus GetStatus() const;
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
bool BufferIsFull() const;
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// Computes an estimate of the size of the TraceLog including all the retained
// objects.
void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead);
void SetArgumentFilterPredicate(
const ArgumentFilterPredicate& argument_filter_predicate);
ArgumentFilterPredicate GetArgumentFilterPredicate() const;
void SetMetadataFilterPredicate(
const MetadataFilterPredicate& metadata_filter_predicate);
MetadataFilterPredicate GetMetadataFilterPredicate() const;
void SetRecordHostAppPackageName(bool record_host_app_package_name);
bool ShouldRecordHostAppPackageName() const;
// Flush all collected events to the given output callback. The callback will
// be called one or more times either synchronously or asynchronously from
// the current thread with IPC-bite-size chunks. The string format is
// undefined. Use TraceResultBuffer to convert one or more trace strings to
// JSON. The callback can be null if the caller doesn't want any data.
// Due to the implementation of thread-local buffers, flush can't be
// done when tracing is enabled. If called when tracing is enabled, the
// callback will be called directly with (empty_string, false) to indicate
// the end of this unsuccessful flush. Flush does the serialization
// on the same thread if the caller doesn't set use_worker_thread explicitly.
using OutputCallback =
base::RepeatingCallback<void(const scoped_refptr<base::RefCountedString>&,
bool has_more_events)>;
void Flush(const OutputCallback& cb, bool use_worker_thread = false);
// Cancels tracing and discards collected data.
void CancelTracing(const OutputCallback& cb);
using AddTraceEventOverrideFunction = void (*)(TraceEvent*,
bool thread_will_flush,
TraceEventHandle* handle);
using OnFlushFunction = void (*)();
using UpdateDurationFunction =
void (*)(const unsigned char* category_group_enabled,
const char* name,
TraceEventHandle handle,
PlatformThreadId thread_id,
bool explicit_timestamps,
const TimeTicks& now,
const ThreadTicks& thread_now);
// The callbacks will be called up until the point where the flush is
// finished, i.e. must be callable until OutputCallback is called with
// has_more_events==false.
void SetAddTraceEventOverrides(
const AddTraceEventOverrideFunction& add_event_override,
const OnFlushFunction& on_flush_callback,
const UpdateDurationFunction& update_duration_callback);
// Called by TRACE_EVENT* macros, don't call this directly.
// The name parameter is a category group for example:
// TRACE_EVENT0("renderer,webkit", "WebViewImpl::HandleInputEvent")
static const unsigned char* GetCategoryGroupEnabled(const char* name);
static const char* GetCategoryGroupName(
const unsigned char* category_group_enabled);
static constexpr const unsigned char* GetBuiltinCategoryEnabled(
const char* name) {
TraceCategory* builtin_category =
CategoryRegistry::GetBuiltinCategoryByName(name);
if (builtin_category)
return builtin_category->state_ptr();
return nullptr;
}
// Called by TRACE_EVENT* macros, don't call this directly.
// If |copy| is set, |name|, |arg_name1| and |arg_name2| will be deep copied
// into the event; see "Memory scoping note" and TRACE_EVENT_COPY_XXX above.
bool ShouldAddAfterUpdatingState(char phase,
const unsigned char* category_group_enabled,
const char* name,
uint64_t id,
PlatformThreadId thread_id,
TraceArguments* args);
TraceEventHandle AddTraceEvent(char phase,
const unsigned char* category_group_enabled,
const char* name,
const char* scope,
uint64_t id,
TraceArguments* args,
unsigned int flags);
TraceEventHandle AddTraceEventWithBindId(
char phase,
const unsigned char* category_group_enabled,
const char* name,
const char* scope,
uint64_t id,
uint64_t bind_id,
TraceArguments* args,
unsigned int flags);
TraceEventHandle AddTraceEventWithProcessId(
char phase,
const unsigned char* category_group_enabled,
const char* name,
const char* scope,
uint64_t id,
ProcessId process_id,
TraceArguments* args,
unsigned int flags);
TraceEventHandle AddTraceEventWithThreadIdAndTimestamp(
char phase,
const unsigned char* category_group_enabled,
const char* name,
const char* scope,
uint64_t id,
PlatformThreadId thread_id,
const TimeTicks& timestamp,
TraceArguments* args,
unsigned int flags);
TraceEventHandle AddTraceEventWithThreadIdAndTimestamp(
char phase,
const unsigned char* category_group_enabled,
const char* name,
const char* scope,
uint64_t id,
uint64_t bind_id,
PlatformThreadId thread_id,
const TimeTicks& timestamp,
TraceArguments* args,
unsigned int flags);
TraceEventHandle AddTraceEventWithThreadIdAndTimestamps(
char phase,
const unsigned char* category_group_enabled,
const char* name,
const char* scope,
uint64_t id,
uint64_t bind_id,
PlatformThreadId thread_id,
const TimeTicks& timestamp,
const ThreadTicks& thread_timestamp,
TraceArguments* args,
unsigned int flags);
// Adds a metadata event that will be written when the trace log is flushed.
void AddMetadataEvent(const unsigned char* category_group_enabled,
const char* name,
TraceArguments* args,
unsigned int flags);
void UpdateTraceEventDuration(const unsigned char* category_group_enabled,
const char* name,
TraceEventHandle handle);
void UpdateTraceEventDurationExplicit(
const unsigned char* category_group_enabled,
const char* name,
TraceEventHandle handle,
PlatformThreadId thread_id,
bool explicit_timestamps,
const TimeTicks& now,
const ThreadTicks& thread_now);
ProcessId process_id() const { return process_id_; }
std::unordered_map<int, std::string> process_labels() const {
AutoLock lock(lock_);
return process_labels_;
}
uint64_t MangleEventId(uint64_t id);
// Exposed for unittesting:
// Allows clearing up our singleton instance.
static void ResetForTesting();
// Allow tests to inspect TraceEvents.
TraceEvent* GetEventByHandle(TraceEventHandle handle);
void SetProcessID(ProcessId process_id);
// Process sort indices, if set, override the order of a process will appear
// relative to other processes in the trace viewer. Processes are sorted first
// on their sort index, ascending, then by their name, and then tid.
void SetProcessSortIndex(int sort_index);
// Helper function to set process_name in base::CurrentProcess.
void OnSetProcessName(const std::string& process_name);
// Processes can have labels in addition to their names. Use labels, for
// instance, to list out the web page titles that a process is handling.
void UpdateProcessLabel(int label_id, const std::string& current_label);
void RemoveProcessLabel(int label_id);
// Thread sort indices, if set, override the order of a thread will appear
// within its process in the trace viewer. Threads are sorted first on their
// sort index, ascending, then by their name, and then tid.
void SetThreadSortIndex(PlatformThreadId thread_id, int sort_index);
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// Allow setting an offset between the current TimeTicks time and the time
// that should be reported.
void SetTimeOffset(TimeDelta offset);
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
size_t GetObserverCountForTest() const;
// Call this method if the current thread may block the message loop to
// prevent the thread from using the thread-local buffer because the thread
// may not handle the flush request in time causing lost of unflushed events.
void SetCurrentThreadBlocksMessageLoop();
#if BUILDFLAG(IS_WIN)
// This function is called by the ETW exporting module whenever the ETW
// keyword (flags) changes. This keyword indicates which categories should be
// exported, so whenever it changes, we adjust accordingly.
void UpdateETWCategoryGroupEnabledFlags();
#endif
// Replaces |logged_events_| with a new TraceBuffer for testing.
void SetTraceBufferForTesting(std::unique_ptr<TraceBuffer> trace_buffer);
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
perfetto::DataSourceConfig GetCurrentTrackEventDataSourceConfig() const;
void InitializePerfettoIfNeeded();
void SetEnabledImpl(const TraceConfig& trace_config,
const perfetto::TraceConfig& perfetto_config);
// perfetto::TrackEventSessionObserver implementation.
void OnSetup(const perfetto::DataSourceBase::SetupArgs&) override;
void OnStart(const perfetto::DataSourceBase::StartArgs&) override;
void OnStop(const perfetto::DataSourceBase::StopArgs&) override;
#endif // BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// Called by the perfetto backend just after incremental state was cleared.
void OnIncrementalStateCleared();
private:
typedef unsigned int InternalTraceOptions;
FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture,
TraceBufferRingBufferGetReturnChunk);
FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture,
TraceBufferRingBufferHalfIteration);
FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture,
TraceBufferRingBufferFullIteration);
FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture, TraceBufferVectorReportFull);
FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture,
ConvertTraceConfigToInternalOptions);
FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture,
TraceRecordAsMuchAsPossibleMode);
FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture, ConfigTraceBufferLimit);
friend class base::NoDestructor<TraceLog>;
// MemoryDumpProvider implementation.
bool OnMemoryDump(const MemoryDumpArgs& args,
ProcessMemoryDump* pmd) override;
// Enable/disable each category group based on the current mode_,
// category_filter_ and event_filters_enabled_.
// Enable the category group in the recording mode if category_filter_ matches
// the category group, is not null.
void UpdateCategoryRegistry();
void UpdateCategoryState(TraceCategory* category);
InternalTraceOptions GetInternalOptionsFromTraceConfig(
const TraceConfig& config);
class OptionalAutoLock;
struct RegisteredAsyncObserver;
explicit TraceLog(int generation);
~TraceLog() override;
void AddMetadataEventsWhileLocked() EXCLUSIVE_LOCKS_REQUIRED(lock_);
template <typename T>
void AddMetadataEventWhileLocked(PlatformThreadId thread_id,
const char* metadata_name,
const char* arg_name,
const T& value)
EXCLUSIVE_LOCKS_REQUIRED(lock_);
InternalTraceOptions trace_options() const {
return trace_options_.load(std::memory_order_relaxed);
}
TraceBuffer* trace_buffer() const { return logged_events_.get(); }
TraceBuffer* CreateTraceBuffer();
std::string EventToConsoleMessage(char phase,
const TimeTicks& timestamp,
TraceEvent* trace_event);
TraceEvent* AddEventToThreadSharedChunkWhileLocked(TraceEventHandle* handle,
bool check_buffer_is_full)
EXCLUSIVE_LOCKS_REQUIRED(lock_);
void CheckIfBufferIsFullWhileLocked() EXCLUSIVE_LOCKS_REQUIRED(lock_);
void SetDisabledWhileLocked(uint8_t modes) EXCLUSIVE_LOCKS_REQUIRED(lock_);
TraceEvent* GetEventByHandleInternal(TraceEventHandle handle,
OptionalAutoLock* lock);
void FlushInternal(const OutputCallback& cb,
bool use_worker_thread,
bool discard_events);
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
tracing::PerfettoPlatform* GetOrCreatePerfettoPlatform();
void OnTraceData(const char* data, size_t size, bool has_more);
#endif // BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// |generation| is used in the following callbacks to check if the callback
// is called for the flush of the current |logged_events_|.
void FlushCurrentThread(int generation, bool discard_events);
// Usually it runs on a different thread.
static void ConvertTraceEventsToTraceFormat(
std::unique_ptr<TraceBuffer> logged_events,
const TraceLog::OutputCallback& flush_output_callback,
const ArgumentFilterPredicate& argument_filter_predicate);
void FinishFlush(int generation, bool discard_events);
void OnFlushTimeout(int generation, bool discard_events);
int generation() const {
return generation_.load(std::memory_order_relaxed);
}
bool CheckGeneration(int generation) const {
return generation == this->generation();
}
void UseNextTraceBuffer();
TimeTicks OffsetNow() const {
// This should be TRACE_TIME_TICKS_NOW but include order makes that hard.
return OffsetTimestamp(base::subtle::TimeTicksNowIgnoringOverride());
}
TimeTicks OffsetTimestamp(const TimeTicks& timestamp) const {
return timestamp - time_offset_;
}
// Internal representation of trace options since we store the currently used
// trace option as an AtomicWord.
static const InternalTraceOptions kInternalNone;
static const InternalTraceOptions kInternalRecordUntilFull;
static const InternalTraceOptions kInternalRecordContinuously;
static const InternalTraceOptions kInternalEchoToConsole;
static const InternalTraceOptions kInternalRecordAsMuchAsPossible;
static const InternalTraceOptions kInternalEnableArgumentFilter;
// This lock protects TraceLog member accesses (except for members protected
// by thread_info_lock_) from arbitrary threads.
mutable Lock lock_;
Lock thread_info_lock_;
bool enabled_{false};
int num_traces_recorded_{0};
std::unique_ptr<TraceBuffer> logged_events_;
std::vector<std::unique_ptr<TraceEvent>> metadata_events_;
// The lock protects observers access.
mutable Lock observers_lock_;
bool dispatching_to_observers_ = false;
std::vector<EnabledStateObserver*> enabled_state_observers_
GUARDED_BY(observers_lock_);
std::map<AsyncEnabledStateObserver*, RegisteredAsyncObserver> async_observers_
GUARDED_BY(observers_lock_);
// Manages ownership of the owned observers. The owned observers will also be
// added to |enabled_state_observers_|.
std::vector<std::unique_ptr<EnabledStateObserver>>
owned_enabled_state_observer_copy_ GUARDED_BY(observers_lock_);
std::vector<IncrementalStateObserver*> incremental_state_observers_
GUARDED_BY(observers_lock_);
std::unordered_map<int, std::string> process_labels_;
int process_sort_index_;
std::unordered_map<PlatformThreadId, int> thread_sort_indices_;
std::unordered_map<PlatformThreadId, std::string> thread_names_
GUARDED_BY(thread_info_lock_);
// The following two maps are used only when ECHO_TO_CONSOLE.
std::unordered_map<PlatformThreadId, base::stack<TimeTicks>>
thread_event_start_times_ GUARDED_BY(thread_info_lock_);
std::unordered_map<std::string, size_t> thread_colors_
GUARDED_BY(thread_info_lock_);
TimeTicks buffer_limit_reached_timestamp_;
// XORed with TraceID to make it unlikely to collide with other processes.
uint64_t process_id_hash_;
ProcessId process_id_;
TimeDelta time_offset_;
std::atomic<InternalTraceOptions> trace_options_;
TraceConfig trace_config_;
// Contains task runners for the threads that have had at least one event
// added into the local event buffer.
std::unordered_map<PlatformThreadId, scoped_refptr<SingleThreadTaskRunner>>
thread_task_runners_;
// For events which can't be added into the thread local buffer, e.g. events
// from threads without a message loop.
std::unique_ptr<TraceBufferChunk> thread_shared_chunk_;
size_t thread_shared_chunk_index_;
// Set when asynchronous Flush is in progress.
OutputCallback flush_output_callback_;
scoped_refptr<SequencedTaskRunner> flush_task_runner_;
ArgumentFilterPredicate argument_filter_predicate_;
MetadataFilterPredicate metadata_filter_predicate_;
bool record_host_app_package_name_{false};
std::atomic<int> generation_;
bool use_worker_thread_;
std::atomic<AddTraceEventOverrideFunction> add_trace_event_override_{nullptr};
std::atomic<OnFlushFunction> on_flush_override_{nullptr};
std::atomic<UpdateDurationFunction> update_duration_override_{nullptr};
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
std::unique_ptr<::base::tracing::PerfettoPlatform> perfetto_platform_;
std::unique_ptr<perfetto::TracingSession> tracing_session_;
perfetto::TraceConfig perfetto_config_;
perfetto::DataSourceConfig track_event_config_ GUARDED_BY(track_event_lock_);
bool track_event_enabled_ GUARDED_BY(track_event_lock_) = false;
mutable Lock track_event_lock_;
#if !BUILDFLAG(IS_NACL)
std::unique_ptr<perfetto::trace_processor::TraceProcessorStorage>
trace_processor_;
std::unique_ptr<JsonStringOutputWriter> json_output_writer_;
OutputCallback proto_output_callback_;
#endif // !BUILDFLAG(IS_NACL)
#endif // BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
#if BUILDFLAG(IS_ANDROID)
absl::optional<TraceConfig> atrace_startup_config_;
#endif
};
} // namespace trace_event
} // namespace base
#endif // BASE_TRACE_EVENT_TRACE_LOG_H_