bugs:
get stuck when debuggee is OOM-killed by linux
investigate `clickhouse local` progress spinner appearing in debugger window despite -t
fix empty symbols loading stage name near the end
investigate regression in symbols loading speed (stuck on "parsing drawf" for ~10 seconds)
investigate why many types are not deduped, e.g. there's DB::Block#441
make stack unwinding work when control is in .plt or .plt.got, see "Recognise PLT entries such as" in libunwind code
fix incorrect column numbers with tab indentation (tabs should be counted as 1 column, but we turn them into multiple spaces)
fix line-wrapped copy-paste (seems to copy only the first line of selection) and line-wrapped C-k (deletes only to the end of post-wrapping line, should use pre-wrapping line)
make auto-downcast check that the types are in the same language, to avoid downcasting C++ type to Rust type with same name (e.g. "parquet::format::PageHeader"); check if type decl/def matching has the same problem

performance:
optimize symbols loading
  profile attribute loading
  madvise
    benchmark with file pre-evicted from page cache (using vmtouch)
  fix warning about unsupported forms, especially implicit_const on flags
  try caching chase results (71M chases, 15M unique results, 205M total DIEs)
  test with dwarf 4
  replace strlen inside .debug_str etc lookups
  simple things found by profiler (samply is nice)
look at profiler on ch server (1500 threads)
test with 10k threads
  fix O(n^2) stuff until syscalls are the slowest part
  time waitpid separately
  waitpid is suspiciously slow; does it iterate over threads? can partially avoid it by anticipating which threads will get events?
  if there are enough free hw breakpoints, activate new breakpoints as hw and thread-specific right away without waiting for them to be spuriously hit by other threads
  maybe avoid duplicate POKEUSER calls for debug registers if the thread already had them assigned
test with watches window filled with long arrays (I saw 50ms frames when there are ~5 of them; maybe just decrease length limit)
investigate unresponsive ui when conditional breakpoint is hit at a high rate

todo:
draggable column widths in watches window
try with odin
zig pretty-printers
support gnu_debugaltlink, as found in zsh in debuginfod
output window (plain text)
get debuglink binaries from debuginfod
"Expected an attribute value to be a string form" on zsh
buffer input when program is quitting, especially k+r and steps
test with breakpad minidumps
function pointers
modifying debuggee variables (memory and registers), modifying ip
log a message if the process forked away
add alternative default key binds for next/prev search result, F3 is not always available on mac
async fetch from debuginfod (load symbols without waiting for it, then on success start over)
show an error if program failed to start (e.g. executable doesn't exist)
panic screen: show stack trace and process state, type something to resume+detach or kill or email stack trace or quit
allow specifying cgroup and user (or maybe it's too hard, need to maybe inherit groups from parent somehow too)
maybe refresh global variables in watches window on periodic timer tick when the program is running
make watch window show dereferenced string-like values by default (otherwise expanding it shows the array of characters and eats all vertical space)
update rust HashMap pretty printer
detect and show if a pointer points to a symbol (function, global variable, vtable)
do something for downcasts in conditional breakpoints (coalesce()? maybe()? ?()? ()?? checkbox to stop on error? 'is' operator? explicit checked downcast? also function to check field presense without evaluating it)
edit breakpoint condition on click
`in` operator, e.g. x in [1, 2, 3]
make names in MetaType etc unexpandable as array
make '^' apply to expressions instead of variable names, e.g. ^(this.query_context)
allow adding unconditional breakpoint on the same line as conditional one, somehow
global variables from .symtab, maybe from .debug_pubnames (maybe also look into .debug_pubtypes)
more built-in pretty printers: boost::multi_index sparsehash, absl, rust b-trees
hotkey to switch between .h/.cpp (/.cc/.hh/.hpp/.cxx - maybe just anything after '.')
reordering watches
reordering tabs
rearranging windows
maximizing windows
meta functions like checking for field presense of superclass type, and also just slice length etc, for use in conditions
key to freeze value in watches window; can also be used for controlling when to run the expression if it has side effects
key to add watch value as new watch, maybe cast to type, e.g. *(0x1234 as *DB::Block)
tooltip in status window showing more log lines with line wrapping
make return key do something in each window: go from threads to stack -> code -> disassembly -> code; from locals create a watch; from watch sub-value create another watch for that field/element; from breakpoints to code/disassembly/watch (depending on breakpoint type)
allow expressions (especially conditions) to have multiple statements separated by ';'
"unexpectedly big memory read" (16 bytes) for f64 local variables on the stack in rust
some kind of non-stop mode to prevent clickhouse server from losing keeper connection when stepping through the code
allow navigating from source to variables (especially global) and types declared there
allow to put breakpoint on a whole file (by adding breakpoint on the first line, since there's never any real code on first line in practice; have a new icon for it; internally use line 0 to cover the garbage LineInfo-s with line 0; or maybe put breakpoint only on function entry points instead of everything)
function pointers and field pointers
handle tail calls: if you step-into a tail call (by single-stepping from instruction pointed by DW_AT_call_pc of a DW_TAG_call_site with DW_AT_call_tail_call), the tail-callee should be added to stack digest (so its stack frame is selected instead of parent)
find function by address (like addr2line)
use actual function name instead of "_" in namespace path, or do something else to make function-static variables usable
show number of function inlined sites in disassembly window, allow setting breakpoint on it, allow opening inline-only functions
show stop reason in status window, next to "suspended"
follow forks, maybe even support multiple processes
allow cast `foo as typeof(bar)`, also `foo as typeof(bar)::T`
show argument values in stack trace
detect dynamic library loads using r_debug thing
hotkey to step to end of basic block or next call/ret
key to follow a jump in disas
allow line-based steps in .plt* (maybe by just turning them into single-instruction-steps)
in disassembly window, make 'left' key jump to the start of inlined function
maximizing windows (hot key and button in a corner)
special breakpoints: panic (rust_panic, core::panicking::panic_fmt, or maybe std::panicking::rust_panic_with_hook; maybe disable find_catch_blocks() for rust at least when panic bp is enabled), exception (__cxa_throw), signals, main function, dynamic library load
make source window autoscroll horizontally in addition to vertically
-v for build datetime
handle partially-optimized-out struct values (common in rust)
read color scheme from file
color threads based on stack hash
search in watches window
assigning to debuggee variables and registers, including rip
key to teleport ip to current line

watches, expressions
  first, implement simple expression watches
  second, write many pretty printers in imaginary language, figure out what it should be; std::vector, unordered_map, unique_ptr, shared_ptr, list, optional, Rust Vec, HashMap, Option, Box, deb's Pool, etc; see if per-field format options are needed
  stateful expression interpreter, yield, functions
  associating print expressions with types (by substring match to cover templates?)
  always show raw value as one of the children, including for container elements (yield raw value when recursing)
  print options: hex, raw, array length (expression), expanded (i.e. omit array contents etc)
  maybe: api to output a tree of nested values, skipping subtrees if collapsed (`if $yield(val, nested)`, `$yield((), end)`?)
  manual refresh for slow expressions, exec time limit (press refresh to double the limit and retry)
  special functions/operators like sizeof() ($sizeof()?), type_name, type_offset, type_binary, offsetof, type_of (treating type as value of a special type, expandable in watch window)
  format matrices prettily
  format options for simd registers to show as vectors of given type and length (expandable)
  make basic types like u16 available without big search, with consistent names
  also consider subset of the language for injecting conditional breakpoint code

unfocus search bars when moving up/down the list (but not when scrolling)
show disassembly even if symbols are missing
group threads by stack trace, to make it easy to exclude pool threads waiting for work
show return value after step-out (and other steps that happened to step out)
data breakpoints (added by pressing 'b' on a watch, the breakpoint would use the address of the watched value at the time of setting the breakpoint, with some indication if it's different from current); maybe added as a new watch with address as a literal; or added in breakpoints window and focus jumps there
thread-specific breakpoints (controlled in breakpoints window), a key to lock all breakpoints to current thread
hotkeys for switching to specific windows
resolve dynamic library call targets in disassembly, instead of showing them as "call .plt.sec+1234h"
research symtab function range overlaps, don't ignore lengths (e.g. in ld-linux-x86-64.so.2, entry point is a NOTYPE symbol _start, which we incorrectly attribute to previous function _dl_help that ends just before the program entry point)
pretty print variable locations (inline frame base and cfa, turn simple expressions from postfix to infix notation)
handle subset of fields being optimized out (seen e.g. for metric_active_threads in ThreadPoolImpl<std::__1::thread>::worker)
maybe show various stop conditions (program start, crashing signals, stepping) uniformly as virtual breakpoints in breakpoints window
test symbols loader with TSAN (for the sketchy bespoke locking in types traversal)
parse and colorize function names, especially the template stuff
parse and colorize type names, especially the template stuff

try refactoring Debugger to move threads outside to avoid re-lookups everywhere
show code in disassembly
show variable names in disassembly
deferred breakpoints that are resolved on future dynamic library loads; allow opening files that don't exist, or something
locking windows
test on very large disassembled functions and large source files
disassembly: show basic block boundaries (jump destinations)
disassembly: show current jump destination like in perf
maybe handle tail calls somehow, for step-over/step-out purposes; probably show it in ui
if a step is interrupted, focus on the stack frame of the step, not the top frame
allow calling functions
refactor loader stack to not contain leaves
snapshots and "step back"; probably don't actually revert the process state, just allow inspecting snapshot of memory+registers
