mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-06-21 11:14:59 -07:00
first pass at new hash-store-based disassembly cache layer; can serve as a general-purpose asynchronous disassembly visualization cache layer, regardless of where that data ultimately comes from - can click into a file, process memory cache, etc.
This commit is contained in:
@@ -33,6 +33,9 @@ main_thread_base_entry_point(void (*entry_point)(CmdLine *cmdline), char **argum
|
||||
#if defined(TEXT_CACHE_H)
|
||||
txt_init();
|
||||
#endif
|
||||
#if defined(DASM_CACHE_H)
|
||||
dasm_init();
|
||||
#endif
|
||||
#if defined(DBGI_H)
|
||||
dbgi_init();
|
||||
#endif
|
||||
|
||||
@@ -110,6 +110,7 @@ dasmi_inst_array_off_from_idx(DASMI_InstArray *array, U64 idx)
|
||||
////////////////////////////////
|
||||
//~ rjf: Disassembly Functions
|
||||
|
||||
#if 0
|
||||
#include "third_party/udis86/config.h"
|
||||
#include "third_party/udis86/udis86.h"
|
||||
#include "third_party/udis86/libudis86/decode.c"
|
||||
@@ -118,6 +119,7 @@ dasmi_inst_array_off_from_idx(DASMI_InstArray *array, U64 idx)
|
||||
#include "third_party/udis86/libudis86/syn-intel.c"
|
||||
#include "third_party/udis86/libudis86/syn.c"
|
||||
#include "third_party/udis86/libudis86/udis86.c"
|
||||
#endif
|
||||
|
||||
internal DASMI_InstChunkList
|
||||
dasmi_inst_chunk_list_from_arch_addr_data(Arena *arena, U64 *bytes_processed_counter, Architecture arch, U64 addr, String8 data)
|
||||
|
||||
@@ -0,0 +1,530 @@
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Instruction Type Functions
|
||||
|
||||
internal void
|
||||
dasm_inst_chunk_list_push(Arena *arena, DASM_InstChunkList *list, U64 cap, DASM_Inst *inst)
|
||||
{
|
||||
DASM_InstChunkNode *node = list->last;
|
||||
if(node == 0 || node->count >= node->cap)
|
||||
{
|
||||
node = push_array(arena, DASM_InstChunkNode, 1);
|
||||
node->v = push_array_no_zero(arena, DASM_Inst, cap);
|
||||
node->cap = cap;
|
||||
SLLQueuePush(list->first, list->last, node);
|
||||
list->node_count += 1;
|
||||
}
|
||||
MemoryCopyStruct(&node->v[node->count], inst);
|
||||
node->count += 1;
|
||||
list->inst_count += 1;
|
||||
}
|
||||
|
||||
internal DASM_InstArray
|
||||
dasm_inst_array_from_chunk_list(Arena *arena, DASM_InstChunkList *list)
|
||||
{
|
||||
DASM_InstArray array = {0};
|
||||
array.count = list->inst_count;
|
||||
array.v = push_array_no_zero(arena, DASM_Inst, array.count);
|
||||
U64 idx = 0;
|
||||
for(DASM_InstChunkNode *n = list->first; n != 0; n = n->next)
|
||||
{
|
||||
MemoryCopy(array.v+idx, n->v, sizeof(DASM_Inst)*n->count);
|
||||
idx += n->count;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
internal U64
|
||||
dasm_inst_array_idx_from_code_off__linear_scan(DASM_InstArray *array, U64 off)
|
||||
{
|
||||
U64 result = 0;
|
||||
for(U64 idx = 0; idx < array->count; idx += 1)
|
||||
{
|
||||
if(array->v[idx].code_off == off)
|
||||
{
|
||||
result = idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal U64
|
||||
dasm_inst_array_code_off_from_idx(DASM_InstArray *array, U64 idx)
|
||||
{
|
||||
U64 off = 0;
|
||||
if(idx < array->count)
|
||||
{
|
||||
off = array->v[idx].code_off;
|
||||
}
|
||||
return off;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Disassembly Decoding Function
|
||||
|
||||
#include "third_party/udis86/config.h"
|
||||
#include "third_party/udis86/udis86.h"
|
||||
#include "third_party/udis86/libudis86/decode.c"
|
||||
#include "third_party/udis86/libudis86/itab.c"
|
||||
#include "third_party/udis86/libudis86/syn-att.c"
|
||||
#include "third_party/udis86/libudis86/syn-intel.c"
|
||||
#include "third_party/udis86/libudis86/syn.c"
|
||||
#include "third_party/udis86/libudis86/udis86.c"
|
||||
|
||||
internal DASM_Info
|
||||
dasm_info_from_arch_addr_data(Arena *arena, U64 *bytes_processed_counter, Architecture arch, U64 addr, String8 data)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
|
||||
//- rjf: decode
|
||||
DASM_InstChunkList inst_list = {0};
|
||||
String8List inst_strings = {0};
|
||||
switch(arch)
|
||||
{
|
||||
default:{}break;
|
||||
|
||||
//- rjf: x86/x64 decoding
|
||||
case Architecture_x64:
|
||||
case Architecture_x86:
|
||||
{
|
||||
// rjf: grab context
|
||||
struct ud udc;
|
||||
ud_init(&udc);
|
||||
ud_set_mode(&udc, bit_size_from_arch(arch));
|
||||
ud_set_pc(&udc, addr);
|
||||
ud_set_input_buffer(&udc, data.str, data.size);
|
||||
ud_set_vendor(&udc, UD_VENDOR_ANY);
|
||||
ud_set_syntax(&udc, UD_SYN_INTEL);
|
||||
|
||||
// rjf: disassemble
|
||||
U64 byte_process_start_off = 0;
|
||||
for(U64 off = 0; off < data.size;)
|
||||
{
|
||||
// rjf: disassemble one instruction
|
||||
U64 size = ud_disassemble(&udc);
|
||||
if(size == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// rjf: analyze
|
||||
struct ud_operand *first_op = (struct ud_operand *)ud_insn_opr(&udc, 0);
|
||||
U64 rel_voff = (first_op != 0 && first_op->type == UD_OP_JIMM) ? ud_syn_rel_target(&udc, first_op) : 0;
|
||||
|
||||
// rjf: push
|
||||
String8 string = push_str8f(scratch.arena, "%s\n", udc.asm_buf);
|
||||
DASM_Inst inst = {off, rel_voff, r1u64(inst_strings.total_size, inst_strings.total_size+string.size-1)};
|
||||
dasm_inst_chunk_list_push(scratch.arena, &inst_list, 1024, &inst);
|
||||
str8_list_push(scratch.arena, &inst_strings, string);
|
||||
|
||||
// rjf: increment
|
||||
off += size;
|
||||
if(bytes_processed_counter != 0 && (off-byte_process_start_off >= 1000))
|
||||
{
|
||||
ins_atomic_u64_add_eval(bytes_processed_counter, (off-byte_process_start_off));
|
||||
byte_process_start_off = off;
|
||||
}
|
||||
}
|
||||
}break;
|
||||
}
|
||||
|
||||
//- rjf: fill result
|
||||
DASM_Info info = {0};
|
||||
info.text = str8_list_join(arena, &inst_strings, 0);
|
||||
info.insts = dasm_inst_array_from_chunk_list(arena, &inst_list);
|
||||
|
||||
scratch_end(scratch);
|
||||
return info;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Main Layer Initialization
|
||||
|
||||
internal void
|
||||
dasm_init(void)
|
||||
{
|
||||
Arena *arena = arena_alloc();
|
||||
dasm_shared = push_array(arena, DASM_Shared, 1);
|
||||
dasm_shared->arena = arena;
|
||||
dasm_shared->slots_count = 1024;
|
||||
dasm_shared->stripes_count = Min(dasm_shared->slots_count, os_logical_core_count());
|
||||
dasm_shared->slots = push_array(arena, DASM_Slot, dasm_shared->slots_count);
|
||||
dasm_shared->stripes = push_array(arena, DASM_Stripe, dasm_shared->stripes_count);
|
||||
for(U64 idx = 0; idx < dasm_shared->stripes_count; idx += 1)
|
||||
{
|
||||
dasm_shared->stripes[idx].arena = arena_alloc();
|
||||
dasm_shared->stripes[idx].rw_mutex = os_rw_mutex_alloc();
|
||||
dasm_shared->stripes[idx].cv = os_condition_variable_alloc();
|
||||
}
|
||||
dasm_shared->u2p_ring_size = KB(64);
|
||||
dasm_shared->u2p_ring_base = push_array_no_zero(arena, U8, dasm_shared->u2p_ring_size);
|
||||
dasm_shared->u2p_ring_cv = os_condition_variable_alloc();
|
||||
dasm_shared->u2p_ring_mutex = os_mutex_alloc();
|
||||
dasm_shared->parse_thread_count = 1;
|
||||
dasm_shared->parse_threads = push_array(arena, OS_Handle, dasm_shared->parse_thread_count);
|
||||
for(U64 idx = 0; idx < dasm_shared->parse_thread_count; idx += 1)
|
||||
{
|
||||
dasm_shared->parse_threads[idx] = os_launch_thread(dasm_parse_thread__entry_point, (void *)idx, 0);
|
||||
}
|
||||
dasm_shared->evictor_thread = os_launch_thread(dasm_evictor_thread__entry_point, 0, 0);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: User Clock
|
||||
|
||||
internal void
|
||||
dasm_user_clock_tick(void)
|
||||
{
|
||||
ins_atomic_u64_inc_eval(&dasm_shared->user_clock_idx);
|
||||
}
|
||||
|
||||
internal U64
|
||||
dasm_user_clock_idx(void)
|
||||
{
|
||||
U64 idx = ins_atomic_u64_eval(&dasm_shared->user_clock_idx);
|
||||
return idx;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Scoped Access
|
||||
|
||||
internal DASM_Scope *
|
||||
dasm_scope_open(void)
|
||||
{
|
||||
if(dasm_tctx == 0)
|
||||
{
|
||||
Arena *arena = arena_alloc();
|
||||
dasm_tctx = push_array(arena, DASM_TCTX, 1);
|
||||
dasm_tctx->arena = arena;
|
||||
}
|
||||
DASM_Scope *scope = dasm_tctx->free_scope;
|
||||
if(scope != 0)
|
||||
{
|
||||
SLLStackPop(dasm_tctx->free_scope);
|
||||
}
|
||||
else
|
||||
{
|
||||
scope = push_array_no_zero(dasm_tctx->arena, DASM_Scope, 1);
|
||||
}
|
||||
MemoryZeroStruct(scope);
|
||||
return scope;
|
||||
}
|
||||
|
||||
internal void
|
||||
dasm_scope_close(DASM_Scope *scope)
|
||||
{
|
||||
for(DASM_Touch *t = scope->top_touch, *next = 0; t != 0; t = next)
|
||||
{
|
||||
next = t->next;
|
||||
U64 slot_idx = t->hash.u64[1]%dasm_shared->slots_count;
|
||||
U64 stripe_idx = slot_idx%dasm_shared->stripes_count;
|
||||
DASM_Slot *slot = &dasm_shared->slots[slot_idx];
|
||||
DASM_Stripe *stripe = &dasm_shared->stripes[stripe_idx];
|
||||
OS_MutexScopeR(stripe->rw_mutex)
|
||||
{
|
||||
for(DASM_Node *n = slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(u128_match(t->hash, n->hash) && t->addr == n->addr && t->arch == n->arch)
|
||||
{
|
||||
ins_atomic_u64_dec_eval(&n->scope_ref_count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
SLLStackPush(dasm_tctx->free_touch, t);
|
||||
}
|
||||
SLLStackPush(dasm_tctx->free_scope, scope);
|
||||
}
|
||||
|
||||
internal void
|
||||
dasm_scope_touch_node__stripe_r_guarded(DASM_Scope *scope, DASM_Node *node)
|
||||
{
|
||||
DASM_Touch *touch = dasm_tctx->free_touch;
|
||||
ins_atomic_u64_inc_eval(&node->scope_ref_count);
|
||||
ins_atomic_u64_eval_assign(&node->last_time_touched_us, os_now_microseconds());
|
||||
ins_atomic_u64_eval_assign(&node->last_user_clock_idx_touched, dasm_user_clock_idx());
|
||||
if(touch != 0)
|
||||
{
|
||||
SLLStackPop(dasm_tctx->free_touch);
|
||||
}
|
||||
else
|
||||
{
|
||||
touch = push_array_no_zero(dasm_tctx->arena, DASM_Touch, 1);
|
||||
}
|
||||
MemoryZeroStruct(touch);
|
||||
touch->hash = node->hash;
|
||||
touch->addr = node->addr;
|
||||
touch->arch = node->arch;
|
||||
SLLStackPush(scope->top_touch, touch);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cache Lookups
|
||||
|
||||
internal DASM_Info
|
||||
dasm_info_from_hash_addr_arch(DASM_Scope *scope, U128 hash, U64 addr, Architecture arch)
|
||||
{
|
||||
DASM_Info info = {0};
|
||||
if(!u128_match(hash, u128_zero()))
|
||||
{
|
||||
U64 slot_idx = hash.u64[1]%dasm_shared->slots_count;
|
||||
U64 stripe_idx = slot_idx%dasm_shared->stripes_count;
|
||||
DASM_Slot *slot = &dasm_shared->slots[slot_idx];
|
||||
DASM_Stripe *stripe = &dasm_shared->stripes[stripe_idx];
|
||||
B32 found = 0;
|
||||
OS_MutexScopeR(stripe->rw_mutex)
|
||||
{
|
||||
for(DASM_Node *n = slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(u128_match(hash, n->hash) && addr == n->addr && arch == n->arch)
|
||||
{
|
||||
MemoryCopyStruct(&info, &n->info);
|
||||
found = 1;
|
||||
dasm_scope_touch_node__stripe_r_guarded(scope, n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
B32 node_is_new = 0;
|
||||
if(!found)
|
||||
{
|
||||
OS_MutexScopeW(stripe->rw_mutex)
|
||||
{
|
||||
DASM_Node *node = 0;
|
||||
for(DASM_Node *n = slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(u128_match(hash, n->hash) && addr == n->addr && arch == n->arch)
|
||||
{
|
||||
node = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(node == 0)
|
||||
{
|
||||
node = stripe->free_node;
|
||||
if(node)
|
||||
{
|
||||
SLLStackPop(stripe->free_node);
|
||||
}
|
||||
else
|
||||
{
|
||||
node = push_array_no_zero(stripe->arena, DASM_Node, 1);
|
||||
}
|
||||
MemoryZeroStruct(node);
|
||||
DLLPushBack(slot->first, slot->last, node);
|
||||
node->hash = hash;
|
||||
node->addr = addr;
|
||||
node->arch = arch;
|
||||
node_is_new = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(node_is_new)
|
||||
{
|
||||
dasm_u2p_enqueue_req(hash, addr, arch, max_U64);
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
internal DASM_Info
|
||||
dasm_info_from_key_addr_arch(DASM_Scope *scope, U128 key, U64 addr, Architecture arch, U128 *hash_out)
|
||||
{
|
||||
DASM_Info result = {0};
|
||||
for(U64 rewind_idx = 0; rewind_idx < 2; rewind_idx += 1)
|
||||
{
|
||||
U128 hash = hs_hash_from_key(key, rewind_idx);
|
||||
result = dasm_info_from_hash_addr_arch(scope, hash, addr, arch);
|
||||
if(result.insts.count != 0)
|
||||
{
|
||||
if(hash_out)
|
||||
{
|
||||
*hash_out = hash;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Parse Threads
|
||||
|
||||
internal B32
|
||||
dasm_u2p_enqueue_req(U128 hash, U64 addr, Architecture arch, U64 endt_us)
|
||||
{
|
||||
B32 good = 0;
|
||||
OS_MutexScope(dasm_shared->u2p_ring_mutex) for(;;)
|
||||
{
|
||||
U64 unconsumed_size = dasm_shared->u2p_ring_write_pos - dasm_shared->u2p_ring_read_pos;
|
||||
U64 available_size = dasm_shared->u2p_ring_size - unconsumed_size;
|
||||
if(available_size >= sizeof(hash)+sizeof(addr)+sizeof(arch))
|
||||
{
|
||||
good = 1;
|
||||
dasm_shared->u2p_ring_write_pos += ring_write_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, &hash);
|
||||
dasm_shared->u2p_ring_write_pos += ring_write_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, &addr);
|
||||
dasm_shared->u2p_ring_write_pos += ring_write_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, &arch);
|
||||
break;
|
||||
}
|
||||
if(os_now_microseconds() >= endt_us)
|
||||
{
|
||||
break;
|
||||
}
|
||||
os_condition_variable_wait(dasm_shared->u2p_ring_cv, dasm_shared->u2p_ring_mutex, endt_us);
|
||||
}
|
||||
if(good)
|
||||
{
|
||||
os_condition_variable_broadcast(dasm_shared->u2p_ring_cv);
|
||||
}
|
||||
return good;
|
||||
}
|
||||
|
||||
internal void
|
||||
dasm_u2p_dequeue_req(U128 *hash_out, U64 *addr_out, Architecture *arch_out)
|
||||
{
|
||||
OS_MutexScope(dasm_shared->u2p_ring_mutex) for(;;)
|
||||
{
|
||||
U64 unconsumed_size = dasm_shared->u2p_ring_write_pos - dasm_shared->u2p_ring_read_pos;
|
||||
if(unconsumed_size >= sizeof(*hash_out)+sizeof(*addr_out)+sizeof(*arch_out))
|
||||
{
|
||||
dasm_shared->u2p_ring_read_pos += ring_read_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, hash_out);
|
||||
dasm_shared->u2p_ring_read_pos += ring_read_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, addr_out);
|
||||
dasm_shared->u2p_ring_read_pos += ring_read_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, arch_out);
|
||||
break;
|
||||
}
|
||||
os_condition_variable_wait(dasm_shared->u2p_ring_cv, dasm_shared->u2p_ring_mutex, max_U64);
|
||||
}
|
||||
os_condition_variable_broadcast(dasm_shared->u2p_ring_mutex);
|
||||
}
|
||||
|
||||
internal void
|
||||
dasm_parse_thread__entry_point(void *p)
|
||||
{
|
||||
ThreadNameF("[dasm] parse thread #%I64u", (U64)p);
|
||||
for(;;)
|
||||
{
|
||||
//- rjf: get next request
|
||||
U128 hash = {0};
|
||||
U64 addr = 0;
|
||||
Architecture arch = Architecture_Null;
|
||||
dasm_u2p_dequeue_req(&hash, &addr, &arch);
|
||||
HS_Scope *hs_scope = hs_scope_open();
|
||||
|
||||
//- rjf: unpack hash
|
||||
U64 slot_idx = hash.u64[1]%dasm_shared->slots_count;
|
||||
U64 stripe_idx = slot_idx%dasm_shared->stripes_count;
|
||||
DASM_Slot *slot = &dasm_shared->slots[slot_idx];
|
||||
DASM_Stripe *stripe = &dasm_shared->stripes[stripe_idx];
|
||||
|
||||
//- rjf: take task
|
||||
B32 got_task = 0;
|
||||
OS_MutexScopeR(stripe->rw_mutex)
|
||||
{
|
||||
for(DASM_Node *n = slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(u128_match(n->hash, hash) && n->addr == addr && n->arch == arch)
|
||||
{
|
||||
got_task = !ins_atomic_u32_eval_cond_assign(&n->is_working, 1, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: hash -> data
|
||||
String8 data = {0};
|
||||
if(got_task)
|
||||
{
|
||||
data = hs_data_from_hash(hs_scope, hash);
|
||||
}
|
||||
|
||||
//- rjf: data -> disassembly info
|
||||
Arena *info_arena = 0;
|
||||
DASM_Info info = {0};
|
||||
if(got_task && data.size != 0)
|
||||
{
|
||||
info_arena = arena_alloc();
|
||||
info = dasm_info_from_arch_addr_data(info_arena, 0, arch, addr, data);
|
||||
}
|
||||
|
||||
//- rjf: commit results to cache
|
||||
if(got_task) OS_MutexScopeW(stripe->rw_mutex)
|
||||
{
|
||||
for(DASM_Node *n = slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(u128_match(n->hash, hash) && n->addr == addr && n->arch == arch)
|
||||
{
|
||||
n->info_arena = info_arena;
|
||||
MemoryCopyStruct(&n->info, &info);
|
||||
ins_atomic_u32_eval_assign(&n->is_working, 0);
|
||||
ins_atomic_u64_inc_eval(&n->load_count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hs_scope_close(hs_scope);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Evictor Threads
|
||||
|
||||
internal void
|
||||
dasm_evictor_thread__entry_point(void *p)
|
||||
{
|
||||
ThreadNameF("[dasm] evictor thread");
|
||||
for(;;)
|
||||
{
|
||||
U64 check_time_us = os_now_microseconds();
|
||||
U64 check_time_user_clocks = dasm_user_clock_idx();
|
||||
U64 evict_threshold_us = 10*1000000;
|
||||
U64 evict_threshold_user_clocks = 10;
|
||||
for(U64 slot_idx = 0; slot_idx < dasm_shared->slots_count; slot_idx += 1)
|
||||
{
|
||||
U64 stripe_idx = slot_idx%dasm_shared->stripes_count;
|
||||
DASM_Slot *slot = &dasm_shared->slots[slot_idx];
|
||||
DASM_Stripe *stripe = &dasm_shared->stripes[stripe_idx];
|
||||
B32 slot_has_work = 0;
|
||||
OS_MutexScopeR(stripe->rw_mutex)
|
||||
{
|
||||
for(DASM_Node *n = slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(n->scope_ref_count == 0 &&
|
||||
n->last_time_touched_us+evict_threshold_us <= check_time_us &&
|
||||
n->last_user_clock_idx_touched+evict_threshold_user_clocks <= check_time_user_clocks &&
|
||||
n->load_count != 0 &&
|
||||
n->is_working == 0)
|
||||
{
|
||||
slot_has_work = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(slot_has_work) OS_MutexScopeW(stripe->rw_mutex)
|
||||
{
|
||||
for(DASM_Node *n = slot->first, *next = 0; n != 0; n = next)
|
||||
{
|
||||
next = n->next;
|
||||
if(n->scope_ref_count == 0 &&
|
||||
n->last_time_touched_us+evict_threshold_us <= check_time_us &&
|
||||
n->last_user_clock_idx_touched+evict_threshold_user_clocks <= check_time_user_clocks &&
|
||||
n->load_count != 0 &&
|
||||
n->is_working == 0)
|
||||
{
|
||||
DLLRemove(slot->first, slot->last, n);
|
||||
if(n->info_arena != 0)
|
||||
{
|
||||
arena_release(n->info_arena);
|
||||
}
|
||||
SLLStackPush(stripe->free_node, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
os_sleep_milliseconds(5);
|
||||
}
|
||||
os_sleep_milliseconds(1000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,4 +4,210 @@
|
||||
#ifndef DASM_CACHE_H
|
||||
#define DASM_CACHE_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Instruction Types
|
||||
|
||||
typedef struct DASM_Inst DASM_Inst;
|
||||
struct DASM_Inst
|
||||
{
|
||||
U64 code_off;
|
||||
U64 addr;
|
||||
Rng1U64 text_range;
|
||||
};
|
||||
|
||||
typedef struct DASM_InstChunkNode DASM_InstChunkNode;
|
||||
struct DASM_InstChunkNode
|
||||
{
|
||||
DASM_InstChunkNode *next;
|
||||
DASM_Inst *v;
|
||||
U64 cap;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
typedef struct DASM_InstChunkList DASM_InstChunkList;
|
||||
struct DASM_InstChunkList
|
||||
{
|
||||
DASM_InstChunkNode *first;
|
||||
DASM_InstChunkNode *last;
|
||||
U64 node_count;
|
||||
U64 inst_count;
|
||||
};
|
||||
|
||||
typedef struct DASM_InstArray DASM_InstArray;
|
||||
struct DASM_InstArray
|
||||
{
|
||||
DASM_Inst *v;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Value Bundle Type
|
||||
|
||||
typedef struct DASM_Info DASM_Info;
|
||||
struct DASM_Info
|
||||
{
|
||||
String8 text;
|
||||
DASM_InstArray insts;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cache Types
|
||||
|
||||
typedef struct DASM_Node DASM_Node;
|
||||
struct DASM_Node
|
||||
{
|
||||
// rjf: links
|
||||
DASM_Node *next;
|
||||
DASM_Node *prev;
|
||||
|
||||
// rjf: key
|
||||
U128 hash;
|
||||
U64 addr;
|
||||
Architecture arch;
|
||||
|
||||
// rjf: value
|
||||
Arena *info_arena;
|
||||
DASM_Info info;
|
||||
|
||||
// rjf: metadata
|
||||
B32 is_working;
|
||||
U64 scope_ref_count;
|
||||
U64 last_time_touched_us;
|
||||
U64 last_user_clock_idx_touched;
|
||||
U64 load_count;
|
||||
};
|
||||
|
||||
typedef struct DASM_Slot DASM_Slot;
|
||||
struct DASM_Slot
|
||||
{
|
||||
DASM_Node *first;
|
||||
DASM_Node *last;
|
||||
};
|
||||
|
||||
typedef struct DASM_Stripe DASM_Stripe;
|
||||
struct DASM_Stripe
|
||||
{
|
||||
Arena *arena;
|
||||
OS_Handle rw_mutex;
|
||||
OS_Handle cv;
|
||||
DASM_Node *free_node;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Scoped Access Types
|
||||
|
||||
typedef struct DASM_Touch DASM_Touch;
|
||||
struct DASM_Touch
|
||||
{
|
||||
DASM_Touch *next;
|
||||
U128 hash;
|
||||
U64 addr;
|
||||
Architecture arch;
|
||||
};
|
||||
|
||||
typedef struct DASM_Scope DASM_Scope;
|
||||
struct DASM_Scope
|
||||
{
|
||||
DASM_Scope *next;
|
||||
DASM_Touch *top_touch;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Thread Context
|
||||
|
||||
typedef struct DASM_TCTX DASM_TCTX;
|
||||
struct DASM_TCTX
|
||||
{
|
||||
Arena *arena;
|
||||
DASM_Scope *free_scope;
|
||||
DASM_Touch *free_touch;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Shared State
|
||||
|
||||
typedef struct DASM_Shared DASM_Shared;
|
||||
struct DASM_Shared
|
||||
{
|
||||
Arena *arena;
|
||||
|
||||
// rjf: user clock
|
||||
U64 user_clock_idx;
|
||||
|
||||
// rjf: cache
|
||||
U64 slots_count;
|
||||
U64 stripes_count;
|
||||
DASM_Slot *slots;
|
||||
DASM_Stripe *stripes;
|
||||
|
||||
// rjf: user -> parse thread
|
||||
U64 u2p_ring_size;
|
||||
U8 *u2p_ring_base;
|
||||
U64 u2p_ring_write_pos;
|
||||
U64 u2p_ring_read_pos;
|
||||
OS_Handle u2p_ring_cv;
|
||||
OS_Handle u2p_ring_mutex;
|
||||
|
||||
// rjf: parse threads
|
||||
U64 parse_thread_count;
|
||||
OS_Handle *parse_threads;
|
||||
|
||||
// rjf: evictor thread
|
||||
OS_Handle evictor_thread;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Globals
|
||||
|
||||
thread_static DASM_TCTX *dasm_tctx = 0;
|
||||
global DASM_Shared *dasm_shared = 0;
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Instruction Type Functions
|
||||
|
||||
internal void dasm_inst_chunk_list_push(Arena *arena, DASM_InstChunkList *list, U64 cap, DASM_Inst *inst);
|
||||
internal DASM_InstArray dasm_inst_array_from_chunk_list(Arena *arena, DASM_InstChunkList *list);
|
||||
internal U64 dasm_inst_array_idx_from_code_off__linear_scan(DASM_InstArray *array, U64 off);
|
||||
internal U64 dasm_inst_array_code_off_from_idx(DASM_InstArray *array, U64 idx);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Disassembly Decoding Function
|
||||
|
||||
internal DASM_Info dasm_info_from_arch_addr_data(Arena *arena, U64 *bytes_processed_counter, Architecture arch, U64 addr, String8 data);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Main Layer Initialization
|
||||
|
||||
internal void dasm_init(void);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: User Clock
|
||||
|
||||
internal void dasm_user_clock_tick(void);
|
||||
internal U64 dasm_user_clock_idx(void);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Scoped Access
|
||||
|
||||
internal DASM_Scope *dasm_scope_open(void);
|
||||
internal void dasm_scope_close(DASM_Scope *scope);
|
||||
internal void dasm_scope_touch_node__stripe_r_guarded(DASM_Scope *scope, DASM_Node *node);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cache Lookups
|
||||
|
||||
internal DASM_Info dasm_info_from_hash_addr_arch(DASM_Scope *scope, U128 hash, U64 addr, Architecture arch);
|
||||
internal DASM_Info dasm_info_from_key_addr_arch(DASM_Scope *scope, U128 key, U64 addr, Architecture arch, U128 *hash_out);
|
||||
////////////////////////////////
|
||||
//~ rjf: Parse Threads
|
||||
|
||||
internal B32 dasm_u2p_enqueue_req(U128 hash, U64 addr, Architecture arch, U64 endt_us);
|
||||
internal void dasm_u2p_dequeue_req(U128 *hash_out, U64 *addr_out, Architecture *arch_out);
|
||||
internal void dasm_parse_thread__entry_point(void *p);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Evictor Threads
|
||||
|
||||
internal void dasm_evictor_thread__entry_point(void *p);
|
||||
|
||||
#endif // DASM_CACHE_H
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "hash_store/hash_store.h"
|
||||
#include "file_stream/file_stream.h"
|
||||
#include "text_cache/text_cache.h"
|
||||
#include "dasm_cache/dasm_cache.h"
|
||||
#include "path/path.h"
|
||||
#include "txti/txti.h"
|
||||
#include "coff/coff.h"
|
||||
@@ -67,6 +68,7 @@
|
||||
#include "hash_store/hash_store.c"
|
||||
#include "file_stream/file_stream.c"
|
||||
#include "text_cache/text_cache.c"
|
||||
#include "dasm_cache/dasm_cache.c"
|
||||
#include "path/path.c"
|
||||
#include "txti/txti.c"
|
||||
#include "coff/coff.c"
|
||||
|
||||
@@ -181,7 +181,7 @@ internal void
|
||||
ts_task_thread__entry_point(void *p)
|
||||
{
|
||||
U64 thread_idx = (U64)p;
|
||||
ThreadNameF("[ts] task thread #%I64u", thread_idx+1);
|
||||
ThreadNameF("[ts] task thread #%I64u", thread_idx);
|
||||
TS_TaskThread *thread = &ts_shared->task_threads[thread_idx];
|
||||
for(;;)
|
||||
{
|
||||
|
||||
@@ -721,7 +721,7 @@ txt_scope_close(TXT_Scope *scope)
|
||||
{
|
||||
for(TXT_Node *n = slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(u128_match(hash, n->hash))
|
||||
if(u128_match(hash, n->hash) && touch->lang == n->lang)
|
||||
{
|
||||
ins_atomic_u64_dec_eval(&n->scope_ref_count);
|
||||
break;
|
||||
@@ -750,6 +750,7 @@ txt_scope_touch_node__stripe_r_guarded(TXT_Scope *scope, TXT_Node *node)
|
||||
}
|
||||
MemoryZeroStruct(touch);
|
||||
touch->hash = node->hash;
|
||||
touch->lang = node->lang;
|
||||
SLLStackPush(scope->top_touch, touch);
|
||||
}
|
||||
|
||||
@@ -1193,12 +1194,11 @@ txt_parse_thread__entry_point(void *p)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
HS_Scope *scope = hs_scope_open();
|
||||
|
||||
//- rjf: get next key
|
||||
U128 hash = {0};
|
||||
TXT_LangKind lang = TXT_LangKind_Null;
|
||||
txt_u2p_dequeue_req(&hash, &lang);
|
||||
HS_Scope *scope = hs_scope_open();
|
||||
|
||||
//- rjf: unpack hash
|
||||
U64 slot_idx = hash.u64[1]%txt_shared->slots_count;
|
||||
|
||||
@@ -125,12 +125,19 @@ typedef TXT_TokenArray TXT_LangLexFunctionType(Arena *arena, U64 *bytes_processe
|
||||
typedef struct TXT_Node TXT_Node;
|
||||
struct TXT_Node
|
||||
{
|
||||
// rjf: links
|
||||
TXT_Node *next;
|
||||
TXT_Node *prev;
|
||||
|
||||
// rjf: key
|
||||
U128 hash;
|
||||
TXT_LangKind lang;
|
||||
|
||||
// rjf: artifacts
|
||||
Arena *arena;
|
||||
TXT_TextInfo info;
|
||||
|
||||
// rjf: metadata
|
||||
B32 is_working;
|
||||
U64 scope_ref_count;
|
||||
U64 last_time_touched_us;
|
||||
@@ -161,6 +168,7 @@ struct TXT_Touch
|
||||
{
|
||||
TXT_Touch *next;
|
||||
U128 hash;
|
||||
TXT_LangKind lang;
|
||||
};
|
||||
|
||||
typedef struct TXT_Scope TXT_Scope;
|
||||
@@ -283,7 +291,7 @@ internal String8 txt_string_from_info_data_line_num(TXT_TextInfo *info, String8
|
||||
internal TXT_LineTokensSlice txt_line_tokens_slice_from_info_data_line_range(Arena *arena, TXT_TextInfo *info, String8 data, Rng1S64 line_range);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Transfer Threads
|
||||
//~ rjf: Parse Threads
|
||||
|
||||
internal B32 txt_u2p_enqueue_req(U128 hash, TXT_LangKind lang, U64 endt_us);
|
||||
internal void txt_u2p_dequeue_req(U128 *hash_out, TXT_LangKind *lang_out);
|
||||
|
||||
Reference in New Issue
Block a user