mirror of
https://gitea.wildfiregames.com/0ad/0ad.git
synced 2026-07-13 05:56:12 +00:00
317f98a6c0
archive: pretty much done except for trace and archive_builder IO: io_manager still needs to be fixed/revised (especially WRT buffer and alignment management) posix: done vfs: need to complete vfs_mount and tie it in to vfs.cpp dir_util: needs further revisions (VFS interface changed since then) This was SVN commit r5475.
236 lines
4.5 KiB
C++
236 lines
4.5 KiB
C++
/**
|
|
* =========================================================================
|
|
* File : trace.cpp
|
|
* Project : 0 A.D.
|
|
* Description : allows recording and 'playing back' a sequence of
|
|
* : I/Os - useful for benchmarking and archive builder.
|
|
* =========================================================================
|
|
*/
|
|
|
|
// license: GPL; see lib/license.txt
|
|
|
|
#include "precompiled.h"
|
|
#include "trace.h"
|
|
|
|
#include "lib/allocators.h"
|
|
#include "lib/timer.h"
|
|
#include "lib/sysdep/cpu.h"
|
|
|
|
// one run per file
|
|
|
|
|
|
/*virtual*/ ITrace::~ITrace()
|
|
{
|
|
}
|
|
|
|
class Trace_Dummy : public ITrace
|
|
{
|
|
public:
|
|
Trace_Dummy(size_t UNUSED(maxSize))
|
|
{
|
|
|
|
}
|
|
|
|
virtual void NotifyLoad(const char* UNUSED(pathname), size_t UNUSED(size))
|
|
{
|
|
}
|
|
|
|
virtual void NotifyStore(const char* UNUSED(pathname), size_t UNUSED(size))
|
|
{
|
|
}
|
|
|
|
virtual void NotifyFree(const char* UNUSED(pathname))
|
|
{
|
|
}
|
|
|
|
virtual LibError Load(const char* UNUSED(vfsPathname))
|
|
{
|
|
return INFO::OK;
|
|
}
|
|
|
|
virtual LibError Store(const char* UNUSED(vfsPathname)) const
|
|
{
|
|
return INFO::OK;
|
|
}
|
|
|
|
virtual const TraceEntry* Entries() const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
virtual size_t NumEntries() const
|
|
{
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
|
|
class Trace : public ITrace
|
|
{
|
|
public:
|
|
Trace(size_t maxSize)
|
|
{
|
|
(void)pool_create(&m_pool, maxSize, sizeof(TraceEntry));
|
|
}
|
|
|
|
virtual ~Trace()
|
|
{
|
|
(void)pool_destroy(&m_pool);
|
|
}
|
|
|
|
virtual void NotifyLoad(const char* pathname, size_t size)
|
|
{
|
|
AddEntry(TO_LOAD, pathname, size);
|
|
}
|
|
|
|
virtual void NotifyStore(const char* pathname, size_t size)
|
|
{
|
|
AddEntry(TO_STORE, pathname, size);
|
|
}
|
|
|
|
virtual void NotifyFree(const char* pathname)
|
|
{
|
|
const size_t size = 0; // meaningless for TO_FREE
|
|
AddEntry(TO_FREE, pathname, size);
|
|
}
|
|
|
|
virtual LibError Load(const char* osPathname)
|
|
{
|
|
pool_free_all(&m_pool);
|
|
|
|
FILE* f = fopen(osPathname, "rt");
|
|
if(!f)
|
|
WARN_RETURN(ERR::FILE_ACCESS);
|
|
for(size_t i = 0; ; i++)
|
|
{
|
|
TraceEntry* t = (TraceEntry*)pool_alloc(&m_pool, 0);
|
|
t->Read(f);
|
|
}
|
|
|
|
fclose(f);
|
|
|
|
return INFO::OK;
|
|
}
|
|
|
|
virtual LibError Store(const char* osPathname) const
|
|
{
|
|
FILE* f = fopen(osPathname, "at");
|
|
if(!f)
|
|
WARN_RETURN(ERR::FILE_ACCESS);
|
|
for(size_t i = 0; i < NumEntries(); i++)
|
|
Entries()[i]->Write(f);
|
|
(void)fclose(f);
|
|
return INFO::OK;
|
|
}
|
|
|
|
virtual const TraceEntry* Entries() const
|
|
{
|
|
return (const TraceEntry*)m_pool.da.base;
|
|
}
|
|
|
|
virtual size_t NumEntries() const
|
|
{
|
|
return m_pool.da.pos / sizeof(TraceEntry);
|
|
}
|
|
|
|
private:
|
|
void AddEntry(TraceOp op, const char* pathname, size_t size, double timestamp = 0.0)
|
|
{
|
|
TraceEntry* t = (TraceEntry*)pool_alloc(&m_pool, 0);
|
|
debug_assert(t);
|
|
t->timestamp = timestamp == 0.0? get_time() : timestamp;
|
|
t->vfsPathname = path_Pool()->UniqueCopy(pathname);
|
|
t->size = size;
|
|
t->op = op;
|
|
}
|
|
|
|
Pool m_pool;
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// parse lines and stuff them in m_pool
|
|
// (as if they had been AddEntry-ed; replaces any existing data)
|
|
// .. bake PATH_MAX limit into string.
|
|
char fmt[30];
|
|
snprintf(fmt, ARRAY_SIZE(fmt), "%%lf: %%c \"%%%d[^\"]\" %%d %%04x\n", PATH_MAX);
|
|
|
|
double timestamp; char opcode; char P_path[PATH_MAX]; size_t size;
|
|
int fieldsRead = fscanf(f, fmt, ×tamp, &opcode, P_path, &size);
|
|
if(fieldsRead == EOF)
|
|
break;
|
|
debug_assert(fieldsRead == 4);
|
|
|
|
|
|
|
|
static char CharFromTraceOp(TraceOp op)
|
|
{
|
|
switch(op)
|
|
{
|
|
case TO_LOAD:
|
|
return 'L';
|
|
case TO_STORE:
|
|
return 'S';
|
|
case TO_FREE:
|
|
return 'F';
|
|
default:
|
|
DEBUG_WARN_ERR(ERR::CORRUPTED);
|
|
}
|
|
}
|
|
|
|
static TraceOp TraceOpFromChar(char c)
|
|
{
|
|
switch(c)
|
|
{
|
|
case 'L':
|
|
return TO_LOAD;
|
|
case 'S':
|
|
return TO_STORE;
|
|
case 'F':
|
|
return TO_FREE;
|
|
default:
|
|
DEBUG_WARN_ERR(ERR::CORRUPTED);
|
|
}
|
|
}
|
|
|
|
static void write_entry(FILE* f, const TraceEntry* ent)
|
|
{
|
|
fprintf(f, "%#010f: %c \"%s\" %d\n", ent->timestamp, CharFromTraceOp(ent->op), ent->vfsPathname, ent->size);
|
|
}
|
|
|
|
{
|
|
// carry out this entry's operation
|
|
switch(ent->op)
|
|
{
|
|
// do not 'run' writes - we'd destroy the existing data.
|
|
case TO_STORE:
|
|
break;
|
|
case TO_LOAD:
|
|
{
|
|
IoBuf buf; size_t size;
|
|
(void)vfs_load(ent->vfsPathname, buf, size, ent->flags);
|
|
break;
|
|
}
|
|
case TO_FREE:
|
|
fileCache.Release(ent->vfsPathname);
|
|
break;
|
|
default:
|
|
debug_warn("unknown TraceOp");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LibError trace_run(const char* osPathname)
|
|
{
|
|
Trace trace;
|
|
RETURN_ERR(trace.Load(osPathname));
|
|
for(uint i = 0; i < trace.NumEntries(); i++)
|
|
trace.Entries()[i]->Run();
|
|
return INFO::OK;
|
|
}
|