// Copyright Epic Games, Inc. All Rights Reserved. #include "Trace/Detail/Channel.h" #include "Math/UnrealMathUtility.h" #include "Trace/Trace.h" #include "Trace/Trace.inl" #include "Trace/Detail/Atomic.h" #include "Trace/Detail/Channel.h" #include #if TRACE_PRIVATE_MINIMAL_ENABLED namespace UE { namespace Trace { //////////////////////////////////////////////////////////////////////////////// struct FTraceChannel : public FChannel { bool IsEnabled() const { return true; } explicit operator bool() const { return true; } }; static FTraceChannel TraceLogChannelDetail; FChannel& TraceLogChannel = TraceLogChannelDetail; /////////////////////////////////////////////////////////////////////////////// UE_TRACE_MINIMAL_EVENT_BEGIN(Trace, ChannelAnnounce, NoSync|Important) UE_TRACE_MINIMAL_EVENT_FIELD(uint32, Id) UE_TRACE_MINIMAL_EVENT_FIELD(bool, IsEnabled) UE_TRACE_MINIMAL_EVENT_FIELD(bool, ReadOnly) UE_TRACE_MINIMAL_EVENT_FIELD(AnsiString, Name) UE_TRACE_MINIMAL_EVENT_END() UE_TRACE_MINIMAL_EVENT_BEGIN(Trace, ChannelToggle, NoSync|Important) UE_TRACE_MINIMAL_EVENT_FIELD(uint32, Id) UE_TRACE_MINIMAL_EVENT_FIELD(bool, IsEnabled) UE_TRACE_MINIMAL_EVENT_END() /////////////////////////////////////////////////////////////////////////////// static FChannel* volatile GHeadChannel; // = nullptr; static FChannel* volatile GNewChannelList; // = nullptr; static bool GChannelsInitialized; //////////////////////////////////////////////////////////////////////////////// static uint32 GetChannelHash(const ANSICHAR* Input, int32 Length) { // Make channel names tolerant to ending 's' (or 'S'). // Example: "Log", "log", "logs", "LOGS" and "LogsChannel" will all match as being the same channel. if (Length > 0 && (Input[Length - 1] | 0x20) == 's') { --Length; } uint32 Result = 0x811c9dc5; for (; Length; ++Input, --Length) { Result ^= *Input | 0x20; // a cheap ASCII-only case insensitivity. Result *= 0x01000193; } return Result; } /////////////////////////////////////////////////////////////////////////////// static uint32 GetChannelNameLength(const ANSICHAR* ChannelName) { // Strip "Channel" suffix if it exists size_t Len = uint32(strlen(ChannelName)); if (Len > 7) { if (strcmp(ChannelName + Len - 7, "Channel") == 0) { Len -= 7; } } return uint32(Len); } /////////////////////////////////////////////////////////////////////////////// FChannel::Iter::~Iter() { if (Inner[2] == nullptr) { return; } using namespace Private; for (auto* Node = (FChannel*)Inner[2];; PlatformYield()) { Node->Next = AtomicLoadRelaxed(&GHeadChannel); if (AtomicCompareExchangeRelaxed(&GHeadChannel, (FChannel*)Inner[1], Node->Next)) { break; } } } /////////////////////////////////////////////////////////////////////////////// const FChannel* FChannel::Iter::GetNext() { auto* Ret = (const FChannel*)Inner[0]; if (Ret != nullptr) { Inner[0] = Ret->Next; if (Inner[0] != nullptr) { Inner[2] = Inner[0]; } } return Ret; } /////////////////////////////////////////////////////////////////////////////// FChannel::Iter FChannel::ReadNew() { using namespace Private; FChannel* List = AtomicLoadRelaxed(&GNewChannelList); if (List == nullptr) { return {}; } while (!AtomicCompareExchangeAcquire(&GNewChannelList, (FChannel*)nullptr, List)) { PlatformYield(); List = AtomicLoadRelaxed(&GNewChannelList); } return { { List, List, List } }; } /////////////////////////////////////////////////////////////////////////////// void FChannel::Setup(const ANSICHAR* InChannelName, const InitArgs& InArgs) { using namespace Private; Name.Ptr = InChannelName; Name.Len = GetChannelNameLength(Name.Ptr); Name.Hash = GetChannelHash(Name.Ptr, Name.Len); Args = InArgs; // Append channel to the linked list of new channels. for (;; PlatformYield()) { FChannel* HeadChannel = AtomicLoadRelaxed(&GNewChannelList); Next = HeadChannel; if (AtomicCompareExchangeRelease(&GNewChannelList, this, Next)) { break; } } // If channel is initialized after all channels are disabled (post static init) // this channel needs to be disabled too. if (GChannelsInitialized) { Enabled = -1; } } /////////////////////////////////////////////////////////////////////////////// void FChannel::Announce() const { UE_TRACE_MINIMAL_LOG(Trace, ChannelAnnounce, TraceLogChannel, Name.Len * sizeof(ANSICHAR)) << ChannelAnnounce.Id(Name.Hash) << ChannelAnnounce.IsEnabled(IsEnabled()) << ChannelAnnounce.ReadOnly(Args.bReadOnly) << ChannelAnnounce.Name(Name.Ptr, Name.Len); } /////////////////////////////////////////////////////////////////////////////// void FChannel::Initialize() { // During static initialization, all channels are created as enabled (zero), // and act like so from the process start until this method is called (i.e. when Trace is initialized). // Now we can disable all channels. // Channels specified on the command line (using -trace= argument) // will be further re-enabled after this call. ToggleAll(false); GChannelsInitialized = true; } /////////////////////////////////////////////////////////////////////////////// void FChannel::ToggleAll(bool bEnabled) { using namespace Private; FChannel* ChannelLists[] = { AtomicLoadAcquire(&GNewChannelList), AtomicLoadAcquire(&GHeadChannel), }; for (FChannel* Channel : ChannelLists) { for (; Channel != nullptr; Channel = (FChannel*)(Channel->Next)) { Channel->Toggle(bEnabled); } } } /////////////////////////////////////////////////////////////////////////////// void FChannel::PanicDisableAll() { using namespace Private; FChannel* ChannelLists[] = { AtomicLoadAcquire(&GNewChannelList), AtomicLoadAcquire(&GHeadChannel), }; for (FChannel* Channel : ChannelLists) { for (; Channel != nullptr; Channel = (FChannel*)(Channel->Next)) { AtomicStoreRelaxed(&Channel->Enabled, -1); } } } /////////////////////////////////////////////////////////////////////////////// FChannel* FChannel::FindChannel(const ANSICHAR* ChannelName) { using namespace Private; const uint32 ChannelNameLen = GetChannelNameLength(ChannelName); const uint32 ChannelNameHash = GetChannelHash(ChannelName, ChannelNameLen); FChannel* ChannelLists[] = { AtomicLoadAcquire(&GNewChannelList), AtomicLoadAcquire(&GHeadChannel), }; for (FChannel* Channel : ChannelLists) { for (; Channel != nullptr; Channel = Channel->Next) { if (Channel->Name.Hash == ChannelNameHash) { return Channel; } } } return nullptr; } /////////////////////////////////////////////////////////////////////////////// FChannel* FChannel::FindChannel(FChannelId ChannelId) { using namespace Private; FChannel* ChannelLists[] = { AtomicLoadAcquire(&GNewChannelList), AtomicLoadAcquire(&GHeadChannel), }; for (FChannel* Channel : ChannelLists) { for (; Channel != nullptr; Channel = Channel->Next) { if (Channel->Name.Hash == ChannelId) { return Channel; } } } return nullptr; } /////////////////////////////////////////////////////////////////////////////// void FChannel::EnumerateChannels(ChannelIterCallback Func, void* User) { using namespace Private; FChannel* ChannelLists[] = { AtomicLoadAcquire(&GNewChannelList), AtomicLoadAcquire(&GHeadChannel), }; FChannelInfo Info; for (FChannel* Channel : ChannelLists) { for (; Channel != nullptr; Channel = Channel->Next) { Info.Name = Channel->Name.Ptr; Info.Desc = Channel->Args.Desc; Info.bIsEnabled = Channel->IsEnabled(); Info.bIsReadOnly = Channel->Args.bReadOnly; Info.Id = Channel->Name.Hash; bool Result = Func(Info, User); if (!Result) { return; } } } } /////////////////////////////////////////////////////////////////////////////// bool FChannel::Toggle(bool bEnabled) { using namespace Private; AtomicStoreRelaxed(&Enabled, bEnabled ? 1 : -1); UE_TRACE_MINIMAL_LOG(Trace, ChannelToggle, TraceLogChannel) << ChannelToggle.Id(Name.Hash) << ChannelToggle.IsEnabled(IsEnabled()); return IsEnabled(); } /////////////////////////////////////////////////////////////////////////////// bool FChannel::Toggle(const ANSICHAR* ChannelName, bool bEnabled) { if (FChannel* Channel = FChannel::FindChannel(ChannelName)) { return Channel->Toggle(bEnabled); } return false; } } // namespace Trace } // namespace UE #endif // TRACE_PRIVATE_MINIMAL_ENABLED