aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Ridgers <[email protected]>2026-02-27 11:47:00 +0100
committerGitHub Enterprise <[email protected]>2026-02-27 11:47:00 +0100
commit4d5caf7d011bf73c7b90ff1d8c1cfdad817fa2f5 (patch)
treed9a86507583d79f852b47ab292e991dcf1399a09
parentadding HttpClient tests (#785) (diff)
downloadzen-4d5caf7d011bf73c7b90ff1d8c1cfdad817fa2f5.tar.xz
zen-4d5caf7d011bf73c7b90ff1d8c1cfdad817fa2f5.zip
Ported "lane trace" feature from UE (by way of IAX) (#771)
* Ported "lane trace" feature from UE (by way of IAX)
-rw-r--r--CHANGELOG.md1
-rw-r--r--src/zencore/include/zencore/trace.h1
-rw-r--r--thirdparty/trace/lane_trace.h146
-rw-r--r--thirdparty/trace/lane_trace.inl646
-rw-r--r--thirdparty/trace/trace.h22
5 files changed, 805 insertions, 11 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e555dd86f..873e88761 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
- Feature: Added 404 page to dashboard, to make it easier to find your way back to a valid URL
- Improvement: `zen oplog-import` now uses partial block requests to reduce download size
- Improvement: Use latency to Cloud Storage host and Zen Cache host when calculating partial block requests
+- Improvement: IAX's lane tracing
- Bugfix: `--plain-progress` style progress bar should now show elapsed time correctly
- Bugfix: Time spent indexing local and remote state during `zen builds download` now show the correct time
diff --git a/src/zencore/include/zencore/trace.h b/src/zencore/include/zencore/trace.h
index 99a565151..d17e018ea 100644
--- a/src/zencore/include/zencore/trace.h
+++ b/src/zencore/include/zencore/trace.h
@@ -13,6 +13,7 @@ ZEN_THIRD_PARTY_INCLUDES_START
# define TRACE_IMPLEMENT 0
#endif
#include <trace.h>
+#include <lane_trace.h>
#undef TRACE_IMPLEMENT
ZEN_THIRD_PARTY_INCLUDES_END
diff --git a/thirdparty/trace/lane_trace.h b/thirdparty/trace/lane_trace.h
new file mode 100644
index 000000000..170526eb2
--- /dev/null
+++ b/thirdparty/trace/lane_trace.h
@@ -0,0 +1,146 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#pragma once
+
+#if __has_include(<Containers/StringView.h>)
+# include <Trace/Trace.h>
+# include <Containers/StringView.h>
+# include <ProfilingDebugging/CpuProfilerTrace.h>
+# include <Templates/UnrealTemplate.h>
+#else
+# define UE_LANETRACE_ENABLED 2
+# define IOSTOREHTTPCLIENT_API
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+#if !defined(UE_LANETRACE_ENABLED)
+# define UE_LANETRACE_ENABLED CPUPROFILERTRACE_ENABLED && !UE_BUILD_SHIPPING
+#endif
+
+#if UE_LANETRACE_ENABLED
+# define LANETRACE_OFF_IMPL(...)
+# define UE_API IOSTOREHTTPCLIENT_API
+#else
+# define LANETRACE_OFF_IMPL(...) { return __VA_ARGS__ ; }
+# define UE_API inline
+#endif
+
+
+////////////////////////////////////////////////////////////////////////////////
+#if UE_LANETRACE_ENABLED == 2
+namespace trace {
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+struct FLaneTraceSpec
+{
+ FAnsiStringView Name;
+ FAnsiStringView Group = "Lanes";
+ const void* Channel;
+ int32 Weight = 100;
+};
+
+class FLaneTrace;
+UE_API FLaneTrace* LaneTrace_New(const FLaneTraceSpec& Spec) LANETRACE_OFF_IMPL(nullptr);
+UE_API void LaneTrace_Delete(FLaneTrace* Lane) LANETRACE_OFF_IMPL();
+UE_API uint32 LaneTrace_NewScope(const FAnsiStringView& Name) LANETRACE_OFF_IMPL(1);
+UE_API void LaneTrace_Enter(FLaneTrace* Lane, uint32 ScopeId) LANETRACE_OFF_IMPL();
+UE_API void LaneTrace_Change(FLaneTrace* Lane, uint32 ScopeId) LANETRACE_OFF_IMPL();
+UE_API void LaneTrace_Leave(FLaneTrace* Lane) LANETRACE_OFF_IMPL();
+UE_API void LaneTrace_LeaveAll(FLaneTrace* Lane) LANETRACE_OFF_IMPL();
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+struct FLanePostcode
+{
+ FLanePostcode(const void* In) : Value(UPTRINT(In)) {}
+ FLanePostcode(UPTRINT In) : Value(In) {}
+ UPTRINT Value;
+};
+
+class FLaneEstate;
+UE_API FLaneEstate* LaneEstate_New(const FLaneTraceSpec& Spec) LANETRACE_OFF_IMPL(nullptr);
+UE_API void LaneEstate_Delete(FLaneEstate* Estate) LANETRACE_OFF_IMPL();
+UE_API FLaneTrace* LaneEstate_Build(FLaneEstate* Estate, FLanePostcode Postcode) LANETRACE_OFF_IMPL(nullptr);
+UE_API FLaneTrace* LaneEstate_Lookup(FLaneEstate* Estate, FLanePostcode Postcode) LANETRACE_OFF_IMPL(nullptr);
+UE_API void LaneEstate_Demolish(FLaneEstate* Estate, FLanePostcode Postcode)LANETRACE_OFF_IMPL();
+
+#undef LANETRACE_OFF_IMPL
+#undef UE_API
+
+
+
+#if UE_LANETRACE_ENABLED
+
+////////////////////////////////////////////////////////////////////////////////
+class FLaneTraceScope
+{
+public:
+ FLaneTraceScope(FLaneTrace* InLane, uint32 Scope);
+ FLaneTraceScope() = default;
+ ~FLaneTraceScope();
+ FLaneTraceScope(FLaneTraceScope&& Rhs);
+ FLaneTraceScope& operator = (FLaneTraceScope&& Rhs);
+ void Change(uint32 Scope) const;
+
+private:
+ FLaneTraceScope(const FLaneTraceScope&) = delete;
+ FLaneTraceScope& operator = (const FLaneTraceScope&) = delete;
+ FLaneTrace* Lane = nullptr;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+inline FLaneTraceScope::FLaneTraceScope(FLaneTrace* InLane, uint32 Scope)
+: Lane(InLane)
+{
+ LaneTrace_Enter(Lane, Scope);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+inline FLaneTraceScope::~FLaneTraceScope()
+{
+ if (Lane)
+ {
+ LaneTrace_Leave(Lane);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+inline FLaneTraceScope::FLaneTraceScope(FLaneTraceScope&& Rhs)
+{
+ Swap(Rhs.Lane, Lane);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+inline FLaneTraceScope& FLaneTraceScope::operator = (FLaneTraceScope&& Rhs)
+{
+ Swap(Rhs.Lane, Lane);
+ return *this;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+inline void FLaneTraceScope::Change(uint32 Scope) const
+{
+ LaneTrace_Change(Lane, Scope);
+}
+
+#else // UE_LANETRACE_ENABLED
+
+class FLaneTraceScope
+{
+public:
+ FLaneTraceScope(...) {}
+ void Change(uint32) const {}
+};
+
+#endif // UE_LANETRACE_ENABLED
+
+////////////////////////////////////////////////////////////////////////////////
+#if UE_LANETRACE_ENABLED == 2
+} // namespace trace
+#endif
+
+#if defined(TRACE_IMPLEMENT) && TRACE_IMPLEMENT
+# include "lane_trace.inl"
+#endif
diff --git a/thirdparty/trace/lane_trace.inl b/thirdparty/trace/lane_trace.inl
new file mode 100644
index 000000000..abfaab027
--- /dev/null
+++ b/thirdparty/trace/lane_trace.inl
@@ -0,0 +1,646 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#if __has_include(<IO/Http/LaneTrace.h>)
+# include <IO/Http/LaneTrace.h>
+# if UE_LANETRACE_ENABLED
+# include <HAL/PlatformTime.h>
+# include <HAL/UnrealMemory.h>
+# include <Trace/Trace.inl>
+# include <Misc/ScopeLock.h>
+# endif
+#endif
+
+#if UE_LANETRACE_ENABLED
+
+////////////////////////////////////////////////////////////////////////////////
+#if UE_LANETRACE_ENABLED == 2
+
+#include <mutex>
+
+#define check(e) do { if ((e) == false) *(int volatile*)0 = 0; } while (false)
+
+struct FCriticalSection
+{
+ void Lock() { Inner.lock(); }
+ void Unlock() { Inner.unlock(); }
+ std::mutex Inner;
+};
+
+struct FScopeLock
+{
+ FScopeLock(FCriticalSection* Lock) : Inner(Lock->Inner) {}
+ std::unique_lock<std::mutex> Inner;
+};
+
+namespace trace {
+#endif
+
+// For this to work Insights would need to know it there may be a ThreadId field
+#define LANETRACE_USE_V2_EVENTS 0
+
+namespace LaneTraceDetail
+{
+
+////////////////////////////////////////////////////////////////////////////////
+UE_TRACE_EVENT_BEGIN($Trace, ThreadInfo, NoSync|Important)
+ UE_TRACE_EVENT_FIELD(uint32, ThreadId)
+ UE_TRACE_EVENT_FIELD(int32, SortHint)
+ UE_TRACE_EVENT_FIELD(UE::Trace::AnsiString, Name)
+UE_TRACE_EVENT_END()
+
+UE_TRACE_EVENT_BEGIN(CpuProfiler, EventBatch)
+ UE_TRACE_EVENT_FIELD(uint8[], Data)
+ UE_TRACE_EVENT_FIELD(uint16, ThreadId)
+UE_TRACE_EVENT_END()
+
+#if LANETRACE_USE_V2_EVENTS
+UE_TRACE_EVENT_BEGIN(CpuProfiler, EventBatchV2)
+ UE_TRACE_EVENT_FIELD(uint8[], Data)
+ UE_TRACE_EVENT_FIELD(uint16, ThreadId)
+UE_TRACE_EVENT_END()
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+static int32 Encode32_7bit(int32 Value, void* __restrict Out)
+{
+ // Calculate the number of bytes
+ int32 Length = 1;
+
+ Length += (Value >= (1 << 7));
+ Length += (Value >= (1 << 14));
+ Length += (Value >= (1 << 21));
+
+ // Add a gap every eigth bit for the continuations
+ int32 Ret = Value;
+ Ret = (Ret & 0x0000'3fff) | ((Ret & 0x0fff'c000) << 2);
+ Ret = (Ret & 0x007f'007f) | ((Ret & 0x3f80'3f80) << 1);
+
+ // Set the bits indicating another byte follows
+ int32 Continuations = 0x0080'8080;
+ Continuations >>= (sizeof(Value) - Length) * 8;
+ Ret |= Continuations;
+
+ ::memcpy(Out, &Ret, sizeof(Value));
+
+ return Length;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+static int32 Encode64_7bit(int64 Value, void* __restrict Out)
+{
+ // Calculate the output length
+ uint32 Length = 1;
+ Length += (Value >= (1ll << 7));
+ Length += (Value >= (1ll << 14));
+ Length += (Value >= (1ll << 21));
+ Length += (Value >= (1ll << 28));
+ Length += (Value >= (1ll << 35));
+ Length += (Value >= (1ll << 42));
+ Length += (Value >= (1ll << 49));
+
+ // Add a gap every eigth bit for the continuations
+ int64 Ret = Value;
+ Ret = (Ret & 0x0000'0000'0fff'ffffull) | ((Ret & 0x00ff'ffff'f000'0000ull) << 4);
+ Ret = (Ret & 0x0000'3fff'0000'3fffull) | ((Ret & 0x0fff'c000'0fff'c000ull) << 2);
+ Ret = (Ret & 0x007f'007f'007f'007full) | ((Ret & 0x3f80'3f80'3f80'3f80ull) << 1);
+
+ // Set the bits indicating another byte follows
+ int64 Continuations = 0x0080'8080'8080'8080ull;
+ Continuations >>= (sizeof(Value) - Length) * 8;
+ Ret |= Continuations;
+
+ ::memcpy(Out, &Ret, sizeof(Value));
+
+ return Length;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+static uint64 TimeGetTimestamp()
+{
+#if UE_LANETRACE_ENABLED != 2
+ return FPlatformTime::Cycles64();
+#else
+ return trace::detail::TimeGetTimestamp();
+#endif
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+class FScopeBuffer
+{
+public:
+ FScopeBuffer(UE::Trace::FChannel& InChannel);
+ void SetThreadId(uint32 Value);
+ bool IsInScope() const;
+ uint32 GetDepth() const;
+ void Flush(bool Force=false);
+ void Enter(uint64 Timestamp, uint32 ScopeId);
+ void Leave(uint64 Timestamp);
+
+private:
+ enum
+ {
+ BufferSize = 128,
+ Overflow = 24,
+ EnterLsb = 1,
+ LeaveLsb = 0,
+ TraceEventBatchVer = 1 + LANETRACE_USE_V2_EVENTS,
+ };
+ uint64 LastTimestamp = 0;
+ uint64 PrevTimestamp = 0;
+ uint8* Cursor = Buffer;
+ UE::Trace::FChannel& Channel;
+ uint32 ThreadIdOverride = 0;
+ uint16 Depth = 0;
+ uint8 Buffer[BufferSize];
+};
+
+////////////////////////////////////////////////////////////////////////////////
+FScopeBuffer::FScopeBuffer(UE::Trace::FChannel& InChannel)
+: Channel(InChannel)
+{
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void FScopeBuffer::SetThreadId(uint32 Value)
+{
+ ThreadIdOverride = Value;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+bool FScopeBuffer::IsInScope() const
+{
+ return GetDepth() > 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+uint32 FScopeBuffer::GetDepth() const
+{
+ return Depth;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void FScopeBuffer::Flush(bool Force)
+{
+ if (Cursor == Buffer)
+ {
+ return;
+ }
+
+ if (Depth > 0 && !Force && (Cursor <= (Buffer + BufferSize - Overflow)))
+ {
+ return;
+ }
+
+ if (TraceEventBatchVer == 1)
+ {
+ UE_TRACE_LOG(CpuProfiler, EventBatch, Channel)
+ << EventBatch.ThreadId(uint16(ThreadIdOverride))
+ << EventBatch.Data(Buffer, uint32(ptrdiff_t(Cursor - Buffer)));
+ }
+#if LANETRACE_USE_V2_EVENTS
+ else
+ {
+ UE_TRACE_LOG(CpuProfiler, EventBatchV2, Channel)
+ << EventBatchV2.ThreadId(uint16(ThreadIdOverride))
+ << EventBatchV2.Data(Buffer, uint32(ptrdiff_t(Cursor - Buffer)));
+
+ // Both protocols should really do this rebase but it make analysis go
+ // bonkers and I'm not looking into that right now
+ PrevTimestamp = 0;
+ }
+#endif
+
+ LastTimestamp = 0;
+ PrevTimestamp = 0;
+
+ Cursor = Buffer;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void FScopeBuffer::Enter(uint64 Timestamp, uint32 ScopeId)
+{
+ check(Timestamp >= LastTimestamp);
+ LastTimestamp = Timestamp;
+
+ PrevTimestamp += (Timestamp -= PrevTimestamp);
+ enum { Shift = (TraceEventBatchVer == 1) ? 1 : 2 };
+ Cursor += Encode64_7bit((Timestamp << Shift) | EnterLsb, Cursor);
+ Cursor += Encode32_7bit(ScopeId, Cursor);
+ Depth++;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void FScopeBuffer::Leave(uint64 Timestamp)
+{
+ check(Timestamp >= LastTimestamp);
+ LastTimestamp = Timestamp;
+
+ if (Depth == 0)
+ {
+ return;
+ }
+
+ PrevTimestamp += (Timestamp -= PrevTimestamp);
+ enum { Shift = (TraceEventBatchVer == 1) ? 1 : 2 };
+ Cursor += Encode64_7bit((Timestamp << Shift) | LeaveLsb, Cursor);
+ Depth--;
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+struct FLoctight
+{
+ struct FScope
+ {
+ FScope() = default;
+ ~FScope();
+ FScope(FScope&& Rhs);
+ FScope(const FScope&) = delete;
+ FScope& operator = (const FScope&) = delete;
+ FScope& operator = (FScope&&) = delete;
+ FCriticalSection* Outer = nullptr;
+ };
+ FScope Scope() const;
+ mutable FCriticalSection Loch;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+FLoctight::FScope::~FScope()
+{
+ if (Outer)
+ {
+ Outer->Unlock();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+FLoctight::FScope::FScope(FScope&& Rhs)
+{
+ Swap(Outer, Rhs.Outer);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+FLoctight::FScope FLoctight::Scope() const
+{
+ Loch.Lock();
+ FScope Ret;
+ Ret.Outer = &Loch;
+ return Ret;
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+class FScopeBufferTs
+ : protected FLoctight
+ , protected FScopeBuffer
+{
+public:
+ void SetThreadId(uint32 Value);
+ bool IsInScope() const;
+ void Flush(bool Force=false);
+ void Enter(uint32 ScopeId);
+ void Leave();
+};
+
+////////////////////////////////////////////////////////////////////////////////
+void FScopeBufferTs::SetThreadId(uint32 Value)
+{
+ FScope _ = Scope();
+ FScopeBuffer::SetThreadId(Value);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+bool FScopeBufferTs::IsInScope() const
+{
+ FScope _ = Scope();
+ return FScopeBuffer::IsInScope();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void FScopeBufferTs::Flush(bool Force)
+{
+ FScope _ = Scope();
+ FScopeBuffer::Flush(Force);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void FScopeBufferTs::Enter(uint32 ScopeId)
+{
+ uint64 Timestamp = TimeGetTimestamp();
+ FScope _ = Scope();
+ FScopeBuffer::Enter(Timestamp, ScopeId);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void FScopeBufferTs::Leave()
+{
+ uint64 Timestamp = TimeGetTimestamp();
+ FScope _ = Scope();
+ FScopeBuffer::Leave(Timestamp);
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+class FLane
+{
+public:
+ FLane(const FLaneTraceSpec& Spec);
+ ~FLane();
+ static uint32 NewScope(const FAnsiStringView& Name);
+ void Enter(uint32 ScopeId);
+ void Change(uint32 ScopeId);
+ void Leave();
+ void LeaveAll();
+
+private:
+ FScopeBuffer Buffer;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+FLane::FLane(const FLaneTraceSpec& Spec)
+: Buffer(*(UE::Trace::FChannel*)(Spec.Channel))
+{
+ static uint32 volatile NextId = 0;
+ uint32 Id = UE::Trace::Private::AtomicAddRelaxed(&NextId, 1u) + 1;
+ Id += 2 << 10;
+
+ uint32 NameSize = uint32(Spec.Name.Len());
+ UE_TRACE_LOG($Trace, ThreadInfo, true, NameSize)
+ << ThreadInfo.ThreadId(Id)
+ << ThreadInfo.SortHint(Spec.Weight)
+ << ThreadInfo.Name(Spec.Name.GetData(), NameSize);
+
+ Buffer.SetThreadId(Id);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+FLane::~FLane()
+{
+ Buffer.Flush(true);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+uint32 FLane::NewScope(const FAnsiStringView& Name)
+{
+#if UE_LANETRACE_ENABLED != 2
+ return FCpuProfilerTrace::OutputEventType(Name.GetData(), "", 0u);
+#else
+ return uint32(ScopeNew(Name));
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void FLane::Enter(uint32 ScopeId)
+{
+ uint64 Timestamp = TimeGetTimestamp();
+ Buffer.Enter(Timestamp, ScopeId);
+ Buffer.Flush(false);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void FLane::Change(uint32 ScopeId)
+{
+ uint64 Timestamp = TimeGetTimestamp();
+ Buffer.Leave(Timestamp);
+ Buffer.Enter(Timestamp, ScopeId);
+ Buffer.Flush(false);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void FLane::Leave()
+{
+ uint64 Timestamp = TimeGetTimestamp();
+ Buffer.Leave(Timestamp);
+ Buffer.Flush(false);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void FLane::LeaveAll()
+{
+ uint64 Timestamp = TimeGetTimestamp();
+ while (Buffer.IsInScope())
+ {
+ Buffer.Leave(Timestamp);
+ }
+ Buffer.Flush(true);
+}
+
+} // namespace LaneTraceDetail
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+class FLaneTrace
+ : public LaneTraceDetail::FLane
+{
+ using LaneTraceDetail::FLane::FLane;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+FLaneTrace* LaneTrace_New(const FLaneTraceSpec& Spec)
+{
+ return new FLaneTrace(Spec);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void LaneTrace_Delete(FLaneTrace* Lane)
+{
+ delete Lane;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+uint32 LaneTrace_NewScope(const FAnsiStringView& Name)
+{
+ return FLaneTrace::NewScope(Name);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void LaneTrace_Enter(FLaneTrace* Lane, uint32 ScopeId)
+{
+ Lane->Enter(ScopeId);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void LaneTrace_Change(FLaneTrace* Lane, uint32 ScopeId)
+{
+ Lane->Change(ScopeId);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void LaneTrace_Leave(FLaneTrace* Lane)
+{
+ Lane->Leave();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void LaneTrace_LeaveAll(FLaneTrace* Lane)
+{
+ Lane->LeaveAll();
+}
+
+
+
+namespace LaneTraceDetail
+{
+
+////////////////////////////////////////////////////////////////////////////////
+class FEstate
+{
+public:
+ FEstate(const FLaneTraceSpec& Spec);
+ ~FEstate();
+ FLaneTrace* Build(UPTRINT Postcode);
+ FLaneTrace* Lookup(UPTRINT Postcode);
+ void Demolish(UPTRINT Postcode);
+
+private:
+ struct FEntry
+ {
+ UPTRINT Postcode = 0;
+ FLaneTrace* Lane = nullptr;
+ };
+
+ enum { GROWTH_SIZE = 4 };
+
+ FLaneTraceSpec LaneSpec;
+ FCriticalSection Lock;
+ TArray<FEntry> Directory;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+FEstate::FEstate(const FLaneTraceSpec& InSpec)
+: LaneSpec(InSpec)
+{
+ Directory.SetNum(GROWTH_SIZE);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+FEstate::~FEstate()
+{
+ for (FEntry& Entry : Directory)
+ {
+ if (Entry.Lane != nullptr)
+ {
+ LaneTrace_Delete(Entry.Lane);
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+FLaneTrace* FEstate::Build(UPTRINT Postcode)
+{
+ auto UseEstate = [this] (UPTRINT Postcode, FEntry& Entry)
+ {
+ Entry.Postcode = Postcode;
+ if (Entry.Lane == nullptr)
+ {
+ Entry.Lane = LaneTrace_New(LaneSpec);
+ }
+
+ return Entry.Lane;
+ };
+
+ FScopeLock _(&Lock);
+
+ for (FEntry& Entry : Directory)
+ {
+ if (Entry.Postcode == 0)
+ {
+ return UseEstate(Postcode, Entry);
+ }
+ }
+
+ int32 NextSize = Directory.Num() + GROWTH_SIZE;
+ Directory.SetNum(NextSize);
+ return UseEstate(Postcode, Directory[NextSize - GROWTH_SIZE]);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+FLaneTrace* FEstate::Lookup(UPTRINT Postcode)
+{
+ FScopeLock _(&Lock);
+
+ for (const FEntry& Entry : Directory)
+ {
+ if (Entry.Postcode == Postcode)
+ {
+ return Entry.Lane;
+ }
+ }
+
+ checkf(false, TEXT("Invalid/unknown postcode given, unable to find estate: %llx"), Postcode);
+ return nullptr;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void FEstate::Demolish(UPTRINT Postcode)
+{
+ FScopeLock _(&Lock);
+
+ for (FEntry& Entry : Directory)
+ {
+ if (Entry.Postcode == Postcode)
+ {
+ LaneTrace_LeaveAll(Entry.Lane);
+ Entry.Postcode = 0;
+ return;
+ }
+ }
+
+ checkf(false, TEXT("Invalid/unknown postcode given, unable to demolish estate: %llx"), Postcode);
+}
+
+} // namespace LaneTraceDetail
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+class FLaneEstate
+ : public LaneTraceDetail::FEstate
+{
+public:
+ using LaneTraceDetail::FEstate::FEstate;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+FLaneEstate*LaneEstate_New(const FLaneTraceSpec& Spec)
+{
+ return new FLaneEstate(Spec);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void LaneEstate_Delete(FLaneEstate* Estate)
+{
+ delete Estate;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+FLaneTrace* LaneEstate_Build(FLaneEstate* Estate, FLanePostcode Postcode)
+{
+ return Estate->Build(Postcode.Value);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+FLaneTrace* LaneEstate_Lookup(FLaneEstate* Estate, FLanePostcode Postcode)
+{
+ return Estate->Lookup(Postcode.Value);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void LaneEstate_Demolish(FLaneEstate* Estate, FLanePostcode Postcode)
+{
+ return Estate->Demolish(Postcode.Value);
+}
+
+#undef LANETRACE_UNTESTED
+
+#if UE_LANETRACE_ENABLED == 2
+} // namespace trace
+#endif
+
+#endif // UE_LANETRACE_ENABLED
diff --git a/thirdparty/trace/trace.h b/thirdparty/trace/trace.h
index a1fce80a6..0835781ba 100644
--- a/thirdparty/trace/trace.h
+++ b/thirdparty/trace/trace.h
@@ -190,7 +190,7 @@ struct TArray
: public std::vector<T>
{
using Super = std::vector<T>;
- using Super::vector;
+ using Super::Super;
using Super::back;
using Super::begin;
using Super::clear;
@@ -260,7 +260,7 @@ struct TStringViewAdapter
: public std::basic_string_view<T>
{
using Super = std::basic_string_view<T>;
- using Super::basic_string_view;
+ using Super::Super;
using Super::size;
using Super::data;
size_t Len() const { return size(); }
@@ -596,8 +596,8 @@ namespace UE {
namespace Trace {
enum AnsiString {};
enum WideString {};
-template<typename IdType>
-struct TEventRef
+template<typename IdType>
+struct TEventRef
{
using ReferenceType = IdType;
TEventRef(IdType InId, uint32 InTypeId)
@@ -632,7 +632,7 @@ inline uint64 TEventRef<uint64>::GetHash() const
return (uint64(RefTypeId) << 32) ^ Id;
}
typedef TEventRef<uint8> FEventRef8;
-typedef TEventRef<uint16> FEventRef16;
+typedef TEventRef<uint16> FEventRef16;
typedef TEventRef<uint32> FEventRef32;
typedef TEventRef<uint64> FEventRef64;
template<typename IdType>
@@ -653,7 +653,7 @@ struct FChannelInfo;
typedef void* AllocFunc(SIZE_T, uint32);
typedef void FreeFunc(void*, SIZE_T);
typedef void ChannelIterFunc(const ANSICHAR*, bool, void*);
-/* The callback provides information about a channel and a user provided pointer.
+/* The callback provides information about a channel and a user provided pointer.
Returning false from the callback will stop the enumeration */
typedef bool ChannelIterCallback(const FChannelInfo& Info, void*/*User*/);
struct FStatistics
@@ -673,7 +673,7 @@ struct FSendFlags
};
UE_TRACE_API void SetMemoryHooks(AllocFunc Alloc, FreeFunc Free) UE_TRACE_IMPL();
UE_TRACE_API void Initialize(const FInitializeDesc& Desc) UE_TRACE_IMPL();
-UE_TRACE_API void StartWorkerThread() UE_TRACE_IMPL();
+UE_TRACE_API void StartWorkerThread() UE_TRACE_IMPL();
UE_TRACE_API void Shutdown() UE_TRACE_IMPL();
UE_TRACE_API void Update() UE_TRACE_IMPL();
UE_TRACE_API void GetStatistics(FStatistics& Out) UE_TRACE_IMPL();
@@ -718,7 +718,7 @@ struct FChannelInfo
bool bIsEnabled;
bool bIsReadOnly;
};
-typedef void ChannelIterFunc(const ANSICHAR*, bool, void*);
+typedef void ChannelIterFunc(const ANSICHAR*, bool, void*);
typedef bool ChannelIterCallback(const FChannelInfo& OutChannelInfo, void*);
/*
A named channel which can be used to filter trace events. Channels can be
@@ -1277,7 +1277,7 @@ struct FNewEventEvent
struct
{
uint16 Offset;
- uint16 RefUid;
+ uint16 RefUid;
uint8 TypeInfo;
uint8 NameSize;
} Reference;
@@ -2519,7 +2519,7 @@ FChannel* FChannel::FindChannel(const ANSICHAR* ChannelName)
}
return nullptr;
}
-void FChannel::EnumerateChannels(ChannelIterFunc Func, void* User)
+void FChannel::EnumerateChannels(ChannelIterFunc Func, void* User)
{
using namespace Private;
FChannel* ChannelLists[] =
@@ -4985,7 +4985,7 @@ UPTRINT TcpSocketConnect(const ANSICHAR* Host, uint16 Port)
{
struct FAddrInfoPtr
{
- ~FAddrInfoPtr()
+ ~FAddrInfoPtr()
{
if (Value != nullptr)
{