#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 Metadata; TArray> 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 StartTime; TOptional 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 PersistentBuffer; TUniquePtr MemAr; TUniquePtr 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 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& 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); };