code wiki / (root) / nx_dbg_step.nx

nx_dbg_step.nx

buildroot/runtime/nx_dbg_step.nx

58248 B1272 linesdepth 6pulls 21 transitivereach 0 importersview sourcekind tool
docsdependenciesstructsconstsfunctions

about

nx_dbg_step.nx -- THE STEPPING DEBUGGER (/compare/lang LN12, watch symbol dbg_step_over). THE RUNG (lang.plan): "Stepping debugger -- the doctor lane's DT1: break and step on our own binaries over the line table we emit (one contract, two pages)". LB5 shipped the line table (lm_stmt_stamp, statement granularity, readelf-validated). This is the debugger ON it. WHAT WAS REUSED, NOT REWRITTEN (nx_spendgate 2026-08-25 named all three): nx_addr2line_lib.nx -- the sovereign DWARF v5 line-program primitives (uleb/sleb/u32/u64le, nx_a2l_prog_off, and nx_a2l_func_at which already reads .debug_info subprograms). Every ULEB/SLEB byte in this file is decoded by THAT library's functions; there is still exactly one DWARF integer decoder. nx_bootcap.nx -- bootcap_machine: the SAME device set the boot rulers measure. rv64im_min_decoder.nx-- nx_rv64im_decode_kind + field extractors, for call/return classification. nx_lineconf_lib.nx -- the one line-anchored conf reader. nx_elf_read.nx -- ELF64 section walk. WHAT IS NEW HERE, AND WHY THE INCUMBENT COULD NOT DO IT: nx_a2l_run answers ONE question -- "which line is at address X" -- and to do it cheaply it keeps only the best row and STOPS AT THE FIRST end_sequence. A debugger needs the WHOLE table and it needs it BOTH WAYS: * file:line -> address (setting a breakpoint) <- the direction the incumbent cannot answer * address -> row index (where am I) * row i -> row i+1 (where does this statement END) <- the step primitive So this file runs the line program once into a two-pass-sized ROW TABLE, across ALL sequences. It also decodes THREE opcode classes nx_a2l_run refuses: SPECIAL opcodes (the compact bias form), DW_LNS_const_add_pc and DW_LNS_fixed_advance_pc, plus any other standard opcode via the header's own standard_opcode_lengths table. That is not scope creep: it is the difference between a table that works on our emitter's output and one that works on a line table from any DWARF v5 producer. Anything still undecodable sets decode_complete=0 and then RESOLUTION REFUSES WITH "UNPROVEN" RATHER THAN "NO SUCH LINE" -- a partial table that answers "absent" is a confident false negative, which is the one failure mode a breakpoint resolver must never have. STEPPING SEMANTICS (the actual rung). A statement step is NOT an instruction step: dbg_step_into -- single-step until the covering line-table ROW CHANGES. Entering a callee is a row change, so this stops on the callee's first statement. dbg_step_over -- the same, except while the call DEPTH is above the starting depth nothing is tested: the whole callee runs and the stop lands on the statement AFTER the call, in the caller. A `ret` past the starting frame is depth < 0, which is tested (that is gdb's behaviour at the end of a function, deliberately kept).

dependencies 6 imports · 0 importers

nx_syscalls.nx nx_lineconf_lib.nx nx_addr2line_lib.nx nx_elf_read.nx nx_bootcap.nx rv64im_min_decoder.nx nx_dbg_step.nx

imports: nx_syscalls.nxnx_lineconf_lib.nxnx_addr2line_lib.nxnx_elf_read.nxnx_bootcap.nxrv64im_min_decoder.nx

imported by: nobody (leaf or entry point)

call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown

main ds_usage ds_puts sys_write ds_streq ds_selftest ds_puts ↻ ds_load_table_raw sys_mmap nxa_die sys_write ↻ sys_exit nxa_lock_take nxa_lock_addr sys_write ↻ nxa_lock_give nxa_lock_addr ↻ nxa_report_overrun sys_write ↻ nxa_dump_printable sys_write ↻ nxa_dump_sizes sys_write ↻ sys_read_file sys_openat_rd sys_lseek sys_mmap ↻ sys_read sys_munmap sys_close ds_puts ↻ ds_num ds_puts ↻ sys_mmap ↻ sys_write ↻ sys_munmap ↻ ds_table_build sys_mmap ↻ ds_parse_prologue nx_a2l_u32

structs

278struct DsLineTable
728struct DsDbg

consts

106const DS_DW_VERSION_5: i64 = 5
107const DS_LNCT_path: i64 = 0x01
108const DS_LNCT_directory_index: i64 = 0x02
109const DS_FORM_string: i64 = 0x08
110const DS_FORM_udata: i64 = 0x0f
111const DS_LNS_copy: i64 = 0x01
112const DS_LNS_advance_pc: i64 = 0x02
113const DS_LNS_advance_line: i64 = 0x03
114const DS_LNS_set_file: i64 = 0x04
115const DS_LNS_const_add_pc: i64 = 0x08
116const DS_LNS_fixed_advance_pc: i64 = 0x09
117const DS_LNE_end_sequence: i64 = 0x01
118const DS_LNE_set_address: i64 = 0x02
121const DS_OFF_UNIT_LEN: i64 = 0
122const DS_OFF_VERSION: i64 = 4
123const DS_OFF_ADDR_SIZE: i64 = 6
124const DS_OFF_SEG_SEL: i64 = 7
125const DS_OFF_HEADER_LEN: i64 = 8
126const DS_OFF_PROLOGUE: i64 = 12 // header_length is measured from HERE
127const DS_U32_BYTES: i64 = 4
128const DS_UNIT_LEN_MIN: i64 = 4
129const DS_HDR_MIN_BYTES: i64 = 12
130const DS_BYTE_MOD: i64 = 256
131const DS_BYTE_SIGN: i64 = 128
132const DS_BYTE_MASK: i64 = 255
133const DS_SPECIAL_MAX: i64 = 255
134const DS_PTR_BYTES: i64 = 8
135const DS_SET_ADDR_BYTES: i64 = 8
136const DS_ASCII_ZERO: i64 = 48
137const DS_ASCII_NINE: i64 = 57
138const DS_ASCII_a: i64 = 97
139const DS_ASCII_f: i64 = 102
140const DS_ASCII_A: i64 = 65
141const DS_ASCII_F: i64 = 70
142const DS_ASCII_SLASH: i64 = 47
143const DS_DECIMAL: i64 = 10
144const DS_HEXBASE: i64 = 16
147const DS_INST_BYTES: i64 = 4
148const DS_RVC_BYTES: i64 = 2
149const DS_ILEN32_MASK: i64 = 3
150const DS_ILEN32_VAL: i64 = 3
151const DS_REG_RA: i64 = 1 // x1, the primary link register
152const DS_REG_T0: i64 = 5 // x5, the ALTERNATE link register (RISC-V spec)
153const DS_RVC_OP_C2: i64 = 2
154const DS_RVC_F3_JALR: i64 = 4
155const DS_RVC_RS2_SHIFT: i64 = 2
156const DS_RVC_RD_SHIFT: i64 = 7
157const DS_RVC_F3_SHIFT: i64 = 13
158const DS_RVC_BIT12_SHIFT: i64 = 12
159const DS_REG_MASK: i64 = 31
160const DS_F3_MASK: i64 = 7
163const DS_STOP_BREAKPOINT: i64 = 1
164const DS_STOP_STEP: i64 = 2
165const DS_STOP_HALT: i64 = 3
166const DS_STOP_BUDGET: i64 = 4
168const DS_EXIT_OK: i64 = 0
169const DS_EXIT_USAGE: i64 = 2
170const DS_EXIT_NOIMAGE: i64 = 3
171const DS_EXIT_NOLINES: i64 = 4
172const DS_EXIT_REFUSED: i64 = 5
173const DS_EXIT_BUDGET: i64 = 6
180const DS_CONF_PATH: *u8 = "knowledge/dbg_step.conf"
181const DS_CONF_KEY_BUDGET: *u8 = "max-steps-per-resume"
183const DS_NAME_CAP_PER_FILE: i64 = 1024 // one path; the arena is sized nfiles * this, then filled exactly
184const DS_FUNCNAME_CAP: i64 = 512
185const DS_SCRATCH: i64 = 64
186const DS_PATHBUF: i64 = 1024
299const DS_LT_BYTES: i64 = 256 // 19 fields * 8 = 152; sized like NX_RV64IM_SIM_BYTES, room to grow
653const DS_RESOLVE_ABSENT: i64 = 0 - 1
654const DS_RESOLVE_UNPROVEN: i64 = 0 - 2
745const DS_DBG_BYTES: i64 = 256
819const DS_MODE_CONTINUE: i64 = 0
820const DS_MODE_INTO: i64 = 1
821const DS_MODE_OVER: i64 = 2

functions

189func ds_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
190func ds_num(v: i64) -> i64
205func ds_hexnib(n: i64) -> i64 { if n < DS_DECIMAL { return DS_ASCII_ZERO + n } return (DS_ASCII_a - DS_DECIMAL) + n }
called by 1: ds_hex
206func ds_hex(v: i64) -> i64
222func ds_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
223func ds_streq(a: *u8, b: *u8) -> i64
234func ds_basename(p: *u8) -> *u8
called by 1: ds_addr_for_line
240func ds_atoi(s: *u8) -> i64
called by 2: ds_do_resolvemain
255func ds_atox(s: *u8) -> i64
called by 1: main
301func ds_u16(b: *u8, o: i64) -> i64 { return (b[o] as i64) | ((b[o + 1] as i64) << 8) }
302func ds_sbyte(b: *u8, o: i64) -> i64
called by 1: ds_parse_prologue
311func ds_parse_prologue(lt: *DsLineTable, buf: *u8, len: i64) -> i64
472func ds_file_name(lt: *DsLineTable, idx: i64) -> *u8
481func ds_run_program(lt: *DsLineTable, buf: *u8, len: i64, measure_only: i64) -> i64
605func ds_table_build(buf: *u8, len: i64) -> *DsLineTable
625func ds_row_at(lt: *DsLineTable, target: i64) -> i64
655func ds_addr_for_line(lt: *DsLineTable, file: *u8, line: i64) -> i64
687func ds_ras_delta(inst: i64) -> i64
called by 1: ds_resume calls 1: ds_is_link_reg
749func ds_code_sum(dbg: *DsDbg) -> i64
called by 2: dbg_attachmain
760func ds_bp_ok(dbg: *DsDbg, addr: i64) -> i64
called by 2: ds_bp_testds_bp_set
765func ds_bp_test(dbg: *DsDbg, addr: i64) -> i64
called by 2: ds_bp_setds_resume calls 1: ds_bp_ok
773func ds_bp_set(dbg: *DsDbg, addr: i64) -> i64
called by 1: main calls 2: ds_bp_okds_bp_test
784func ds_fetch(dbg: *DsDbg, pc: i64) -> i64
called by 1: ds_resume
795func dbg_attach(sim: *NxRv64imSim, lt: *DsLineTable, img_len: i64, budget: i64, budget_src: *u8) -> *DsDbg
called by 1: main calls 2: sys_mmapds_code_sum
822func ds_resume(dbg: *DsDbg, mode: i64) -> i64
876func dbg_continue(dbg: *DsDbg) -> i64 { return ds_resume(dbg, DS_MODE_CONTINUE) }
called by 1: main calls 1: ds_resume
877func dbg_step_into(dbg: *DsDbg) -> i64 { return ds_resume(dbg, DS_MODE_INTO) }
called by 1: main calls 1: ds_resume
881func dbg_step_over(dbg: *DsDbg) -> i64 { return ds_resume(dbg, DS_MODE_OVER) }
called by 1: main calls 1: ds_resume
886func dbg_detach(dbg: *DsDbg) -> i64
called by 1: main
894func ds_func_at(elf: *u8, elen: i64, addr: i64, out: *u8) -> i64
910func ds_find_section(buf: *u8, len: i64, want: *u8, outbox: *i64) -> i64
932func ds_report_table(lt: *DsLineTable) -> i64
called by 1: main calls 2: ds_putsds_num
946func ds_dump_rows(lt: *DsLineTable) -> i64
960func ds_report_stop(dbg: *DsDbg, what: *u8) -> i64
983func dbg_backtrace(dbg: *DsDbg) -> i64
1005func ds_load_table_raw(path: *u8) -> *DsLineTable
1019func ds_load_table_elf(path: *u8) -> *DsLineTable
1040func ds_do_resolve(lt: *DsLineTable, file: *u8, lines: *u8) -> i64
1069func ds_selftest() -> i64
called by 1: main calls 1: ds_puts
1077func ds_usage() -> i64
called by 1: main calls 1: ds_puts
1090func main(argc: i64, argv: *i64) -> i64