GetThreadDescription (win 10+ thread name) API support

This commit is contained in:
Ryan Fleury
2024-01-11 16:02:27 -08:00
parent 1318aad83b
commit e7243a2c28
6 changed files with 121 additions and 143 deletions
+1
View File
@@ -1516,6 +1516,7 @@ ctrl_thread__next_demon_event(Arena *arena, CTRL_Msg *msg, DEMON_RunCtrls *run_c
out_evt->stack_base = demon_stack_base_vaddr_from_thread(event->thread);
out_evt->tls_root = demon_tls_root_vaddr_from_thread(event->thread);
out_evt->rip_vaddr = event->instruction_pointer;
out_evt->string = event->string;
}break;
case DEMON_EventKind_LoadModule:
{
+74
View File
@@ -4,6 +4,8 @@
////////////////////////////////
//~ rjf: Globals
global GetThreadDescriptionFunctionType *demon_w32_GetThreadDescription = 0;
global B32 demon_w32_resume_needed = 0;
global DWORD demon_w32_resume_pid = 0;
global DWORD demon_w32_resume_tid = 0;
@@ -26,6 +28,17 @@ global String8List demon_w32_environment = {0};
////////////////////////////////
//~ rjf: Helpers
internal U64
demon_w32_hash_from_string(String8 string)
{
U64 result = 5381;
for(U64 i = 0; i < string.size; i += 1)
{
result = ((result << 5) + result) + string.str[i];
}
return result;
}
internal DEMON_W32_Ext*
demon_w32_ext_alloc(void){
DEMON_W32_Ext *result = demon_w32_proc_ext_free;
@@ -351,6 +364,11 @@ demon_os_init(void){
demon_w32_ext_arena = arena_alloc();
demon_w32_detach_proc_arena = arena_alloc();
// rjf: load Windows 10+ GetThreadDescription API
{
demon_w32_GetThreadDescription = (GetThreadDescriptionFunctionType *)GetProcAddress(GetModuleHandleA("Kernel32.dll"), "GetThreadDescription");
}
// rjf: setup environment variables
{
CHAR *this_proc_env = GetEnvironmentStrings();
@@ -771,6 +789,19 @@ demon_os_run(Arena *arena, DEMON_OS_RunCtrls *ctrls){
DWORD sus_result = SuspendThread(thread_ext->thread.handle);
(void)sus_result;
// rjf: unpack thread name
String8 thread_name = {0};
if(demon_w32_GetThreadDescription != 0)
{
WCHAR *thread_name_w = 0;
HRESULT hr = demon_w32_GetThreadDescription(evt.u.CreateThread.hThread, &thread_name_w);
if(SUCCEEDED(hr))
{
thread_name = str8_from_16(arena, str16_cstring((U16 *)thread_name_w));
LocalFree(thread_name_w);
}
}
// rjf: determine if this is the halter thread
B32 is_halter = (evt.dwThreadId == demon_w32_halter_thread_id);
@@ -780,6 +811,7 @@ demon_os_run(Arena *arena, DEMON_OS_RunCtrls *ctrls){
e->process = demon_ent_handle_from_ptr(process);
e->thread = demon_ent_handle_from_ptr(thread);
e->code = evt.dwThreadId;
e->string = thread_name;
}
}
}break;
@@ -1140,6 +1172,48 @@ demon_os_run(Arena *arena, DEMON_OS_RunCtrls *ctrls){
}
}
//- rjf: gather new thread-names
if(demon_w32_GetThreadDescription != 0)
{
for(DEMON_Entity *process = demon_ent_root->first;
process != 0;
process = process->next)
{
if(process->kind != DEMON_EntityKind_Process) { continue; }
for(DEMON_Entity *thread = process->first;
thread != 0;
thread = thread->next)
{
if(thread->kind != DEMON_EntityKind_Thread) { continue; }
DEMON_W32_Ext *thread_ext = demon_w32_ext(thread);
if(thread_ext->thread.last_name_hash == 0 ||
thread_ext->thread.name_gather_time_us+1000000 <= os_now_microseconds())
{
String8 name = {0};
{
WCHAR *thread_name_w = 0;
HRESULT hr = demon_w32_GetThreadDescription(thread_ext->thread.handle, &thread_name_w);
if(SUCCEEDED(hr))
{
name = str8_from_16(scratch.arena, str16_cstring((U16 *)thread_name_w));
LocalFree(thread_name_w);
}
}
U64 name_hash = demon_w32_hash_from_string(name);
if(name.size != 0 && name_hash != thread_ext->thread.last_name_hash)
{
DEMON_Event *e = demon_push_event(arena, &result, DEMON_EventKind_SetThreadName);
e->process = demon_ent_handle_from_ptr(process);
e->thread = demon_ent_handle_from_ptr(thread);
e->string = push_str8_copy(arena, name);
}
thread_ext->thread.name_gather_time_us = os_now_microseconds();
thread_ext->thread.last_name_hash = name_hash;
}
}
}
}
// TODO(allen): handle errors? if (!got_new_event) ? if (!good_state) ?
// TODO(allen): per-Architecture
+4
View File
@@ -35,6 +35,8 @@ union DEMON_W32_Ext
struct{
HANDLE handle;
U64 thread_local_base;
U64 last_name_hash;
U64 name_gather_time_us;
} thread;
struct{
HANDLE handle;
@@ -68,6 +70,7 @@ struct DEMON_W32_EntityNode
DEMON_Entity *entity;
};
typedef HRESULT GetThreadDescriptionFunctionType(HANDLE hThread, WCHAR **ppszThreadDescription);
////////////////////////////////
//~ NOTE(allen): Win32 Demon Exceptions
@@ -352,6 +355,7 @@ struct DEMON_PeOptionalHeader32Plus
////////////////////////////////
//~ rjf: Helpers
internal U64 demon_w32_hash_from_string(String8 string);
internal DEMON_W32_Ext* demon_w32_ext_alloc(void);
internal DEMON_W32_Ext* demon_w32_ext(DEMON_Entity *entity);
+31 -1
View File
@@ -1852,6 +1852,7 @@ df_entity_change_parent(DF_StateDeltaHistory *hist, DF_Entity *entity, DF_Entity
internal void
df_entity_equip_txt_pt(DF_Entity *entity, TxtPt point)
{
df_require_entity_nonnil(entity, return);
entity->text_point = point;
entity->flags |= DF_EntityFlag_HasTextPoint;
df_entity_notify_mutation(entity);
@@ -1860,6 +1861,7 @@ df_entity_equip_txt_pt(DF_Entity *entity, TxtPt point)
internal void
df_entity_equip_txt_pt_alt(DF_Entity *entity, TxtPt point)
{
df_require_entity_nonnil(entity, return);
entity->text_point_alt = point;
entity->flags |= DF_EntityFlag_HasTextPointAlt;
df_entity_notify_mutation(entity);
@@ -1868,6 +1870,7 @@ df_entity_equip_txt_pt_alt(DF_Entity *entity, TxtPt point)
internal void
df_entity_equip_entity_handle(DF_Entity *entity, DF_Handle handle)
{
df_require_entity_nonnil(entity, return);
entity->entity_handle = handle;
entity->flags |= DF_EntityFlag_HasEntityHandle;
df_entity_notify_mutation(entity);
@@ -1876,6 +1879,7 @@ df_entity_equip_entity_handle(DF_Entity *entity, DF_Handle handle)
internal void
df_entity_equip_b32(DF_Entity *entity, B32 b32)
{
df_require_entity_nonnil(entity, return);
entity->b32 = b32;
entity->flags |= DF_EntityFlag_HasB32;
df_entity_notify_mutation(entity);
@@ -1884,6 +1888,7 @@ df_entity_equip_b32(DF_Entity *entity, B32 b32)
internal void
df_entity_equip_u64(DF_Entity *entity, U64 u64)
{
df_require_entity_nonnil(entity, return);
entity->u64 = u64;
entity->flags |= DF_EntityFlag_HasU64;
df_entity_notify_mutation(entity);
@@ -1892,6 +1897,7 @@ df_entity_equip_u64(DF_Entity *entity, U64 u64)
internal void
df_entity_equip_rng1u64(DF_Entity *entity, Rng1U64 range)
{
df_require_entity_nonnil(entity, return);
entity->rng1u64 = range;
entity->flags |= DF_EntityFlag_HasRng1U64;
df_entity_notify_mutation(entity);
@@ -1900,6 +1906,7 @@ df_entity_equip_rng1u64(DF_Entity *entity, Rng1U64 range)
internal void
df_entity_equip_color_rgba(DF_Entity *entity, Vec4F32 rgba)
{
df_require_entity_nonnil(entity, return);
Vec3F32 rgb = v3f32(rgba.x, rgba.y, rgba.z);
Vec3F32 hsv = hsv_from_rgb(rgb);
Vec4F32 hsva = v4f32(hsv.x, hsv.y, hsv.z, rgba.w);
@@ -1909,6 +1916,7 @@ df_entity_equip_color_rgba(DF_Entity *entity, Vec4F32 rgba)
internal void
df_entity_equip_color_hsva(DF_Entity *entity, Vec4F32 hsva)
{
df_require_entity_nonnil(entity, return);
entity->color_hsva = hsva;
entity->flags |= DF_EntityFlag_HasColor;
df_entity_notify_mutation(entity);
@@ -1917,6 +1925,7 @@ df_entity_equip_color_hsva(DF_Entity *entity, Vec4F32 hsva)
internal void
df_entity_equip_death_timer(DF_Entity *entity, F32 seconds_til_death)
{
df_require_entity_nonnil(entity, return);
entity->flags |= DF_EntityFlag_DiesWithTime;
entity->life_left = seconds_til_death;
df_entity_notify_mutation(entity);
@@ -1925,6 +1934,7 @@ df_entity_equip_death_timer(DF_Entity *entity, F32 seconds_til_death)
internal void
df_entity_equip_cfg_src(DF_Entity *entity, DF_CfgSrc cfg_src)
{
df_require_entity_nonnil(entity, return);
entity->cfg_src = cfg_src;
df_entity_notify_mutation(entity);
}
@@ -1934,6 +1944,7 @@ df_entity_equip_cfg_src(DF_Entity *entity, DF_CfgSrc cfg_src)
internal void
df_entity_equip_ctrl_machine_id(DF_Entity *entity, CTRL_MachineID machine_id)
{
df_require_entity_nonnil(entity, return);
entity->ctrl_machine_id = machine_id;
entity->flags |= DF_EntityFlag_HasCtrlMachineID;
df_entity_notify_mutation(entity);
@@ -1942,6 +1953,7 @@ df_entity_equip_ctrl_machine_id(DF_Entity *entity, CTRL_MachineID machine_id)
internal void
df_entity_equip_ctrl_handle(DF_Entity *entity, CTRL_Handle handle)
{
df_require_entity_nonnil(entity, return);
entity->ctrl_handle = handle;
entity->flags |= DF_EntityFlag_HasCtrlHandle;
df_entity_notify_mutation(entity);
@@ -1950,6 +1962,7 @@ df_entity_equip_ctrl_handle(DF_Entity *entity, CTRL_Handle handle)
internal void
df_entity_equip_arch(DF_Entity *entity, Architecture arch)
{
df_require_entity_nonnil(entity, return);
entity->arch = arch;
entity->flags |= DF_EntityFlag_HasArch;
df_entity_notify_mutation(entity);
@@ -1958,6 +1971,7 @@ df_entity_equip_arch(DF_Entity *entity, Architecture arch)
internal void
df_entity_equip_ctrl_id(DF_Entity *entity, U32 id)
{
df_require_entity_nonnil(entity, return);
entity->ctrl_id = id;
entity->flags |= DF_EntityFlag_HasCtrlID;
df_entity_notify_mutation(entity);
@@ -1966,6 +1980,7 @@ df_entity_equip_ctrl_id(DF_Entity *entity, U32 id)
internal void
df_entity_equip_stack_base(DF_Entity *entity, U64 stack_base)
{
df_require_entity_nonnil(entity, return);
entity->stack_base = stack_base;
entity->flags |= DF_EntityFlag_HasStackBase;
df_entity_notify_mutation(entity);
@@ -1974,6 +1989,7 @@ df_entity_equip_stack_base(DF_Entity *entity, U64 stack_base)
internal void
df_entity_equip_tls_root(DF_Entity *entity, U64 tls_root)
{
df_require_entity_nonnil(entity, return);
entity->tls_root = tls_root;
entity->flags |= DF_EntityFlag_HasTLSRoot;
df_entity_notify_mutation(entity);
@@ -1982,6 +1998,7 @@ df_entity_equip_tls_root(DF_Entity *entity, U64 tls_root)
internal void
df_entity_equip_vaddr_rng(DF_Entity *entity, Rng1U64 range)
{
df_require_entity_nonnil(entity, return);
entity->vaddr_rng = range;
entity->flags |= DF_EntityFlag_HasVAddrRng;
df_entity_notify_mutation(entity);
@@ -1990,6 +2007,7 @@ df_entity_equip_vaddr_rng(DF_Entity *entity, Rng1U64 range)
internal void
df_entity_equip_vaddr(DF_Entity *entity, U64 vaddr)
{
df_require_entity_nonnil(entity, return);
entity->vaddr = vaddr;
entity->flags |= DF_EntityFlag_HasVAddr;
df_entity_notify_mutation(entity);
@@ -2000,7 +2018,19 @@ df_entity_equip_vaddr(DF_Entity *entity, U64 vaddr)
internal void
df_entity_equip_name(DF_StateDeltaHistory *hist, DF_Entity *entity, String8 name)
{
entity->name = df_name_alloc(hist, name);
df_require_entity_nonnil(entity, return);
if(entity->name.size != 0)
{
df_name_release(hist, entity->name);
}
if(name.size != 0)
{
entity->name = df_name_alloc(hist, name);
}
else
{
entity->name = str8_zero();
}
entity->name_generation += 1;
df_entity_notify_mutation(entity);
}
+1
View File
@@ -1355,6 +1355,7 @@ internal DF_CoreCmdKind df_core_cmd_kind_from_string(String8 string);
//- rjf: nil
internal B32 df_entity_is_nil(DF_Entity *entity);
#define df_require_entity_nonnil(entity, if_nil_stmts) do{if(df_entity_is_nil(entity)){if_nil_stmts;}}while(0)
//- rjf: handle <-> entity conversions
internal U64 df_index_from_entity(DF_Entity *entity);
+10 -142
View File
@@ -32,12 +32,6 @@
// at all where the font ever was) The font path will now remain "stable"
// in the sense that it won't rewrite it anymore. But you cannot use the
// debugger because it's the wrong font path, so you get no text.
// [x] When I click in the theme window on something like "Code (Meta)", it
// opens up a tiny window, which only shows a tiny bit of what I imagine
// is the entire color picker? It makes the picker unusable because more
// of the UI for it is clipped and cannot be accessed.
// [x] Clicking anywhere in the color picker that isn't a button closes the
// color picker for some reason?
// [ ] In a "hover watch" (where you hover over a variable and it shows a pop-
// up watch window), if you expand an item near the bottom of the listing,
// it will be clipped to the bottom of the listing instead of showing the
@@ -45,8 +39,6 @@
// actually visible)
//
// [ ] @bug @feature @cleanup general feedback from casey
// [x] ** I don't like how panels get dimmer when they are not active, because
// it makes them harder for me to read. Is there a way to turn this off?
// [ ] ** I couldn't figure out how to really view threads in the debugger.
// The only place I found a thread list was in "The Scheduler", but it
// only lists threads by ID, which is hard to use. I can hover over them
@@ -211,6 +203,8 @@
// [ ] @feature search-in-all-files
// [ ] @feature memory view mutation controls
// [ ] @feature memory view user-made annotations
//
// [ ] @feature auto-scroll output window
////////////////////////////////
//~ rjf: 2024/2 tasks
@@ -316,140 +310,14 @@
////////////////////////////////
//~ rjf: Completed Tasks
//
// [x] adding watches to source locations
// [x] tab overflow buttons
// [x] "working set" of targets, just used 'enabled' slot on target entities
// [x] convert hover-eval ui to a frontend-wide global ui concept, so that we can have watch-hover trees from anywhere
// [x] annotate code slices with relevant watches
// [x] ability to duplicate targets easily
// [x] ability to label targets separately from their EXE
//
// [x] txti layer needs lexing
// [x] dbgi autoconversion layer needs to work reliably
// [x] new dbgi system on frontend
// [x] new bini system on frontend
// [x] new dbgi system needs in-parallel typegraph builders
// [x] eval is busted
// [x] fail or debug non-text
// [x] clean loading
// [x] watch pins
// [x] snapping should still work if a file is loading while the snap first occurs
// [x] keyboard usage (set-next-statement, run-to-line, etc.)
// [x] go-to-name (both file & symbol)
//
// [x] in-memory disassembly view
// [x] visualize conversion tasks as they occur
// [x] raddbg.exe self-sufficiency => built-in converter execution mode
// [x] move path-mapping fallback from pending-entity into code view
// [x] per-target entry point overrides
// [x] logic on when double click on callstack entry goes to source or disassembly is very confusing,
// because it seems it matters whether disassembly tab is in same panel as source, or is placed in
// different panel in VS double clicking on stack stays in disassembly if disassembly tab is currently
// active. Otherwise if source is active, it goes to source code (regardless if disasm is open/visible
// or not)
// [x] global variable (just regular C++ bool in the same file) does not show value when hovering over source
// code, or when put into watch window.
//
// [x] debugger ui thread needs wakeup when a debug event is hit
// [x] ctrl thread needs to re-resolve breakpoints as modules/threads come in during a run
// [x] automatically pull in members from containing struct in member functions
// when looking up locals
// [x] iron out weird state machine bugs with hover-eval
//
// [x] thread-local eval
// [x] tabs in source code render as squares
//
// [x] breakpoint stop-conditions
// [x] first-chance exception hitting (d3d11 as an example offender)
// [x] soft-halt refresh
//
// [x] conditional breakpoints cannot be submitted if they don't compile
// need to (a) visualize and (b) equip the ctrl thread with a 'halt on
// new debug info feature', which can be done repeatedly until something
// compiles (?)
//
// [x] outputdebugstring logs
//
// [x] memory view
//
// [x] when application starts then the focus is on cmd.exe window of application not
// debugger. This means if I press F11 to step into main(), the application cmd.exe will go fullscreen
// hiding debugger (F11 key for ConEmu I use makes it go fullscreen)
// [x] I'd prefer if ctrl+click on word would go to function definition, not double click
// double clicking in source code should select word - which easy way to double click and ctrl+c to copy
// it, pretty much standard thing in any text editorr/viewer and triple click for selecting whole line
// [x] command line DF_CfgSrc -> explicitly visualize as temporary, provide UI
// path to make permanent (in either user or profile?)
//
// [x] memory view annotations: bytes in a range of memory actually have a
// *stack* of possible interpretations, for any particular low-level
// representation (assuming 1 is fine for this case). a U32 member in
// a local struct on the stack has 3 layers: U32 -> local -> stack.
// each of these are useful information about that U32's range of bytes.
// so, the memory view annotation system ought to be expanded to support-
// ing stacks of annotations, and gracefully visualizing multiple nested
// ones. after that is supported, we can use the type info to derive the
// lowest level representation of some bytes. each stack can only have
// one low-level type. that low-level type, then, can be used to directly
// interpret the bytes - the others can be used to show the "conceptual
// stack".
//
// [x] entity tree ui replacement (scheduler, modules, breakpoints, pins)
// [x] theme menu
// [x] complete transition to UI_ScrollPt coordinate space scrolling - eliminate
// old scrolls & scroll regions
// [x] settings menu
// [x] exception filter settings & controls in ctrl layer
//
// [x] @polish @cleanup convert eval/watch to scroll list
// [x] @feature memory view keyboard navigation
// [x] @bug fix general ui line edit editing rules - conflicting with navigation, etc.
// [x] @polish have expander space even if not used in watch window
// [x] @bug references do not expand properly
// [x] @bug panel deletion improper size bug
// [x] @bug panel serialization/deserialization bug?
// [x] unfinished char/string literal lexing
//
// [x] C++ problems in watch window: evaluating "this" shows no members. The type seems correct reference
// to struct (which is local variable) does not show any members, like I have "tm_mem_zone_rt& z = ..."
// in source code, and doing "z" produces no children, nor does "&z" but then at least it shows correct
// address as value. if I ask for member of this when currently inside of member function (like "m_zone_stack")
// it shows "unknown identifier m_zone_stack" but "this->m_zone_stack" works fine!
// [x] if I write x'z in watch window and press enter, it only shows x and loses 'z - it does not show it, but when I edit cell then 'z comes back. It pretends I entered just x and shows x value.
// [x] it seems the glyph advance for default font size is kind of wrong, as characters are a blitted a bit over top of each other
//
// [x] @bug deleting a watch tree while it is expanded causes cursor to go back to first row - cannot simply increment cursor. maybe keep in same spot, but rebuild viz blocks?
// [x] @bug do not squish partially-cut-off view rule block uis
// [x] @feature "solo step", or "solo mode" freeze-all-unselected-threads-on-step-commands
// [x] @feature typing autocomplete lister
// [x] @feature directional navigation of panel focus
// [x] @cleanup @feature cache eviction in texture cache & hash store
//
//- 2023/12/7
//
// [x] @bug hash store cache eviction can only work if user never blindly tries to go from hash -> data, because
// they must be able to retry... hmm...
// [x] txt cell revamp. keyboard focus in both default & non w/ multiple options, helper lister, etc.
// [x] `bitmap:(w:width, h:height, [fmt:fmt])` - interpret memory as raw bitmap data
// [x] `geo:n[ topology stride]` - interpret memory as geometry
// [x] cursor helper -> upgraded txt cell. classify inputs & show dropdowns (locals, globals, types, view rules, etc.)
// [x] @bug page-up and page-down in src view, when near the end of file
//
//- 2023/12/8
//
// [x] @bug parse `unsigned int` correctly in eval parser
// [x] @bug ., + operators should work on registers
// [x] @bug straighten out register eval problems
// [x] @cleanup finish ui_em transition
//
//- 2023/12/22
//
// [x] @bug set-bp-while-running seems to not resume after soft-halt, might be a soft-halt bug
// [x] @bug weird view snapping in watch scrolling down
//
//- 2024/01/10
//
// [x] @feature allow `,count`, `,x`, `,b` style watch window expression extensions, which add to view rule, for VS-like behavior fastpaths
// [x] ** I don't like how panels get dimmer when they are not active, because
// it makes them harder for me to read. Is there a way to turn this off?
// [x] When I click in the theme window on something like "Code (Meta)", it
// opens up a tiny window, which only shows a tiny bit of what I imagine
// is the entire color picker? It makes the picker unusable because more
// of the UI for it is clipped and cannot be accessed.
// [x] Clicking anywhere in the color picker that isn't a button closes the
// color picker for some reason?
////////////////////////////////
//~ rjf: Includes