2021-08-18 07:36:31 -04:00
|
|
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
|
|
|
|
#include "Pch.h"
|
|
|
|
|
#include "Store.h"
|
|
|
|
|
|
2022-03-21 09:36:32 -04:00
|
|
|
#if TS_USING(TS_PLATFORM_LINUX)
|
|
|
|
|
# include <sys/inotify.h>
|
|
|
|
|
#endif
|
|
|
|
|
#if TS_USING(TS_PLATFORM_MAC)
|
|
|
|
|
# include <CoreServices/CoreServices.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
2021-08-18 10:20:33 -04:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Pre-C++20 there is now way to convert between clocks. C++20 onwards it is
|
|
|
|
|
// possible to create a clock with an Unreal epoch and convert file times to it.
|
|
|
|
|
// But at time of writing, C++20 isn't complete enough across the board.
|
|
|
|
|
static const uint64 UnrealEpochYear = 1;
|
|
|
|
|
#if TS_USING(TS_PLATFORM_WINDOWS)
|
|
|
|
|
static const uint64 FsEpochYear = 1601;
|
|
|
|
|
#else
|
|
|
|
|
static const uint64 FsEpochYear = 1970;
|
|
|
|
|
#endif
|
|
|
|
|
static int64 FsToUnrealEpochBiasSeconds = uint64(double(FsEpochYear - UnrealEpochYear) * 365.2425) * 86400;
|
|
|
|
|
|
2021-08-18 07:36:31 -04:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
#if TS_USING(TS_PLATFORM_WINDOWS)
|
|
|
|
|
class FStore::FDirWatcher
|
|
|
|
|
: public asio::windows::object_handle
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
using asio::windows::object_handle::object_handle;
|
|
|
|
|
};
|
2022-03-21 09:36:32 -04:00
|
|
|
#elif TS_USING(TS_PLATFORM_LINUX)
|
2021-08-18 07:36:31 -04:00
|
|
|
class FStore::FDirWatcher
|
2022-03-21 09:36:32 -04:00
|
|
|
: public asio::posix::stream_descriptor
|
2021-08-18 07:36:31 -04:00
|
|
|
{
|
2022-03-21 09:36:32 -04:00
|
|
|
typedef std::function<void(asio::error_code error)> HandlerType;
|
2021-08-18 07:36:31 -04:00
|
|
|
public:
|
2022-03-21 09:36:32 -04:00
|
|
|
using asio::posix::stream_descriptor::stream_descriptor;
|
|
|
|
|
void async_wait(HandlerType InHandler)
|
|
|
|
|
{
|
|
|
|
|
asio::posix::stream_descriptor::async_wait(asio::posix::stream_descriptor::wait_read, InHandler);
|
|
|
|
|
}
|
2021-08-18 07:36:31 -04:00
|
|
|
};
|
2022-03-21 09:36:32 -04:00
|
|
|
#elif TS_USING(TS_PLATFORM_MAC)
|
|
|
|
|
class FStore::FDirWatcher
|
|
|
|
|
{
|
|
|
|
|
typedef std::function<void(asio::error_code error)> HandlerType;
|
|
|
|
|
public:
|
|
|
|
|
FDirWatcher(const char* InStoreDir)
|
|
|
|
|
{
|
|
|
|
|
std::error_code ErrorCode;
|
|
|
|
|
StoreDir = std::filesystem::absolute(InStoreDir, ErrorCode);
|
|
|
|
|
}
|
|
|
|
|
void async_wait(HandlerType InHandler);
|
|
|
|
|
void cancel()
|
|
|
|
|
{
|
|
|
|
|
close();
|
|
|
|
|
}
|
|
|
|
|
void close()
|
|
|
|
|
{
|
|
|
|
|
if (bIsRunning)
|
|
|
|
|
{
|
|
|
|
|
FSEventStreamStop(EventStream);
|
|
|
|
|
FSEventStreamUnscheduleFromRunLoop(EventStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
|
|
|
|
FSEventStreamInvalidate(EventStream);
|
|
|
|
|
FSEventStreamRelease(EventStream);
|
|
|
|
|
bIsRunning = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
bool is_open() { return bIsRunning; }
|
|
|
|
|
|
|
|
|
|
void ProcessChanges(size_t EventCount, void* EventPaths, const FSEventStreamEventFlags EventFlags[])
|
|
|
|
|
{
|
|
|
|
|
Handler(asio::error_code());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FSEventStreamRef EventStream;
|
|
|
|
|
HandlerType Handler;
|
|
|
|
|
private:
|
|
|
|
|
bool bIsRunning = false;
|
|
|
|
|
std::filesystem::path StoreDir;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void MacCallback(ConstFSEventStreamRef StreamRef,
|
|
|
|
|
void* InDirWatcherPtr,
|
|
|
|
|
size_t EventCount,
|
|
|
|
|
void* EventPaths,
|
|
|
|
|
const FSEventStreamEventFlags EventFlags[],
|
|
|
|
|
const FSEventStreamEventId EventIDs[])
|
|
|
|
|
{
|
|
|
|
|
FStore::FDirWatcher* DirWatcherPtr = (FStore::FDirWatcher*)InDirWatcherPtr;
|
|
|
|
|
check(DirWatcherPtr);
|
|
|
|
|
check(DirWatcherPtr->EventStream == StreamRef);
|
|
|
|
|
|
|
|
|
|
DirWatcherPtr->ProcessChanges(EventCount, EventPaths, EventFlags);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FStore::FDirWatcher::async_wait(HandlerType InHandler)
|
|
|
|
|
{
|
|
|
|
|
if (bIsRunning)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CFAbsoluteTime Latency = 0.2; // seconds
|
|
|
|
|
|
|
|
|
|
FSEventStreamContext Context;
|
|
|
|
|
Context.version = 0;
|
|
|
|
|
Context.info = this;
|
|
|
|
|
Context.retain = NULL;
|
|
|
|
|
Context.release = NULL;
|
|
|
|
|
Context.copyDescription = NULL;
|
|
|
|
|
|
|
|
|
|
// Set up streaming and turn it on
|
|
|
|
|
std::string Path = StoreDir;
|
|
|
|
|
CFStringRef FullPathMac = CFStringCreateWithBytes(
|
|
|
|
|
kCFAllocatorDefault,
|
|
|
|
|
(const uint8*)Path.data(),
|
|
|
|
|
Path.size(),
|
|
|
|
|
kCFStringEncodingUnicode,
|
|
|
|
|
false);
|
|
|
|
|
|
|
|
|
|
CFArrayRef PathsToWatch = CFArrayCreate(NULL, (const void**)&FullPathMac, 1, NULL);
|
|
|
|
|
|
|
|
|
|
EventStream = FSEventStreamCreate(NULL,
|
|
|
|
|
&MacCallback,
|
|
|
|
|
&Context,
|
|
|
|
|
PathsToWatch,
|
|
|
|
|
kFSEventStreamEventIdSinceNow,
|
|
|
|
|
Latency,
|
|
|
|
|
kFSEventStreamCreateFlagUseCFTypes | kFSEventStreamCreateFlagNoDefer
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
FSEventStreamScheduleWithRunLoop(EventStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
|
|
|
|
FSEventStreamStart(EventStream);
|
|
|
|
|
bIsRunning = true;
|
|
|
|
|
|
|
|
|
|
Handler = InHandler;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2021-08-18 07:36:31 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
FStore::FTrace::FTrace(const char* InPath)
|
|
|
|
|
: Path(InPath)
|
|
|
|
|
{
|
|
|
|
|
// Extract the trace's name
|
|
|
|
|
const char* Dot = std::strrchr(*Path, '.');
|
|
|
|
|
if (Dot == nullptr)
|
|
|
|
|
{
|
|
|
|
|
Dot = *Path;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const char* c = Dot; c > *Path; --c)
|
|
|
|
|
{
|
|
|
|
|
if (c[-1] == '\\' || c[-1] == '/')
|
|
|
|
|
{
|
|
|
|
|
Name = FStringView(c, int32(Dot - c));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Id = QuickStoreHash(Name);
|
|
|
|
|
|
2021-08-18 10:20:33 -04:00
|
|
|
// Calculate that trace's timestamp. Bias in seconds then convert to 0.1us.
|
2021-08-18 07:36:31 -04:00
|
|
|
std::filesystem::file_time_type LastWriteTime = std::filesystem::last_write_time(InPath);
|
2021-08-18 10:20:33 -04:00
|
|
|
auto LastWriteDuration = LastWriteTime.time_since_epoch();
|
|
|
|
|
Timestamp = std::chrono::duration_cast<std::chrono::seconds>(LastWriteDuration).count();
|
|
|
|
|
Timestamp += FsToUnrealEpochBiasSeconds;
|
|
|
|
|
Timestamp *= 10'000'000;
|
2021-08-18 07:36:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
const FStringView& FStore::FTrace::GetName() const
|
|
|
|
|
{
|
|
|
|
|
return Name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
uint32 FStore::FTrace::GetId() const
|
|
|
|
|
{
|
|
|
|
|
return Id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
uint64 FStore::FTrace::GetSize() const
|
|
|
|
|
{
|
|
|
|
|
return std::filesystem::file_size(*Path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
uint64 FStore::FTrace::GetTimestamp() const
|
|
|
|
|
{
|
|
|
|
|
return Timestamp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
2022-03-21 09:36:32 -04:00
|
|
|
FStore::FMount::FMount(const fs::path& InDir)
|
|
|
|
|
: Id(QuickStoreHash(InDir.c_str()))
|
2021-08-18 07:36:31 -04:00
|
|
|
{
|
2022-03-21 09:36:32 -04:00
|
|
|
std::error_code ErrorCode;
|
|
|
|
|
Dir = fs::absolute(InDir, ErrorCode);
|
|
|
|
|
fs::create_directories(Dir, ErrorCode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
FString FStore::FMount::GetDir() const
|
|
|
|
|
{
|
|
|
|
|
return fs::ToFString(Dir);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
uint32 FStore::FMount::GetId() const
|
|
|
|
|
{
|
|
|
|
|
return Id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
uint32 FStore::FMount::GetTraceCount() const
|
|
|
|
|
{
|
|
|
|
|
return Traces.Num();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
const FStore::FTrace* FStore::FMount::GetTraceInfo(uint32 Index) const
|
|
|
|
|
{
|
|
|
|
|
if (Index >= uint32(Traces.Num()))
|
|
|
|
|
{
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Traces[Index];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
FStore::FTrace* FStore::FMount::GetTrace(uint32 Id) const
|
|
|
|
|
{
|
|
|
|
|
for (FTrace* Trace : Traces)
|
|
|
|
|
{
|
|
|
|
|
if (Trace->GetId() == Id)
|
|
|
|
|
{
|
|
|
|
|
return Trace;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
FStore::FTrace* FStore::FMount::AddTrace(const char* Path)
|
|
|
|
|
{
|
|
|
|
|
FTrace NewTrace(Path);
|
|
|
|
|
|
|
|
|
|
uint32 Id = NewTrace.GetId();
|
|
|
|
|
if (FTrace* Existing = GetTrace(Id))
|
|
|
|
|
{
|
|
|
|
|
return Existing;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FTrace* Trace = new FTrace(MoveTemp(NewTrace));
|
|
|
|
|
Traces.Add(Trace);
|
|
|
|
|
return Trace;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
uint32 FStore::FMount::Refresh()
|
|
|
|
|
{
|
|
|
|
|
uint32 ChangeSerial = 0;
|
|
|
|
|
for (auto& DirItem : std::filesystem::directory_iterator(Dir))
|
|
|
|
|
{
|
|
|
|
|
if (DirItem.is_directory())
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::filesystem::path Extension = DirItem.path().extension();
|
|
|
|
|
if (Extension != ".utrace")
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FTrace* Trace = AddTrace(DirItem.path().string().c_str());
|
|
|
|
|
if (Trace != nullptr)
|
|
|
|
|
{
|
|
|
|
|
ChangeSerial += Trace->GetId();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ChangeSerial;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
FStore::FStore(asio::io_context& InIoContext, const fs::path& InStoreDir)
|
|
|
|
|
: IoContext(InIoContext)
|
|
|
|
|
{
|
|
|
|
|
fs::path StoreDir = InStoreDir / "001";
|
|
|
|
|
AddMount(StoreDir);
|
2021-08-18 07:36:31 -04:00
|
|
|
|
|
|
|
|
Refresh();
|
|
|
|
|
|
|
|
|
|
#if TS_USING(TS_PLATFORM_WINDOWS)
|
2022-03-21 09:36:32 -04:00
|
|
|
std::wstring StoreDirW = StoreDir;
|
|
|
|
|
HANDLE DirWatchHandle = FindFirstChangeNotificationW(StoreDirW.c_str(), false, FILE_NOTIFY_CHANGE_FILE_NAME);
|
2021-08-18 07:36:31 -04:00
|
|
|
if (DirWatchHandle == INVALID_HANDLE_VALUE)
|
|
|
|
|
{
|
|
|
|
|
DirWatchHandle = 0;
|
|
|
|
|
}
|
|
|
|
|
DirWatcher = new FDirWatcher(IoContext, DirWatchHandle);
|
2022-03-21 09:36:32 -04:00
|
|
|
#elif TS_USING(TS_PLATFORM_LINUX)
|
|
|
|
|
int inotfd = inotify_init();
|
|
|
|
|
int watch_desc = inotify_add_watch(inotfd, StoreDir.c_str(), IN_CREATE|IN_DELETE);
|
|
|
|
|
DirWatcher = new FDirWatcher(IoContext, inotfd);
|
|
|
|
|
#elif PLATFORM_MAC
|
|
|
|
|
DirWatcher = new FDirWatcher(*StoreDir);
|
|
|
|
|
#endif
|
2021-08-18 07:36:31 -04:00
|
|
|
|
|
|
|
|
WatchDir();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
FStore::~FStore()
|
|
|
|
|
{
|
|
|
|
|
if (DirWatcher != nullptr)
|
|
|
|
|
{
|
|
|
|
|
check(!DirWatcher->is_open());
|
|
|
|
|
delete DirWatcher;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
void FStore::Close()
|
|
|
|
|
{
|
|
|
|
|
if (DirWatcher != nullptr)
|
|
|
|
|
{
|
|
|
|
|
DirWatcher->cancel();
|
|
|
|
|
DirWatcher->close();
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-21 09:36:32 -04:00
|
|
|
for (FMount* Mount : Mounts)
|
|
|
|
|
{
|
|
|
|
|
delete Mount;
|
|
|
|
|
}
|
2021-08-18 07:36:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
2022-03-21 09:36:32 -04:00
|
|
|
bool FStore::AddMount(const fs::path& Dir)
|
2021-08-18 07:36:31 -04:00
|
|
|
{
|
2022-03-21 09:36:32 -04:00
|
|
|
FMount* Mount = new FMount(Dir);
|
|
|
|
|
Mounts.Add(Mount);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
bool FStore::RemoveMount(uint32 Id)
|
|
|
|
|
{
|
|
|
|
|
for (uint32 i = 1, n = Mounts.Num() - 1; i <= n; ++i) // 1 because 0th must always exist
|
|
|
|
|
{
|
|
|
|
|
if (Mounts[i]->GetId() == Id)
|
|
|
|
|
{
|
|
|
|
|
std::swap(Mounts[n], Mounts[i]);
|
|
|
|
|
Mounts.SetNum(n);
|
|
|
|
|
Refresh();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
const FStore::FMount* FStore::GetMount(uint32 Id) const
|
|
|
|
|
{
|
|
|
|
|
for (FMount* Mount : Mounts)
|
|
|
|
|
{
|
|
|
|
|
if (Mount->GetId() == Id)
|
|
|
|
|
{
|
|
|
|
|
return Mount;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
uint32 FStore::GetMountCount() const
|
|
|
|
|
{
|
|
|
|
|
return uint32(Mounts.Num());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
const FStore::FMount* FStore::GetMountInfo(uint32 Index) const
|
|
|
|
|
{
|
|
|
|
|
if (Index >= uint32(Mounts.Num()))
|
|
|
|
|
{
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Mounts[Index];
|
2021-08-18 07:36:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
void FStore::WatchDir()
|
|
|
|
|
{
|
|
|
|
|
if (DirWatcher == nullptr)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DirWatcher->async_wait([this] (asio::error_code ErrorCode)
|
|
|
|
|
{
|
|
|
|
|
if (ErrorCode)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if TS_USING(TS_PLATFORM_WINDOWS)
|
2021-08-19 04:14:53 -04:00
|
|
|
// Windows doesn't update modified timestamps in a timely fashion when
|
|
|
|
|
// copying files (or it could be Explorer that doesn't update it until
|
|
|
|
|
// later). This is a not-so-pretty "wait for a little bit" workaround.
|
|
|
|
|
auto* DelayTimer = new asio::steady_timer(IoContext);
|
|
|
|
|
DelayTimer->expires_after(std::chrono::seconds(2));
|
|
|
|
|
DelayTimer->async_wait([this, DelayTimer] (const asio::error_code& ErrorCode)
|
|
|
|
|
{
|
|
|
|
|
delete DelayTimer;
|
|
|
|
|
|
|
|
|
|
Refresh();
|
|
|
|
|
|
|
|
|
|
FindNextChangeNotification(DirWatcher->native_handle());
|
|
|
|
|
WatchDir();
|
|
|
|
|
});
|
|
|
|
|
#else
|
2021-08-18 07:36:31 -04:00
|
|
|
Refresh();
|
|
|
|
|
WatchDir();
|
2021-08-19 04:14:53 -04:00
|
|
|
#endif
|
2021-08-18 07:36:31 -04:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
2022-03-21 09:36:32 -04:00
|
|
|
FString FStore::GetStoreDir() const
|
2021-08-18 07:36:31 -04:00
|
|
|
{
|
2022-03-21 09:36:32 -04:00
|
|
|
FMount* Mount = Mounts[0];
|
|
|
|
|
return Mount->GetDir();
|
2021-08-18 07:36:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
uint32 FStore::GetChangeSerial() const
|
|
|
|
|
{
|
|
|
|
|
return ChangeSerial;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
uint32 FStore::GetTraceCount() const
|
|
|
|
|
{
|
2022-03-21 09:36:32 -04:00
|
|
|
uint32 Count = 0;
|
|
|
|
|
for (const FMount* Mount : Mounts)
|
|
|
|
|
{
|
|
|
|
|
Count += Mount->GetTraceCount();
|
|
|
|
|
}
|
|
|
|
|
return Count;
|
2021-08-18 07:36:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
const FStore::FTrace* FStore::GetTraceInfo(uint32 Index) const
|
|
|
|
|
{
|
2022-03-21 09:36:32 -04:00
|
|
|
for (const FMount* Mount : Mounts)
|
2021-08-18 07:36:31 -04:00
|
|
|
{
|
2022-03-21 09:36:32 -04:00
|
|
|
uint32 Count = Mount->GetTraceCount();
|
|
|
|
|
if (Index < Count)
|
2021-08-18 07:36:31 -04:00
|
|
|
{
|
2022-03-21 09:36:32 -04:00
|
|
|
return Mount->GetTraceInfo(Index);
|
2021-08-18 07:36:31 -04:00
|
|
|
}
|
2022-03-21 09:36:32 -04:00
|
|
|
Index -= Count;
|
2021-08-18 07:36:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
2022-03-21 09:36:32 -04:00
|
|
|
FStore::FTrace* FStore::GetTrace(uint32 Id, FMount** OutMount) const
|
2021-08-18 07:36:31 -04:00
|
|
|
{
|
2022-03-21 09:36:32 -04:00
|
|
|
for (FMount* Mount : Mounts)
|
2021-08-18 07:36:31 -04:00
|
|
|
{
|
2022-03-21 09:36:32 -04:00
|
|
|
if (FTrace* Trace = Mount->GetTrace(Id))
|
|
|
|
|
{
|
|
|
|
|
if (OutMount != nullptr)
|
|
|
|
|
{
|
|
|
|
|
*OutMount = Mount;
|
|
|
|
|
}
|
|
|
|
|
return Trace;
|
|
|
|
|
}
|
2021-08-18 07:36:31 -04:00
|
|
|
}
|
|
|
|
|
|
2022-03-21 09:36:32 -04:00
|
|
|
return nullptr;
|
2021-08-18 07:36:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
FStore::FNewTrace FStore::CreateTrace()
|
|
|
|
|
{
|
|
|
|
|
FString TracePath;
|
|
|
|
|
#if 0
|
|
|
|
|
bool bOk = false;
|
|
|
|
|
for (int i = 0; i < 256; ++i)
|
|
|
|
|
{
|
|
|
|
|
uint32 TraceId = ++LastTraceId;
|
|
|
|
|
|
|
|
|
|
TracePath = StoreDir;
|
|
|
|
|
TracePath.Appendf("/%05d", TraceId);
|
|
|
|
|
|
|
|
|
|
if (!std::filesystem::is_directory(*TracePath) && std::filesystem::create_directories(*TracePath))
|
|
|
|
|
{
|
|
|
|
|
bOk = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!bOk)
|
|
|
|
|
{
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TracePath += "/data.utrace";
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
// N.B. Not thread safe!?
|
|
|
|
|
char Prefix[24];
|
|
|
|
|
std::time_t Now = std::time(nullptr);
|
|
|
|
|
std::tm* LocalNow = std::localtime(&Now);
|
|
|
|
|
std::strftime(Prefix, TS_ARRAY_COUNT(Prefix), "%Y%m%d_%H%M%S", LocalNow);
|
|
|
|
|
|
2022-03-21 09:36:32 -04:00
|
|
|
FMount* DefaultMount = Mounts[0];
|
|
|
|
|
|
|
|
|
|
TracePath = DefaultMount->GetDir();
|
2021-08-18 07:36:31 -04:00
|
|
|
TracePath += "/";
|
|
|
|
|
TracePath += Prefix;
|
|
|
|
|
TracePath += ".utrace";
|
|
|
|
|
for (uint32 Index = 0; std::filesystem::is_regular_file(*TracePath); ++Index)
|
|
|
|
|
{
|
|
|
|
|
char Suffix[64];
|
|
|
|
|
std::snprintf(Suffix, TS_ARRAY_COUNT(Suffix), "/%s_%02d.utrace", Prefix, Index);
|
|
|
|
|
|
2022-03-21 09:36:32 -04:00
|
|
|
TracePath = DefaultMount->GetDir();
|
2021-08-18 07:36:31 -04:00
|
|
|
TracePath += *Suffix;
|
|
|
|
|
}
|
|
|
|
|
#endif // 0
|
|
|
|
|
|
|
|
|
|
FAsioWriteable* File = FAsioFile::WriteFile(IoContext, *TracePath);
|
|
|
|
|
if (File == nullptr)
|
|
|
|
|
{
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-21 09:36:32 -04:00
|
|
|
FTrace* Trace = DefaultMount->AddTrace(*TracePath);
|
2021-08-18 07:36:31 -04:00
|
|
|
if (Trace == nullptr)
|
|
|
|
|
{
|
|
|
|
|
delete File;
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return { Trace->GetId(), File };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
bool FStore::HasTrace(uint32 Id) const
|
|
|
|
|
{
|
|
|
|
|
return GetTrace(Id) != nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
FAsioReadable* FStore::OpenTrace(uint32 Id)
|
|
|
|
|
{
|
2022-03-21 09:36:32 -04:00
|
|
|
FMount* Mount;
|
|
|
|
|
FTrace* Trace = GetTrace(Id, &Mount);
|
2021-08-18 07:36:31 -04:00
|
|
|
if (Trace == nullptr)
|
|
|
|
|
{
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FString TracePath;
|
2022-03-21 09:36:32 -04:00
|
|
|
TracePath = Mount->GetDir();
|
2021-08-18 07:36:31 -04:00
|
|
|
#if 0
|
|
|
|
|
TracePath.Appendf("/%05d/data.utrace", Id);
|
|
|
|
|
#else
|
|
|
|
|
TracePath += "/";
|
|
|
|
|
TracePath += Trace->GetName();
|
|
|
|
|
TracePath += ".utrace";
|
|
|
|
|
#endif // 0
|
|
|
|
|
|
|
|
|
|
return FAsioFile::ReadFile(IoContext, *TracePath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
void FStore::Refresh()
|
|
|
|
|
{
|
2022-03-21 09:36:32 -04:00
|
|
|
ChangeSerial = 0;
|
|
|
|
|
for (FMount* Mount : Mounts)
|
2021-08-18 07:36:31 -04:00
|
|
|
{
|
2022-03-21 09:36:32 -04:00
|
|
|
ChangeSerial += Mount->Refresh();
|
|
|
|
|
}
|
2021-08-18 07:36:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* vim: set noexpandtab : */
|