blob: 2fbda5642dffd7800706d641f6932a7018894388 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/trace_event/typed_macros_internal.h"
#include "base/notreached.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/typed_macros.h"
namespace {
base::ThreadTicks ThreadNow() {
return base::ThreadTicks::IsSupported()
? base::subtle::ThreadTicksNowIgnoringOverride()
: base::ThreadTicks();
}
base::trace_event::PrepareTrackEventFunction g_typed_event_callback = nullptr;
base::trace_event::PrepareTracePacketFunction g_trace_packet_callback = nullptr;
base::trace_event::EmitEmptyTracePacketFunction g_empty_packet_callback =
nullptr;
std::pair<char /*phase*/, unsigned long long /*id*/>
GetPhaseAndIdForTraceLog(bool explicit_track, uint64_t track_uuid, char phase) {
if (!explicit_track)
return std::make_pair(phase, trace_event_internal::kNoId);
switch (phase) {
case TRACE_EVENT_PHASE_BEGIN:
phase = TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN;
break;
case TRACE_EVENT_PHASE_END:
phase = TRACE_EVENT_PHASE_NESTABLE_ASYNC_END;
break;
case TRACE_EVENT_PHASE_INSTANT:
phase = TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT;
break;
default:
NOTREACHED();
break;
}
return std::make_pair(phase, static_cast<unsigned long long>(track_uuid));
}
} // namespace
namespace trace_event_internal {
const perfetto::Track kDefaultTrack{};
} // namespace trace_event_internal
namespace base {
namespace trace_event {
void EnableTypedTraceEvents(
PrepareTrackEventFunction typed_event_callback,
PrepareTracePacketFunction trace_packet_callback,
EmitEmptyTracePacketFunction empty_packet_callback) {
g_typed_event_callback = typed_event_callback;
g_trace_packet_callback = trace_packet_callback;
g_empty_packet_callback = empty_packet_callback;
}
void ResetTypedTraceEventsForTesting() {
g_typed_event_callback = nullptr;
g_trace_packet_callback = nullptr;
g_empty_packet_callback = nullptr;
}
TrackEventHandle::TrackEventHandle(TrackEvent* event,
IncrementalState* incremental_state,
CompletionListener* listener,
bool filter_debug_annotations)
: event_(event),
incremental_state_(incremental_state),
listener_(listener),
filter_debug_annotations_(filter_debug_annotations) {}
TrackEventHandle::TrackEventHandle()
: TrackEventHandle(nullptr, nullptr, nullptr, false) {}
TrackEventHandle::~TrackEventHandle() {
if (listener_)
listener_->OnTrackEventCompleted();
}
TrackEventHandle::CompletionListener::~CompletionListener() = default;
TracePacketHandle::TracePacketHandle(PerfettoPacketHandle packet,
CompletionListener* listener)
: packet_(std::move(packet)), listener_(listener) {}
TracePacketHandle::TracePacketHandle()
: TracePacketHandle(PerfettoPacketHandle(), nullptr) {}
TracePacketHandle::~TracePacketHandle() {
if (listener_)
listener_->OnTracePacketCompleted();
}
TracePacketHandle::TracePacketHandle(TracePacketHandle&& handle) noexcept {
*this = std::move(handle);
}
TracePacketHandle& TracePacketHandle::operator=(TracePacketHandle&& handle) {
this->packet_ = std::move(handle.packet_);
this->listener_ = handle.listener_;
return *this;
}
TracePacketHandle::CompletionListener::~CompletionListener() = default;
} // namespace trace_event
} // namespace base
namespace trace_event_internal {
base::trace_event::TrackEventHandle CreateTrackEvent(
char phase,
const unsigned char* category_group_enabled,
perfetto::StaticString name,
base::TimeTicks ts,
uint64_t track_uuid,
bool explicit_track) {
DCHECK(phase == TRACE_EVENT_PHASE_BEGIN || phase == TRACE_EVENT_PHASE_END ||
phase == TRACE_EVENT_PHASE_INSTANT);
DCHECK(category_group_enabled);
if (!g_typed_event_callback)
return base::trace_event::TrackEventHandle();
const auto thread_id = base::PlatformThread::CurrentId();
auto* trace_log = base::trace_event::TraceLog::GetInstance();
DCHECK(trace_log);
// Provide events emitted onto different tracks as NESTABLE_ASYNC events to
// TraceLog, so that e.g. ETW export is aware of them not being a sync event
// for the current thread.
auto phase_and_id_for_trace_log =
GetPhaseAndIdForTraceLog(explicit_track, track_uuid, phase);
if (!trace_log->ShouldAddAfterUpdatingState(
phase_and_id_for_trace_log.first, category_group_enabled, name.value,
phase_and_id_for_trace_log.second, thread_id, nullptr)) {
return base::trace_event::TrackEventHandle();
}
unsigned int flags = TRACE_EVENT_FLAG_NONE;
if (ts.is_null()) {
ts = TRACE_TIME_TICKS_NOW();
} else {
flags |= TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP;
}
if (phase == TRACE_EVENT_PHASE_INSTANT && !explicit_track) {
flags |= TRACE_EVENT_SCOPE_THREAD;
}
// Only emit thread time / instruction count for events on the default track
// without explicit timestamp.
base::ThreadTicks thread_now;
if ((flags & TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP) == 0 && !explicit_track) {
thread_now = ThreadNow();
}
base::trace_event::TraceEvent event(
thread_id, ts, thread_now, phase, category_group_enabled, name.value,
trace_event_internal::kGlobalScope, trace_event_internal::kNoId,
trace_event_internal::kNoId, nullptr, flags);
return g_typed_event_callback(&event);
}
base::trace_event::TracePacketHandle CreateTracePacket() {
// We only call CreateTracePacket() if the embedder installed a valid
// g_typed_event_callback, and in that case we also expect a valid
// g_trace_packet_callback.
DCHECK(g_trace_packet_callback);
return g_trace_packet_callback();
}
void AddEmptyPacket() {
if (g_empty_packet_callback)
g_empty_packet_callback();
}
bool ShouldEmitTrackDescriptor(
uint64_t track_uuid,
base::trace_event::TrackEventHandle::IncrementalState* incr_state) {
auto it_and_inserted = incr_state->seen_tracks.insert(track_uuid);
return it_and_inserted.second;
}
} // namespace trace_event_internal