334 lines
8.2 KiB
C++
334 lines
8.2 KiB
C++
// 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 <ctype.h>
|
|
|
|
#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=<channels> 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
|