EM_Task/TraceInsights/Public/Insights/ViewModels/MinimalTimerExporter.h
Boshuang Zhao 5144a49c9b add
2026-02-13 16:18:33 +08:00

194 lines
6.2 KiB
C++

#pragma once
#include "CoreMinimal.h"
#include "TraceServices/Model/TimingProfiler.h"
namespace TraceTimer
{
/** 导出列标志位枚举 */
enum class EExportField : uint32
{
None = 0,
Name = 1 << 0,
Start = 1 << 1,
End = 1 << 2,
Duration = 1 << 3,
Depth = 1 << 4,
ThreadId = 1 << 5,
Metadata = 1 << 6,
TimerId = 1 << 7,
All = 0xFFFFFFFF
};
ENUM_CLASS_FLAGS(EExportField);
/** 导出数据载体:兼容平铺模式与树状模式 */
struct FEventExportData
{
const Trace::FTimingProfilerTimer* Timer = nullptr;
double Start = 0;
double End = 0;
uint32 Depth = 0;
uint32 ThreadId = 0;
TArrayView<const uint8> Metadata;
TArray<TSharedPtr<FEventExportData>> Children;
void MoveTo(FEventExportData& OutTarget)
{
OutTarget.Timer = Timer;
OutTarget.Start = Start;
OutTarget.End = End;
OutTarget.Depth = Depth;
OutTarget.ThreadId = ThreadId;
OutTarget.Metadata = Metadata;
OutTarget.Children = MoveTemp(Children);
}
};
/** 导出过滤配置 */
struct FExportFilter
{
FString FuncName;
TOptional<double> StartTime;
TOptional<double> EndTime;
EExportField Fields = EExportField::All;
uint32 TrackIndex = UINT32_MAX;
bool bOnlyMetadata = false;
double MinDurationMs = 0.0;
FExportFilter(FString InFuncName): FuncName(InFuncName) {};
double GetStartTime() const { return StartTime.Get(0.0); }
double GetEndTime() const { return EndTime.Get(DBL_MAX); }
bool ShouldFilterByTrack(uint32 TimelineIndex) const
{
return TrackIndex != UINT32_MAX && TrackIndex != TimelineIndex;
}
bool ShouldFilterByEvent(FEventExportData& Node) const;
};
/** 物理文件分块配置 */
struct FExportConfig
{
static constexpr uint32 DefaultMaxMemSize = 512 * 1024;
FString Folder;
FString BaseName;
FString Extension;
uint32 EventsPerChunk = 1000000;
uint32 MaxMemSize = DefaultMaxMemSize;
FExportConfig() = default;
FExportConfig(const FString& Filename);
};
class IEventWriter;
/** 物理分块写入管理器 */
class ChunkingFileWriter
{
public:
ChunkingFileWriter() = default;
~ChunkingFileWriter();
void Initialize(const FExportConfig& InConfig);
FArchive* GetCurrentArchive();
void FlushToDisk();
void Close();
uint32 GetWrittenEventCount() const { return WrittenEventCount; }
void NotifyEventWritten() { WrittenEventCount++; }
void WriteEvent(IEventWriter* Writer, const FEventExportData& Data);
void SwitchToNextChunk(IEventWriter* Writer);
const FExportConfig& GetConfig() const { return Config; }
private:
void CheckFlush(IEventWriter* Writer);
FExportConfig Config;
TArray<uint8> PersistentBuffer;
TUniquePtr<FMemoryWriter> MemAr;
TUniquePtr<FArchive> FileAr;
uint32 ChunkIndex = 1;
uint32 WrittenEventCount = 0;
FString CurrentFilename;
};
/** 导出器抽象基类 */
class IEventWriter
{
public:
IEventWriter(const EExportField& InFields): Fields(InFields) {};
virtual ~IEventWriter() = default;
virtual bool WantsStructuredPath() const { return false; }
virtual void WriteHeader(FArchive& Ar) {}
virtual void WriteFooter(FArchive& Ar) {}
/** 统一写入接口:子类根据需要处理 Data 中的 Children 递归数据 */
virtual void ProcessEvent(FArchive& Ar, const FEventExportData& Data) = 0;
protected:
EExportField Fields = EExportField::All;
};
/** CSV 实现:仅处理平铺数据 */
class CsvEventWriter: public IEventWriter
{
public:
using IEventWriter::IEventWriter;
virtual void WriteHeader(FArchive& Ar) override;
virtual void ProcessEvent(FArchive& Ar, const FEventExportData& Data) override;
};
/** JSON Lines 实现:支持深度嵌套递归 */
class JsonEventWriter: public IEventWriter
{
public:
using IEventWriter::IEventWriter;
virtual bool WantsStructuredPath() const override { return true; }
virtual void ProcessEvent(FArchive& Ar, const FEventExportData& Data) override;
void WriteNode(FArchive& Ar, const FEventExportData& Node);
};
/** 导出上下文:管理 Trace Session 的元数据映射 */
struct FExportContext
{
const Trace::IAnalysisSession& Session;
const Trace::ITimingProfilerProvider* Provider = nullptr;
const Trace::ITimingProfilerTimerReader* TimerReader = nullptr;
TMap<uint32, uint32> TimelineIndexToThreadId;
FExportContext(const Trace::IAnalysisSession& InSession);
bool Initialize(const Trace::ITimingProfilerProvider* InProvider);
uint32 GetThreadId(uint32 TimelineIndex) const;
};
/** 核心导出处理器 */
class FTraceExportProcessor
{
public:
FTraceExportProcessor(FExportContext& InContext, IEventWriter& InWriter, ChunkingFileWriter& InChunkWriter, const FExportFilter& InFilter);
void Run();
static void DecodeMetadataToString(FString& Str,const TArrayView<const uint8>& Metadata);
private:
void ProcessTimeline(uint32 TimelineIndex);
void EnumerateFlat(const Trace::ITimingProfilerProvider::Timeline& Timeline, uint32 ThreadId);
void EnumerateStructured(const Trace::ITimingProfilerProvider::Timeline& Timeline, uint32 ThreadId);
FExportContext& Context;
IEventWriter& Writer;
ChunkingFileWriter& ChunkWriter;
const FExportFilter& Filter;
};
} // namespace TraceTimer
/** 静态入口类 */
class FMinimalTimerExporter
{
public:
static bool ExportTimersToCSV(const Trace::IAnalysisSession& Session, const FString& Filename);
static bool ExportMetadataToCSV(const Trace::IAnalysisSession& Session, const FString& Filename, const TraceTimer::FExportFilter& InFilter);
static bool ExportTimingEventsToCSV(const Trace::IAnalysisSession& Session, const FString& Filename, const TraceTimer::FExportFilter& InFilter);
static bool ExportTimingEventsToJSON(const Trace::IAnalysisSession& Session, const FString& Filename, const TraceTimer::FExportFilter& InFilter);
private:
static bool ExecuteTimingExport(const Trace::IAnalysisSession& Session, const FString& Filename, TraceTimer::FExportFilter& Filter, TraceTimer::IEventWriter& Writer);
};