// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Trace/Config.h" #if TRACE_PRIVATE_MINIMAL_ENABLED #include "Atomic.h" #include "EventNode.h" #include "HAL/Platform.h" // for PLATFORM_BREAK #include "LogScope.h" #include "Protocol.h" #include "Writer.inl" #include "AutoRTFM.h" namespace UE { namespace Trace { namespace Private { //////////////////////////////////////////////////////////////////////////////// extern TRACELOG_API uint64 GStartCycle; extern TRACELOG_API uint32 volatile GLogSerial; //////////////////////////////////////////////////////////////////////////////// UE_AUTORTFM_ALWAYS_OPEN inline void FLogScope::Commit() const { AtomicStoreRelease((uint8**) &(Buffer->Committed), Buffer->Cursor); } //////////////////////////////////////////////////////////////////////////////// UE_AUTORTFM_ALWAYS_OPEN inline void FLogScope::Commit(FWriteBuffer* __restrict LatestBuffer) const { if (LatestBuffer != Buffer) { AtomicStoreRelease((uint8**) &(LatestBuffer->Committed), LatestBuffer->Cursor); } Commit(); } //////////////////////////////////////////////////////////////////////////////// template UE_AUTORTFM_ALWAYS_OPEN inline auto FLogScope::EnterImpl(uint32 Uid, uint32 Size) { TLogScope<(Flags & FEventInfo::Flag_MaybeHasAux) != 0> Ret; if ((Flags & FEventInfo::Flag_NoSync) != 0) { Ret.EnterNoSync(Uid, Size); } else { Ret.Enter(Uid, Size); } return Ret; } //////////////////////////////////////////////////////////////////////////////// template UE_AUTORTFM_ALWAYS_OPEN inline void FLogScope::EnterPrelude(uint32 Size) { uint32 AllocSize = sizeof(HeaderType) + Size; Buffer = Writer_GetBuffer(); if (UNLIKELY(Buffer->Cursor + AllocSize > (uint8*)Buffer)) { Buffer = Writer_NextBuffer(); } #if !UE_BUILD_SHIPPING && !UE_BUILD_TEST if (AllocSize >= Buffer->Size) { // This situation is terminal. Someone's trying to trace an event that // is far too large. This should never happen as 'maximum_field_count * // largest_field_size' won't exceed Buffer.Size. PLATFORM_BREAK(); } #endif Ptr = Buffer->Cursor + sizeof(HeaderType); Buffer->Cursor += AllocSize; } //////////////////////////////////////////////////////////////////////////////// UE_AUTORTFM_ALWAYS_OPEN inline void FLogScope::Enter(uint32 Uid, uint32 Size) { EnterPrelude(Size); uint16 Uid16 = uint16(Uid) | int32(EKnownEventUids::Flag_TwoByteUid); uint32 Serial = uint32(AtomicAddRelaxed(&GLogSerial, 1u)); // Event header FEventHeaderSync memcpy(Ptr - 3, &Serial, sizeof(Serial)); /* FEventHeaderSync::SerialHigh,SerialLow */ memcpy(Ptr - 5, &Uid16, sizeof(Uid16)); /* FEventHeaderSync::Uid */ } //////////////////////////////////////////////////////////////////////////////// UE_AUTORTFM_ALWAYS_OPEN inline void FLogScope::EnterNoSync(uint32 Uid, uint32 Size) { EnterPrelude(Size); uint16 Uid16 = uint16(Uid) | int32(EKnownEventUids::Flag_TwoByteUid); // Event header FEventHeader auto* Header = (uint16*)(Ptr); memcpy(Header - 1, &Uid16, sizeof(Uid16)); /* FEventHeader::Uid */ } //////////////////////////////////////////////////////////////////////////////// template UE_AUTORTFM_ALWAYS_OPEN inline void TLogScope::operator += (const FLogScope&) const { if constexpr (bMaybeHasAux) { FWriteBuffer* LatestBuffer = Writer_GetBuffer(); LatestBuffer->Cursor[0] = uint8(EKnownEventUids::AuxDataTerminal << EKnownEventUids::_UidShift); LatestBuffer->Cursor++; Commit(LatestBuffer); } else { Commit(); } } //////////////////////////////////////////////////////////////////////////////// inline void FScopedLogScope::Deinit() { if (!bActive) { return; } if (AutoRTFM::IsClosed()) { // If we are operating closed (in a transaction) we have to forget about // our previously registered on-abort handler, because we're currently // about to close out the scope within the transaction. AutoRTFM::PopOnAbortHandler(this); AutoRTFM::Open([this] { this->Deinit(); }); return; } uint8 LeaveUid = uint8(EKnownEventUids::LeaveScope << EKnownEventUids::_UidShift); FWriteBuffer* Buffer = Writer_GetBuffer(); if (UNLIKELY(int32((uint8*)Buffer - Buffer->Cursor) < int32(sizeof(LeaveUid)))) { Buffer = Writer_NextBuffer(); } Buffer->Cursor[0] = LeaveUid; Buffer->Cursor += sizeof(LeaveUid); AtomicStoreRelease((uint8**) &(Buffer->Committed), Buffer->Cursor); } //////////////////////////////////////////////////////////////////////////////// inline FScopedLogScope::~FScopedLogScope() { Deinit(); } //////////////////////////////////////////////////////////////////////////////// inline void FScopedLogScope::SetActive() { if (bActive) { // We don't want to re-activate an already active scope! return; } if (AutoRTFM::IsClosed()) { // If we are operating closed (in a transaction) we could abort before // `this` hits its destructor. In that case we need to remember when // we abort to close out the scoped trace event. AutoRTFM::PushOnAbortHandler(this, [this] { this->Deinit(); }); AutoRTFM::Open([this] { this->SetActive(); }); return; } bActive = true; } //////////////////////////////////////////////////////////////////////////////// inline void FScopedStampedLogScope::Deinit() { if (!bActive) { return; } if (AutoRTFM::IsClosed()) { // If we are operating closed (in a transaction) we have to forget about // our previously registered on-abort handler, because we're currently // about to close out the scope within the transaction. AutoRTFM::PopOnAbortHandler(this); AutoRTFM::Open([this] { this->Deinit(); }); return; } uint64 Stamp = TimeGetTimestamp() - GStartCycle; FWriteBuffer* Buffer = Writer_GetBuffer(); if (UNLIKELY(int32((uint8*)Buffer - Buffer->Cursor) < int32(sizeof(Stamp)))) { Buffer = Writer_NextBuffer(); } Stamp <<= 8; Stamp += uint8(EKnownEventUids::LeaveScope_TB) << EKnownEventUids::_UidShift; memcpy((uint64*)(Buffer->Cursor), &Stamp, sizeof(Stamp)); Buffer->Cursor += sizeof(Stamp); AtomicStoreRelease((uint8**) &(Buffer->Committed), Buffer->Cursor); } //////////////////////////////////////////////////////////////////////////////// inline FScopedStampedLogScope::~FScopedStampedLogScope() { Deinit(); } //////////////////////////////////////////////////////////////////////////////// inline void FScopedStampedLogScope::SetActive() { if (bActive) { // We don't want to re-activate an already active scope! return; } if (AutoRTFM::IsClosed()) { // If we are operating closed (in a transaction) we could abort before // `this` hits its destructor. In that case we need to remember when // we abort to close out the scoped trace event. AutoRTFM::PushOnAbortHandler(this, [this] { this->Deinit(); }); AutoRTFM::Open([this] { this->SetActive(); }); return; } bActive = true; } //////////////////////////////////////////////////////////////////////////////// template UE_AUTORTFM_ALWAYS_OPEN FORCENOINLINE auto FLogScope::Enter() { uint32 Size = EventType::GetSize(); uint32 Uid = EventType::GetUid(); return EnterImpl(Uid, Size); } //////////////////////////////////////////////////////////////////////////////// template UE_AUTORTFM_ALWAYS_OPEN FORCENOINLINE auto FLogScope::ScopedEnter() { uint8 EnterUid = uint8(EKnownEventUids::EnterScope << EKnownEventUids::_UidShift); FWriteBuffer* Buffer = Writer_GetBuffer(); constexpr int32 EventHeaderSize = (EventType::EventFlags & FEventInfo::Flag_NoSync) != 0 ? sizeof(FEventHeader) : sizeof(FEventHeaderSync); constexpr int32 RequiredSize = sizeof(EnterUid) + EventHeaderSize + EventType::GetSize(); if (UNLIKELY(int32((uint8*)Buffer - Buffer->Cursor) < RequiredSize)) { Buffer = Writer_NextBuffer(); } Buffer->Cursor[0] = EnterUid; Buffer->Cursor += sizeof(EnterUid); AtomicStoreRelease((uint8**) &(Buffer->Committed), Buffer->Cursor); return Enter(); } //////////////////////////////////////////////////////////////////////////////// template UE_AUTORTFM_ALWAYS_OPEN FORCENOINLINE auto FLogScope::ScopedStampedEnter() { uint64 Stamp; FWriteBuffer* Buffer = Writer_GetBuffer(); constexpr int32 EventHeaderSize = (EventType::EventFlags & FEventInfo::Flag_NoSync) != 0 ? sizeof(FEventHeader) : sizeof(FEventHeaderSync); constexpr int32 RequiredSize = sizeof(Stamp) + EventHeaderSize + EventType::GetSize(); if (UNLIKELY(int32((uint8*)Buffer - Buffer->Cursor) < RequiredSize)) { Buffer = Writer_NextBuffer(); } Stamp = TimeGetTimestamp() - GStartCycle; Stamp <<= 8; Stamp += uint8(EKnownEventUids::EnterScope_TB) << EKnownEventUids::_UidShift; memcpy((uint64*)(Buffer->Cursor), &Stamp, sizeof(Stamp)); Buffer->Cursor += sizeof(Stamp); AtomicStoreRelease((uint8**) &(Buffer->Committed), Buffer->Cursor); return Enter(); } //////////////////////////////////////////////////////////////////////////////// template struct FLogScope::FFieldSet { UE_AUTORTFM_ALWAYS_OPEN static void Impl(FLogScope* Scope, const Type& Value) { uint8* Dest = (uint8*)(Scope->Ptr) + FieldMeta::Offset; ::memcpy(Dest, &Value, sizeof(Type)); } }; //////////////////////////////////////////////////////////////////////////////// template struct FLogScope::FFieldSet { UE_AUTORTFM_ALWAYS_OPEN static void Impl(FLogScope*, Type const* Data, int32 Num) { static const uint32 Index = FieldMeta::Index & int32(EIndexPack::NumFieldsMask); int32 Size = (Num * sizeof(Type)) & (FAuxHeader::SizeLimit - 1) & ~(sizeof(Type) - 1); Field_WriteAuxData(Index, (const uint8*)Data, Size); } }; #if STATICALLY_SIZED_ARRAY_FIELDS_SUPPORT //////////////////////////////////////////////////////////////////////////////// template struct FLogScope::FFieldSet { static void Impl(FLogScope*, Type const* Data, int32 Num=-1) = delete; }; #endif // STATICALLY_SIZED_ARRAY_FIELDS_SUPPORT //////////////////////////////////////////////////////////////////////////////// template struct FLogScope::FFieldSet { UE_AUTORTFM_ALWAYS_OPEN static void Impl(FLogScope*, const ANSICHAR* String, int32 Length=-1) { if (Length < 0) { Length = int32(strlen(String)); } static const uint32 Index = FieldMeta::Index & int32(EIndexPack::NumFieldsMask); Field_WriteStringAnsi(Index, String, Length); } UE_AUTORTFM_ALWAYS_OPEN static void Impl(FLogScope*, const WIDECHAR* String, int32 Length=-1) { if (Length < 0) { Length = 0; for (const WIDECHAR* c = String; *c; ++c, ++Length); } static const uint32 Index = FieldMeta::Index & int32(EIndexPack::NumFieldsMask); Field_WriteStringAnsi(Index, String, Length); } }; //////////////////////////////////////////////////////////////////////////////// template struct FLogScope::FFieldSet { UE_AUTORTFM_ALWAYS_OPEN static void Impl(FLogScope*, const WIDECHAR* String, int32 Length=-1) { if (Length < 0) { Length = 0; for (const WIDECHAR* c = String; *c; ++c, ++Length); } static const uint32 Index = FieldMeta::Index & int32(EIndexPack::NumFieldsMask); Field_WriteStringWide(Index, String, Length); } }; //////////////////////////////////////////////////////////////////////////////// template struct FLogScope::FFieldSet> { UE_AUTORTFM_ALWAYS_OPEN static void Impl(FLogScope* Scope, const TEventRef& Reference) { FFieldSet::Impl(Scope, Reference.Id); } }; } // namespace Private } // namespace Trace } // namespace UE #endif // TRACE_PRIVATE_MINIMAL_ENABLED