mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-08-08 23:08:08 +00:00
1765 lines
60 KiB
C
1765 lines
60 KiB
C
// Copyright (c) Epic Games Tools
|
|
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Helpers
|
|
|
|
internal DI_Key
|
|
di_key_zero(void)
|
|
{
|
|
DI_Key key = {0};
|
|
return key;
|
|
}
|
|
|
|
internal B32
|
|
di_key_match(DI_Key a, DI_Key b)
|
|
{
|
|
B32 result = MemoryMatchStruct(&a, &b);
|
|
return result;
|
|
}
|
|
|
|
internal void
|
|
di_key_list_push(Arena *arena, DI_KeyList *list, DI_Key key)
|
|
{
|
|
DI_KeyNode *n = push_array(arena, DI_KeyNode, 1);
|
|
n->v = key;
|
|
SLLQueuePush(list->first, list->last, n);
|
|
list->count += 1;
|
|
}
|
|
|
|
internal DI_KeyArray
|
|
di_key_array_from_list(Arena *arena, DI_KeyList *list)
|
|
{
|
|
DI_KeyArray array = {0};
|
|
array.count = list->count;
|
|
array.v = push_array(arena, DI_Key, array.count);
|
|
U64 idx = 0;
|
|
for EachNode(n, DI_KeyNode, list->first)
|
|
{
|
|
array.v[idx] = n->v;
|
|
idx += 1;
|
|
}
|
|
return array;
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Main Layer Initialization
|
|
|
|
internal void
|
|
di_init(CmdLine *cmdline)
|
|
{
|
|
Arena *arena = arena_alloc();
|
|
di_shared = push_array(arena, DI_Shared, 1);
|
|
di_shared->arena = arena;
|
|
di_shared->key2path_slots_count = 4096;
|
|
di_shared->key2path_slots = push_array(arena, DI_KeySlot, di_shared->key2path_slots_count);
|
|
di_shared->key2path_stripes = stripe_array_alloc(arena);
|
|
di_shared->path2key_slots_count = 4096;
|
|
di_shared->path2key_slots = push_array(arena, DI_KeySlot, di_shared->path2key_slots_count);
|
|
di_shared->path2key_stripes = stripe_array_alloc(arena);
|
|
di_shared->slots_count = 4096;
|
|
di_shared->slots = push_array(arena, DI_Slot, di_shared->slots_count);
|
|
di_shared->stripes = stripe_array_alloc(arena);
|
|
for EachElement(idx, di_shared->req_batches)
|
|
{
|
|
di_shared->req_batches[idx].mutex = mutex_alloc();
|
|
di_shared->req_batches[idx].arena = arena_alloc();
|
|
}
|
|
U64 signal_pid = 0;
|
|
String8 signal_pid_string = cmd_line_string(cmdline, str8_lit("signal_pid"));
|
|
B32 has_parent = 1;
|
|
if(!try_u64_from_str8_c_rules(signal_pid_string, &signal_pid))
|
|
{
|
|
has_parent = 0;
|
|
signal_pid = get_process_info()->pid;
|
|
}
|
|
U64 signal_code = 0;
|
|
String8 signal_code_string = cmd_line_string(cmdline, str8_lit("signal_code"));
|
|
try_u64_from_str8_c_rules(signal_code_string, &signal_code);
|
|
di_shared->conversion_completion_code = signal_code;
|
|
di_shared->conversion_completion_lock_semaphore_name = str8f(arena, "conversion_completion_lock_pid_%I64u", signal_pid);
|
|
di_shared->conversion_completion_signal_semaphore_name = str8f(arena, "conversion_completion_signal_pid_%I64u", signal_pid);
|
|
di_shared->conversion_completion_shared_memory_name = str8f(arena, "conversion_completion_shared_memory_pid_%I64u", signal_pid);
|
|
if(has_parent)
|
|
{
|
|
di_shared->conversion_completion_lock_semaphore = semaphore_open(di_shared->conversion_completion_lock_semaphore_name);
|
|
di_shared->conversion_completion_signal_semaphore = semaphore_open(di_shared->conversion_completion_signal_semaphore_name);
|
|
di_shared->conversion_completion_shared_memory = shared_memory_open(di_shared->conversion_completion_shared_memory_name);
|
|
}
|
|
else
|
|
{
|
|
di_shared->conversion_completion_lock_semaphore = semaphore_alloc(1, 1, di_shared->conversion_completion_lock_semaphore_name);
|
|
di_shared->conversion_completion_signal_semaphore = semaphore_alloc(0, 65536, di_shared->conversion_completion_signal_semaphore_name);
|
|
di_shared->conversion_completion_shared_memory = shared_memory_alloc(KB(4), di_shared->conversion_completion_shared_memory_name);
|
|
di_shared->conversion_completion_signal_receiver_thread = thread_launch(di_conversion_completion_signal_receiver_thread_entry_point, 0);
|
|
}
|
|
di_shared->conversion_completion_shared_memory_base = (U64 *)shared_memory_view_open(di_shared->conversion_completion_shared_memory, r1u64(0, KB(4)));
|
|
di_shared->completion_mutex = mutex_alloc();
|
|
di_shared->completion_arena = arena_alloc();
|
|
di_shared->event_mutex = mutex_alloc();
|
|
di_shared->event_arena = arena_alloc();
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Path * Timestamp Cache Submission & Lookup
|
|
|
|
internal DI_Key
|
|
di_key_from_path_timestamp(String8 path, U64 min_timestamp)
|
|
{
|
|
//- rjf: unpack key
|
|
U64 hash = u64_hash_from_str8(path);
|
|
U64 slot_idx = hash%di_shared->path2key_slots_count;
|
|
DI_KeySlot *slot = &di_shared->path2key_slots[slot_idx];
|
|
Stripe *stripe = stripe_from_slot_idx(&di_shared->path2key_stripes, slot_idx);
|
|
|
|
//- rjf: look up key, create if needed
|
|
DI_Key key = {0};
|
|
for(B32 write_mode = 0; write_mode <= 1; write_mode += 1)
|
|
{
|
|
// rjf: look up node, with this write mode, to find existing key computation
|
|
B32 found = 0;
|
|
RWMutexScope(stripe->rw_mutex, write_mode)
|
|
{
|
|
DI_KeyPathNode *node = 0;
|
|
for(DI_KeyPathNode *n = slot->first; n != 0; n = n->next)
|
|
{
|
|
if(str8_match(n->path, path, 0) && min_timestamp <= n->min_timestamp)
|
|
{
|
|
found = 1;
|
|
node = n;
|
|
key = node->key;
|
|
break;
|
|
}
|
|
}
|
|
if(!found && write_mode)
|
|
{
|
|
node = stripe->free;
|
|
if(node)
|
|
{
|
|
stripe->free = node->next;
|
|
}
|
|
else
|
|
{
|
|
node = push_array(stripe->arena, DI_KeyPathNode, 1);
|
|
}
|
|
node->path = str8_copy(stripe->arena, path);
|
|
node->min_timestamp = min_timestamp;
|
|
node->key = key;
|
|
DLLPushBack(slot->first, slot->last, node);
|
|
}
|
|
}
|
|
|
|
// rjf: found the key? abort
|
|
if(found)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// rjf: didn't find the key on our read lookup? compute the key before entering
|
|
// write mode
|
|
if(!found && !write_mode)
|
|
{
|
|
B32 made_key = 0;
|
|
|
|
//- rjf: try to make key from file's contents
|
|
if(!made_key)
|
|
{
|
|
File file = file_open(AccessFlag_Read|AccessFlag_ShareRead, path);
|
|
FileProperties props = properties_from_file(file);
|
|
if(min_timestamp <= props.modified)
|
|
{
|
|
//- rjf: PDB magic => use GUID for key
|
|
if(!made_key)
|
|
{
|
|
B32 is_pdb = 0;
|
|
if(!is_pdb)
|
|
{
|
|
read_only local_persist char msf_msf20_magic[] = "Microsoft C/C++ program database 2.00\r\n\x1aJG\0\0";
|
|
U8 msf20_magic_maybe[sizeof(msf_msf20_magic)] = {0};
|
|
file_read(file, r1u64(0, sizeof(msf20_magic_maybe)), msf20_magic_maybe);
|
|
if(MemoryMatch(msf20_magic_maybe, msf_msf20_magic, sizeof(msf20_magic_maybe)))
|
|
{
|
|
is_pdb = 1;
|
|
}
|
|
}
|
|
if(!is_pdb)
|
|
{
|
|
read_only local_persist char msf_msf70_magic[] = "Microsoft C/C++ MSF 7.00\r\n\032DS\0\0";
|
|
U8 msf70_magic_maybe[sizeof(msf_msf70_magic)] = {0};
|
|
file_read(file, r1u64(0, sizeof(msf70_magic_maybe)), msf70_magic_maybe);
|
|
if(MemoryMatch(msf70_magic_maybe, msf_msf70_magic, sizeof(msf70_magic_maybe)))
|
|
{
|
|
is_pdb = 1;
|
|
}
|
|
}
|
|
if(is_pdb)
|
|
{
|
|
// TODO(rjf)
|
|
}
|
|
}
|
|
}
|
|
file_close(file);
|
|
}
|
|
|
|
//- rjf: fallback: hash from path/timestamp
|
|
if(!made_key)
|
|
{
|
|
made_key = 1;
|
|
U128 hash = u128_hash_from_seed_str8(min_timestamp, path);
|
|
MemoryCopy(&key, &hash, Min(sizeof(hash), sizeof(key)));
|
|
}
|
|
|
|
//- rjf: made key -> store in (key -> path/timestamp) table
|
|
if(made_key)
|
|
{
|
|
U64 key_hash = u64_hash_from_str8(str8_struct(&key));
|
|
U64 key_slot_idx = key_hash%di_shared->key2path_slots_count;
|
|
DI_KeySlot *key_slot = &di_shared->key2path_slots[key_slot_idx];
|
|
Stripe *key_stripe = stripe_from_slot_idx(&di_shared->key2path_stripes, key_slot_idx);
|
|
RWMutexScope(key_stripe->rw_mutex, 1)
|
|
{
|
|
DI_KeyPathNode *node = 0;
|
|
for EachNode(n, DI_KeyPathNode, key_slot->first)
|
|
{
|
|
if(di_key_match(n->key, key))
|
|
{
|
|
node = n;
|
|
break;
|
|
}
|
|
}
|
|
if(node == 0)
|
|
{
|
|
node = key_stripe->free;
|
|
if(node != 0)
|
|
{
|
|
key_stripe->free = node->next;
|
|
}
|
|
else
|
|
{
|
|
node = push_array(key_stripe->arena, DI_KeyPathNode, 1);
|
|
}
|
|
DLLPushBack(key_slot->first, key_slot->last, node);
|
|
node->path = str8_copy(key_stripe->arena, path);
|
|
node->min_timestamp = min_timestamp;
|
|
node->key = key;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return key;
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Debug Info Opening / Closing
|
|
|
|
internal void
|
|
di_open(DI_Key key)
|
|
{
|
|
//- rjf: unpack key
|
|
U64 hash = u64_hash_from_str8(str8_struct(&key));
|
|
U64 slot_idx = hash%di_shared->slots_count;
|
|
DI_Slot *slot = &di_shared->slots[slot_idx];
|
|
Stripe *stripe = stripe_from_slot_idx(&di_shared->stripes, slot_idx);
|
|
|
|
//- rjf: bump this key's node's refcount; create if needed
|
|
B32 node_is_new = 0;
|
|
RWMutexScope(stripe->rw_mutex, 1)
|
|
{
|
|
DI_Node *node = 0;
|
|
for(DI_Node *n = slot->first; n != 0; n = n->next)
|
|
{
|
|
if(di_key_match(n->key, key))
|
|
{
|
|
node = n;
|
|
break;
|
|
}
|
|
}
|
|
if(node == 0)
|
|
{
|
|
node_is_new = 1;
|
|
node = stripe->free;
|
|
if(node)
|
|
{
|
|
stripe->free = node->next;
|
|
}
|
|
else
|
|
{
|
|
node = push_array_no_zero(stripe->arena, DI_Node, 1);
|
|
}
|
|
MemoryZeroStruct(node);
|
|
DLLPushBack(slot->first, slot->last, node);
|
|
node->key = key;
|
|
node->batch_request_counts[1] = 1;
|
|
}
|
|
node->refcount += 1;
|
|
}
|
|
|
|
//- rjf: if new, submit low-priority request to load this key
|
|
if(node_is_new)
|
|
{
|
|
DI_RequestBatch *batch = &di_shared->req_batches[1];
|
|
MutexScope(batch->mutex)
|
|
{
|
|
DI_RequestNode *n = push_array(batch->arena, DI_RequestNode, 1);
|
|
SLLQueuePush(batch->first, batch->last, n);
|
|
n->v.key = key;
|
|
batch->count += 1;
|
|
}
|
|
cond_var_broadcast(async_tick_start_cond_var);
|
|
ins_atomic_u32_eval_assign(&async_loop_again, 1);
|
|
}
|
|
}
|
|
|
|
internal void
|
|
di_close(DI_Key key, B32 force_closed)
|
|
{
|
|
//- rjf: unpack key
|
|
U64 hash = u64_hash_from_str8(str8_struct(&key));
|
|
U64 slot_idx = hash%di_shared->slots_count;
|
|
DI_Slot *slot = &di_shared->slots[slot_idx];
|
|
Stripe *stripe = stripe_from_slot_idx(&di_shared->stripes, slot_idx);
|
|
|
|
//- rjf: decrement this key's node's refcount; remove if needed
|
|
B32 node_released = 0;
|
|
File file = {0};
|
|
FileMap file_map = {0};
|
|
FileProperties file_props = {0};
|
|
void *file_base = 0;
|
|
Arena *arena = 0;
|
|
RWMutexScope(stripe->rw_mutex, 1)
|
|
{
|
|
DI_Node *node = 0;
|
|
for(DI_Node *n = slot->first; n != 0; n = n->next)
|
|
{
|
|
if(di_key_match(n->key, key) && ins_atomic_u64_eval(&n->completion_count) > 0)
|
|
{
|
|
node = n;
|
|
break;
|
|
}
|
|
}
|
|
if(node)
|
|
{
|
|
if(force_closed)
|
|
{
|
|
node->refcount = 0;
|
|
}
|
|
else
|
|
{
|
|
node->refcount -= 1;
|
|
}
|
|
if(node->refcount == 0)
|
|
{
|
|
for(;;)
|
|
{
|
|
if(access_pt_is_expired(&node->access_pt, .time = 0, .update_idxs = 0))
|
|
{
|
|
node_released = 1;
|
|
DLLRemove(slot->first, slot->last, node);
|
|
node->next = stripe->free;
|
|
stripe->free = node;
|
|
file = node->file;
|
|
file_map = node->file_map;
|
|
file_props = node->file_props;
|
|
file_base = node->file_base;
|
|
arena = node->arena;
|
|
break;
|
|
}
|
|
cond_var_wait_rw(stripe->cv, stripe->rw_mutex, 1, max_U64);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//- rjf: release node's resources if needed
|
|
if(node_released)
|
|
{
|
|
ins_atomic_u64_dec_eval(&di_shared->load_count);
|
|
ins_atomic_u64_inc_eval(&di_shared->load_gen);
|
|
file_map_view_close(file_map, file_base, r1u64(0, file_props.size));
|
|
file_map_close(file_map);
|
|
file_close(file);
|
|
if(arena != 0)
|
|
{
|
|
arena_release(arena);
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Debug Info Lookups
|
|
|
|
internal U64
|
|
di_load_gen(void)
|
|
{
|
|
U64 result = ins_atomic_u64_eval(&di_shared->load_gen);
|
|
return result;
|
|
}
|
|
|
|
internal U64
|
|
di_load_count(void)
|
|
{
|
|
U64 result = ins_atomic_u64_eval(&di_shared->load_count);
|
|
return result;
|
|
}
|
|
|
|
internal DI_KeyArray
|
|
di_push_all_loaded_keys(Arena *arena)
|
|
{
|
|
Temp scratch = scratch_begin(&arena, 1);
|
|
DI_KeyList list = {0};
|
|
{
|
|
for EachIndex(slot_idx, di_shared->key2path_slots_count)
|
|
{
|
|
DI_KeySlot *slot = &di_shared->key2path_slots[slot_idx];
|
|
Stripe *stripe = stripe_from_slot_idx(&di_shared->key2path_stripes, slot_idx);
|
|
RWMutexScope(stripe->rw_mutex, 0)
|
|
{
|
|
for(DI_KeyPathNode *n = slot->first; n != 0; n = n->next)
|
|
{
|
|
DI_KeyNode *dst_n = push_array(scratch.arena, DI_KeyNode, 1);
|
|
SLLQueuePush(list.first, list.last, dst_n);
|
|
list.count += 1;
|
|
dst_n->v = n->key;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
DI_KeyArray array = {0};
|
|
array.count = list.count;
|
|
array.v = push_array(arena, DI_Key, array.count);
|
|
{
|
|
U64 idx = 0;
|
|
for EachNode(n, DI_KeyNode, list.first)
|
|
{
|
|
array.v[idx] = n->v;
|
|
idx += 1;
|
|
}
|
|
}
|
|
scratch_end(scratch);
|
|
return array;
|
|
}
|
|
|
|
internal RDI_Parsed *
|
|
di_rdi_from_key(Access *access, DI_Key key, B32 high_priority, U64 endt_us)
|
|
{
|
|
RDI_Parsed *rdi = &rdi_parsed_nil;
|
|
{
|
|
U64 hash = u64_hash_from_str8(str8_struct(&key));
|
|
U64 slot_idx = hash%di_shared->slots_count;
|
|
DI_Slot *slot = &di_shared->slots[slot_idx];
|
|
Stripe *stripe = stripe_from_slot_idx(&di_shared->stripes, slot_idx);
|
|
RWMutexScope(stripe->rw_mutex, 0) for(;;)
|
|
{
|
|
// rjf: try to grab current results
|
|
B32 found = 0;
|
|
B32 need_hi_request = 0;
|
|
B32 grabbed = 0;
|
|
for(DI_Node *n = slot->first; n != 0; n = n->next)
|
|
{
|
|
if(di_key_match(n->key, key) && ins_atomic_u64_eval(&n->refcount) > 0)
|
|
{
|
|
found = 1;
|
|
if(high_priority && ins_atomic_u64_eval_cond_assign(&n->batch_request_counts[0], 1, 0) == 0)
|
|
{
|
|
need_hi_request = 1;
|
|
}
|
|
if(ins_atomic_u64_eval(&n->completion_count) > 0)
|
|
{
|
|
grabbed = 1;
|
|
rdi = &n->rdi;
|
|
access_touch(access, &n->access_pt, stripe->cv);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// rjf: push high-priority request if needed
|
|
if(need_hi_request)
|
|
{
|
|
DI_RequestBatch *batch = &di_shared->req_batches[0];
|
|
MutexScope(batch->mutex)
|
|
{
|
|
DI_RequestNode *n = push_array(batch->arena, DI_RequestNode, 1);
|
|
SLLQueuePush(batch->first, batch->last, n);
|
|
n->v.key = key;
|
|
batch->count += 1;
|
|
}
|
|
cond_var_broadcast(async_tick_start_cond_var);
|
|
ins_atomic_u32_eval_assign(&async_loop_again, 1);
|
|
ins_atomic_u32_eval_assign(&async_loop_again_high_priority, 1);
|
|
}
|
|
|
|
// rjf: found current results, or out-of-time? abort
|
|
if(grabbed || now_time_us() >= endt_us)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// rjf: wait on stripe change
|
|
cond_var_wait_rw(stripe->cv, stripe->rw_mutex, 0, endt_us);
|
|
}
|
|
}
|
|
return rdi;
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Events
|
|
|
|
internal DI_EventList
|
|
di_get_events(Arena *arena)
|
|
{
|
|
DI_EventList dst = {0};
|
|
MutexScope(di_shared->event_mutex)
|
|
{
|
|
for EachNode(src_n, DI_EventNode, di_shared->events.first)
|
|
{
|
|
DI_EventNode *dst_n = push_array(arena, DI_EventNode, 1);
|
|
MemoryCopyStruct(&dst_n->v, &src_n->v);
|
|
dst_n->v.string = str8_copy(arena, dst_n->v.string);
|
|
SLLQueuePush(dst.first, dst.last, dst_n);
|
|
dst.count += 1;
|
|
}
|
|
MemoryZeroStruct(&di_shared->events);
|
|
arena_clear(di_shared->event_arena);
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Asynchronous Tick
|
|
|
|
internal void
|
|
di_async_tick(void)
|
|
{
|
|
Temp scratch = scratch_begin(0, 0);
|
|
|
|
//////////////////////////////
|
|
//- rjf: do single-lane update: pop requests, update tasks, gather RDI paths to parse wide
|
|
//
|
|
typedef struct ParseTask ParseTask;
|
|
struct ParseTask
|
|
{
|
|
DI_Key key;
|
|
String8 rdi_path;
|
|
};
|
|
ParseTask *parse_tasks = 0;
|
|
U64 parse_tasks_count = 0;
|
|
if(lane_idx() == 0)
|
|
{
|
|
typedef struct ParseTaskNode ParseTaskNode;
|
|
struct ParseTaskNode
|
|
{
|
|
ParseTaskNode *next;
|
|
ParseTask v;
|
|
};
|
|
ParseTaskNode *first_parse_task = 0;
|
|
ParseTaskNode *last_parse_task = 0;
|
|
|
|
////////////////////////////
|
|
//- rjf: pop all requests, high priority first
|
|
//
|
|
DI_RequestNode *first_req[2] = {0};
|
|
DI_RequestNode *last_req[2] = {0};
|
|
for EachElement(idx, di_shared->req_batches)
|
|
{
|
|
DI_RequestBatch *b = &di_shared->req_batches[idx];
|
|
MutexScope(b->mutex)
|
|
{
|
|
for EachNode(n, DI_RequestNode, b->first)
|
|
{
|
|
DI_RequestNode *n_copy = push_array(scratch.arena, DI_RequestNode, 1);
|
|
MemoryCopyStruct(&n_copy->v, &n->v);
|
|
SLLQueuePush(first_req[idx], last_req[idx], n_copy);
|
|
}
|
|
arena_clear(b->arena);
|
|
b->first = b->last = 0;
|
|
b->count = 0;
|
|
}
|
|
}
|
|
|
|
////////////////////////////
|
|
//- rjf: gather all completions
|
|
//
|
|
DI_LoadCompletion *first_completion = 0;
|
|
DI_LoadCompletion *last_completion = 0;
|
|
MutexScope(di_shared->completion_mutex)
|
|
{
|
|
for EachNode(c, DI_LoadCompletion, di_shared->first_completion)
|
|
{
|
|
DI_LoadCompletion *dst_c = push_array(scratch.arena, DI_LoadCompletion, 1);
|
|
SLLQueuePush(first_completion, last_completion, dst_c);
|
|
dst_c->code = c->code;
|
|
}
|
|
arena_clear(di_shared->completion_arena);
|
|
di_shared->first_completion = di_shared->last_completion = 0;
|
|
}
|
|
|
|
////////////////////////////
|
|
//- rjf: generate load tasks for all unique requests
|
|
//
|
|
for EachElement(priority_idx, first_req)
|
|
{
|
|
for EachNode(n, DI_RequestNode, first_req[priority_idx])
|
|
{
|
|
// rjf: unpack request
|
|
DI_Key key = n->v.key;
|
|
|
|
// rjf: determine if this request is a duplicate
|
|
B32 request_is_duplicate = 1;
|
|
{
|
|
U64 hash = u64_hash_from_str8(str8_struct(&key));
|
|
U64 slot_idx = hash%di_shared->slots_count;
|
|
DI_Slot *slot = &di_shared->slots[slot_idx];
|
|
Stripe *stripe = stripe_from_slot_idx(&di_shared->stripes, slot_idx);
|
|
RWMutexScope(stripe->rw_mutex, 0)
|
|
{
|
|
for(DI_Node *n = slot->first; n != 0; n = n->next)
|
|
{
|
|
if(di_key_match(n->key, key) && ins_atomic_u64_eval(&n->completion_count) == 0)
|
|
{
|
|
request_is_duplicate = (ins_atomic_u64_eval_cond_assign(&n->working_count, 1, 0) != 0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// rjf: if not a duplicate, create new task
|
|
if(!request_is_duplicate)
|
|
{
|
|
DI_LoadTask *t = di_shared->free_load_task;
|
|
if(t)
|
|
{
|
|
SLLStackPop(di_shared->free_load_task);
|
|
}
|
|
else
|
|
{
|
|
t = push_array_no_zero(di_shared->arena, DI_LoadTask, 1);
|
|
}
|
|
MemoryZeroStruct(t);
|
|
DLLPushBack(di_shared->first_load_task[priority_idx], di_shared->last_load_task[priority_idx], t);
|
|
t->key = key;
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////
|
|
//- rjf: update tasks: configure, launch if we can, & retire if we can
|
|
//
|
|
for EachElement(priority_idx, di_shared->first_load_task)
|
|
{
|
|
for(DI_LoadTask *t = di_shared->first_load_task[priority_idx], *next = 0; t != 0; t = next)
|
|
{
|
|
next = t->next;
|
|
|
|
//- rjf: unpack key
|
|
DI_Key key = t->key;
|
|
U64 key_hash = u64_hash_from_str8(str8_struct(&key));
|
|
U64 key_slot_idx = key_hash%di_shared->key2path_slots_count;
|
|
DI_KeySlot *key_slot = &di_shared->key2path_slots[key_slot_idx];
|
|
Stripe *key_stripe = stripe_from_slot_idx(&di_shared->key2path_stripes, key_slot_idx);
|
|
|
|
//- rjf: get key's O.G. path
|
|
String8 og_path = {0};
|
|
U64 og_min_timestamp = 0;
|
|
RWMutexScope(key_stripe->rw_mutex, 0)
|
|
{
|
|
for(DI_KeyPathNode *n = key_slot->first; n != 0; n = n->next)
|
|
{
|
|
if(di_key_match(n->key, key))
|
|
{
|
|
og_path = str8_copy(scratch.arena, n->path);
|
|
og_min_timestamp = n->min_timestamp;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//- rjf: analyze O.G. debug info
|
|
if(!t->og_analyzed)
|
|
{
|
|
t->og_analyzed = 1;
|
|
File file = file_open(AccessFlag_ShareRead|AccessFlag_Read, og_path);
|
|
FileProperties props = properties_from_file(file);
|
|
t->og_size = props.size;
|
|
U64 rdi_magic_maybe = 0;
|
|
if(file_read_struct(file, 0, &rdi_magic_maybe) == 8 &&
|
|
rdi_magic_maybe == RDI_MAGIC_CONSTANT)
|
|
{
|
|
t->og_is_rdi = 1;
|
|
}
|
|
file_close(file);
|
|
}
|
|
U64 og_size = t->og_size;
|
|
B32 og_is_rdi = t->og_is_rdi;
|
|
B32 og_is_good = (og_size > 0);
|
|
|
|
//- rjf: compute key's RDI path
|
|
String8 rdi_path = {0};
|
|
{
|
|
if(og_is_rdi)
|
|
{
|
|
rdi_path = og_path;
|
|
}
|
|
else
|
|
{
|
|
rdi_path = str8f(scratch.arena, "%S.rdi", str8_chop_last_dot(og_path));
|
|
}
|
|
}
|
|
|
|
//- rjf: determine if RDI is stale
|
|
if(!t->rdi_analyzed)
|
|
{
|
|
t->rdi_analyzed = 1;
|
|
File file = file_open(AccessFlag_ShareRead|AccessFlag_Read, rdi_path);
|
|
FileProperties props = properties_from_file(file);
|
|
if(props.modified < og_min_timestamp)
|
|
{
|
|
t->rdi_is_stale = 1;
|
|
}
|
|
else
|
|
{
|
|
t->rdi_is_stale = 1;
|
|
RDI_Header header = {0};
|
|
if(file_read_struct(file, 0, &header) == sizeof(header))
|
|
{
|
|
t->rdi_is_stale = (header.encoding_version != RDI_ENCODING_VERSION);
|
|
}
|
|
}
|
|
file_close(file);
|
|
}
|
|
B32 rdi_is_stale = t->rdi_is_stale;
|
|
|
|
//- rjf: calculate thread counts for conversion processes
|
|
if(!og_is_rdi && rdi_is_stale && t->thread_count == 0)
|
|
{
|
|
U64 thread_count = 1;
|
|
U64 max_thread_count = get_system_info()->logical_processor_count/2;
|
|
if(priority_idx > 0)
|
|
{
|
|
max_thread_count = Max(1, max_thread_count/2);
|
|
}
|
|
{
|
|
if(0){}
|
|
else if(og_size <= MB(4)) {thread_count = 1;}
|
|
else if(og_size <= MB(256)) {thread_count = max_thread_count/4;}
|
|
else if(og_size <= MB(512)) {thread_count = max_thread_count/3;}
|
|
else if(og_size <= GB(1)) {thread_count = max_thread_count/2;}
|
|
else {thread_count = max_thread_count;}
|
|
}
|
|
thread_count = Max(1, thread_count);
|
|
t->thread_count = thread_count;
|
|
}
|
|
|
|
//- rjf: determine if there are threads available
|
|
B32 threads_available = 0;
|
|
{
|
|
U64 max_threads = get_system_info()->logical_processor_count/2;
|
|
U64 current_threads = di_shared->conversion_thread_count;
|
|
U64 needed_threads = (current_threads + t->thread_count);
|
|
threads_available = (max_threads >= needed_threads);
|
|
}
|
|
|
|
//- rjf: if this conversion will overwrite an RDI we already have in cache,
|
|
// then we need to evict the old one from the cache.
|
|
B32 ready_to_launch_conversion = (threads_available && !og_is_rdi && rdi_is_stale && t->thread_count != 0 && t->status != DI_LoadTaskStatus_Active);
|
|
if(ready_to_launch_conversion)
|
|
{
|
|
U64 path2key_hash = u64_hash_from_str8(og_path);
|
|
U64 path2key_slot_idx = path2key_hash%di_shared->path2key_slots_count;
|
|
DI_KeySlot *path2key_slot = &di_shared->path2key_slots[path2key_slot_idx];
|
|
Stripe *path2key_stripe = stripe_from_slot_idx(&di_shared->path2key_stripes, path2key_slot_idx);
|
|
RWMutexScope(path2key_stripe->rw_mutex, 0)
|
|
{
|
|
// NOTE(rjf): we need to iterate from last -> first, since we want to evict the
|
|
// most recent key.
|
|
for(DI_KeyPathNode *n = path2key_slot->last; n != 0; n = n->prev)
|
|
{
|
|
if(str8_match(n->path, og_path, 0) && !di_key_match(key, n->key))
|
|
{
|
|
di_close(n->key, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//- rjf: launch conversion processes
|
|
if(og_is_good && ready_to_launch_conversion)
|
|
{
|
|
B32 should_compress = 0;
|
|
ProcessLaunchParams params = {0};
|
|
params.path = get_process_info()->binary_path;
|
|
params.inherit_env = 1;
|
|
params.consoleless = 1;
|
|
str8_list_pushf(scratch.arena, ¶ms.cmd_line, "raddbg");
|
|
str8_list_pushf(scratch.arena, ¶ms.cmd_line, "--bin");
|
|
str8_list_pushf(scratch.arena, ¶ms.cmd_line, "--quiet");
|
|
if(should_compress)
|
|
{
|
|
str8_list_pushf(scratch.arena, ¶ms.cmd_line, "--compress");
|
|
}
|
|
// str8_list_pushf(scratch.arena, ¶ms.cmd_line, "--capture");
|
|
str8_list_pushf(scratch.arena, ¶ms.cmd_line, "--rdi");
|
|
str8_list_pushf(scratch.arena, ¶ms.cmd_line, "--out:%S", rdi_path);
|
|
str8_list_pushf(scratch.arena, ¶ms.cmd_line, "--thread_count:%I64u", t->thread_count);
|
|
str8_list_pushf(scratch.arena, ¶ms.cmd_line, "--signal_pid:%I64u", (U64)get_process_info()->pid);
|
|
str8_list_pushf(scratch.arena, ¶ms.cmd_line, "--signal_code:%I64u", (U64)t);
|
|
str8_list_pushf(scratch.arena, ¶ms.cmd_line, "%S", og_path);
|
|
ProfMsg("launch creation for %.*s", str8_varg(rdi_path));
|
|
t->process = process_launch(¶ms);
|
|
t->status = DI_LoadTaskStatus_Active;
|
|
di_shared->conversion_process_count += 1;
|
|
di_shared->conversion_thread_count += t->thread_count;
|
|
|
|
// rjf: send event
|
|
MutexScope(di_shared->event_mutex)
|
|
{
|
|
DI_EventNode *n = push_array(di_shared->event_arena, DI_EventNode, 1);
|
|
SLLQueuePush(di_shared->events.first, di_shared->events.last, n);
|
|
di_shared->events.count += 1;
|
|
n->v.kind = DI_EventKind_ConversionStarted;
|
|
n->v.string = str8_copy(di_shared->event_arena, rdi_path);
|
|
}
|
|
}
|
|
|
|
//- rjf: if active & process has completed, mark as done
|
|
{
|
|
U64 exit_code = 0;
|
|
if(t->status == DI_LoadTaskStatus_Active)
|
|
{
|
|
B32 task_is_done = 0;
|
|
for(DI_LoadCompletion *c = first_completion; c != 0; c = c->next)
|
|
{
|
|
if(c->code == (U64)t)
|
|
{
|
|
task_is_done = 1;
|
|
break;
|
|
}
|
|
}
|
|
if(!task_is_done)
|
|
{
|
|
task_is_done = process_join(t->process, 0, 0);
|
|
}
|
|
if(task_is_done)
|
|
{
|
|
t->status = DI_LoadTaskStatus_Done;
|
|
di_shared->conversion_process_count -= 1;
|
|
di_shared->conversion_thread_count -= t->thread_count;
|
|
}
|
|
}
|
|
}
|
|
|
|
//- rjf: ready to launch, but bad O.G. file -> just immediately mark as done
|
|
if(!og_is_good && ready_to_launch_conversion)
|
|
{
|
|
t->status = DI_LoadTaskStatus_Done;
|
|
}
|
|
|
|
//- rjf: if the RDI for this task is not stale, then we're already done - mark this
|
|
// task as done & prepped for storing into the cache
|
|
if(!rdi_is_stale)
|
|
{
|
|
t->status = DI_LoadTaskStatus_Done;
|
|
}
|
|
|
|
//- rjf: if the RDI for this task *is* stale, but the O.G. path is actually RDI,
|
|
// then we can't actually re-convert to produce a non-stale RDI. in this case, just
|
|
// mark as done.
|
|
if(rdi_is_stale && og_is_rdi)
|
|
{
|
|
t->status = DI_LoadTaskStatus_Done;
|
|
}
|
|
|
|
//- rjf: if task is done, retire & recycle task; gather path to load
|
|
if(t->status == DI_LoadTaskStatus_Done)
|
|
{
|
|
if(!process_match(t->process, process_zero())) MutexScope(di_shared->event_mutex)
|
|
{
|
|
DI_EventNode *n = push_array(di_shared->event_arena, DI_EventNode, 1);
|
|
SLLQueuePush(di_shared->events.first, di_shared->events.last, n);
|
|
di_shared->events.count += 1;
|
|
n->v.kind = DI_EventKind_ConversionEnded;
|
|
n->v.string = str8_copy(di_shared->event_arena, rdi_path);
|
|
}
|
|
DLLRemove(di_shared->first_load_task[priority_idx], di_shared->last_load_task[priority_idx], t);
|
|
SLLStackPush(di_shared->free_load_task, t);
|
|
ParseTaskNode *n = push_array(scratch.arena, ParseTaskNode, 1);
|
|
n->v.key = key;
|
|
n->v.rdi_path = rdi_path;
|
|
SLLQueuePush(first_parse_task, last_parse_task, n);
|
|
parse_tasks_count += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////
|
|
//- rjf: join all parse tasks
|
|
//
|
|
parse_tasks = push_array(scratch.arena, ParseTask, parse_tasks_count);
|
|
{
|
|
U64 idx = 0;
|
|
for EachNode(n, ParseTaskNode, first_parse_task)
|
|
{
|
|
parse_tasks[idx] = n->v;
|
|
idx += 1;
|
|
}
|
|
}
|
|
}
|
|
lane_sync_u64(&parse_tasks, 0);
|
|
lane_sync_u64(&parse_tasks_count, 0);
|
|
lane_sync();
|
|
|
|
//////////////////////////////
|
|
//- rjf: do wide load of all prepped RDIs
|
|
//
|
|
U64 parse_task_take_counter = 0;
|
|
U64 *parse_task_take_counter_ptr = 0;
|
|
if(lane_idx() == 0)
|
|
{
|
|
parse_task_take_counter_ptr = &parse_task_take_counter;
|
|
}
|
|
lane_sync_u64(&parse_task_take_counter_ptr, 0);
|
|
{
|
|
for(;;)
|
|
{
|
|
//- rjf: take next task
|
|
U64 parse_task_idx = ins_atomic_u64_inc_eval(parse_task_take_counter_ptr) - 1;
|
|
if(parse_task_idx >= parse_tasks_count)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//- rjf: unpack task
|
|
DI_Key key = parse_tasks[parse_task_idx].key;
|
|
String8 rdi_path = parse_tasks[parse_task_idx].rdi_path;
|
|
ProfBegin("parse %.*s", str8_varg(rdi_path));
|
|
|
|
//- rjf: open file
|
|
File file = {0};
|
|
FileMap file_map = {0};
|
|
FileProperties file_props = {0};
|
|
void *file_base = 0;
|
|
{
|
|
file = file_open(AccessFlag_Read|AccessFlag_ShareRead|AccessFlag_ShareWrite, rdi_path);
|
|
file_map = file_map_open(AccessFlag_Read, file);
|
|
file_props = properties_from_file(file);
|
|
file_base = file_map_view_open(file_map, AccessFlag_Read, r1u64(0, file_props.size));
|
|
}
|
|
|
|
//- rjf: do initial parse of rdi
|
|
RDI_Parsed rdi_parsed_maybe_compressed = rdi_parsed_nil;
|
|
{
|
|
RDI_ParseStatus parse_status = rdi_parse((U8 *)file_base, file_props.size, &rdi_parsed_maybe_compressed);
|
|
(void)parse_status;
|
|
}
|
|
|
|
//- rjf: decompress & re-parse, if necessary
|
|
Arena *rdi_parsed_arena = 0;
|
|
RDI_Parsed rdi_parsed = rdi_parsed_maybe_compressed;
|
|
{
|
|
U64 decompressed_size = rdi_decompressed_size_from_parsed(&rdi_parsed_maybe_compressed);
|
|
if(decompressed_size > file_props.size)
|
|
{
|
|
rdi_parsed_arena = arena_alloc();
|
|
U8 *decompressed_data = push_array_no_zero(rdi_parsed_arena, U8, decompressed_size);
|
|
rdi_decompress_parsed(decompressed_data, decompressed_size, &rdi_parsed_maybe_compressed);
|
|
RDI_ParseStatus parse_status = rdi_parse(decompressed_data, decompressed_size, &rdi_parsed);
|
|
(void)parse_status;
|
|
}
|
|
}
|
|
|
|
//- rjf: commit parsed info to cache
|
|
{
|
|
ProfMsg("commit %.*s", str8_varg(rdi_path));
|
|
U64 hash = u64_hash_from_str8(str8_struct(&key));
|
|
U64 slot_idx = hash%di_shared->slots_count;
|
|
DI_Slot *slot = &di_shared->slots[slot_idx];
|
|
Stripe *stripe = stripe_from_slot_idx(&di_shared->stripes, slot_idx);
|
|
RWMutexScope(stripe->rw_mutex, 1)
|
|
{
|
|
DI_Node *node = 0;
|
|
for(DI_Node *n = slot->first; n != 0; n = n->next)
|
|
{
|
|
if(di_key_match(n->key, key))
|
|
{
|
|
node = n;
|
|
break;
|
|
}
|
|
}
|
|
if(node)
|
|
{
|
|
node->file = file;
|
|
node->file_map = file_map;
|
|
node->file_props = file_props;
|
|
node->file_base = file_base;
|
|
node->arena = rdi_parsed_arena;
|
|
MemoryCopyStruct(&node->rdi, &rdi_parsed);
|
|
node->completion_count += 1;
|
|
node->working_count -= 1;
|
|
if(node->rdi.raw_data_size != 0)
|
|
{
|
|
ins_atomic_u64_inc_eval(&di_shared->load_gen);
|
|
}
|
|
ins_atomic_u64_inc_eval(&di_shared->load_count);
|
|
}
|
|
else
|
|
{
|
|
if(rdi_parsed_arena != 0)
|
|
{
|
|
arena_release(rdi_parsed_arena);
|
|
}
|
|
file_map_view_close(file_map, file_base, r1u64(0, file_props.size));
|
|
file_map_close(file_map);
|
|
file_close(file);
|
|
}
|
|
}
|
|
cond_var_broadcast(stripe->cv);
|
|
}
|
|
|
|
ProfEnd();
|
|
}
|
|
}
|
|
lane_sync();
|
|
|
|
scratch_end(scratch);
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Conversion Completion Signal Receiver Thread
|
|
|
|
internal void
|
|
di_signal_completion(void)
|
|
{
|
|
semaphore_take(di_shared->conversion_completion_lock_semaphore, max_U64);
|
|
di_shared->conversion_completion_shared_memory_base[0] = di_shared->conversion_completion_code;
|
|
semaphore_drop(di_shared->conversion_completion_lock_semaphore);
|
|
semaphore_drop(di_shared->conversion_completion_signal_semaphore);
|
|
}
|
|
|
|
internal void
|
|
di_conversion_completion_signal_receiver_thread_entry_point(void *p)
|
|
{
|
|
ThreadNameF("di_conversion_completion_signal_receiver_thread");
|
|
for(;;)
|
|
{
|
|
if(semaphore_take(di_shared->conversion_completion_signal_semaphore, max_U64))
|
|
{
|
|
// rjf: get the next retired code
|
|
U64 retired_code = 0;
|
|
semaphore_take(di_shared->conversion_completion_lock_semaphore, max_U64);
|
|
retired_code = di_shared->conversion_completion_shared_memory_base[0];
|
|
semaphore_drop(di_shared->conversion_completion_lock_semaphore);
|
|
|
|
// rjf: push completion record
|
|
MutexScope(di_shared->completion_mutex)
|
|
{
|
|
DI_LoadCompletion *c = push_array(di_shared->completion_arena, DI_LoadCompletion, 1);
|
|
SLLQueuePush(di_shared->first_completion, di_shared->last_completion, c);
|
|
c->code = retired_code;
|
|
}
|
|
|
|
// rjf: signal async system to resume
|
|
ProfMsg("signal conversion completion");
|
|
ins_atomic_u32_eval_assign(&async_loop_again, 1);
|
|
ins_atomic_u32_eval_assign(&async_loop_again_high_priority, 1);
|
|
cond_var_broadcast(async_tick_start_cond_var);
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Search Artifact Cache Hooks / Lookups
|
|
|
|
internal AC_Artifact
|
|
di_search_artifact_create(String8 key, B32 *cancel_signal, B32 *retry_out, U64 *gen_out)
|
|
{
|
|
ProfBeginFunction();
|
|
Access *access = access_open();
|
|
Temp scratch = scratch_begin(0, 0);
|
|
AC_Artifact artifact = {0};
|
|
{
|
|
//- rjf: unpack key
|
|
RDI_SectionKind section_kind = RDI_SectionKind_NULL;
|
|
String8 query = {0};
|
|
{
|
|
U64 key_read_off = 0;
|
|
key_read_off += str8_deserial_read_struct(key, key_read_off, §ion_kind);
|
|
key_read_off += str8_deserial_read_struct(key, key_read_off, &query.size);
|
|
query.str = push_array(scratch.arena, U8, query.size);
|
|
key_read_off += str8_deserial_read(key, key_read_off, query.str, query.size, 1);
|
|
}
|
|
|
|
//- rjf: gather all debug info keys we'll search on
|
|
DI_KeyArray keys = {0};
|
|
ProfScope("gather all debug info keys we'll search on")
|
|
{
|
|
if(lane_idx() == 0)
|
|
{
|
|
keys = di_push_all_loaded_keys(scratch.arena);
|
|
}
|
|
lane_sync_u64(&keys.v, 0);
|
|
lane_sync_u64(&keys.count, 0);
|
|
}
|
|
|
|
//- rjf: map all debug info keys -> RDIs
|
|
RDI_Parsed **rdis = 0;
|
|
ProfScope("map all debug info keys -> RDIs")
|
|
{
|
|
if(lane_idx() == 0)
|
|
{
|
|
rdis = push_array(scratch.arena, RDI_Parsed *, keys.count);
|
|
}
|
|
lane_sync_u64(&rdis, 0);
|
|
{
|
|
Rng1U64 range = lane_range(keys.count);
|
|
for EachInRange(idx, range)
|
|
{
|
|
rdis[idx] = di_rdi_from_key(access, keys.v[idx], 0, 0);
|
|
}
|
|
}
|
|
}
|
|
lane_sync();
|
|
|
|
//- rjf: do wide search on all lanes
|
|
Arena *arena = arena_alloc();
|
|
Arena **arenas = 0;
|
|
U64 arenas_count = lane_count();
|
|
if(lane_idx() == 0)
|
|
{
|
|
arenas = push_array(arena, Arena *, arenas_count);
|
|
}
|
|
lane_sync_u64(&arenas, 0);
|
|
arenas[lane_idx()] = arena;
|
|
DI_SearchItemChunkList *lanes_items = 0;
|
|
ProfScope("do wide search on all lanes")
|
|
{
|
|
if(lane_idx() == 0)
|
|
{
|
|
lanes_items = push_array(scratch.arena, DI_SearchItemChunkList, lane_count());
|
|
}
|
|
lane_sync_u64(&lanes_items, 0);
|
|
{
|
|
DI_SearchItemChunkList *lane_items = &lanes_items[lane_idx()];
|
|
for EachIndex(rdi_idx, keys.count)
|
|
{
|
|
DI_Key key = keys.v[rdi_idx];
|
|
RDI_Parsed *rdi = rdis[rdi_idx];
|
|
|
|
// rjf: unpack table info
|
|
U64 element_count = 0;
|
|
void *table_base = rdi_section_raw_table_from_kind(rdi, section_kind, &element_count);
|
|
U64 element_size = rdi_section_element_size_table[section_kind];
|
|
|
|
// rjf: do search
|
|
Rng1U64 range = lane_range(element_count);
|
|
for EachInRange(idx, range)
|
|
{
|
|
//- rjf: every so often, check if we need to cancel, and cancel
|
|
if(idx%10000 == 0 && !!ins_atomic_u32_eval(cancel_signal))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//- rjf: get element, map to string; if empty, continue to next element
|
|
void *element = (U8 *)table_base + element_size*idx;
|
|
String8 name = {0};
|
|
switch(section_kind)
|
|
{
|
|
case RDI_SectionKind_UDTs:
|
|
{
|
|
RDI_UDT *udt = (RDI_UDT *)element;
|
|
RDI_TypeNode *type_node = rdi_element_from_name_idx(rdi, TypeNodes, udt->self_type_idx);
|
|
name = fully_qualified_from_rdi_string_and_container(arena, rdi, type_node->user_defined.name_string_idx, udt->container_idx, udt->container_flags);
|
|
}break;
|
|
case RDI_SectionKind_SourceFiles:
|
|
{
|
|
Temp scratch = scratch_begin(&arena, 1);
|
|
RDI_SourceFile *file = (RDI_SourceFile *)element;
|
|
String8List path_parts = {0};
|
|
for(RDI_FilePathNode *fpn = rdi_element_from_name_idx(rdi, FilePathNodes, file->file_path_node_idx);
|
|
fpn != rdi_element_from_name_idx(rdi, FilePathNodes, 0);
|
|
fpn = rdi_element_from_name_idx(rdi, FilePathNodes, fpn->parent_path_node))
|
|
{
|
|
String8 path_part = {0};
|
|
path_part.str = rdi_string_from_idx(rdi, fpn->name_string_idx, &path_part.size);
|
|
str8_list_push_front(scratch.arena, &path_parts, path_part);
|
|
}
|
|
StringJoin join = {0};
|
|
join.sep = str8_lit("/");
|
|
name = str8_list_join(arena, &path_parts, &join);
|
|
scratch_end(scratch);
|
|
}break;
|
|
default:
|
|
{
|
|
RDI_Symbol *symbol = (RDI_Symbol *)element;
|
|
name = fully_qualified_str8_from_rdi_symbol(arena, rdi, symbol);
|
|
}break;
|
|
}
|
|
if(name.size == 0) { continue; }
|
|
|
|
//- rjf: fuzzy match against query
|
|
FuzzyMatchRangeList matches = fuzzy_match_find(arena, query, name);
|
|
|
|
//- rjf: collect
|
|
if(matches.count == matches.needle_part_count)
|
|
{
|
|
DI_SearchItemChunk *chunk = lane_items->last;
|
|
if(chunk == 0 || chunk->count >= chunk->cap)
|
|
{
|
|
chunk = push_array(scratch.arena, DI_SearchItemChunk, 1);
|
|
chunk->base_idx = lane_items->total_count;
|
|
chunk->cap = 1024;
|
|
chunk->count = 0;
|
|
chunk->v = push_array_no_zero(scratch.arena, DI_SearchItem, chunk->cap);
|
|
SLLQueuePush(lane_items->first, lane_items->last, chunk);
|
|
lane_items->chunk_count += 1;
|
|
}
|
|
chunk->v[chunk->count].idx = idx;
|
|
chunk->v[chunk->count].key = key;
|
|
chunk->v[chunk->count].match_ranges = matches;
|
|
chunk->v[chunk->count].missed_size = (name.size > matches.total_dim) ? (name.size-matches.total_dim) : 0;
|
|
chunk->count += 1;
|
|
lane_items->total_count += 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
lane_sync();
|
|
|
|
//- rjf: join all lane chunk lists
|
|
DI_SearchItemChunkList *all_items = &lanes_items[0];
|
|
if(lane_idx() == 0) ProfScope("join all lane chunk lists")
|
|
{
|
|
for(U64 lidx = 1; lidx < lane_count(); lidx += 1)
|
|
{
|
|
DI_SearchItemChunkList *dst = all_items;
|
|
DI_SearchItemChunkList *to_push = &lanes_items[lidx];
|
|
for EachNode(n, DI_SearchItemChunk, to_push->first)
|
|
{
|
|
n->base_idx += dst->total_count;
|
|
}
|
|
if(dst->first && to_push->first)
|
|
{
|
|
dst->last->next = to_push->first;
|
|
dst->last = to_push->last;
|
|
dst->chunk_count += to_push->chunk_count;
|
|
dst->total_count += to_push->total_count;
|
|
}
|
|
else if(dst->first == 0)
|
|
{
|
|
MemoryCopyStruct(dst, to_push);
|
|
}
|
|
MemoryZeroStruct(to_push);
|
|
}
|
|
}
|
|
lane_sync();
|
|
|
|
//- rjf: decide if we cancelled
|
|
B32 cancelled = 0;
|
|
if(lane_idx() == 0 && !!ins_atomic_u32_eval(cancel_signal))
|
|
{
|
|
cancelled = 1;
|
|
}
|
|
lane_sync_u64(&cancelled, 0);
|
|
|
|
//- rjf: produce sort records
|
|
typedef struct SortRecord SortRecord;
|
|
struct SortRecord
|
|
{
|
|
U64 key;
|
|
DI_SearchItem *item;
|
|
};
|
|
U64 sort_records_count = all_items->total_count;
|
|
SortRecord *sort_records = 0;
|
|
SortRecord *sort_records__swap = 0;
|
|
if(!cancelled) ProfScope("produce sort records")
|
|
{
|
|
if(lane_idx() == 0)
|
|
{
|
|
sort_records = push_array_no_zero(scratch.arena, SortRecord, sort_records_count);
|
|
}
|
|
if(lane_idx() == lane_from_task_idx(1))
|
|
{
|
|
sort_records__swap = push_array_no_zero(scratch.arena, SortRecord, sort_records_count);
|
|
}
|
|
lane_sync_u64(&sort_records, 0);
|
|
lane_sync_u64(&sort_records__swap, lane_from_task_idx(1));
|
|
for EachNode(n, DI_SearchItemChunk, all_items->first)
|
|
{
|
|
Rng1U64 range = lane_range(n->count);
|
|
U64 dst_idx = n->base_idx + range.min;
|
|
for EachInRange(n_idx, range)
|
|
{
|
|
DI_SearchItem *item = &n->v[n_idx];
|
|
sort_records[dst_idx].item = item;
|
|
sort_records[dst_idx].key = (((item->missed_size & 0xffffffffull) << 32) | (u64_hash_from_seed_str8(item->idx, str8_struct(&key)) & 0xffffffffull));
|
|
dst_idx += 1;
|
|
}
|
|
}
|
|
}
|
|
lane_sync();
|
|
|
|
//- rjf: sort records
|
|
if(!cancelled) ProfScope("sort records")
|
|
{
|
|
//- rjf: set up common data
|
|
U64 bits_per_digit = 8;
|
|
U64 digits_count = 64 / bits_per_digit;
|
|
U64 num_possible_values_per_digit = 1 << bits_per_digit;
|
|
U32 **lanes_digit_counts = 0;
|
|
U32 **lanes_digit_offsets = 0;
|
|
if(lane_idx() == 0)
|
|
{
|
|
lanes_digit_counts = push_array(scratch.arena, U32 *, lane_count());
|
|
lanes_digit_offsets = push_array(scratch.arena, U32 *, lane_count());
|
|
}
|
|
lane_sync_u64(&lanes_digit_counts, 0);
|
|
lane_sync_u64(&lanes_digit_offsets, 0);
|
|
|
|
//- rjf: set up this lane
|
|
lanes_digit_counts[lane_idx()] = push_array(scratch.arena, U32, num_possible_values_per_digit);
|
|
lanes_digit_offsets[lane_idx()] = push_array(scratch.arena, U32, num_possible_values_per_digit);
|
|
SortRecord *src = sort_records;
|
|
SortRecord *dst = sort_records__swap;
|
|
U64 count = sort_records_count;
|
|
|
|
//- rjf: do all per-digit sorts
|
|
for EachIndex(digit_idx, digits_count)
|
|
{
|
|
// rjf: count digit value occurrences per-lane
|
|
{
|
|
U32 *digit_counts = lanes_digit_counts[lane_idx()];
|
|
MemoryZero(digit_counts, sizeof(digit_counts[0])*num_possible_values_per_digit);
|
|
Rng1U64 range = lane_range(count);
|
|
for EachInRange(idx, range)
|
|
{
|
|
SortRecord *r = &src[idx];
|
|
U16 digit_value = (U16)(U8)(r->key >> (digit_idx*bits_per_digit));
|
|
digit_counts[digit_value] += 1;
|
|
}
|
|
}
|
|
lane_sync();
|
|
|
|
// rjf: compute thread * digit value *relative* offset table
|
|
{
|
|
Rng1U64 range = lane_range(num_possible_values_per_digit);
|
|
for EachInRange(value_idx, range)
|
|
{
|
|
U64 layout_off = 0;
|
|
for EachIndex(lane_idx, lane_count())
|
|
{
|
|
lanes_digit_offsets[lane_idx][value_idx] = layout_off;
|
|
layout_off += lanes_digit_counts[lane_idx][value_idx];
|
|
}
|
|
}
|
|
}
|
|
lane_sync();
|
|
|
|
// rjf: convert relative offsets -> absolute offsets
|
|
if(lane_idx() == 0)
|
|
{
|
|
U64 last_off = 0;
|
|
U64 num_of_nonzero_digit = 0;
|
|
for EachIndex(value_idx, num_possible_values_per_digit)
|
|
{
|
|
for EachIndex(lane_idx, lane_count())
|
|
{
|
|
lanes_digit_offsets[lane_idx][value_idx] += last_off;
|
|
}
|
|
last_off = lanes_digit_offsets[lane_count()-1][value_idx] + lanes_digit_counts[lane_count()-1][value_idx];
|
|
}
|
|
// NOTE(rjf): required that: (last_off == element_count)
|
|
}
|
|
lane_sync();
|
|
|
|
// rjf: move
|
|
{
|
|
U32 *lane_digit_offsets = lanes_digit_offsets[lane_idx()];
|
|
Rng1U64 range = lane_range(count);
|
|
for EachInRange(idx, range)
|
|
{
|
|
SortRecord *src_r = &src[idx];
|
|
U16 digit_value = (U16)(U8)(src_r->key >> (digit_idx*bits_per_digit));
|
|
U64 dst_off = lane_digit_offsets[digit_value];
|
|
lane_digit_offsets[digit_value] += 1;
|
|
MemoryCopyStruct(&dst[dst_off], src_r);
|
|
}
|
|
}
|
|
lane_sync();
|
|
|
|
// rjf: swap
|
|
{
|
|
SortRecord *swap = src;
|
|
src = dst;
|
|
dst = swap;
|
|
}
|
|
}
|
|
}
|
|
lane_sync();
|
|
|
|
//- rjf: produce final array
|
|
DI_SearchItemArray items = {0};
|
|
if(!cancelled) ProfScope("produce final array")
|
|
{
|
|
if(lane_idx() == 0)
|
|
{
|
|
items.count = all_items->total_count;
|
|
items.v = push_array_no_zero(arena, DI_SearchItem, items.count);
|
|
}
|
|
lane_sync_u64(&items.count, 0);
|
|
lane_sync_u64(&items.v, 0);
|
|
Rng1U64 range = lane_range(sort_records_count);
|
|
for EachInRange(idx, range)
|
|
{
|
|
SortRecord *record = &sort_records[idx];
|
|
DI_SearchItem *dst_item = &items.v[idx];
|
|
MemoryCopyStruct(dst_item, record->item);
|
|
}
|
|
}
|
|
lane_sync();
|
|
|
|
//- rjf: bundle as artifact
|
|
if(!cancelled)
|
|
{
|
|
artifact.u64[0] = (U64)arenas;
|
|
artifact.u64[1] = arenas_count;
|
|
artifact.u64[2] = (U64)items.v;
|
|
artifact.u64[3] = items.count;
|
|
}
|
|
|
|
//- rjf: release results on cancel
|
|
else
|
|
{
|
|
arena_release(arena);
|
|
}
|
|
}
|
|
scratch_end(scratch);
|
|
access_close(access);
|
|
ProfEnd();
|
|
return artifact;
|
|
}
|
|
|
|
internal void
|
|
di_search_artifact_destroy(AC_Artifact artifact)
|
|
{
|
|
Temp scratch = scratch_begin(0, 0);
|
|
Arena **arenas = (Arena **)artifact.u64[0];
|
|
U64 arenas_count = artifact.u64[1];
|
|
Arena **arenas_copy = push_array(scratch.arena, Arena *, arenas_count);
|
|
MemoryCopy(arenas_copy, arenas, sizeof(Arena *) * arenas_count);
|
|
for EachIndex(idx, arenas_count)
|
|
{
|
|
if(arenas_copy[idx])
|
|
{
|
|
arena_release(arenas_copy[idx]);
|
|
}
|
|
}
|
|
scratch_end(scratch);
|
|
}
|
|
|
|
internal DI_SearchItemArray
|
|
di_search_item_array_from_target_query(Access *access, RDI_SectionKind target, String8 query, U64 endt_us, B32 *stale_out)
|
|
{
|
|
DI_SearchItemArray result = {0};
|
|
{
|
|
Temp scratch = scratch_begin(0, 0);
|
|
|
|
// rjf: form key
|
|
String8List key_parts = {0};
|
|
str8_list_push(scratch.arena, &key_parts, str8_struct(&target));
|
|
str8_list_push(scratch.arena, &key_parts, str8_struct(&query.size));
|
|
str8_list_push(scratch.arena, &key_parts, query);
|
|
String8 key = str8_list_join(scratch.arena, &key_parts, 0);
|
|
|
|
// rjf: get artifact
|
|
AC_Artifact artifact = ac_artifact_from_key(access, key, di_search_artifact_create, di_search_artifact_destroy, endt_us, .gen = di_load_gen(), .flags = AC_Flag_Wide, .evict_threshold_us = 100000, .stale_out = stale_out);
|
|
|
|
// rjf: unpack artifact
|
|
result.v = (DI_SearchItem *)artifact.u64[2];
|
|
result.count = artifact.u64[3];
|
|
|
|
scratch_end(scratch);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Match Artifact Cache Hooks / Lookups
|
|
|
|
internal AC_Artifact
|
|
di_match_artifact_create(String8 key, B32 *cancel_signal, B32 *retry_out, U64 *gen_out)
|
|
{
|
|
ProfBeginFunction();
|
|
Temp scratch = scratch_begin(0, 0);
|
|
|
|
//- rjf: unpack key
|
|
U64 index = 0;
|
|
String8 name = {0};
|
|
DI_Key preferred_key = {0};
|
|
{
|
|
U64 key_read_off = 0;
|
|
key_read_off += str8_deserial_read_struct(key, key_read_off, &index);
|
|
key_read_off += str8_deserial_read_struct(key, key_read_off, &preferred_key);
|
|
key_read_off += str8_deserial_read_struct(key, key_read_off, &name.size);
|
|
name.str = push_array_no_zero(scratch.arena, U8, name.size);
|
|
key_read_off += str8_deserial_read(key, key_read_off, name.str, name.size, 1);
|
|
}
|
|
|
|
//- rjf: find split point between container part of name & "leaf"
|
|
U64 container_part_opl = 0;
|
|
U64 leaf_part_first = 0;
|
|
for(U64 off = 0; off+1 < name.size; off += 1)
|
|
{
|
|
if(name.str[off] == '.')
|
|
{
|
|
container_part_opl = off;
|
|
leaf_part_first = off+1;
|
|
}
|
|
else if(name.str[off] == ':' && name.str[off+1] == ':')
|
|
{
|
|
container_part_opl = off;
|
|
leaf_part_first = off+2;
|
|
}
|
|
}
|
|
|
|
//- rjf: unpack container/leaf
|
|
String8 container_part_of_name = str8_prefix(name, container_part_opl);
|
|
String8 leaf_part_of_name = str8_skip(name, leaf_part_first);
|
|
|
|
//- rjf: produce the fully qualified name, sanitize the container part (convert all `.`s to `::`s)
|
|
String8 fully_qualified_name = name;
|
|
if(container_part_of_name.size != 0)
|
|
{
|
|
// rjf: gather parts of container
|
|
String8List parts = {0};
|
|
for(U64 off = 0, next_off = 0; off < container_part_of_name.size; off = next_off)
|
|
{
|
|
// rjf: find the next separator
|
|
U64 next_separator_pos = container_part_of_name.size;
|
|
U64 next_separator_size = 0;
|
|
{
|
|
S64 template_nest_depth = 0;
|
|
for(U64 off2 = off; off2+1 < container_part_of_name.size; off2 += 1)
|
|
{
|
|
if(template_nest_depth == 0 && container_part_of_name.str[off2] == '.')
|
|
{
|
|
next_separator_pos = off2;
|
|
next_separator_size = 1;
|
|
break;
|
|
}
|
|
else if(template_nest_depth == 0 && container_part_of_name.str[off2] == ':' && container_part_of_name.str[off2+1] == ':')
|
|
{
|
|
next_separator_pos = off2;
|
|
next_separator_size = 2;
|
|
break;
|
|
}
|
|
else if(container_part_of_name.str[off2] == '<')
|
|
{
|
|
template_nest_depth += 1;
|
|
}
|
|
else if(container_part_of_name.str[off2] == '>')
|
|
{
|
|
template_nest_depth -= 1;
|
|
template_nest_depth = ClampBot(0, template_nest_depth);
|
|
}
|
|
}
|
|
}
|
|
|
|
// rjf: gather portions of container
|
|
str8_list_push(scratch.arena, &parts, str8_substr(container_part_of_name, r1u64(off, next_separator_pos)));
|
|
next_off = next_separator_pos + next_separator_size;
|
|
}
|
|
|
|
// rjf: add leaf & join
|
|
str8_list_push(scratch.arena, &parts, leaf_part_of_name);
|
|
StringJoin join = {.sep = str8_lit("::")};
|
|
fully_qualified_name = str8_list_join(scratch.arena, &parts, &join);
|
|
}
|
|
|
|
//- rjf: get all loaded keys
|
|
DI_KeyArray dbgi_keys = di_push_all_loaded_keys(scratch.arena);
|
|
|
|
//- rjf: take cancellation signal
|
|
B32 cancelled = 0;
|
|
if(lane_idx() == 0)
|
|
{
|
|
cancelled = ins_atomic_u32_eval(cancel_signal);
|
|
}
|
|
lane_sync_u64(&cancelled, 0);
|
|
|
|
//- rjf: wide search across all debug infos
|
|
DI_Match *lane_matches = 0;
|
|
if(!cancelled)
|
|
{
|
|
if(lane_idx() == 0)
|
|
{
|
|
lane_matches = push_array(scratch.arena, DI_Match, lane_count());
|
|
}
|
|
lane_sync_u64(&lane_matches, 0);
|
|
{
|
|
read_only local_persist RDI_NameMapKind name_map_kinds[] =
|
|
{
|
|
RDI_NameMapKind_GlobalVariables,
|
|
RDI_NameMapKind_ThreadVariables,
|
|
RDI_NameMapKind_Constants,
|
|
RDI_NameMapKind_Procedures,
|
|
RDI_NameMapKind_Types,
|
|
};
|
|
read_only local_persist RDI_SectionKind name_map_section_kinds[] =
|
|
{
|
|
RDI_SectionKind_GlobalVariables,
|
|
RDI_SectionKind_ThreadVariables,
|
|
RDI_SectionKind_Constants,
|
|
RDI_SectionKind_Procedures,
|
|
RDI_SectionKind_TypeNodes,
|
|
};
|
|
Rng1U64 range = lane_range(dbgi_keys.count);
|
|
for EachInRange(dbgi_idx, range)
|
|
{
|
|
Access *access = access_open();
|
|
{
|
|
DI_Key dbgi_key = dbgi_keys.v[dbgi_idx];
|
|
RDI_Parsed *rdi = di_rdi_from_key(access, dbgi_key, 0, 0);
|
|
for EachElement(name_map_kind_idx, name_map_kinds)
|
|
{
|
|
// rjf: unpack name map
|
|
RDI_NameMap *name_map = rdi_element_from_name_idx(rdi, NameMaps, name_map_kinds[name_map_kind_idx]);
|
|
RDI_ParsedNameMap parsed_name_map = {0};
|
|
rdi_parsed_from_name_map(rdi, name_map, &parsed_name_map);
|
|
|
|
// rjf: find matches for (possibly leaf) name
|
|
RDI_NameMapNode *map_node = rdi_name_map_lookup(rdi, &parsed_name_map, leaf_part_of_name.str, leaf_part_of_name.size);
|
|
U32 matches_count = 0;
|
|
U32 *matches = rdi_matches_from_map_node(rdi, map_node, &matches_count);
|
|
|
|
// rjf: do we have a container? -> filter matches according to container string also
|
|
U32 *filtered_matches = matches;
|
|
U32 filtered_matches_count = matches_count;
|
|
if(container_part_of_name.size != 0)
|
|
{
|
|
filtered_matches = push_array(scratch.arena, U32, matches_count);
|
|
filtered_matches_count = 0;
|
|
for EachIndex(match_idx, matches_count)
|
|
{
|
|
Temp scratch = scratch_begin(0, 0);
|
|
String8 match_fully_qualified_name = {0};
|
|
switch(name_map_section_kinds[name_map_kind_idx])
|
|
{
|
|
default:{}break;
|
|
case RDI_SectionKind_GlobalVariables:
|
|
case RDI_SectionKind_ThreadVariables:
|
|
case RDI_SectionKind_Constants:
|
|
case RDI_SectionKind_Procedures:
|
|
{
|
|
RDI_Symbol *symbol = (RDI_Symbol *)rdi_section_raw_element_from_kind_idx(rdi, name_map_section_kinds[name_map_kind_idx], matches[match_idx]);
|
|
String8 match_fully_qualified_name = fully_qualified_str8_from_rdi_symbol(scratch.arena, rdi, symbol);
|
|
if(str8_match(match_fully_qualified_name, fully_qualified_name, 0))
|
|
{
|
|
filtered_matches[filtered_matches_count] = matches[match_idx];
|
|
filtered_matches_count += 1;
|
|
}
|
|
}break;
|
|
case RDI_SectionKind_TypeNodes:
|
|
{
|
|
RDI_TypeNode *type_node = rdi_element_from_name_idx(rdi, TypeNodes, match_idx);
|
|
if(RDI_TypeKind_FirstUserDefined <= type_node->kind && type_node->kind <= RDI_TypeKind_LastUserDefined)
|
|
{
|
|
String8 match_fully_qualified_name = fully_qualified_str8_from_rdi_type(scratch.arena, rdi, type_node);
|
|
if(str8_match(match_fully_qualified_name, fully_qualified_name, 0))
|
|
{
|
|
filtered_matches[filtered_matches_count] = matches[match_idx];
|
|
filtered_matches_count += 1;
|
|
}
|
|
}
|
|
}break;
|
|
}
|
|
scratch_end(scratch);
|
|
}
|
|
}
|
|
|
|
// rjf: do we have a specific unit queried? -> filter matches according to containing unit
|
|
#if 0
|
|
if(target_unit_index != 0)
|
|
{
|
|
|
|
}
|
|
#endif
|
|
|
|
// rjf: given matches, pick selected & return as result
|
|
if(filtered_matches_count != 0)
|
|
{
|
|
U64 selected_match_idx = (filtered_matches_count-1) - Min(index, filtered_matches_count-1);
|
|
lane_matches[lane_idx()].key = dbgi_key;
|
|
lane_matches[lane_idx()].section_kind = name_map_section_kinds[name_map_kind_idx];
|
|
lane_matches[lane_idx()].idx = filtered_matches[selected_match_idx];
|
|
}
|
|
}
|
|
}
|
|
access_close(access);
|
|
}
|
|
}
|
|
}
|
|
lane_sync();
|
|
|
|
//- rjf: pick match
|
|
DI_Match match = {0};
|
|
if(lane_matches != 0)
|
|
{
|
|
for EachIndex(idx, lane_count())
|
|
{
|
|
if(lane_matches[idx].idx != 0)
|
|
{
|
|
match = lane_matches[idx];
|
|
if(di_key_match(di_key_zero(), preferred_key) || di_key_match(match.key, preferred_key))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//- rjf: package as artifact
|
|
AC_Artifact artifact = {0};
|
|
{
|
|
StaticAssert(ArrayCount(artifact.u64) >= 4, artifact_size_check);
|
|
artifact.u64[0] = match.key.u64[0];
|
|
artifact.u64[1] = match.key.u64[1];
|
|
artifact.u64[2] = match.section_kind;
|
|
artifact.u64[3] = match.idx;
|
|
}
|
|
|
|
lane_sync();
|
|
scratch_end(scratch);
|
|
ProfEnd();
|
|
return artifact;
|
|
}
|
|
|
|
internal DI_Match
|
|
di_match_from_string(String8 string, U64 match_index, DI_Key preferred_dbgi_key, U64 endt_us)
|
|
{
|
|
DI_Match result = {0};
|
|
Access *access = access_open();
|
|
Temp scratch = scratch_begin(0, 0);
|
|
{
|
|
String8List key_parts = {0};
|
|
str8_list_push(scratch.arena, &key_parts, str8_struct(&match_index));
|
|
str8_list_push(scratch.arena, &key_parts, str8_struct(&preferred_dbgi_key));
|
|
str8_list_push(scratch.arena, &key_parts, str8_struct(&string.size));
|
|
str8_list_push(scratch.arena, &key_parts, string);
|
|
String8 key = str8_list_join(scratch.arena, &key_parts, 0);
|
|
U64 dbgi_count = di_load_count();
|
|
B32 wide = (dbgi_count > 256);
|
|
AC_Artifact artifact = ac_artifact_from_key(access, key, di_match_artifact_create, 0, endt_us, .flags = wide ? AC_Flag_Wide : 0, .gen = di_load_gen(), .evict_threshold_us = wide ? 20000000 : 10000000);
|
|
result.key.u64[0] = artifact.u64[0];
|
|
result.key.u64[1] = artifact.u64[1];
|
|
result.section_kind = artifact.u64[2];
|
|
result.idx = artifact.u64[3];
|
|
}
|
|
scratch_end(scratch);
|
|
access_close(access);
|
|
return result;
|
|
}
|