kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

step.c (9298B)


      1 /* Resume-mode state machine.
      2  *
      3  * STEP_INSN uses the displaced-step primitive directly via pending_pc_override
      4  * and the outer session_resume's signal/wait. STEP_LINE / NEXT_LINE / STEP_OUT
      5  * drive their own signal/wait cycles in-loop and set pending_done so the outer
      6  * resume short-circuits. */
      7 
      8 #include <string.h>
      9 
     10 #include "core/slice.h"
     11 #include "dbg/dbg.h"
     12 
     13 #define DBG_STEP_LINE_INSN_CAP 1024u
     14 
     15 static KitStatus run_step_line_loop(KitDebugSession* s);
     16 
     17 /* DWARF line/CFI tables are authored in image-relative vaddrs (kit's
     18  * debug emitter writes them, the JIT view applies relocs against final
     19  * image vaddrs).  Stop PCs and the values dropped onto the stack by
     20  * BL/RET, on the other hand, live in runtime address space.  Every
     21  * DWARF call from the session translates at the boundary. */
     22 static uint64_t step_rt_to_img(KitDebugSession* s, uint64_t pc) {
     23   uint64_t v = kit_jit_runtime_to_image(s->jit, pc);
     24   return v ? v : pc;
     25 }
     26 
     27 static KitStatus prepare_step_insn(KitDebugSession* s) {
     28   uint64_t pc = s->stop.regs.pc;
     29   uint64_t scratch_entry = 0;
     30   KitStatus st = dbg_displaced_prepare(s, pc, &scratch_entry);
     31   if (st != KIT_OK) return st;
     32   s->pending_has_pc = 1;
     33   s->pending_pc_override = scratch_entry;
     34   return KIT_OK;
     35 }
     36 
     37 /* Drive a single displaced-step cycle synchronously. Returns KIT_OK on
     38  * success; the session is parked again at the post-step PC. */
     39 static KitStatus do_one_displaced(KitDebugSession* s) {
     40   KitStatus st = prepare_step_insn(s);
     41   if (st != KIT_OK) return st;
     42   st = dbg_session_signal_resume(s);
     43   if (st != KIT_OK) return st;
     44   return dbg_session_wait_stop(s);
     45 }
     46 
     47 static int stop_is_internal_completion(const KitDebugSession* s) {
     48   return s->stop.kind == KIT_STOP_BREAKPOINT && s->stop.bp_id == 0;
     49 }
     50 
     51 /* Arm a one-shot internal bp at `target`, resume the worker, and wait for the
     52  * next stop. on_fault clears the internal bp only if it is the bp that traps;
     53  * if a user bp / signal / exit intervenes first the one-shot would linger and
     54  * later resurface as a spurious bp_id==0 stop, so clear it here on any return
     55  * path that is not its own completion. Mirrors the displaced-step sentinel
     56  * cleanup. */
     57 static KitStatus run_to_internal_bp(KitDebugSession* s, uint64_t target) {
     58   u32 bp_id = 0;
     59   KitStatus st = dbg_bp_set_internal(s, target, &bp_id);
     60   if (st != KIT_OK) return st;
     61   st = dbg_session_signal_resume(s);
     62   if (st != KIT_OK) {
     63     dbg_bp_clear(s, bp_id);
     64     return st;
     65   }
     66   st = dbg_session_wait_stop(s);
     67   if (st != KIT_OK) {
     68     dbg_bp_clear(s, bp_id);
     69     return st;
     70   }
     71   if (!stop_is_internal_completion(s)) {
     72     dbg_bp_clear(s, bp_id);
     73   }
     74   return KIT_OK;
     75 }
     76 
     77 static KitStatus direct_call_target(KitDebugSession* s, uint64_t* target) {
     78   ArchDbgInsn insn;
     79   if (!s->arch_dbg || !s->arch_dbg->direct_call_target) return KIT_NOT_FOUND;
     80   if (dbg_arch_decode_insn(s, s->stop.regs.pc, &insn) != KIT_OK)
     81     return KIT_NOT_FOUND;
     82   return s->arch_dbg->direct_call_target(&insn, target);
     83 }
     84 
     85 static KitStatus direct_jump_target(KitDebugSession* s, uint64_t* target) {
     86   ArchDbgInsn insn;
     87   if (!s->arch_dbg || !s->arch_dbg->direct_jump_target) return KIT_NOT_FOUND;
     88   if (dbg_arch_decode_insn(s, s->stop.regs.pc, &insn) != KIT_OK)
     89     return KIT_NOT_FOUND;
     90   return s->arch_dbg->direct_jump_target(&insn, target);
     91 }
     92 
     93 static KitStatus dwarf_line_for(KitDebugSession* s, uint64_t pc, KitSlice* file,
     94                                 uint32_t* line) {
     95   uint32_t col = 0;
     96   *file = KIT_SLICE_NULL;
     97   *line = 0;
     98   return kit_dwarf_addr_to_line(s->dwarf, step_rt_to_img(s, pc), file, line,
     99                                 &col);
    100 }
    101 
    102 static KitStatus dwarf_sub_for(KitDebugSession* s, uint64_t pc,
    103                                KitDwarfSubprogram* out) {
    104   memset(out, 0, sizeof(*out));
    105   return kit_dwarf_subprogram_at(s->dwarf, step_rt_to_img(s, pc), out);
    106 }
    107 
    108 static int line_changed(KitSlice base_file, uint32_t base_line,
    109                         KitSlice new_file, uint32_t new_line) {
    110   if (new_line == 0) return 0;
    111   if (base_line == 0) return 1;
    112   if (new_line != base_line) return 1;
    113   if (base_file.s != new_file.s) {
    114     if (!base_file.s || !new_file.s) return 1;
    115     if (!slice_eq(base_file, new_file)) return 1;
    116   }
    117   return 0;
    118 }
    119 
    120 static KitStatus try_step_into_direct_call(KitDebugSession* s, int* did) {
    121   uint64_t target = 0;
    122   KitStatus st;
    123   *did = 0;
    124   st = direct_call_target(s, &target);
    125   if (st == KIT_NOT_FOUND || st == KIT_UNSUPPORTED) return KIT_OK;
    126   if (st != KIT_OK) return st;
    127   *did = 1;
    128   st = run_to_internal_bp(s, target);
    129   if (st != KIT_OK) return st;
    130   if (stop_is_internal_completion(s)) {
    131     return run_step_line_loop(s);
    132   }
    133   return KIT_OK;
    134 }
    135 
    136 static KitStatus try_follow_direct_jump(KitDebugSession* s, int* did) {
    137   uint64_t target = 0;
    138   KitStatus st;
    139   *did = 0;
    140   st = direct_jump_target(s, &target);
    141   if (st == KIT_NOT_FOUND || st == KIT_UNSUPPORTED) return KIT_OK;
    142   if (st != KIT_OK) return st;
    143   *did = 1;
    144   return run_to_internal_bp(s, target);
    145 }
    146 
    147 static KitStatus run_step_line_loop(KitDebugSession* s) {
    148   KitSlice base_file = KIT_SLICE_NULL;
    149   uint32_t base_line = 0;
    150   KitDwarfSubprogram base_sub;
    151   int have_sub;
    152   u32 i;
    153 
    154   (void)dwarf_line_for(s, s->stop.regs.pc, &base_file, &base_line);
    155   have_sub = (dwarf_sub_for(s, s->stop.regs.pc, &base_sub) == KIT_OK);
    156 
    157   for (i = 0; i < DBG_STEP_LINE_INSN_CAP; ++i) {
    158     KitSlice new_file = KIT_SLICE_NULL;
    159     uint32_t new_line = 0;
    160     KitDwarfSubprogram new_sub;
    161     int have_new_sub;
    162     int did_call = 0;
    163     int did_jump = 0;
    164     KitStatus st;
    165 
    166     st = try_step_into_direct_call(s, &did_call);
    167     if (st != KIT_OK) return st;
    168     if (did_call) return KIT_OK;
    169 
    170     st = try_follow_direct_jump(s, &did_jump);
    171     if (st != KIT_OK) return st;
    172 
    173     if (!did_jump && do_one_displaced(s) != KIT_OK) {
    174       /* Lifter declined; surface whatever the current stop is. */
    175       return KIT_OK;
    176     }
    177     if (!stop_is_internal_completion(s)) {
    178       /* User breakpoint, signal, or exit — surface. */
    179       return KIT_OK;
    180     }
    181 
    182     (void)dwarf_line_for(s, s->stop.regs.pc, &new_file, &new_line);
    183     have_new_sub = (dwarf_sub_for(s, s->stop.regs.pc, &new_sub) == KIT_OK);
    184 
    185     if (have_sub && have_new_sub) {
    186       if (new_sub.low_pc != base_sub.low_pc) {
    187         /* Left the original subprogram — STEP_LINE follows in. */
    188         return KIT_OK;
    189       }
    190     }
    191 
    192     if (line_changed(base_file, base_line, new_file, new_line)) {
    193       return KIT_OK;
    194     }
    195   }
    196   return KIT_OK;
    197 }
    198 
    199 static KitStatus run_step_out(KitDebugSession* s) {
    200   KitUnwindFrame frame;
    201   KitStatus st;
    202   frame = s->stop.regs;
    203   frame.pc = step_rt_to_img(s, frame.pc); /* CFI lookup is in image space */
    204   st = kit_dwarf_unwind_step(s->dwarf, &frame);
    205   if (st != KIT_OK) {
    206     if (!s->arch_dbg || !s->arch_dbg->link_register_return_address ||
    207         s->arch_dbg->link_register_return_address(&s->stop.regs, &frame.pc) !=
    208             KIT_OK) {
    209       return st;
    210     }
    211   }
    212   /* On success unwind_step or the link-register fallback writes frame.pc from
    213    * the saved return-address register / stack slot — already a runtime PC, no
    214    * inverse translation needed before the internal bp install. */
    215   if (frame.pc == 0) return KIT_NOT_FOUND;
    216   return run_to_internal_bp(s, frame.pc);
    217 }
    218 
    219 static KitStatus run_next_line(KitDebugSession* s) {
    220   ArchDbgInsn insn;
    221 
    222   if (!s->arch_dbg || !s->arch_dbg->is_call ||
    223       dbg_arch_decode_insn(s, s->stop.regs.pc, &insn) != KIT_OK) {
    224     return run_step_line_loop(s);
    225   }
    226   if (!s->arch_dbg->is_call(&insn)) {
    227     return run_step_line_loop(s);
    228   }
    229 
    230   /* Step OVER the call by setting an internal bp at the unwound return PC
    231    * and CONTINUE-ing. After the bp fires, fall into the STEP_LINE loop to
    232    * keep advancing until the source line actually changes. */
    233   {
    234     KitUnwindFrame frame = s->stop.regs;
    235     KitStatus st;
    236     frame.pc = step_rt_to_img(s, frame.pc);
    237     if (kit_dwarf_unwind_step(s->dwarf, &frame) != KIT_OK || frame.pc == 0) {
    238       /* Fall back to stepping into the call. */
    239       return run_step_line_loop(s);
    240     }
    241     /* frame.pc is now a runtime return-address (from the stack). */
    242     st = run_to_internal_bp(s, frame.pc);
    243     if (st != KIT_OK) return st;
    244     if (stop_is_internal_completion(s)) {
    245       return run_step_line_loop(s);
    246     }
    247     return KIT_OK;
    248   }
    249 }
    250 
    251 KitStatus dbg_step_resume(KitDebugSession* s, KitResumeMode mode) {
    252   switch (mode) {
    253     case KIT_RESUME_ABORT:
    254       return KIT_OK;
    255     case KIT_RESUME_CONTINUE:
    256       if (dbg_bp_lookup_index(s, s->stop.regs.pc) != 0) {
    257         return prepare_step_insn(s);
    258       }
    259       return KIT_OK;
    260 
    261     case KIT_RESUME_STEP_INSN:
    262       return prepare_step_insn(s);
    263 
    264     case KIT_RESUME_STEP_LINE: {
    265       KitStatus st;
    266       if (!s->dwarf) return KIT_INVALID;
    267       st = run_step_line_loop(s);
    268       if (st != KIT_OK) return st;
    269       s->pending_done = 1;
    270       return KIT_OK;
    271     }
    272 
    273     case KIT_RESUME_NEXT_LINE: {
    274       KitStatus st;
    275       if (!s->dwarf) return KIT_INVALID;
    276       st = run_next_line(s);
    277       if (st != KIT_OK) return st;
    278       s->pending_done = 1;
    279       return KIT_OK;
    280     }
    281 
    282     case KIT_RESUME_STEP_OUT: {
    283       KitStatus st;
    284       if (!s->dwarf) return KIT_INVALID;
    285       st = run_step_out(s);
    286       if (st != KIT_OK) return st;
    287       s->pending_done = 1;
    288       return KIT_OK;
    289     }
    290   }
    291   return KIT_INVALID;
    292 }