2006-01-23 21:05:09 +01:00
|
|
|
#include "precompiled.h"
|
|
|
|
|
|
|
|
#include "lib/allocators.h"
|
|
|
|
#include "lib/timer.h"
|
2006-01-25 08:21:45 +01:00
|
|
|
#include "file_internal.h"
|
2006-01-23 21:05:09 +01:00
|
|
|
|
2006-02-23 08:55:32 +01:00
|
|
|
# include "ps/VFSUtil.h"
|
|
|
|
|
2006-01-28 23:19:42 +01:00
|
|
|
static uintptr_t trace_initialized; // set via CAS
|
2006-01-23 21:05:09 +01:00
|
|
|
static Pool trace_pool;
|
|
|
|
|
2006-01-28 23:19:42 +01:00
|
|
|
// call at before using trace_pool. no-op if called more than once.
|
|
|
|
static inline void trace_init()
|
|
|
|
{
|
|
|
|
if(CAS(&trace_initialized, 0, 1))
|
|
|
|
(void)pool_create(&trace_pool, 4*MiB, sizeof(TraceEntry));
|
|
|
|
}
|
2006-01-23 21:05:09 +01:00
|
|
|
|
|
|
|
void trace_shutdown()
|
|
|
|
{
|
2006-01-28 23:19:42 +01:00
|
|
|
if(CAS(&trace_initialized, 1, 2))
|
2006-01-23 21:05:09 +01:00
|
|
|
(void)pool_destroy(&trace_pool);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-02-25 20:26:00 +01:00
|
|
|
// enabled by default. by the time we can decide whether a trace needs to
|
|
|
|
// be generated (see should_rebuild_main_archive), file accesses will
|
|
|
|
// already have occurred; hence default enabled and disable if not needed.
|
|
|
|
static bool trace_enabled = true;
|
|
|
|
static bool trace_force_enabled = false; // see below
|
|
|
|
|
|
|
|
// note: explicitly enabling trace means the user wants one to be
|
|
|
|
// generated even if an up-to-date version exists.
|
|
|
|
// (mechanism: ignore any attempts to disable)
|
2006-01-28 23:19:42 +01:00
|
|
|
void trace_enable(bool want_enabled)
|
|
|
|
{
|
|
|
|
trace_enabled = want_enabled;
|
2006-02-25 20:26:00 +01:00
|
|
|
|
|
|
|
if(want_enabled)
|
|
|
|
trace_force_enabled = true;
|
|
|
|
if(trace_force_enabled)
|
|
|
|
trace_enabled = true;
|
2006-01-23 21:05:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-02-02 05:11:07 +01:00
|
|
|
static void trace_add(TraceOp op, const char* P_fn, size_t size, uint flags = 0, double timestamp = 0.0)
|
2006-01-23 21:05:09 +01:00
|
|
|
{
|
2006-01-28 23:19:42 +01:00
|
|
|
trace_init();
|
|
|
|
if(!trace_enabled)
|
2006-01-23 21:05:09 +01:00
|
|
|
return;
|
2006-01-28 23:19:42 +01:00
|
|
|
|
|
|
|
if(timestamp == 0.0)
|
|
|
|
timestamp = get_time();
|
2006-01-23 21:05:09 +01:00
|
|
|
|
|
|
|
TraceEntry* t = (TraceEntry*)pool_alloc(&trace_pool, 0);
|
|
|
|
if(!t)
|
|
|
|
return;
|
2006-01-28 23:19:42 +01:00
|
|
|
t->timestamp = timestamp;
|
2006-02-02 05:11:07 +01:00
|
|
|
t->atom_fn = file_make_unique_fn_copy(P_fn);
|
|
|
|
t->size = size;
|
|
|
|
t->op = op;
|
|
|
|
t->flags = flags;
|
2006-01-23 21:05:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-02-02 05:11:07 +01:00
|
|
|
void trace_notify_load(const char* P_fn, size_t size, uint flags)
|
2006-01-23 21:05:09 +01:00
|
|
|
{
|
2006-02-02 05:11:07 +01:00
|
|
|
trace_add(TO_LOAD, P_fn, size, flags);
|
2006-01-28 23:19:42 +01:00
|
|
|
}
|
2006-01-23 21:05:09 +01:00
|
|
|
|
2006-02-02 05:11:07 +01:00
|
|
|
void trace_notify_free(const char* P_fn, size_t size)
|
2006-01-28 23:19:42 +01:00
|
|
|
{
|
2006-02-02 05:11:07 +01:00
|
|
|
trace_add(TO_FREE, P_fn, size);
|
2006-01-28 23:19:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void trace_get(Trace* t)
|
|
|
|
{
|
|
|
|
t->ents = (const TraceEntry*)trace_pool.da.base;
|
|
|
|
t->num_ents = (uint)(trace_pool.da.pos / sizeof(TraceEntry));
|
|
|
|
}
|
|
|
|
|
2006-01-31 04:47:52 +01:00
|
|
|
void trace_clear()
|
|
|
|
{
|
|
|
|
pool_free_all(&trace_pool);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-01-28 23:19:42 +01:00
|
|
|
LibError trace_write_to_file(const char* trace_filename)
|
|
|
|
{
|
2006-02-25 20:26:00 +01:00
|
|
|
if(!trace_enabled)
|
|
|
|
return INFO_SKIPPED;
|
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
char N_fn[PATH_MAX];
|
|
|
|
RETURN_ERR(file_make_full_native_path(trace_filename, N_fn));
|
|
|
|
FILE* f = fopen(N_fn, "wt");
|
|
|
|
if(!f)
|
2006-01-28 23:19:42 +01:00
|
|
|
WARN_RETURN(ERR_FILE_ACCESS);
|
2006-01-23 21:05:09 +01:00
|
|
|
|
|
|
|
Trace t;
|
|
|
|
trace_get(&t);
|
2006-01-28 23:19:42 +01:00
|
|
|
const TraceEntry* ent = t.ents;
|
|
|
|
for(size_t i = 0; i < t.num_ents; i++, ent++)
|
|
|
|
{
|
|
|
|
char opcode = '?';
|
|
|
|
switch(ent->op)
|
|
|
|
{
|
|
|
|
case TO_LOAD: opcode = 'L'; break;
|
|
|
|
case TO_FREE: opcode = 'F'; break;
|
|
|
|
default: debug_warn("invalid TraceOp");
|
|
|
|
}
|
|
|
|
|
2006-01-31 04:47:52 +01:00
|
|
|
debug_assert(ent->op == TO_LOAD || ent->op == TO_FREE);
|
2006-02-02 05:11:07 +01:00
|
|
|
fprintf(f, "%#010f: %c \"%s\" %d %04x\n", ent->timestamp, opcode, ent->atom_fn, ent->size, ent->flags);
|
2006-01-28 23:19:42 +01:00
|
|
|
}
|
2006-01-23 21:05:09 +01:00
|
|
|
|
|
|
|
(void)fclose(f);
|
|
|
|
return ERR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-01-28 23:19:42 +01:00
|
|
|
LibError trace_read_from_file(const char* trace_filename, Trace* t)
|
2006-01-23 21:05:09 +01:00
|
|
|
{
|
2006-02-09 06:59:33 +01:00
|
|
|
trace_clear();
|
2006-01-31 04:47:52 +01:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
char N_fn[PATH_MAX];
|
|
|
|
RETURN_ERR(file_make_full_native_path(trace_filename, N_fn));
|
|
|
|
FILE* f = fopen(N_fn, "rt");
|
|
|
|
if(!f)
|
2006-01-28 23:19:42 +01:00
|
|
|
WARN_RETURN(ERR_FILE_NOT_FOUND);
|
2006-01-23 21:05:09 +01:00
|
|
|
|
2006-02-09 06:59:33 +01:00
|
|
|
// we use trace_add, which is the same mechanism called by trace_notify*;
|
|
|
|
// therefore, tracing needs to be enabled.
|
|
|
|
trace_enabled = true;
|
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
// parse lines and stuff them in trace_pool
|
|
|
|
// (as if they had been trace_add-ed; replaces any existing data)
|
2006-01-31 04:47:52 +01:00
|
|
|
// .. bake PATH_MAX limit into string.
|
|
|
|
char fmt[30];
|
2006-02-02 05:11:07 +01:00
|
|
|
snprintf(fmt, ARRAY_SIZE(fmt), "%%lf: %%c \"%%%d[^\"]\" %%d %%04x\n", PATH_MAX);
|
2006-01-23 21:05:09 +01:00
|
|
|
for(;;)
|
|
|
|
{
|
2006-02-02 05:11:07 +01:00
|
|
|
double timestamp; char opcode; char P_path[PATH_MAX]; size_t size; uint flags;
|
|
|
|
int ret = fscanf(f, fmt, ×tamp, &opcode, P_path, &size, &flags);
|
2006-01-23 21:05:09 +01:00
|
|
|
if(ret == EOF)
|
|
|
|
break;
|
2006-02-02 05:11:07 +01:00
|
|
|
debug_assert(ret == 5);
|
2006-01-23 21:05:09 +01:00
|
|
|
|
2006-01-28 23:19:42 +01:00
|
|
|
TraceOp op = TO_LOAD; // default in case file is garbled
|
|
|
|
switch(opcode)
|
|
|
|
{
|
|
|
|
case 'L': op = TO_LOAD; break;
|
|
|
|
case 'F': op = TO_FREE; break;
|
|
|
|
default: debug_warn("invalid TraceOp");
|
|
|
|
}
|
|
|
|
|
2006-02-02 05:11:07 +01:00
|
|
|
trace_add(op, P_path, size, flags, timestamp);
|
2006-01-23 21:05:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fclose(f);
|
2006-01-28 23:19:42 +01:00
|
|
|
|
|
|
|
trace_get(t);
|
2006-01-31 04:47:52 +01:00
|
|
|
|
|
|
|
// all previous trace entries were hereby lost (overwritten),
|
|
|
|
// so there's no sense in continuing.
|
|
|
|
trace_enabled = false;
|
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
return ERR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-01-28 23:19:42 +01:00
|
|
|
enum SimulateFlags
|
2006-01-23 21:05:09 +01:00
|
|
|
{
|
2006-01-28 23:19:42 +01:00
|
|
|
SF_SYNC_TO_TIMESTAMP = 1
|
|
|
|
};
|
|
|
|
|
|
|
|
LibError trace_simulate(const char* trace_filename, uint flags)
|
|
|
|
{
|
2006-01-31 04:47:52 +01:00
|
|
|
Trace t;
|
|
|
|
RETURN_ERR(trace_read_from_file(trace_filename, &t));
|
|
|
|
|
2006-01-28 23:19:42 +01:00
|
|
|
// prevent the actions we carry out below from generating
|
|
|
|
// trace_add-s.
|
|
|
|
trace_enabled = false;
|
|
|
|
|
|
|
|
const double start_time = get_time();
|
|
|
|
const double first_timestamp = t.ents[0].timestamp;
|
2006-01-23 21:05:09 +01:00
|
|
|
|
2006-01-28 23:19:42 +01:00
|
|
|
const TraceEntry* ent = t.ents;
|
|
|
|
for(uint i = 0; i < t.num_ents; i++, ent++)
|
|
|
|
{
|
|
|
|
// wait until time for next entry if caller requested this
|
|
|
|
if(flags & SF_SYNC_TO_TIMESTAMP)
|
|
|
|
{
|
|
|
|
while(get_time()-start_time < ent->timestamp-first_timestamp)
|
|
|
|
{
|
|
|
|
// busy-wait (don't sleep - can skew results)
|
|
|
|
}
|
|
|
|
}
|
2006-01-23 21:05:09 +01:00
|
|
|
|
2006-01-28 23:19:42 +01:00
|
|
|
// carry out this entry's operation
|
|
|
|
FileIOBuf buf; size_t size;
|
|
|
|
switch(ent->op)
|
|
|
|
{
|
|
|
|
case TO_LOAD:
|
|
|
|
(void)vfs_load(ent->atom_fn, buf, size, ent->flags);
|
|
|
|
break;
|
|
|
|
case TO_FREE:
|
|
|
|
buf = file_cache_find(ent->atom_fn, &size);
|
|
|
|
(void)file_buf_free(buf);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
debug_warn("unknown TraceOp");
|
|
|
|
}
|
|
|
|
}
|
2006-01-23 21:05:09 +01:00
|
|
|
|
2006-01-31 04:47:52 +01:00
|
|
|
trace_clear();
|
|
|
|
|
2006-01-28 23:19:42 +01:00
|
|
|
return ERR_OK;
|
|
|
|
}
|
2006-01-23 21:05:09 +01:00
|
|
|
|
|
|
|
|
2006-01-28 23:19:42 +01:00
|
|
|
//-----------------------------------------------------------------------------
|
2006-01-23 21:05:09 +01:00
|
|
|
|
2006-02-09 06:59:33 +01:00
|
|
|
|
|
|
|
|
|
|
|
// enough for 64K unique files - ought to suffice.
|
|
|
|
typedef u16 FileId;
|
|
|
|
static const FileId NULL_ID = 0;
|
|
|
|
|
2006-03-04 19:58:09 +01:00
|
|
|
|
|
|
|
struct FileNode
|
|
|
|
{
|
|
|
|
const char* atom_fn;
|
|
|
|
|
|
|
|
FileId prev : 15;
|
|
|
|
u32 visited : 1;
|
|
|
|
FileId next : 15;
|
|
|
|
u32 output : 1;
|
|
|
|
|
|
|
|
FileNode(const char* atom_fn_)
|
|
|
|
{
|
|
|
|
atom_fn = atom_fn_;
|
|
|
|
|
|
|
|
prev = next = NULL_ID;
|
|
|
|
visited = output = 0;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef std::vector<FileNode> FileNodes;
|
|
|
|
|
|
|
|
|
|
|
|
|
2006-02-09 06:59:33 +01:00
|
|
|
class IdMgr
|
2006-01-23 21:05:09 +01:00
|
|
|
{
|
2006-02-09 06:59:33 +01:00
|
|
|
FileId cur;
|
|
|
|
typedef std::map<const char*, FileId> Map;
|
|
|
|
Map map;
|
2006-03-04 19:58:09 +01:00
|
|
|
FileNodes* nodes;
|
|
|
|
|
|
|
|
void associate_node_with_fn(const FileNode* node, const char* atom_fn)
|
|
|
|
{
|
|
|
|
FileId id = id_from_node(node);
|
|
|
|
const Map::value_type item = std::make_pair(atom_fn, id);
|
|
|
|
std::pair<Map::iterator, bool> ret = map.insert(item);
|
|
|
|
if(!ret.second)
|
|
|
|
debug_warn("atom_fn already associated with node");
|
|
|
|
}
|
|
|
|
|
2006-02-09 06:59:33 +01:00
|
|
|
public:
|
2006-03-04 19:58:09 +01:00
|
|
|
FileId id_from_node(const FileNode* node) const
|
2006-02-09 06:59:33 +01:00
|
|
|
{
|
2006-03-04 19:58:09 +01:00
|
|
|
// +1 to skip NULL_ID value
|
|
|
|
FileId id = node - &((*nodes)[0]) +1;
|
|
|
|
debug_assert(id <= nodes->size());
|
2006-02-09 06:59:33 +01:00
|
|
|
return id;
|
|
|
|
}
|
2006-03-04 19:58:09 +01:00
|
|
|
|
|
|
|
FileId id_from_fn(const char* atom_fn) const
|
|
|
|
{
|
|
|
|
Map::const_iterator cit = map.find(atom_fn);
|
|
|
|
if(cit == map.end())
|
|
|
|
{
|
|
|
|
debug_warn("id_from_fn: not found");
|
|
|
|
return NULL_ID;
|
|
|
|
}
|
|
|
|
return cit->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
void init(FileNodes* nodes_)
|
|
|
|
{
|
|
|
|
cur = NULL_ID+1;
|
|
|
|
map.clear();
|
|
|
|
nodes = nodes_;
|
|
|
|
|
|
|
|
for(FileNodes::const_iterator cit = nodes->begin(); cit != nodes->end(); ++cit)
|
|
|
|
{
|
|
|
|
const FileNode& node = *cit;
|
|
|
|
associate_node_with_fn(&node, node.atom_fn);
|
|
|
|
}
|
|
|
|
}
|
2006-01-23 21:05:09 +01:00
|
|
|
};
|
2006-02-09 06:59:33 +01:00
|
|
|
static IdMgr id_mgr;
|
2006-01-23 21:05:09 +01:00
|
|
|
|
2006-01-28 23:19:42 +01:00
|
|
|
|
2006-03-04 19:58:09 +01:00
|
|
|
/*
|
|
|
|
file gatherer - build vector of the complex file objects; enum via VFS
|
|
|
|
connection builder - encapsulate trace details; stitch those into series of connections
|
|
|
|
tour builder: take this list, sort it, use it to generate a nice ordering of all files
|
|
|
|
anything not covered will be added to output list by default
|
|
|
|
*/
|
|
|
|
|
|
|
|
// build list of FileNode - exactly one per file in VFS
|
|
|
|
class FileNodeGatherer
|
2006-01-23 21:05:09 +01:00
|
|
|
{
|
2006-03-04 19:58:09 +01:00
|
|
|
struct EntCbParams
|
|
|
|
{
|
|
|
|
FileNodes* file_nodes;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void EntCb(const char* path, const DirEnt* ent, void* context)
|
|
|
|
{
|
|
|
|
EntCbParams* params = (EntCbParams*)context;
|
|
|
|
FileNodes* file_nodes = params->file_nodes;
|
2006-01-28 23:19:42 +01:00
|
|
|
|
2006-03-04 19:58:09 +01:00
|
|
|
if(!DIRENT_IS_DIR(ent))
|
|
|
|
{
|
|
|
|
const char* atom_fn = file_make_unique_fn_copy(path);
|
|
|
|
file_nodes->push_back(FileNode(atom_fn));
|
|
|
|
}
|
|
|
|
}
|
2006-01-28 23:19:42 +01:00
|
|
|
|
2006-03-04 19:58:09 +01:00
|
|
|
public:
|
|
|
|
FileNodeGatherer(FileNodes& file_nodes)
|
2006-01-28 23:19:42 +01:00
|
|
|
{
|
2006-03-04 19:58:09 +01:00
|
|
|
// jump-start allocation (avoids frequent initial reallocs)
|
|
|
|
file_nodes.reserve(500);
|
|
|
|
|
|
|
|
// TODO: for each mount point (with VFS_MOUNT_ARCHIVE flag set):
|
|
|
|
EntCbParams params = { &file_nodes };
|
|
|
|
VFSUtil::EnumDirEnts("", VFSUtil::RECURSIVE, 0, EntCb, ¶ms);
|
2006-01-28 23:19:42 +01:00
|
|
|
}
|
2006-02-09 06:59:33 +01:00
|
|
|
};
|
2006-01-28 23:19:42 +01:00
|
|
|
|
|
|
|
|
2006-03-04 19:58:09 +01:00
|
|
|
typedef u32 ConnectionId;
|
|
|
|
cassert(sizeof(FileId)*2 <= sizeof(ConnectionId));
|
|
|
|
ConnectionId cid_make(FileId first, FileId second)
|
|
|
|
{
|
|
|
|
return u32_from_u16(first, second);
|
|
|
|
}
|
|
|
|
FileId cid_first(ConnectionId id)
|
|
|
|
{
|
|
|
|
return u32_hi(id);
|
|
|
|
}
|
|
|
|
FileId cid_second(ConnectionId id)
|
|
|
|
{
|
|
|
|
return u32_lo(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Connection
|
|
|
|
{
|
|
|
|
ConnectionId id;
|
|
|
|
// repeated edges ("connections") are reflected in
|
|
|
|
// the 'occurrences' count; we optimize the ordering so that
|
|
|
|
// files with frequent connections are nearby.
|
|
|
|
uint occurrences;
|
|
|
|
|
|
|
|
Connection(ConnectionId id_)
|
|
|
|
: id(id_), occurrences(1) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef std::vector<Connection> Connections;
|
|
|
|
|
|
|
|
class ConnectionBuilder
|
2006-01-23 21:05:09 +01:00
|
|
|
{
|
2006-02-09 06:59:33 +01:00
|
|
|
// put all entries in one trace file: easier to handle; obviates FS enum code
|
2006-02-23 08:55:32 +01:00
|
|
|
// rationale: don't go through trace in order; instead, process most recent
|
2006-02-09 06:59:33 +01:00
|
|
|
// run first, to give more weight to it (TSP code should go with first entry
|
|
|
|
// when #occurrences are equal)
|
|
|
|
struct Run
|
|
|
|
{
|
|
|
|
const TraceEntry* first;
|
|
|
|
uint count;
|
2006-01-23 21:05:09 +01:00
|
|
|
|
2006-02-09 06:59:33 +01:00
|
|
|
// note: up to caller to initialize count (that's done when
|
|
|
|
// starting the next run
|
|
|
|
Run(const TraceEntry* first_) : first(first_) {}
|
|
|
|
};
|
2006-01-23 21:05:09 +01:00
|
|
|
|
2006-02-09 06:59:33 +01:00
|
|
|
// note: passing i and comparing timestamp with previous timestamp
|
|
|
|
// avoids having to keep an extra local cur_time variable.
|
2006-03-04 19:58:09 +01:00
|
|
|
bool is_start_of_run(uint i, const TraceEntry* ent) const
|
2006-01-28 23:19:42 +01:00
|
|
|
{
|
2006-02-09 06:59:33 +01:00
|
|
|
// first item is always start of a run (protects [-1] below)
|
|
|
|
if(i == 0)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// timestamp started over from 0 (e.g. 29, 30, 1) -> start of new run.
|
|
|
|
if(ent->timestamp < ent[-1].timestamp)
|
2006-01-28 23:19:42 +01:00
|
|
|
return true;
|
2006-02-09 06:59:33 +01:00
|
|
|
|
|
|
|
return false;
|
2006-01-28 23:19:42 +01:00
|
|
|
}
|
|
|
|
|
2006-02-09 06:59:33 +01:00
|
|
|
typedef std::vector<Run> Runs;
|
2006-03-04 19:58:09 +01:00
|
|
|
|
|
|
|
void split_trace_into_runs(const Trace* t, Runs& runs)
|
2006-02-09 06:59:33 +01:00
|
|
|
{
|
|
|
|
uint cur_run_length = 0;
|
|
|
|
const TraceEntry* cur_entry = t->ents;
|
|
|
|
for(uint i = 0; i < t->num_ents; i++)
|
|
|
|
{
|
|
|
|
cur_run_length++;
|
|
|
|
if(is_start_of_run(i, cur_entry))
|
|
|
|
{
|
2006-03-04 19:58:09 +01:00
|
|
|
// not first time: mark previous run as complete
|
2006-02-09 06:59:33 +01:00
|
|
|
if(!runs.empty())
|
|
|
|
runs.back().count = cur_run_length;
|
|
|
|
cur_run_length = 0;
|
2006-03-04 19:58:09 +01:00
|
|
|
|
2006-02-09 06:59:33 +01:00
|
|
|
runs.push_back(Run(cur_entry));
|
|
|
|
}
|
|
|
|
cur_entry++;
|
|
|
|
}
|
|
|
|
// set the last run's length
|
|
|
|
if(!runs.empty())
|
|
|
|
runs.back().count = cur_run_length;
|
|
|
|
}
|
2006-01-28 23:19:42 +01:00
|
|
|
|
2006-03-04 19:58:09 +01:00
|
|
|
// simulate carrying out the entry's TraceOp to determine
|
|
|
|
// whether this IO would be satisfied by the file_buf cache.
|
|
|
|
bool requires_actual_io(const TraceEntry* ent) const
|
2006-02-09 06:59:33 +01:00
|
|
|
{
|
2006-03-04 19:58:09 +01:00
|
|
|
FileIOBuf buf;
|
|
|
|
size_t size = ent->size;
|
|
|
|
const char* atom_fn = ent->atom_fn;
|
|
|
|
switch(ent->op)
|
|
|
|
{
|
|
|
|
case TO_LOAD:
|
2006-02-09 06:59:33 +01:00
|
|
|
{
|
2006-03-04 19:58:09 +01:00
|
|
|
bool long_lived = (ent->flags & FILE_LONG_LIVED) != 0;
|
|
|
|
buf = file_cache_retrieve(atom_fn, &size, long_lived);
|
|
|
|
// would not be in cache: add to list of real IOs
|
|
|
|
if(!buf)
|
|
|
|
{
|
|
|
|
buf = file_buf_alloc(size, atom_fn, long_lived);
|
|
|
|
(void)file_cache_add(buf, size, atom_fn);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case TO_FREE:
|
|
|
|
buf = file_cache_find(atom_fn, &size);
|
|
|
|
(void)file_buf_free(buf);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
debug_warn("unknown TraceOp");
|
2006-02-09 06:59:33 +01:00
|
|
|
}
|
2006-01-28 23:19:42 +01:00
|
|
|
|
2006-03-04 19:58:09 +01:00
|
|
|
return false;
|
2006-02-09 06:59:33 +01:00
|
|
|
}
|
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
|
2006-03-04 19:58:09 +01:00
|
|
|
class ConnectionAdder
|
2006-02-09 06:59:33 +01:00
|
|
|
{
|
2006-03-04 19:58:09 +01:00
|
|
|
// we need to check before inserting a new connection if it has
|
|
|
|
// come up before (to increment occurrences). this map speeds
|
|
|
|
// things up from n*n to n*log(n) (n = # files).
|
|
|
|
typedef std::map<ConnectionId, Connection*> Map;
|
|
|
|
Map map;
|
2006-01-23 21:05:09 +01:00
|
|
|
|
2006-03-04 19:58:09 +01:00
|
|
|
FileId prev_id;
|
2006-01-28 23:19:42 +01:00
|
|
|
|
2006-03-04 19:58:09 +01:00
|
|
|
public:
|
|
|
|
ConnectionAdder() : prev_id(NULL_ID) {}
|
|
|
|
|
|
|
|
void operator()(Connections& connections, const char* new_fn)
|
2006-02-09 06:59:33 +01:00
|
|
|
{
|
2006-03-04 19:58:09 +01:00
|
|
|
// we connect previous node with the one identified by new_fn;
|
|
|
|
// on first call, there's nothing to do.
|
|
|
|
const bool was_first_call = (prev_id == NULL_ID);
|
|
|
|
FileId id = id_mgr.id_from_fn(new_fn);
|
|
|
|
const ConnectionId c_id = cid_make(prev_id, id);
|
|
|
|
prev_id = id;
|
|
|
|
if(was_first_call)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// if this connection already exists, increment its occurence
|
|
|
|
// count; otherwise, add it anew.
|
|
|
|
//
|
|
|
|
// note: checking return value of map.insert is more efficient
|
|
|
|
// than find + insert, but requires we know next_c beforehand
|
|
|
|
// (bit of a hack, but safe due to coonnections.reserve() above)
|
|
|
|
Connection* next_c = &connections[0] + connections.size();
|
|
|
|
const std::pair<ConnectionId, Connection*> item = std::make_pair(c_id, next_c);
|
|
|
|
std::pair<Map::iterator, bool> ret = map.insert(item);
|
|
|
|
if(!ret.second) // already existed
|
|
|
|
{
|
|
|
|
Map::iterator inserted_at = ret.first;
|
|
|
|
Connection* c = inserted_at->second; // std::map "payload"
|
|
|
|
c->occurrences++;
|
|
|
|
}
|
|
|
|
else // first time we've seen this connection
|
|
|
|
connections.push_back(Connection(c_id));
|
2006-02-09 06:59:33 +01:00
|
|
|
}
|
|
|
|
};
|
2006-01-31 04:47:52 +01:00
|
|
|
|
2006-03-04 19:58:09 +01:00
|
|
|
void add_connections_from_runs(const Runs& runs, Connections& connections)
|
|
|
|
{
|
|
|
|
file_cache_reset();
|
2006-01-23 21:05:09 +01:00
|
|
|
|
2006-03-04 19:58:09 +01:00
|
|
|
ConnectionAdder add_connection;
|
|
|
|
|
|
|
|
// extract accesses from each run (starting with most recent
|
|
|
|
// first. this isn't critical, but may help a bit since
|
|
|
|
// files that are equally strongly 'connected' are ordered
|
|
|
|
// according to position in file_nodes. that means files from
|
|
|
|
// more recent traces tend to go first, which is good.)
|
|
|
|
for(Runs::const_iterator cit = runs.begin(); cit != runs.end(); ++cit)
|
|
|
|
{
|
|
|
|
const Run& run = *cit;
|
|
|
|
const TraceEntry* ent = run.first;
|
|
|
|
for(uint i = 0; i < run.count; i++, ent++)
|
|
|
|
{
|
|
|
|
// improvement: postprocess the trace and remove all IOs that would be
|
|
|
|
// satisfied by our cache. often repeated IOs would otherwise potentially
|
|
|
|
// be arranged badly.
|
|
|
|
if(requires_actual_io(ent))
|
|
|
|
add_connection(connections, ent->atom_fn);
|
|
|
|
}
|
|
|
|
|
|
|
|
file_cache_reset();
|
|
|
|
}
|
|
|
|
}
|
2006-01-23 21:05:09 +01:00
|
|
|
|
2006-03-04 19:58:09 +01:00
|
|
|
public:
|
|
|
|
ConnectionBuilder(const char* trace_filename, Connections& connections)
|
2006-02-09 06:59:33 +01:00
|
|
|
{
|
2006-03-04 19:58:09 +01:00
|
|
|
Trace t;
|
|
|
|
THROW_ERR(trace_read_from_file(trace_filename, &t));
|
|
|
|
|
2006-02-09 06:59:33 +01:00
|
|
|
// reserve memory for worst-case amount of connections (happens if
|
|
|
|
// all accesses are unique). this is necessary because we store
|
|
|
|
// pointers to Connection in the map, which would be invalidated if
|
|
|
|
// connections[] ever expands.
|
2006-03-04 19:58:09 +01:00
|
|
|
// may waste up to ~3x the memory (about 1mb) for a short time,
|
|
|
|
// which is ok.
|
|
|
|
connections.reserve(t.num_ents-1);
|
2006-02-09 06:59:33 +01:00
|
|
|
|
2006-03-04 19:58:09 +01:00
|
|
|
Runs runs;
|
|
|
|
split_trace_into_runs(&t, runs);
|
2006-02-09 06:59:33 +01:00
|
|
|
|
2006-03-04 19:58:09 +01:00
|
|
|
add_connections_from_runs(runs, connections);
|
|
|
|
}
|
|
|
|
};
|
2006-01-23 21:05:09 +01:00
|
|
|
|
2006-03-04 19:58:09 +01:00
|
|
|
|
|
|
|
class TourBuilder
|
|
|
|
{
|
|
|
|
// sort by decreasing occurrence
|
|
|
|
struct Occurrence_greater: public std::binary_function<const Connection&, const Connection&, bool>
|
|
|
|
{
|
|
|
|
bool operator()(const Connection& c1, const Connection& c2) const
|
|
|
|
{
|
|
|
|
return (c1.occurrences > c2.occurrences);
|
2006-02-09 06:59:33 +01:00
|
|
|
}
|
2006-03-04 19:58:09 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
// not const because we change the graph-related members
|
|
|
|
FileNodes& file_nodes;
|
2006-02-09 06:59:33 +01:00
|
|
|
|
|
|
|
bool has_cycle;
|
|
|
|
void detect_cycleR(FileId node)
|
2006-01-28 23:19:42 +01:00
|
|
|
{
|
2006-03-04 19:58:09 +01:00
|
|
|
FileNode* pnode = &file_nodes[node];
|
|
|
|
pnode->visited = 1;
|
2006-02-09 06:59:33 +01:00
|
|
|
FileId next = pnode->next;
|
|
|
|
if(next != NULL_ID)
|
|
|
|
{
|
2006-03-04 19:58:09 +01:00
|
|
|
FileNode* pnext = &file_nodes[next];
|
2006-02-09 06:59:33 +01:00
|
|
|
if(pnext->visited)
|
|
|
|
has_cycle = true;
|
|
|
|
else
|
|
|
|
detect_cycleR(next);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bool is_cycle_at(FileId node)
|
|
|
|
{
|
|
|
|
has_cycle = false;
|
2006-03-04 19:58:09 +01:00
|
|
|
for(FileNodes::iterator it = file_nodes.begin(); it != file_nodes.end(); ++it)
|
2006-02-09 06:59:33 +01:00
|
|
|
it->visited = 0;
|
|
|
|
detect_cycleR(node);
|
|
|
|
return has_cycle;
|
2006-01-28 23:19:42 +01:00
|
|
|
}
|
2006-01-23 21:05:09 +01:00
|
|
|
|
2006-02-23 08:55:32 +01:00
|
|
|
void try_add_edge(const Connection& c)
|
2006-01-28 23:19:42 +01:00
|
|
|
{
|
2006-02-23 08:55:32 +01:00
|
|
|
FileId first_id = cid_first(c.id);
|
|
|
|
FileId second_id = cid_second(c.id);
|
2006-02-09 06:59:33 +01:00
|
|
|
|
2006-03-04 19:58:09 +01:00
|
|
|
FileNode& first = file_nodes[first_id];
|
|
|
|
FileNode& second = file_nodes[second_id];
|
|
|
|
// one of them has already been hooked up - bail
|
2006-02-23 08:55:32 +01:00
|
|
|
if(first.next != NULL_ID || second.prev != NULL_ID)
|
2006-02-09 06:59:33 +01:00
|
|
|
return;
|
2006-01-23 21:05:09 +01:00
|
|
|
|
2006-02-23 08:55:32 +01:00
|
|
|
first.next = second_id;
|
|
|
|
second.prev = first_id;
|
2006-02-09 06:59:33 +01:00
|
|
|
|
2006-03-04 19:58:09 +01:00
|
|
|
const bool introduced_cycle = is_cycle_at(second_id);
|
2006-02-23 08:55:32 +01:00
|
|
|
debug_assert(introduced_cycle == is_cycle_at(first_id));
|
2006-02-09 06:59:33 +01:00
|
|
|
if(introduced_cycle)
|
|
|
|
{
|
|
|
|
// undo
|
2006-02-23 08:55:32 +01:00
|
|
|
first.next = second.prev = NULL_ID;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// pointer to this is returned by TourBuilder()!
|
2006-03-01 23:31:11 +01:00
|
|
|
std::vector<const char*>& fn_vector;
|
2006-02-23 08:55:32 +01:00
|
|
|
|
2006-03-04 19:58:09 +01:00
|
|
|
void output_chain(FileNode& node)
|
2006-02-23 08:55:32 +01:00
|
|
|
{
|
2006-03-04 19:58:09 +01:00
|
|
|
// early out: if this access was already visited, so must the entire
|
2006-02-23 08:55:32 +01:00
|
|
|
// chain of which it is a part. bail to save lots of time.
|
2006-03-04 19:58:09 +01:00
|
|
|
if(node.output)
|
2006-02-09 06:59:33 +01:00
|
|
|
return;
|
2006-02-23 08:55:32 +01:00
|
|
|
|
|
|
|
// follow prev links starting with c until no more are left;
|
|
|
|
// start ends up the beginning of the chain including <c>.
|
2006-03-04 19:58:09 +01:00
|
|
|
FileNode* start = &node;
|
2006-02-23 08:55:32 +01:00
|
|
|
while(start->prev != NULL_ID)
|
2006-03-04 19:58:09 +01:00
|
|
|
start = &file_nodes[start->prev];
|
2006-02-23 08:55:32 +01:00
|
|
|
|
|
|
|
// iterate over the chain - add to Filenames list and mark as visited
|
2006-03-04 19:58:09 +01:00
|
|
|
FileNode* cur = start;
|
2006-02-23 08:55:32 +01:00
|
|
|
do
|
|
|
|
{
|
2006-03-04 19:58:09 +01:00
|
|
|
if(!cur->output)
|
2006-02-23 08:55:32 +01:00
|
|
|
{
|
|
|
|
fn_vector.push_back(cur->atom_fn);
|
2006-03-04 19:58:09 +01:00
|
|
|
cur->output = 1;
|
2006-02-23 08:55:32 +01:00
|
|
|
}
|
2006-03-04 19:58:09 +01:00
|
|
|
cur = &file_nodes[cur->next];
|
2006-02-09 06:59:33 +01:00
|
|
|
}
|
2006-02-23 08:55:32 +01:00
|
|
|
while(cur->next != NULL_ID);
|
2006-01-28 23:19:42 +01:00
|
|
|
}
|
2006-01-23 21:05:09 +01:00
|
|
|
|
2006-02-09 06:59:33 +01:00
|
|
|
public:
|
2006-03-04 19:58:09 +01:00
|
|
|
TourBuilder(FileNodes& file_nodes_, Connections& connections, std::vector<const char*>& fns_)
|
|
|
|
: file_nodes(file_nodes_), fn_vector(fns_)
|
2006-01-28 23:19:42 +01:00
|
|
|
{
|
2006-02-23 08:55:32 +01:00
|
|
|
std::sort(connections.begin(), connections.end(), Occurrence_greater());
|
|
|
|
|
2006-02-09 06:59:33 +01:00
|
|
|
for(Connections::iterator it = connections.begin(); it != connections.end(); ++it)
|
2006-02-23 08:55:32 +01:00
|
|
|
try_add_edge(*it);
|
2006-02-09 06:59:33 +01:00
|
|
|
|
2006-03-04 19:58:09 +01:00
|
|
|
for(FileNodes::iterator it = file_nodes.begin(); it != file_nodes.end(); ++it)
|
2006-02-23 08:55:32 +01:00
|
|
|
output_chain(*it);
|
2006-01-28 23:19:42 +01:00
|
|
|
}
|
2006-02-09 06:59:33 +01:00
|
|
|
|
|
|
|
// should never be copied; this also squelches warning
|
|
|
|
private:
|
|
|
|
TourBuilder(const TourBuilder& rhs);
|
|
|
|
TourBuilder& operator=(const TourBuilder& rhs);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2006-03-01 23:31:11 +01:00
|
|
|
// autobuild logic: decides when to (re)build an archive.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
static const ssize_t REBUILD_MAIN_ARCHIVE_THRESHOLD = 50;
|
|
|
|
static const ssize_t BUILD_MINI_ARCHIVE_THRESHOLD = 20;
|
2006-02-09 06:59:33 +01:00
|
|
|
|
|
|
|
typedef std::vector<const char*> FnVector;
|
|
|
|
static FnVector loose_files;
|
2006-03-01 23:31:11 +01:00
|
|
|
static ssize_t loose_file_total, non_loose_file_total;
|
2006-02-09 06:59:33 +01:00
|
|
|
|
2006-03-04 19:58:09 +01:00
|
|
|
static std::set<const char*> loose;
|
|
|
|
static std::set<const char*> archive;
|
|
|
|
|
2006-02-09 06:59:33 +01:00
|
|
|
void vfs_opt_notify_loose_file(const char* atom_fn)
|
2006-01-23 21:05:09 +01:00
|
|
|
{
|
2006-03-01 23:31:11 +01:00
|
|
|
loose_file_total++;
|
|
|
|
|
2006-03-04 19:58:09 +01:00
|
|
|
loose.insert(atom_fn);
|
|
|
|
|
2006-03-01 23:31:11 +01:00
|
|
|
// only add if it's not yet clear the main archive will be
|
|
|
|
// rebuilt anyway (otherwise we'd just waste time and memory)
|
|
|
|
if(loose_files.size() > REBUILD_MAIN_ARCHIVE_THRESHOLD)
|
|
|
|
loose_files.push_back(atom_fn);
|
|
|
|
}
|
2006-01-23 21:05:09 +01:00
|
|
|
|
2006-03-04 19:58:09 +01:00
|
|
|
void vfs_opt_notify_non_loose_file(const char* atom_fn)
|
2006-03-01 23:31:11 +01:00
|
|
|
{
|
2006-03-04 19:58:09 +01:00
|
|
|
archive.insert(atom_fn);
|
|
|
|
|
2006-03-01 23:31:11 +01:00
|
|
|
non_loose_file_total++;
|
2006-02-09 06:59:33 +01:00
|
|
|
}
|
2006-01-23 21:05:09 +01:00
|
|
|
|
|
|
|
|
2006-03-01 23:31:11 +01:00
|
|
|
static bool should_rebuild_main_archive(const char* P_archive_path,
|
|
|
|
const char* trace_filename)
|
|
|
|
{
|
2006-03-04 19:58:09 +01:00
|
|
|
std::vector<const char*> diff;
|
|
|
|
set_difference(loose.begin(), loose.end(), archive.begin(), archive.end(), back_inserter(diff));
|
|
|
|
debug_printf("loose only:\n");
|
|
|
|
for(std::vector<const char*>::iterator it = diff.begin(); it != diff.end(); ++it)
|
|
|
|
debug_printf("%s\n", *it);
|
|
|
|
|
2006-03-01 23:31:11 +01:00
|
|
|
// if there's no trace file, no point in building a main archive.
|
|
|
|
struct stat s;
|
|
|
|
if(file_stat(trace_filename, &s) != ERR_OK)
|
|
|
|
return false;
|
|
|
|
// otherwise, if trace is up-to-date, stop recording a new one.
|
|
|
|
const time_t vfs_mtime = tree_most_recent_mtime();
|
|
|
|
if(s.st_mtime >= vfs_mtime)
|
|
|
|
trace_enable(false);
|
|
|
|
|
|
|
|
|
|
|
|
const ssize_t loose_files_only = loose_file_total - non_loose_file_total;
|
|
|
|
if(loose_files_only >= REBUILD_MAIN_ARCHIVE_THRESHOLD)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// more than 3 mini archives
|
|
|
|
|
|
|
|
// development build only: archive is more than 2 weeks old
|
|
|
|
#ifndef FINAL
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool should_build_mini_archive()
|
|
|
|
{
|
|
|
|
const ssize_t loose_files_only = loose_file_total - non_loose_file_total;
|
|
|
|
if(loose_files_only >= BUILD_MINI_ARCHIVE_THRESHOLD)
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static ArchiveBuildState ab;
|
|
|
|
static std::vector<const char*> fn_vector;
|
|
|
|
|
2006-02-09 06:59:33 +01:00
|
|
|
|
2006-03-01 23:31:11 +01:00
|
|
|
static void vfs_opt_init(const char* P_archive_fn_fmt, const char* trace_filename)
|
2006-02-23 08:55:32 +01:00
|
|
|
{
|
2006-03-04 19:58:09 +01:00
|
|
|
FileNodes file_nodes;
|
|
|
|
FileNodeGatherer gatherer(file_nodes);
|
2006-02-09 06:59:33 +01:00
|
|
|
|
2006-03-04 19:58:09 +01:00
|
|
|
id_mgr.init(&file_nodes);
|
|
|
|
|
|
|
|
Connections connections;
|
|
|
|
ConnectionBuilder cbuilder(trace_filename, connections);
|
2006-02-23 08:55:32 +01:00
|
|
|
|
2006-03-04 19:58:09 +01:00
|
|
|
TourBuilder builder(file_nodes, connections, fn_vector);
|
2006-03-01 23:31:11 +01:00
|
|
|
fn_vector.push_back(0);
|
|
|
|
Filenames V_fns = &fn_vector[0];
|
2006-02-23 08:55:32 +01:00
|
|
|
|
2006-03-01 23:31:11 +01:00
|
|
|
char archive_fn[PATH_MAX];
|
|
|
|
static NextNumberedFilenameInfo archive_nfi;
|
|
|
|
next_numbered_filename(P_archive_fn_fmt, &archive_nfi, archive_fn, false);
|
|
|
|
archive_build_init(archive_fn, V_fns, &ab);
|
|
|
|
}
|
2006-02-23 08:55:32 +01:00
|
|
|
|
|
|
|
|
2006-03-01 23:31:11 +01:00
|
|
|
static int vfs_opt_continue()
|
|
|
|
{
|
|
|
|
int ret = archive_build_continue(&ab);
|
|
|
|
if(ret == ERR_OK)
|
|
|
|
{
|
|
|
|
// do NOT delete source files or archives! some apps might want to
|
|
|
|
// keep them (e.g. for source control), or name them differently.
|
|
|
|
|
|
|
|
// rebuild is required to make sure the new archive is used. this is
|
|
|
|
// already taken care of by VFS dir watch, unless it's disabled..
|
2006-02-23 08:55:32 +01:00
|
|
|
#ifdef NO_DIR_WATCH
|
2006-03-01 23:31:11 +01:00
|
|
|
(void)mount_rebuild();
|
2006-02-23 08:55:32 +01:00
|
|
|
#endif
|
2006-03-01 23:31:11 +01:00
|
|
|
}
|
2006-02-09 06:59:33 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-03-01 23:31:11 +01:00
|
|
|
static enum
|
2006-02-09 06:59:33 +01:00
|
|
|
{
|
2006-03-01 23:31:11 +01:00
|
|
|
DECIDE_IF_BUILD,
|
|
|
|
IN_PROGRESS,
|
|
|
|
NOP
|
|
|
|
}
|
|
|
|
state = DECIDE_IF_BUILD;
|
2006-02-09 06:59:33 +01:00
|
|
|
|
2006-03-01 23:31:11 +01:00
|
|
|
void vfs_opt_cancel()
|
|
|
|
{
|
|
|
|
archive_build_cancel(&ab);
|
|
|
|
state = NOP;
|
2006-02-09 06:59:33 +01:00
|
|
|
}
|
|
|
|
|
2006-03-01 23:31:11 +01:00
|
|
|
|
|
|
|
LibError vfs_opt_rebuild_main_archive(const char* P_archive_fn_fmt, const char* trace_filename)
|
2006-02-09 06:59:33 +01:00
|
|
|
{
|
2006-03-01 23:31:11 +01:00
|
|
|
vfs_opt_init(P_archive_fn_fmt, trace_filename);
|
|
|
|
for(;;)
|
|
|
|
{
|
|
|
|
int ret = vfs_opt_continue();
|
|
|
|
RETURN_ERR(ret);
|
|
|
|
if(ret == ERR_OK)
|
|
|
|
return ERR_OK;
|
|
|
|
}
|
2006-02-09 06:59:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-03-01 23:31:11 +01:00
|
|
|
int vfs_opt_auto_build(const char* P_dst_path,
|
|
|
|
const char* main_archive_name_fmt, const char* trace_filename)
|
2006-02-09 06:59:33 +01:00
|
|
|
{
|
2006-03-01 23:31:11 +01:00
|
|
|
if(state == NOP)
|
|
|
|
return INFO_ALL_COMPLETE;
|
|
|
|
|
|
|
|
if(state == DECIDE_IF_BUILD)
|
2006-02-09 06:59:33 +01:00
|
|
|
{
|
2006-03-01 23:31:11 +01:00
|
|
|
char P_archive_fn_fmt[PATH_MAX];
|
|
|
|
(void)vfs_path_append(P_archive_fn_fmt, P_dst_path, main_archive_name_fmt);
|
|
|
|
if(should_rebuild_main_archive(P_archive_fn_fmt, trace_filename))
|
|
|
|
{
|
|
|
|
vfs_opt_init(P_archive_fn_fmt, trace_filename);
|
|
|
|
state = IN_PROGRESS;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// note: only think about building mini archive if not rebuilding
|
|
|
|
// the main archive.
|
|
|
|
if(should_build_mini_archive())
|
|
|
|
{
|
|
|
|
Filenames V_fns = (Filenames)malloc((loose_files.size()+1) * sizeof(const char*));
|
|
|
|
if(!V_fns)
|
|
|
|
return ERR_NO_MEM;
|
|
|
|
std::copy(loose_files.begin(), loose_files.end(), &V_fns[0]);
|
|
|
|
V_fns[loose_files.size()] = 0; // terminator
|
|
|
|
|
|
|
|
// get new unused mini archive name at P_dst_path
|
|
|
|
char mini_archive_fn[VFS_MAX_PATH];
|
|
|
|
char fn_fmt[VFS_MAX_PATH];
|
|
|
|
(void)vfs_path_append(fn_fmt, P_dst_path, "mini%d.zip");
|
|
|
|
static NextNumberedFilenameInfo nfi;
|
|
|
|
next_numbered_filename(fn_fmt, &nfi, mini_archive_fn, false);
|
|
|
|
|
|
|
|
RETURN_ERR(archive_build(mini_archive_fn, V_fns));
|
|
|
|
}
|
|
|
|
|
|
|
|
state = NOP;
|
|
|
|
return ERR_OK; // "finished"
|
|
|
|
}
|
2006-02-09 06:59:33 +01:00
|
|
|
}
|
2006-01-31 04:47:52 +01:00
|
|
|
|
2006-03-01 23:31:11 +01:00
|
|
|
if(state == IN_PROGRESS)
|
|
|
|
{
|
|
|
|
int ret = vfs_opt_continue();
|
|
|
|
if(ret == ERR_OK)
|
|
|
|
state = NOP;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
UNREACHABLE;
|
2006-01-23 21:05:09 +01:00
|
|
|
}
|