1448 lines
47 KiB
C++
1448 lines
47 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "Misc/QueuedThreadPool.h"
|
|
|
|
#include "Common/PagedArray.h"
|
|
#include "Model/AsyncEnumerateTask.h"
|
|
#include "Model/MonotonicTimelineData.h"
|
|
#include "TraceServices/Containers/Timelines.h"
|
|
|
|
namespace TraceServices
|
|
{
|
|
|
|
struct FMonotonicTimelineDefaultSettings
|
|
{
|
|
enum
|
|
{
|
|
MaxDepth = 1024,
|
|
ScopeEntriesPageSize = 65536,
|
|
EventsPageSize = 65536,
|
|
DetailLevelsCount = 6,
|
|
};
|
|
|
|
constexpr static double DetailLevelResolution(int32 Index)
|
|
{
|
|
const double DetailLevels[DetailLevelsCount] = { 0.0, 0.0001, 0.001, 0.008, 0.04, 0.2 };
|
|
return DetailLevels[Index];
|
|
}
|
|
};
|
|
|
|
/*
|
|
* An interface that can consume timed serial events (a timeline).
|
|
*/
|
|
template<typename InEventType>
|
|
class IEditableTimeline
|
|
{
|
|
public:
|
|
virtual ~IEditableTimeline() = default;
|
|
|
|
/*
|
|
* Begin a new timed event.
|
|
*
|
|
* @param StartTime The starting timestamp of the event in seconds.
|
|
* @param Event The event information.
|
|
*/
|
|
virtual void AppendBeginEvent(double StartTime, const InEventType& Event) = 0;
|
|
|
|
/*
|
|
* End a new timed event. This ends the event started by the prior call to AppendBeginEvent.
|
|
*
|
|
* @param EndTime The ending timestamp of the event in seconds.
|
|
*/
|
|
virtual void AppendEndEvent(double EndTime) = 0;
|
|
|
|
virtual double GetLastTimestamp() const { return 0.0; }
|
|
};
|
|
|
|
template<typename InEventType, typename SettingsType = FMonotonicTimelineDefaultSettings>
|
|
class TMonotonicTimeline
|
|
: public ITimeline<InEventType>
|
|
, public IEditableTimeline<InEventType>
|
|
{
|
|
friend class FEnumerateAsyncTask<InEventType, SettingsType>;
|
|
|
|
typedef FEventInfoStackEntry<InEventType, SettingsType> FEventInfoStackEntry;
|
|
typedef FDetailLevel<InEventType, SettingsType> FDetailLevel;
|
|
typedef FDetailLevelDepthState<InEventType> FDetailLevelDepthState;
|
|
typedef FEnumerateAsyncTask<InEventType, SettingsType> FEnumarateAsyncTask;
|
|
|
|
public:
|
|
using EventType = InEventType;
|
|
|
|
TMonotonicTimeline(ILinearAllocator& InAllocator)
|
|
: Allocator(InAllocator)
|
|
{
|
|
for (int32 DetailLevelIndex = 0; DetailLevelIndex < SettingsType::DetailLevelsCount; ++DetailLevelIndex)
|
|
{
|
|
double Resolution = SettingsType::DetailLevelResolution(DetailLevelIndex);
|
|
DetailLevels.Emplace(Allocator, Resolution);
|
|
}
|
|
}
|
|
|
|
virtual ~TMonotonicTimeline() = default;
|
|
|
|
virtual double GetLastTimestamp() const override
|
|
{
|
|
return DetailLevels[0].InsertionState.LastTime;
|
|
}
|
|
|
|
virtual void AppendBeginEvent(double StartTime, const EventType& Event) override
|
|
{
|
|
int32 CurrentDepth = DetailLevels[0].InsertionState.CurrentDepth;
|
|
if (CurrentDepth >= SettingsType::MaxDepth)
|
|
{
|
|
++ExtraDepthEvents;
|
|
return;
|
|
}
|
|
|
|
AddScopeEntry(DetailLevels[0], StartTime, true);
|
|
AddEvent(DetailLevels[0], Event);
|
|
|
|
check(CurrentDepth < SettingsType::MaxDepth);
|
|
|
|
FDetailLevelDepthState& Lod0DepthState = DetailLevels[0].InsertionState.DepthStates[CurrentDepth];
|
|
Lod0DepthState.EnterTime = StartTime;
|
|
Lod0DepthState.DominatingEvent = Event;
|
|
//Lod0DepthState.DebugDominatingEventType = Owner.EventTypes[TypeId];
|
|
|
|
for (int32 DetailLevelIndex = 1; DetailLevelIndex < SettingsType::DetailLevelsCount; ++DetailLevelIndex)
|
|
{
|
|
FDetailLevel& DetailLevel = DetailLevels[DetailLevelIndex];
|
|
FDetailLevelDepthState& CurrentDepthState = DetailLevel.InsertionState.DepthStates[CurrentDepth];
|
|
|
|
if (CurrentDepthState.PendingScopeEnterIndex < 0 || StartTime >= CurrentDepthState.EnterTime + DetailLevel.Resolution)
|
|
{
|
|
if (CurrentDepthState.PendingEventIndex >= 0)
|
|
{
|
|
check(DetailLevel.InsertionState.PendingDepth < SettingsType::MaxDepth);
|
|
for (int32 Depth = DetailLevel.InsertionState.PendingDepth; Depth >= CurrentDepth; --Depth)
|
|
{
|
|
FDetailLevelDepthState& DepthState = DetailLevel.InsertionState.DepthStates[Depth];
|
|
check(DepthState.PendingScopeEnterIndex >= 0);
|
|
AddScopeEntry(DetailLevel, DepthState.ExitTime, false);
|
|
|
|
DepthState.PendingScopeEnterIndex = -1;
|
|
DepthState.PendingEventIndex = -1;
|
|
}
|
|
}
|
|
DetailLevel.InsertionState.PendingDepth = CurrentDepth;
|
|
|
|
uint64 EnterScopeIndex = DetailLevel.ScopeEntries.Num();
|
|
uint64 EventIndex = DetailLevel.Events.Num();
|
|
|
|
AddScopeEntry(DetailLevel, StartTime, true);
|
|
AddEvent(DetailLevel, Event);
|
|
|
|
CurrentDepthState.DominatingEventStartTime = StartTime;
|
|
CurrentDepthState.DominatingEventEndTime = StartTime;
|
|
CurrentDepthState.DominatingEventDuration = 0.0;
|
|
CurrentDepthState.PendingScopeEnterIndex = EnterScopeIndex;
|
|
CurrentDepthState.PendingEventIndex = EventIndex;
|
|
CurrentDepthState.EnterTime = StartTime;
|
|
CurrentDepthState.DominatingEvent = Event;
|
|
//CurrentDepthState.DebugDominatingEventType = Owner.EventTypes[TypeId];
|
|
}
|
|
else if (CurrentDepth > DetailLevel.InsertionState.PendingDepth)
|
|
{
|
|
DetailLevel.InsertionState.PendingDepth = CurrentDepth;
|
|
}
|
|
DetailLevel.SetEvent(CurrentDepthState.PendingEventIndex, Event);
|
|
}
|
|
|
|
++ModCount;
|
|
}
|
|
|
|
virtual void AppendEndEvent(double EndTime) override
|
|
{
|
|
if (ExtraDepthEvents > 0)
|
|
{
|
|
--ExtraDepthEvents;
|
|
return;
|
|
}
|
|
|
|
check(DetailLevels[0].InsertionState.CurrentDepth <= SettingsType::MaxDepth);
|
|
|
|
AddScopeEntry(DetailLevels[0], EndTime, false);
|
|
|
|
int32 CurrentDepth = DetailLevels[0].InsertionState.CurrentDepth;
|
|
check(CurrentDepth < SettingsType::MaxDepth);
|
|
|
|
for (int32 DetailLevelIndex = 1; DetailLevelIndex < SettingsType::DetailLevelsCount; ++DetailLevelIndex)
|
|
{
|
|
FDetailLevel& DetailLevel = DetailLevels[DetailLevelIndex];
|
|
|
|
DetailLevel.InsertionState.DepthStates[CurrentDepth].ExitTime = EndTime;
|
|
|
|
UpdateDominatingEvent(DetailLevel, CurrentDepth, EndTime);
|
|
|
|
FDetailLevelDepthState& CurrentDepthState = DetailLevel.InsertionState.DepthStates[CurrentDepth];
|
|
check(CurrentDepthState.PendingScopeEnterIndex >= 0);
|
|
if (EndTime >= CurrentDepthState.EnterTime + DetailLevel.Resolution)
|
|
{
|
|
check(DetailLevel.InsertionState.PendingDepth < SettingsType::MaxDepth);
|
|
for (int32 Depth = DetailLevel.InsertionState.PendingDepth; Depth >= CurrentDepth; --Depth)
|
|
{
|
|
FDetailLevelDepthState& DepthState = DetailLevel.InsertionState.DepthStates[Depth];
|
|
check(DepthState.PendingScopeEnterIndex >= 0);
|
|
AddScopeEntry(DetailLevel, DepthState.ExitTime, false);
|
|
|
|
DepthState.PendingScopeEnterIndex = -1;
|
|
DepthState.PendingEventIndex = -1;
|
|
}
|
|
DetailLevel.InsertionState.PendingDepth = CurrentDepth - 1;
|
|
}
|
|
}
|
|
|
|
++ModCount;
|
|
}
|
|
|
|
virtual uint64 GetModCount() const override
|
|
{
|
|
return ModCount;
|
|
}
|
|
|
|
virtual uint64 GetEventCount() const override
|
|
{
|
|
return DetailLevels[0].Events.Num();
|
|
}
|
|
|
|
virtual const EventType& GetEvent(uint64 InIndex) const override
|
|
{
|
|
return DetailLevels[0].Events[InIndex];
|
|
}
|
|
|
|
virtual double GetStartTime() const override
|
|
{
|
|
return DetailLevels[0].ScopeEntries.Num() > 0 ? FMath::Abs(DetailLevels[0].ScopeEntries[0].Time) : 0.0;
|
|
}
|
|
|
|
virtual double GetEndTime() const override
|
|
{
|
|
uint64 NumScopeEntries = DetailLevels[0].ScopeEntries.Num();
|
|
return NumScopeEntries > 0 ? FMath::Abs(DetailLevels[0].ScopeEntries[NumScopeEntries - 1].Time) : 0.0;
|
|
}
|
|
|
|
virtual void EnumerateEventsDownSampled(double IntervalStart, double IntervalEnd, double Resolution, typename ITimeline<EventType>::EventCallback Callback) const override
|
|
{
|
|
int32 DetailLevelIndex = SettingsType::DetailLevelsCount - 1;
|
|
for (; DetailLevelIndex > 0; --DetailLevelIndex)
|
|
{
|
|
if (DetailLevels[DetailLevelIndex].Resolution <= Resolution)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
const FDetailLevel& DetailLevel = DetailLevels[DetailLevelIndex];
|
|
if (DetailLevel.ScopeEntries.Num() == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
uint64 FirstScopePageIndex = Algo::UpperBoundBy(DetailLevel.ScopeEntries, IntervalStart, [](const FEventScopeEntryPage& Page)
|
|
{
|
|
return Page.BeginTime;
|
|
});
|
|
if (FirstScopePageIndex > 0)
|
|
{
|
|
--FirstScopePageIndex;
|
|
}
|
|
const FEventScopeEntryPage* ScopePage = DetailLevel.ScopeEntries.GetPage(FirstScopePageIndex);
|
|
if (ScopePage->BeginTime > IntervalEnd)
|
|
{
|
|
return;
|
|
}
|
|
if (ScopePage->EndTime < IntervalStart)
|
|
{
|
|
return;
|
|
}
|
|
|
|
struct FEnumerationStackEntry
|
|
{
|
|
double StartTime;
|
|
EventType Event;
|
|
};
|
|
FEnumerationStackEntry EventStack[SettingsType::MaxDepth];
|
|
int32 CurrentStackDepth = ScopePage->InitialStackCount;
|
|
for (int32 InitialStackIndex = 0; InitialStackIndex < CurrentStackDepth; ++InitialStackIndex)
|
|
{
|
|
FEnumerationStackEntry& EnumerationStackEntry = EventStack[InitialStackIndex];
|
|
const FEventStackEntry& EventStackEntry = ScopePage->InitialStack[InitialStackIndex];
|
|
EnumerationStackEntry.StartTime = DetailLevel.GetScopeEntryTime(EventStackEntry.EnterScopeIndex);
|
|
EnumerationStackEntry.Event = DetailLevel.GetEvent(EventStackEntry.EventIndex);
|
|
}
|
|
|
|
auto ScopeEntryIterator = DetailLevel.ScopeEntries.GetIteratorFromPage(FirstScopePageIndex);
|
|
const FEventScopeEntry* ScopeEntry = ScopeEntryIterator.GetCurrentItem();
|
|
|
|
auto EventsIterator = DetailLevel.Events.GetIteratorFromItem(ScopePage->BeginEventIndex);
|
|
const EventType* Event = EventsIterator.GetCurrentItem();
|
|
|
|
while (ScopeEntry && FMath::Abs(ScopeEntry->Time) < IntervalStart)
|
|
{
|
|
if (ScopeEntry->Time < 0.0)
|
|
{
|
|
check(CurrentStackDepth < SettingsType::MaxDepth);
|
|
FEnumerationStackEntry& StackEntry = EventStack[CurrentStackDepth++];
|
|
StackEntry.Event = *Event;
|
|
StackEntry.StartTime = -ScopeEntry->Time;
|
|
Event = EventsIterator.NextItem();
|
|
}
|
|
else
|
|
{
|
|
check(CurrentStackDepth > 0);
|
|
--CurrentStackDepth;
|
|
}
|
|
ScopeEntry = ScopeEntryIterator.NextItem();
|
|
}
|
|
if (CurrentStackDepth == 1 && EventStack[0].StartTime > IntervalEnd)
|
|
{
|
|
return;
|
|
}
|
|
for (int32 StackIndex = 0; StackIndex < CurrentStackDepth; ++StackIndex)
|
|
{
|
|
FEnumerationStackEntry& StackEntry = EventStack[StackIndex];
|
|
if (Callback(true, StackEntry.StartTime, StackEntry.Event) == EEventEnumerate::Stop)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
while (ScopeEntry && FMath::Abs(ScopeEntry->Time) <= IntervalEnd)
|
|
{
|
|
if (ScopeEntry->Time < 0.0)
|
|
{
|
|
check(CurrentStackDepth < SettingsType::MaxDepth);
|
|
FEnumerationStackEntry& StackEntry = EventStack[CurrentStackDepth++];
|
|
StackEntry.Event = *Event;
|
|
if (Callback(true, -ScopeEntry->Time, StackEntry.Event) == EEventEnumerate::Stop)
|
|
{
|
|
return;
|
|
}
|
|
Event = EventsIterator.NextItem();
|
|
}
|
|
else
|
|
{
|
|
check(CurrentStackDepth > 0);
|
|
FEnumerationStackEntry& StackEntry = EventStack[--CurrentStackDepth];
|
|
if (Callback(false, ScopeEntry->Time, StackEntry.Event) == EEventEnumerate::Stop)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
ScopeEntry = ScopeEntryIterator.NextItem();
|
|
}
|
|
|
|
bool bSearchEndTimeUsingPages = false;
|
|
uint64 LastPageIndex = ScopeEntryIterator.GetCurrentPageIndex();
|
|
uint32 ExitDepth = 0;
|
|
while (CurrentStackDepth > 0 && ScopeEntry)
|
|
{
|
|
if (ScopeEntryIterator.GetCurrentPageIndex() != LastPageIndex)
|
|
{
|
|
bSearchEndTimeUsingPages = true;
|
|
break;
|
|
}
|
|
if (ScopeEntry->Time < 0.0)
|
|
{
|
|
++ExitDepth;
|
|
}
|
|
else
|
|
{
|
|
if (ExitDepth == 0)
|
|
{
|
|
FEnumerationStackEntry& StackEntry = EventStack[--CurrentStackDepth];
|
|
if (Callback(false, ScopeEntry->Time, StackEntry.Event) == EEventEnumerate::Stop)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
--ExitDepth;
|
|
}
|
|
}
|
|
|
|
LastPageIndex = ScopeEntryIterator.GetCurrentPageIndex();
|
|
ScopeEntry = ScopeEntryIterator.NextItem();
|
|
}
|
|
|
|
if (bSearchEndTimeUsingPages)
|
|
{
|
|
const FEventScopeEntryPage* CurrentScopePage = ScopeEntryIterator.GetCurrentPage();
|
|
do
|
|
{
|
|
check(CurrentStackDepth <= CurrentScopePage->InitialStackCount);
|
|
while (CurrentStackDepth > 0 && CurrentScopePage->InitialStack[CurrentStackDepth - 1].EndTime > 0)
|
|
{
|
|
--CurrentStackDepth;
|
|
EventType CurrentEvent = DetailLevel.GetEvent(CurrentScopePage->InitialStack[CurrentStackDepth].EventIndex);
|
|
if (Callback(false, CurrentScopePage->InitialStack[CurrentStackDepth].EndTime, CurrentEvent) == EEventEnumerate::Stop)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
CurrentScopePage = ScopeEntryIterator.NextPage();
|
|
}
|
|
while (CurrentScopePage != nullptr);
|
|
}
|
|
|
|
while (CurrentStackDepth > 0)
|
|
{
|
|
FEnumerationStackEntry& StackEntry = EventStack[--CurrentStackDepth];
|
|
if (Callback(false, DetailLevel.InsertionState.LastTime, StackEntry.Event) == EEventEnumerate::Stop)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual void EnumerateEventsBackwardsDownSampled(double IntervalEnd, double IntervalStart, double Resolution, typename ITimeline<EventType>::EventCallback Callback) const override
|
|
{
|
|
int32 DetailLevelIndex = SettingsType::DetailLevelsCount - 1;
|
|
for (; DetailLevelIndex > 0; --DetailLevelIndex)
|
|
{
|
|
if (DetailLevels[DetailLevelIndex].Resolution <= Resolution)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
const FDetailLevel& DetailLevel = DetailLevels[DetailLevelIndex];
|
|
if (DetailLevel.ScopeEntries.Num() == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
uint64 LastScopePageIndex = Algo::UpperBoundBy(DetailLevel.ScopeEntries, IntervalEnd, [](const FEventScopeEntryPage& Page)
|
|
{
|
|
return Page.BeginTime;
|
|
});
|
|
|
|
struct FEnumerationStackEntry
|
|
{
|
|
double EndTime;
|
|
EventType Event;
|
|
};
|
|
|
|
// By default, we start from the very end of the session.
|
|
auto ScopeEntryIterator = DetailLevel.ScopeEntries.GetIteratorFromItem(DetailLevel.ScopeEntries.Num() - 1);
|
|
auto EventsIterator = DetailLevel.Events.GetIteratorFromItem(DetailLevel.Events.Num() - 1);
|
|
FEnumerationStackEntry EventStack[SettingsType::MaxDepth];
|
|
int32 CurrentStackDepth = 0;
|
|
const FEventScopeEntry* ScopeEntry = nullptr;
|
|
|
|
if (LastScopePageIndex > 0 && LastScopePageIndex < DetailLevel.ScopeEntries.NumPages())
|
|
{
|
|
// If we have a page we can start from, start enumerating backwards from the begining of that page.
|
|
ScopeEntryIterator = DetailLevel.ScopeEntries.GetIteratorFromPage(LastScopePageIndex);
|
|
const FEventScopeEntryPage* ScopePage = DetailLevel.ScopeEntries.GetPage(LastScopePageIndex);
|
|
|
|
EventsIterator = DetailLevel.Events.GetIteratorFromItem(ScopePage->BeginEventIndex);
|
|
CurrentStackDepth = ScopePage->InitialStackCount;
|
|
for (int32 InitialStackIndex = 0; InitialStackIndex < CurrentStackDepth; ++InitialStackIndex)
|
|
{
|
|
FEnumerationStackEntry& EnumerationStackEntry = EventStack[InitialStackIndex];
|
|
const FEventStackEntry& EventStackEntry = ScopePage->InitialStack[InitialStackIndex];
|
|
EnumerationStackEntry.EndTime = EventStackEntry.EndTime;
|
|
if (EnumerationStackEntry.EndTime < 0)
|
|
{
|
|
// We need to search for the EndTime of the event using pages.
|
|
auto PageIterator = ScopeEntryIterator;
|
|
while (const FEventScopeEntryPage* Page = PageIterator.NextPage())
|
|
{
|
|
if (Page->InitialStack[InitialStackIndex].EndTime > 0)
|
|
{
|
|
EnumerationStackEntry.EndTime = Page->InitialStack[InitialStackIndex].EndTime;
|
|
break;
|
|
}
|
|
}
|
|
if (EnumerationStackEntry.EndTime < 0)
|
|
{
|
|
EnumerationStackEntry.EndTime = DetailLevel.InsertionState.LastTime;
|
|
}
|
|
}
|
|
EnumerationStackEntry.Event = DetailLevel.GetEvent(EventStackEntry.EventIndex);
|
|
}
|
|
// We start enumerating from the previous page.
|
|
ScopeEntry = ScopeEntryIterator.PrevItem();
|
|
EventsIterator.PrevItem();
|
|
}
|
|
else
|
|
{
|
|
// If we start from the end of the session, we use InsertionState as the initial stack.
|
|
CurrentStackDepth = DetailLevel.InsertionState.CurrentDepth;
|
|
for (int32 InitialStackIndex = 0; InitialStackIndex < CurrentStackDepth; ++InitialStackIndex)
|
|
{
|
|
FEnumerationStackEntry& EnumerationStackEntry = EventStack[InitialStackIndex];
|
|
const FEventStackEntry& EventStackEntry = DetailLevel.InsertionState.EventStack[InitialStackIndex];
|
|
EnumerationStackEntry.EndTime = DetailLevel.InsertionState.LastTime;
|
|
EnumerationStackEntry.Event = DetailLevel.GetEvent(EventStackEntry.EventIndex);
|
|
}
|
|
ScopeEntry = ScopeEntryIterator.GetCurrentItem();
|
|
}
|
|
|
|
// Enumerate backwards until we reach IntervalEnd, without calling the Callback because thess events are not in the provided interval.
|
|
const EventType* Event = EventsIterator.GetCurrentItem();
|
|
while (ScopeEntry && FMath::Abs(ScopeEntry->Time) > IntervalEnd)
|
|
{
|
|
if (ScopeEntry->Time < 0.0)
|
|
{
|
|
check(CurrentStackDepth > 0);
|
|
--CurrentStackDepth;
|
|
Event = EventsIterator.PrevItem();
|
|
}
|
|
else
|
|
{
|
|
check(CurrentStackDepth < SettingsType::MaxDepth);
|
|
FEnumerationStackEntry& StackEntry = EventStack[CurrentStackDepth++];
|
|
StackEntry.EndTime = ScopeEntry->Time;
|
|
}
|
|
ScopeEntry = ScopeEntryIterator.PrevItem();
|
|
}
|
|
|
|
// Call the callback for the events that are open at IntervalEnd.
|
|
for (int32 StackIndex = 0; StackIndex < CurrentStackDepth; ++StackIndex)
|
|
{
|
|
FEnumerationStackEntry& StackEntry = EventStack[StackIndex];
|
|
if (Callback(true, StackEntry.EndTime, StackEntry.Event) == EEventEnumerate::Stop)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Enumerate backwards between IntervalEnd and IntervalStart.
|
|
while (ScopeEntry && FMath::Abs(ScopeEntry->Time) >= IntervalStart)
|
|
{
|
|
if (ScopeEntry->Time < 0.0)
|
|
{
|
|
check(CurrentStackDepth > 0);
|
|
FEnumerationStackEntry& StackEntry = EventStack[--CurrentStackDepth];
|
|
StackEntry.Event = *Event;
|
|
if (Callback(false, -ScopeEntry->Time, StackEntry.Event) == EEventEnumerate::Stop)
|
|
{
|
|
return;
|
|
}
|
|
Event = EventsIterator.PrevItem();
|
|
}
|
|
else
|
|
{
|
|
check(CurrentStackDepth < SettingsType::MaxDepth);
|
|
FEnumerationStackEntry& StackEntry = EventStack[CurrentStackDepth++];
|
|
StackEntry.EndTime = ScopeEntry->Time;
|
|
|
|
if (Callback(true, ScopeEntry->Time, StackEntry.Event) == EEventEnumerate::Stop)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
ScopeEntry = ScopeEntryIterator.PrevItem();
|
|
}
|
|
|
|
// Find the StartTime of the events that are open at IntervalStart.
|
|
bool bSearchStartTimeUsingPages = false;
|
|
uint64 LastPageIndex = ScopeEntryIterator.GetCurrentPageIndex();
|
|
uint32 ExitDepth = 0;
|
|
while (CurrentStackDepth > 0 && ScopeEntry)
|
|
{
|
|
if (ScopeEntryIterator.GetCurrentPageIndex() != LastPageIndex)
|
|
{
|
|
bSearchStartTimeUsingPages = true;
|
|
break;
|
|
}
|
|
if (ScopeEntry->Time < 0.0)
|
|
{
|
|
if (ExitDepth == 0)
|
|
{
|
|
FEnumerationStackEntry& StackEntry = EventStack[--CurrentStackDepth];
|
|
if (Callback(false, -ScopeEntry->Time, *EventsIterator.GetCurrentItem()) == EEventEnumerate::Stop)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
--ExitDepth;
|
|
}
|
|
EventsIterator.PrevItem();
|
|
}
|
|
else
|
|
{
|
|
++ExitDepth;
|
|
}
|
|
|
|
LastPageIndex = ScopeEntryIterator.GetCurrentPageIndex();
|
|
ScopeEntry = ScopeEntryIterator.PrevItem();
|
|
}
|
|
|
|
if (bSearchStartTimeUsingPages)
|
|
{
|
|
ScopeEntryIterator.NextPage();
|
|
const FEventScopeEntryPage* CurrentScopePage = ScopeEntryIterator.GetCurrentPage();
|
|
check(CurrentStackDepth <= CurrentScopePage->InitialStackCount);
|
|
|
|
while (CurrentStackDepth > 0)
|
|
{
|
|
--CurrentStackDepth;
|
|
EventType CurrentEvent = DetailLevel.GetEvent(CurrentScopePage->InitialStack[CurrentStackDepth].EventIndex);
|
|
double StartTime = DetailLevel.ScopeEntries[CurrentScopePage->InitialStack[CurrentStackDepth].EnterScopeIndex].Time;
|
|
if (Callback(false, StartTime, CurrentEvent) == EEventEnumerate::Stop)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual void EnumerateEventsBackwardsDownSampled(double IntervalEnd, double IntervalStart, double Resolution, typename ITimeline<EventType>::EventRangeCallback Callback) const override
|
|
{
|
|
struct FStackEntry
|
|
{
|
|
double EndTime;
|
|
EventType Event;
|
|
};
|
|
FStackEntry EventStack[SettingsType::MaxDepth];
|
|
uint32 CurrentDepth = 0;
|
|
|
|
EnumerateEventsBackwardsDownSampled(IntervalEnd, IntervalStart, Resolution, [&EventStack, &CurrentDepth, Callback](bool IsEnter, double Time, const EventType& Event)
|
|
{
|
|
if (IsEnter)
|
|
{
|
|
FStackEntry& StackEntry = EventStack[CurrentDepth];
|
|
StackEntry.Event = Event;
|
|
StackEntry.EndTime = Time;
|
|
++CurrentDepth;
|
|
}
|
|
else
|
|
{
|
|
FStackEntry& StackEntry = EventStack[--CurrentDepth];
|
|
EEventEnumerate Ret = Callback(Time, StackEntry.EndTime, CurrentDepth, Event);
|
|
if (Ret != EEventEnumerate::Continue)
|
|
{
|
|
return Ret;
|
|
}
|
|
}
|
|
|
|
return EEventEnumerate::Continue;
|
|
});
|
|
}
|
|
|
|
virtual void EnumerateEventsDownSampledAsync(const typename ITimeline<EventType>::EnumerateAsyncParams& EnumerateAsyncParams) const override
|
|
{
|
|
// Exactly one of these 2 callbacks must be set.
|
|
check((EnumerateAsyncParams.EventRangeCallback == nullptr) ^ (EnumerateAsyncParams.EventCallback == nullptr));
|
|
|
|
if (EnumerateAsyncParams.IntervalEnd < EnumerateAsyncParams.IntervalStart)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int32 DetailLevelIndex = SettingsType::DetailLevelsCount - 1;
|
|
for (; DetailLevelIndex > 0; --DetailLevelIndex)
|
|
{
|
|
if (DetailLevels[DetailLevelIndex].Resolution <= EnumerateAsyncParams.Resolution)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
const FDetailLevel& DetailLevel = DetailLevels[DetailLevelIndex];
|
|
if (DetailLevel.ScopeEntries.Num() == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
uint64 FirstScopePageIndex = Algo::UpperBoundBy(DetailLevel.ScopeEntries, EnumerateAsyncParams.IntervalStart, [](const FEventScopeEntryPage& Page)
|
|
{
|
|
return Page.BeginTime;
|
|
});
|
|
|
|
if (FirstScopePageIndex > 0)
|
|
{
|
|
--FirstScopePageIndex;
|
|
}
|
|
|
|
uint64 LastScopePageIndex = Algo::UpperBoundBy(DetailLevel.ScopeEntries, EnumerateAsyncParams.IntervalEnd, [](const FEventScopeEntryPage& Page)
|
|
{
|
|
return Page.BeginTime;
|
|
});
|
|
|
|
TArray<TSharedRef<FAsyncTask<FEnumarateAsyncTask>>> WorkerTasks;
|
|
|
|
check(LastScopePageIndex >= FirstScopePageIndex);
|
|
uint32 NumPages = FMath::Max(static_cast<uint32>(LastScopePageIndex - FirstScopePageIndex), 1u);
|
|
uint32 NumThreads = GThreadPool->GetNumThreads();
|
|
|
|
if (EnumerateAsyncParams.MaxOccupancy > 0.0f)
|
|
{
|
|
NumThreads = FMath::Max(static_cast<uint32>(NumThreads * EnumerateAsyncParams.MaxOccupancy), 1u);
|
|
}
|
|
|
|
uint32 NumTasks = FMath::Min(NumThreads, NumPages);
|
|
|
|
uint32 PagesPerTask = NumPages / NumTasks;
|
|
uint32 RemainingPages = NumPages % NumTasks; // The remaining pages will be split between the first tasks
|
|
|
|
EnumerateAsyncParams.SetupCallback(NumTasks);
|
|
|
|
for (uint32 TaskIndex = 0; TaskIndex < NumTasks; ++TaskIndex)
|
|
{
|
|
uint32 NrPagesToProcess = TaskIndex < RemainingPages ? PagesPerTask + 1 : PagesPerTask;
|
|
|
|
FAsyncEnumerateTaskData<EventType, SettingsType> TaskData;
|
|
TaskData.TaskIndex = TaskIndex;
|
|
TaskData.NumTasks = NumTasks;
|
|
TaskData.StartPageIndex = FirstScopePageIndex;
|
|
TaskData.EndPageIndex = FMath::Min(LastScopePageIndex, FirstScopePageIndex + NrPagesToProcess);
|
|
TaskData.StartTime = EnumerateAsyncParams.IntervalStart;
|
|
TaskData.EndTime = EnumerateAsyncParams.IntervalEnd;
|
|
TaskData.SortOrder = EnumerateAsyncParams.SortOrder;
|
|
TaskData.DetailLevel = &DetailLevel;
|
|
TaskData.EventCallback = EnumerateAsyncParams.EventCallback;
|
|
TaskData.EventRangeCallback = EnumerateAsyncParams.EventRangeCallback;
|
|
|
|
TSharedRef<FAsyncTask<FEnumarateAsyncTask>> AsyncTask = MakeShared<FAsyncTask<FEnumarateAsyncTask>>(TaskData);
|
|
WorkerTasks.Add(AsyncTask);
|
|
AsyncTask->StartBackgroundTask();
|
|
|
|
FirstScopePageIndex += NrPagesToProcess;
|
|
}
|
|
|
|
for (uint32 TaskIndex = 0; TaskIndex < NumTasks; ++TaskIndex)
|
|
{
|
|
WorkerTasks[TaskIndex]->EnsureCompletion();
|
|
}
|
|
}
|
|
|
|
virtual void EnumerateEventsDownSampled(double IntervalStart, double IntervalEnd, double Resolution, typename ITimeline<EventType>::EventRangeCallback Callback) const override
|
|
{
|
|
struct FStackEntry
|
|
{
|
|
uint64 LocalEventIndex;
|
|
};
|
|
FStackEntry EventStack[SettingsType::MaxDepth];
|
|
uint32 CurrentDepth = 0;
|
|
|
|
struct FOutputEvent
|
|
{
|
|
double StartTime;
|
|
double EndTime;
|
|
uint32 Depth;
|
|
EventType Event;
|
|
};
|
|
TArray<FOutputEvent> OutputEvents;
|
|
|
|
EnumerateEventsDownSampled(IntervalStart, IntervalEnd, Resolution, [&EventStack, &OutputEvents, &CurrentDepth, Callback](bool IsEnter, double Time, const EventType& Event)
|
|
{
|
|
if (IsEnter)
|
|
{
|
|
FStackEntry& StackEntry = EventStack[CurrentDepth];
|
|
StackEntry.LocalEventIndex = OutputEvents.Num();
|
|
FOutputEvent& OutputEvent = OutputEvents.AddDefaulted_GetRef();
|
|
OutputEvent.StartTime = Time;
|
|
OutputEvent.EndTime = Time;
|
|
OutputEvent.Depth = CurrentDepth;
|
|
OutputEvent.Event = Event;
|
|
++CurrentDepth;
|
|
}
|
|
else
|
|
{
|
|
{
|
|
FStackEntry& StackEntry = EventStack[--CurrentDepth];
|
|
FOutputEvent* OutputEvent = OutputEvents.GetData() + StackEntry.LocalEventIndex;
|
|
OutputEvent->EndTime = Time;
|
|
}
|
|
if (CurrentDepth == 0)
|
|
{
|
|
for (FOutputEvent& OutputEvent : OutputEvents)
|
|
{
|
|
EEventEnumerate Ret = Callback(OutputEvent.StartTime, OutputEvent.EndTime, OutputEvent.Depth, OutputEvent.Event);
|
|
if (Ret != EEventEnumerate::Continue)
|
|
{
|
|
return Ret;
|
|
}
|
|
}
|
|
OutputEvents.Empty(OutputEvents.Num());
|
|
}
|
|
}
|
|
|
|
return EEventEnumerate::Continue;
|
|
});
|
|
}
|
|
|
|
virtual void EnumerateEvents(double IntervalStart, double IntervalEnd, typename ITimeline<EventType>::EventCallback Callback) const override
|
|
{
|
|
EnumerateEventsDownSampled(IntervalStart, IntervalEnd, 0.0, Callback);
|
|
}
|
|
|
|
virtual void EnumerateEvents(double IntervalStart, double IntervalEnd, typename ITimeline<EventType>::EventRangeCallback Callback) const override
|
|
{
|
|
EnumerateEventsDownSampled(IntervalStart, IntervalEnd, 0.0, Callback);
|
|
}
|
|
|
|
virtual void EnumerateEventsBackwards(double IntervalEnd, double IntervalStart, typename ITimeline<EventType>::EventCallback Callback) const override
|
|
{
|
|
EnumerateEventsBackwardsDownSampled(IntervalEnd, IntervalStart, 0.0, Callback);
|
|
}
|
|
|
|
virtual void EnumerateEventsBackwards(double IntervalEnd, double IntervalStart, typename ITimeline<EventType>::EventRangeCallback Callback) const override
|
|
{
|
|
EnumerateEventsBackwardsDownSampled(IntervalEnd, IntervalStart, 0.0, Callback);
|
|
}
|
|
|
|
virtual bool GetEventInfo(double InTime, double DeltaTime, int32 Depth, typename ITimeline<InEventType>::FTimelineEventInfo& EventInfo) const override
|
|
{
|
|
if (Depth >= SettingsType::MaxDepth || Depth < 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const FDetailLevel& DetailLevel = DetailLevels[0];
|
|
|
|
if (DetailLevel.ScopeEntries.Num() == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (DetailLevel.InsertionState.LastTime < InTime - DeltaTime)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
uint64 FirstScopePageIndex = Algo::UpperBoundBy(DetailLevel.ScopeEntries, InTime, [](const FEventScopeEntryPage& Page)
|
|
{
|
|
return Page.BeginTime;
|
|
});
|
|
|
|
if (FirstScopePageIndex > 0)
|
|
{
|
|
--FirstScopePageIndex;
|
|
}
|
|
|
|
auto ScopeEntryIterator = DetailLevel.ScopeEntries.GetIteratorFromPage(FirstScopePageIndex);
|
|
|
|
FEventStackEntry OutScopeEntry;
|
|
bool bIsFound = FindEventUsingPageInitialStack(ScopeEntryIterator, InTime, DeltaTime, Depth, DetailLevel, OutScopeEntry);
|
|
if (bIsFound)
|
|
{
|
|
EventInfo.StartTime = DetailLevel.GetScopeEntryTime(OutScopeEntry.EnterScopeIndex);
|
|
EventInfo.ExclTime = OutScopeEntry.ExclTime;
|
|
EventInfo.EndTime = OutScopeEntry.EndTime;
|
|
EventInfo.Event = DetailLevel.GetEvent(OutScopeEntry.EventIndex);
|
|
|
|
if (EventInfo.EndTime < 0)
|
|
{
|
|
//The end of the event has not been reached by analysis
|
|
EventInfo.EndTime = DetailLevel.InsertionState.LastTime;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
const FEventScopeEntryPage* ScopePage = ScopeEntryIterator.GetCurrentPage();
|
|
double IntervalStart = FMath::Max(InTime - DeltaTime, 0.0);
|
|
|
|
while (ScopePage->BeginTime > IntervalStart && FirstScopePageIndex > 0)
|
|
{
|
|
--FirstScopePageIndex;
|
|
ScopeEntryIterator = DetailLevel.ScopeEntries.GetIteratorFromPage(FirstScopePageIndex);
|
|
ScopePage = DetailLevel.ScopeEntries.GetPage(FirstScopePageIndex);
|
|
}
|
|
|
|
auto EventsIterator = DetailLevel.Events.GetIteratorFromItem(ScopePage->BeginEventIndex);
|
|
FEventInfoStackEntry EventStack[SettingsType::MaxDepth];
|
|
|
|
int32 CurrentStackDepth = ScopePage->InitialStackCount;
|
|
for (int32 InitialStackIndex = 0; InitialStackIndex < CurrentStackDepth; ++InitialStackIndex)
|
|
{
|
|
EventStack[InitialStackIndex] = FEventInfoStackEntry(ScopePage->InitialStack[InitialStackIndex], DetailLevel);
|
|
}
|
|
|
|
const FEventScopeEntry* ScopeEntry = ScopeEntryIterator.GetCurrentItem();
|
|
const EventType* Event = EventsIterator.GetCurrentItem();
|
|
double LastTime = 0.0;
|
|
auto LastTimeIterator = ScopeEntryIterator;
|
|
if (LastTimeIterator.PrevItem())
|
|
{
|
|
LastTime = abs(LastTimeIterator.GetCurrentItem()->Time);
|
|
}
|
|
|
|
//Iterate from the start of the page to the start time of our interval
|
|
while (ScopeEntry && FMath::Abs(ScopeEntry->Time) <= IntervalStart)
|
|
{
|
|
if (ScopeEntry->Time < 0.0)
|
|
{
|
|
check(CurrentStackDepth < SettingsType::MaxDepth);
|
|
FEventInfoStackEntry& StackEntry = EventStack[CurrentStackDepth++];
|
|
StackEntry.Event = *Event;
|
|
StackEntry.StartTime = -ScopeEntry->Time;
|
|
StackEntry.ExclTime = 0.0;
|
|
if (CurrentStackDepth > 1)
|
|
{
|
|
FEventInfoStackEntry& ParentStackEntry = EventStack[CurrentStackDepth - 2];
|
|
ParentStackEntry.ExclTime += StackEntry.StartTime - LastTime;
|
|
}
|
|
Event = EventsIterator.NextItem();
|
|
LastTime = -ScopeEntry->Time;
|
|
}
|
|
else
|
|
{
|
|
check(CurrentStackDepth > 0);
|
|
--CurrentStackDepth;
|
|
|
|
if (CurrentStackDepth == Depth)
|
|
{
|
|
FEventInfoStackEntry& StackEntry = EventStack[CurrentStackDepth];
|
|
StackEntry.ExclTime += ScopeEntry->Time - LastTime;
|
|
}
|
|
LastTime = ScopeEntry->Time;
|
|
}
|
|
|
|
ScopeEntry = ScopeEntryIterator.NextItem();
|
|
}
|
|
|
|
//Check if we have an event between InTime - InDeltaTime and InTime + InDeltaTime
|
|
FFindBestMatchEventInParams InParams;
|
|
InParams.IterationState.ScopeIterator = ScopeEntryIterator;
|
|
InParams.IterationState.EventsIterator = EventsIterator;
|
|
InParams.IterationState.EventStack = EventStack;
|
|
InParams.IterationState.StackDepth = CurrentStackDepth;
|
|
InParams.IterationState.LastIterationTime = LastTime;
|
|
|
|
InParams.TargetExactTime = InTime;
|
|
InParams.TargetEndTime = InTime + DeltaTime;
|
|
InParams.TargetDepth = Depth;
|
|
|
|
FFindBestMatchEventOutParams OutParams;
|
|
|
|
bool bMatchFound = FindBestMatchEvent(InParams, OutParams);
|
|
if (!bMatchFound)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool bMatchEventStartsInsidePage = true;
|
|
if (ScopePage->InitialStackCount > Depth)
|
|
{
|
|
double StartTime = DetailLevel.GetScopeEntryTime(ScopePage->InitialStack[Depth].EnterScopeIndex);
|
|
if (StartTime == OutParams.EventInfo.StartTime)
|
|
{
|
|
bMatchEventStartsInsidePage = false;
|
|
}
|
|
}
|
|
|
|
//If we have found both start time and end time for our event, we can return the result
|
|
if (OutParams.bHasEndTime && bMatchEventStartsInsidePage)
|
|
{
|
|
EventInfo.StartTime = OutParams.EventInfo.StartTime;
|
|
EventInfo.EndTime = OutParams.EndTime;
|
|
EventInfo.ExclTime = OutParams.EventInfo.ExclTime;
|
|
EventInfo.Event = OutParams.EventInfo.Event;
|
|
|
|
if (EventInfo.EndTime < 0)
|
|
{
|
|
//The end of the event has not been reached by analysis
|
|
EventInfo.EndTime = DetailLevel.InsertionState.LastTime;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//We continue searching for the end scope event
|
|
ScopeEntryIterator = OutParams.IterationState.ScopeIterator;
|
|
EventsIterator = OutParams.IterationState.EventsIterator;
|
|
CurrentStackDepth = OutParams.IterationState.StackDepth;
|
|
LastTime = OutParams.IterationState.LastIterationTime;
|
|
|
|
FEventInfoStackEntry TargetEntry = OutParams.EventInfo;
|
|
|
|
auto ScopeEntryIteratorAtEvent = ScopeEntryIterator;
|
|
|
|
//We find the page where the target event ends
|
|
ScopeEntryIterator = DetailLevel.ScopeEntries.GetIteratorFromPage(FirstScopePageIndex);
|
|
auto EventLastPageIterator = ScopeEntryIterator;
|
|
|
|
while (const FEventScopeEntryPage* CurrentScopePage = ScopeEntryIterator.NextPage())
|
|
{
|
|
if (CurrentScopePage->InitialStackCount <= Depth)
|
|
{
|
|
break;
|
|
}
|
|
|
|
double StartTime = DetailLevel.GetScopeEntryTime(CurrentScopePage->InitialStack[Depth].EnterScopeIndex);
|
|
if (StartTime != TargetEntry.StartTime)
|
|
{
|
|
break;
|
|
}
|
|
|
|
EventLastPageIterator = ScopeEntryIterator;
|
|
}
|
|
|
|
if (EventLastPageIterator.GetCurrentPageIndex() != FirstScopePageIndex || !bMatchEventStartsInsidePage)
|
|
{
|
|
//If the end scope event is on a different page than the start scope one we can get the event info from the InitialStack of the last page
|
|
ScopeEntryIterator = EventLastPageIterator;
|
|
ScopePage = ScopeEntryIterator.GetCurrentPage();
|
|
|
|
check(ScopePage->InitialStackCount > Depth);
|
|
FEventStackEntry& TargetStackEntry = ScopePage->InitialStack[Depth];
|
|
EventInfo.StartTime = DetailLevel.GetScopeEntryTime(TargetStackEntry.EnterScopeIndex);
|
|
EventInfo.EndTime = TargetStackEntry.EndTime;
|
|
EventInfo.ExclTime = TargetStackEntry.ExclTime;
|
|
EventInfo.Event = DetailLevel.GetEvent(TargetStackEntry.EventIndex);
|
|
|
|
if (EventInfo.EndTime < 0)
|
|
{
|
|
//The end of the event has not been reached by analysis
|
|
EventInfo.EndTime = DetailLevel.InsertionState.LastTime;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
//If the end scope event is in the same page, we continue iterating from the point where FindBestMatchEvent stopped
|
|
ScopeEntryIterator = ScopeEntryIteratorAtEvent;
|
|
ScopeEntry = ScopeEntryIterator.GetCurrentItem();
|
|
}
|
|
|
|
while (ScopeEntry &&
|
|
Depth < CurrentStackDepth)
|
|
{
|
|
if (ScopeEntry->Time < 0.0)
|
|
{
|
|
check(CurrentStackDepth < SettingsType::MaxDepth);
|
|
FEventInfoStackEntry& StackEntry = EventStack[CurrentStackDepth++];
|
|
StackEntry.Event = *Event;
|
|
StackEntry.StartTime = -ScopeEntry->Time;
|
|
if (CurrentStackDepth > 1)
|
|
{
|
|
FEventInfoStackEntry& ParentStackEntry = EventStack[CurrentStackDepth - 2];
|
|
ParentStackEntry.ExclTime += StackEntry.StartTime - LastTime;
|
|
}
|
|
Event = EventsIterator.NextItem();
|
|
LastTime = -ScopeEntry->Time;
|
|
}
|
|
else
|
|
{
|
|
check(CurrentStackDepth > 0);
|
|
--CurrentStackDepth;
|
|
if (CurrentStackDepth == Depth)
|
|
{
|
|
FEventInfoStackEntry& StackEntry = EventStack[CurrentStackDepth];
|
|
StackEntry.ExclTime += ScopeEntry->Time - LastTime;
|
|
}
|
|
|
|
LastTime = ScopeEntry->Time;
|
|
}
|
|
ScopeEntry = ScopeEntryIterator.NextItem();
|
|
}
|
|
|
|
TargetEntry = EventStack[Depth];
|
|
|
|
EventInfo.StartTime = TargetEntry.StartTime;
|
|
EventInfo.EndTime = LastTime;
|
|
EventInfo.ExclTime = TargetEntry.ExclTime;
|
|
EventInfo.Event = TargetEntry.Event;
|
|
|
|
return true;
|
|
}
|
|
|
|
virtual int32 GetDepthAt(double Time) const override
|
|
{
|
|
const FDetailLevel& DetailLevel = DetailLevels[0];
|
|
if (DetailLevel.ScopeEntries.Num() == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
uint64 FirstScopePageIndex = Algo::UpperBoundBy(DetailLevel.ScopeEntries, Time, [](const FEventScopeEntryPage& Page)
|
|
{
|
|
return Page.BeginTime;
|
|
});
|
|
if (FirstScopePageIndex > 0)
|
|
{
|
|
--FirstScopePageIndex;
|
|
}
|
|
const FEventScopeEntryPage* ScopePage = DetailLevel.ScopeEntries.GetPage(FirstScopePageIndex);
|
|
if (ScopePage->BeginTime > Time)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int32 CurrentStackDepth = ScopePage->InitialStackCount;
|
|
|
|
auto ScopeEntryIterator = DetailLevel.ScopeEntries.GetIteratorFromPage(FirstScopePageIndex);
|
|
const FEventScopeEntry* ScopeEntry = ScopeEntryIterator.GetCurrentItem();
|
|
while (ScopeEntry && FMath::Abs(ScopeEntry->Time) < Time)
|
|
{
|
|
if (ScopeEntry->Time < 0.0)
|
|
{
|
|
CurrentStackDepth++;
|
|
}
|
|
else
|
|
{
|
|
check(CurrentStackDepth > 0);
|
|
--CurrentStackDepth;
|
|
}
|
|
ScopeEntry = ScopeEntryIterator.NextItem();
|
|
}
|
|
|
|
return CurrentStackDepth;
|
|
}
|
|
|
|
private:
|
|
|
|
struct FIterationState
|
|
{
|
|
typename TPagedArray<FEventScopeEntry, FEventScopeEntryPage>::TIterator ScopeIterator;
|
|
typename TPagedArray<EventType>::TIterator EventsIterator;
|
|
FEventInfoStackEntry* EventStack;
|
|
int32 StackDepth;
|
|
double LastIterationTime;
|
|
};
|
|
|
|
struct FFindBestMatchEventInParams
|
|
{
|
|
FIterationState IterationState;
|
|
|
|
double TargetExactTime;
|
|
double TargetEndTime;
|
|
int32 TargetDepth;
|
|
};
|
|
|
|
struct FFindBestMatchEventOutParams
|
|
{
|
|
FIterationState IterationState;
|
|
|
|
FEventInfoStackEntry EventInfo;
|
|
bool bHasEndTime;
|
|
double EndTime;
|
|
};
|
|
|
|
void UpdateDominatingEvent(FDetailLevel& DetailLevel, int32 Depth, double CurrentTime)
|
|
{
|
|
FDetailLevelDepthState& Lod0DepthState = DetailLevels[0].InsertionState.DepthStates[Depth];
|
|
double Lod0EventDuration = CurrentTime - Lod0DepthState.EnterTime;
|
|
FDetailLevelDepthState& CurrentDepthState = DetailLevel.InsertionState.DepthStates[Depth];
|
|
if (Lod0EventDuration > CurrentDepthState.DominatingEventDuration)
|
|
{
|
|
check(CurrentDepthState.PendingScopeEnterIndex >= 0);
|
|
check(CurrentDepthState.PendingEventIndex >= 0);
|
|
|
|
CurrentDepthState.DominatingEvent = Lod0DepthState.DominatingEvent;
|
|
CurrentDepthState.DominatingEventStartTime = Lod0DepthState.EnterTime;
|
|
CurrentDepthState.DominatingEventEndTime = CurrentTime;
|
|
CurrentDepthState.DominatingEventDuration = Lod0EventDuration;
|
|
|
|
DetailLevel.SetEvent(CurrentDepthState.PendingEventIndex, CurrentDepthState.DominatingEvent);
|
|
|
|
//CurrentDepthState.DebugDominatingEventType = Owner.EventTypes[CurrentDepthState.DominatingEventType];
|
|
}
|
|
}
|
|
|
|
void AddScopeEntry(FDetailLevel& DetailLevel, double Time, bool IsEnter)
|
|
{
|
|
checkf(Time >= DetailLevel.InsertionState.LastTime, TEXT("Time=%.9f LastTime=%.9f"), Time, DetailLevel.InsertionState.LastTime);
|
|
|
|
uint64 EventIndex = DetailLevel.Events.Num();
|
|
uint64 ScopeIndex = DetailLevel.ScopeEntries.Num();
|
|
|
|
FEventScopeEntry& ScopeEntry = DetailLevel.ScopeEntries.PushBack();
|
|
ScopeEntry.Time = IsEnter ? -Time : Time;
|
|
FEventScopeEntryPage* LastPage = DetailLevel.ScopeEntries.GetLastPage();
|
|
uint64 LastPageIndex = DetailLevel.ScopeEntries.NumPages() - 1;
|
|
if (LastPageIndex != DetailLevel.InsertionState.CurrentScopeEntryPageIndex)
|
|
{
|
|
// At the very first call, CurrentScopeEntryPage will be -1
|
|
if (DetailLevel.InsertionState.CurrentScopeEntryPageIndex != (uint64) -1)
|
|
{
|
|
FEventScopeEntryPage* CurrentScopeEntryPage = DetailLevel.ScopeEntries.GetPage(DetailLevel.InsertionState.CurrentScopeEntryPageIndex);
|
|
int32 PreviousPageInitialStackCount = CurrentScopeEntryPage->InitialStackCount;
|
|
check(DetailLevel.InsertionState.CurrentDepth <= SettingsType::MaxDepth);
|
|
int32 CurrentDepth = DetailLevel.InsertionState.CurrentDepth;
|
|
// Update the open scopes that were also open at the beginning of the last page so the values
|
|
// represent stats up to and including the current page
|
|
int ii = 0;
|
|
for (; ii < PreviousPageInitialStackCount && ii < CurrentDepth; ++ii)
|
|
{
|
|
FEventStackEntry& InsertionStateStackEntry = DetailLevel.InsertionState.EventStack[ii];
|
|
FEventStackEntry& PreviousPageStackEntry = CurrentScopeEntryPage->InitialStack[ii];
|
|
|
|
if (InsertionStateStackEntry.EnterScopeIndex == PreviousPageStackEntry.EnterScopeIndex
|
|
&& InsertionStateStackEntry.EventIndex == PreviousPageStackEntry.EventIndex)
|
|
{
|
|
PreviousPageStackEntry.ExclTime = InsertionStateStackEntry.ExclTime;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
DetailLevel.InsertionState.CurrentScopeEntryPageIndex = LastPageIndex;
|
|
LastPage->BeginTime = Time;
|
|
LastPage->BeginEventIndex = DetailLevel.Events.Num();
|
|
LastPage->EndEventIndex = LastPage->BeginEventIndex;
|
|
LastPage->InitialStackCount = DetailLevel.InsertionState.CurrentDepth;
|
|
if (LastPage->InitialStackCount)
|
|
{
|
|
LastPage->InitialStack = reinterpret_cast<FEventStackEntry*>(Allocator.Allocate(LastPage->InitialStackCount * sizeof(FEventStackEntry)));
|
|
memcpy(LastPage->InitialStack, DetailLevel.InsertionState.EventStack, LastPage->InitialStackCount * sizeof(FEventStackEntry));
|
|
}
|
|
}
|
|
LastPage->EndTime = Time;
|
|
|
|
if (IsEnter)
|
|
{
|
|
++DetailLevel.InsertionState.CurrentDepth;
|
|
check(DetailLevel.InsertionState.CurrentDepth <= SettingsType::MaxDepth);
|
|
|
|
FEventStackEntry& StackEntry = DetailLevel.InsertionState.EventStack[DetailLevel.InsertionState.CurrentDepth - 1];
|
|
StackEntry.EventIndex = EventIndex;
|
|
StackEntry.EnterScopeIndex = ScopeIndex;
|
|
StackEntry.ExclTime = 0.0;
|
|
StackEntry.EndTime = -1.0;
|
|
|
|
if (DetailLevel.InsertionState.CurrentDepth > 1)
|
|
{
|
|
FEventStackEntry& ParentStackEntry = DetailLevel.InsertionState.EventStack[DetailLevel.InsertionState.CurrentDepth - 2];
|
|
ParentStackEntry.ExclTime += Time - DetailLevel.InsertionState.LastTime;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
check(DetailLevel.InsertionState.CurrentDepth <= SettingsType::MaxDepth);
|
|
check(DetailLevel.InsertionState.CurrentDepth > 0);
|
|
--DetailLevel.InsertionState.CurrentDepth;
|
|
|
|
FEventStackEntry& StackEntry = DetailLevel.InsertionState.EventStack[DetailLevel.InsertionState.CurrentDepth];
|
|
StackEntry.ExclTime += Time - DetailLevel.InsertionState.LastTime;
|
|
|
|
FEventScopeEntryPage* CurrentScopeEntryPage = DetailLevel.ScopeEntries.GetPage(DetailLevel.InsertionState.CurrentScopeEntryPageIndex);
|
|
if (DetailLevel.InsertionState.CurrentDepth < CurrentScopeEntryPage->InitialStackCount)
|
|
{
|
|
FEventStackEntry& PreviousPageStackEntry = CurrentScopeEntryPage->InitialStack[DetailLevel.InsertionState.CurrentDepth];
|
|
|
|
if (StackEntry.EnterScopeIndex == PreviousPageStackEntry.EnterScopeIndex
|
|
&& StackEntry.EventIndex == PreviousPageStackEntry.EventIndex)
|
|
{
|
|
PreviousPageStackEntry.ExclTime = StackEntry.ExclTime;
|
|
PreviousPageStackEntry.EndTime = Time;
|
|
}
|
|
}
|
|
}
|
|
|
|
DetailLevel.InsertionState.LastTime = Time;
|
|
|
|
//ScopeEntry.DebugDepth = DetailLevel.InsertionState.CurrentDepth;
|
|
}
|
|
|
|
void AddEvent(FDetailLevel& DetailLevel, const EventType& Event)
|
|
{
|
|
FEventScopeEntryPage* CurrentScopeEntryPage = DetailLevel.ScopeEntries.GetPage(DetailLevel.InsertionState.CurrentScopeEntryPageIndex);
|
|
++CurrentScopeEntryPage->EndEventIndex;
|
|
DetailLevel.Events.PushBack() = Event;
|
|
|
|
//Event.DebugType = Owner.EventTypes[TypeIndex];
|
|
}
|
|
|
|
bool FindEventUsingPageInitialStack(typename TPagedArray<FEventScopeEntry, FEventScopeEntryPage>::TIterator ScopeEntryIterator,
|
|
double Time,
|
|
double DeltaTime,
|
|
int32 Depth,
|
|
const FDetailLevel& DetailLevel,
|
|
FEventStackEntry& OutPageStackEntry) const
|
|
{
|
|
const FEventScopeEntryPage* CurrentScopePage = ScopeEntryIterator.GetCurrentPage();
|
|
bool bIsInCurrentPageInitStack = false;
|
|
|
|
if (CurrentScopePage->InitialStackCount > Depth)
|
|
{
|
|
FEventStackEntry& CurrentPageStackEntry = CurrentScopePage->InitialStack[Depth];
|
|
if (CurrentPageStackEntry.EndTime < 0 || CurrentPageStackEntry.EndTime > Time)
|
|
{
|
|
bIsInCurrentPageInitStack = true;
|
|
}
|
|
}
|
|
|
|
if (!bIsInCurrentPageInitStack)
|
|
{
|
|
const FEventScopeEntryPage* NextScopePage = ScopeEntryIterator.NextPage();
|
|
if (NextScopePage != nullptr &&
|
|
NextScopePage->InitialStackCount > Depth)
|
|
{
|
|
FEventStackEntry& NextPageStackEntry = NextScopePage->InitialStack[Depth];
|
|
double StartTime = DetailLevel.GetScopeEntryTime(NextPageStackEntry.EnterScopeIndex);
|
|
if (StartTime < Time)
|
|
{
|
|
CurrentScopePage = NextScopePage;
|
|
bIsInCurrentPageInitStack = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bIsInCurrentPageInitStack)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
while (CurrentScopePage->InitialStack[Depth].EndTime < 0)
|
|
{
|
|
const FEventScopeEntryPage* NextScopePage = ScopeEntryIterator.NextPage();
|
|
if (NextScopePage == nullptr)
|
|
{
|
|
break;
|
|
}
|
|
CurrentScopePage = NextScopePage;
|
|
}
|
|
|
|
OutPageStackEntry = CurrentScopePage->InitialStack[Depth];
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FindBestMatchEvent(const FFindBestMatchEventInParams &InParams, FFindBestMatchEventOutParams &OutParams) const
|
|
{
|
|
FIterationState IterationState = InParams.IterationState;
|
|
const FEventScopeEntry* ScopeEntry = IterationState.ScopeIterator.GetCurrentItem();
|
|
const EventType* Event = IterationState.EventsIterator.GetCurrentItem();
|
|
|
|
FEventInfoStackEntry BestMatchEntry;
|
|
double BestMatchEndTime = 0.0;
|
|
bool bHasEndEvent = false;
|
|
|
|
//In the first step, we iterate up to TargetExactTime, storing the last event with our target depth that ended during iteration
|
|
//......]...]....]...TargetExactTime
|
|
while (ScopeEntry && FMath::Abs(ScopeEntry->Time) <= InParams.TargetExactTime)
|
|
{
|
|
if (ScopeEntry->Time < 0.0)
|
|
{
|
|
check(IterationState.StackDepth < SettingsType::MaxDepth);
|
|
FEventInfoStackEntry& StackEntry = IterationState.EventStack[IterationState.StackDepth++];
|
|
StackEntry.Event = *Event;
|
|
StackEntry.StartTime = -ScopeEntry->Time;
|
|
StackEntry.ExclTime = 0.0;
|
|
if (IterationState.StackDepth > 1)
|
|
{
|
|
FEventInfoStackEntry& ParentStackEntry = IterationState.EventStack[IterationState.StackDepth - 2];
|
|
ParentStackEntry.ExclTime += StackEntry.StartTime - IterationState.LastIterationTime;
|
|
}
|
|
Event = IterationState.EventsIterator.NextItem();
|
|
IterationState.LastIterationTime = -ScopeEntry->Time;
|
|
}
|
|
else
|
|
{
|
|
check(IterationState.StackDepth > 0);
|
|
--IterationState.StackDepth;
|
|
|
|
if (IterationState.StackDepth == InParams.TargetDepth)
|
|
{
|
|
FEventInfoStackEntry& StackEntry = IterationState.EventStack[IterationState.StackDepth];
|
|
StackEntry.ExclTime += ScopeEntry->Time - IterationState.LastIterationTime;
|
|
BestMatchEndTime = ScopeEntry->Time;
|
|
BestMatchEntry = StackEntry;
|
|
bHasEndEvent = true;
|
|
}
|
|
IterationState.LastIterationTime = ScopeEntry->Time;
|
|
}
|
|
|
|
ScopeEntry = IterationState.ScopeIterator.NextItem();
|
|
}
|
|
|
|
//If the iteration stack depth is as deep as our target depth, that we have a perfect match,
|
|
//an event that is ongoing at TargetExactTime, so we just return it
|
|
//....[..TargetExactTime.....]
|
|
//The strict '>' comparison is needed because CurrentStackDepth is "RealDepth + 1"
|
|
if (IterationState.StackDepth > InParams.TargetDepth)
|
|
{
|
|
OutParams.EventInfo = IterationState.EventStack[InParams.TargetDepth];
|
|
OutParams.bHasEndTime = false;
|
|
OutParams.IterationState = IterationState;
|
|
|
|
return true;
|
|
}
|
|
|
|
//We continue iterating until TargetEndTime or until we find the start of an event with the target depth
|
|
//TargetExactTime.....[
|
|
bool bHasStartEvent = false;
|
|
while (ScopeEntry && FMath::Abs(ScopeEntry->Time) <= InParams.TargetEndTime)
|
|
{
|
|
if (ScopeEntry->Time < 0.0)
|
|
{
|
|
check(IterationState.StackDepth < SettingsType::MaxDepth);
|
|
FEventInfoStackEntry& StackEntry = IterationState.EventStack[IterationState.StackDepth++];
|
|
StackEntry.Event = *Event;
|
|
StackEntry.StartTime = -ScopeEntry->Time;
|
|
StackEntry.ExclTime = 0.0;
|
|
if (IterationState.StackDepth > 1)
|
|
{
|
|
FEventInfoStackEntry& ParentStackEntry = IterationState.EventStack[IterationState.StackDepth - 2];
|
|
ParentStackEntry.ExclTime += StackEntry.StartTime - IterationState.LastIterationTime;
|
|
}
|
|
|
|
Event = IterationState.EventsIterator.NextItem();
|
|
IterationState.LastIterationTime = -ScopeEntry->Time;
|
|
|
|
if (IterationState.StackDepth == InParams.TargetDepth + 1)
|
|
{
|
|
bHasStartEvent = true;
|
|
ScopeEntry = IterationState.ScopeIterator.NextItem();
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
check(IterationState.StackDepth > 0);
|
|
--IterationState.StackDepth;
|
|
|
|
if (IterationState.StackDepth == InParams.TargetDepth)
|
|
{
|
|
FEventInfoStackEntry& StackEntry = IterationState.EventStack[IterationState.StackDepth];
|
|
StackEntry.ExclTime += ScopeEntry->Time - IterationState.LastIterationTime;
|
|
}
|
|
IterationState.LastIterationTime = ScopeEntry->Time;
|
|
}
|
|
|
|
ScopeEntry = IterationState.ScopeIterator.NextItem();
|
|
}
|
|
|
|
if (bHasStartEvent == false && bHasEndEvent == false)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//We choose the event that is closer to TargetExactTime
|
|
if (bHasStartEvent)
|
|
{
|
|
double EndTimeDelta = InParams.TargetExactTime - BestMatchEndTime;
|
|
double StartTimeDelta = IterationState.LastIterationTime - InParams.TargetExactTime;
|
|
if (!bHasEndEvent || StartTimeDelta < EndTimeDelta)
|
|
{
|
|
//The event that just started is the best match
|
|
bHasEndEvent = false;
|
|
BestMatchEntry = IterationState.EventStack[InParams.TargetDepth];
|
|
}
|
|
}
|
|
|
|
OutParams.EventInfo = BestMatchEntry;
|
|
OutParams.bHasEndTime = bHasEndEvent;
|
|
if (bHasEndEvent)
|
|
{
|
|
OutParams.EndTime = BestMatchEndTime;
|
|
}
|
|
|
|
OutParams.IterationState = IterationState;
|
|
return true;
|
|
}
|
|
|
|
ILinearAllocator& Allocator;
|
|
TArray<FDetailLevel> DetailLevels;
|
|
int32 ExtraDepthEvents = 0; // the number of events virtually pushed on the stack when depth exceeds SettingsType::MaxDepth
|
|
uint64 ModCount = 0; // a serial number increased each time the timeline is modified
|
|
};
|
|
|
|
} // namespace TraceServices
|