| // 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 |