https://github.com/akkartik/mu/blob/main/linux/bootstrap/039debug.cc

      1 //:: Some helpers for debugging.
      2
      3 //: Load the 'map' file generated during 'bootstrap --debug translate' when running
      4 //: 'bootstrap --trace run'.
      5 //: (It'll only affect the trace.)
      6
      7 :(before "End Globals")
      8 map</*address*/uint32_t, string> Symbol_name;  // used only by 'bootstrap run'
      9 map</*address*/uint32_t, string> Source_line;  // used only by 'bootstrap run'
     10 :(before "End --trace Settings")
     11 load_labels();
     12 load_source_lines();
     13 :(code)
     14 void load_labels() {
     15   ifstream fin("labels");
     16   if (fin.fail()) return;
     17   fin >> std::hex;
     18   while (has_data(fin)) {
     19     uint32_t addr = 0;
     20     fin >> addr;
     21     string name;
     22     fin >> name;
     23     put(Symbol_name, addr, name);
     24   }
     25 }
     26
     27 void load_source_lines() {
     28   ifstream fin("source_lines");
     29   if (fin.fail()) return;
     30   fin >> std::hex;
     31   while (has_data(fin)) {
     32     uint32_t addr = 0;
     33     fin >> addr;
     34     string line;
     35     getline(fin, line);
     36     put(Source_line, addr, hacky_squeeze_out_whitespace(line));
     37   }
     38 }
     39
     40 :(after "Run One Instruction")
     41 if (contains_key(Symbol_name, EIP))
     42   trace(Callstack_depth, "run") << "== label " << get(Symbol_name, EIP) << end();
     43 if (contains_key(Source_line, EIP))
     44   trace(Callstack_depth, "run") << "inst: " << get(Source_line, EIP) << end();
     45 else
     46   // no source line info; do what you can
     47   trace(Callstack_depth, "run") << "inst: " << debug_info(EIP) << end();
     48
     49 :(code)
     50 string debug_info(uint32_t inst_address) {
     51   uint8_t op = read_mem_u8(inst_address);
     52   if (op != 0xe8) {
     53     ostringstream out;
     54     out << HEXBYTE << NUM(op);
     55     return out.str();
     56   }
     57   int32_t offset = read_mem_i32(inst_address+/*skip op*/1);
     58   uint32_t next_eip = inst_address+/*inst length*/5+offset;
     59   if (contains_key(Symbol_name, next_eip))
     60     return "e8/call "+get(Symbol_name, next_eip);
     61   ostringstream out;
     62   out << "e8/call 0x" << HEXWORD << next_eip;
     63   return out.str();
     64 }
     65
     66 //: If a label starts with '$watch-', make a note of the effective address
     67 //: computed by the next instruction. Start dumping out its contents to the
     68 //: trace after every subsequent instruction.
     69
     70 :(after "Run One Instruction")
     71 dump_watch_points();
     72 :(before "End Globals")
     73 map<string, uint32_t> Watch_points;
     74 :(before "End Reset")
     75 Watch_points.clear();
     76 :(code)
     77 void dump_watch_points() {
     78   if (Watch_points.empty()) return;
     79   trace(Callstack_depth, "dbg") << "watch points:" << end();
     80   for (map<string, uint32_t>::iterator p = Watch_points.begin();  p != Watch_points.end();  ++p)
     81     trace(Callstack_depth, "dbg") << "  " << p->first << ": " << HEXWORD << p->second << " -> " << HEXWORD << read_mem_u32(p->second) << end();
     82 }
     83
     84 :(before "End Globals")
     85 string Watch_this_effective_address;
     86 :(after "Run One Instruction")
     87 Watch_this_effective_address = "";
     88 if (contains_key(Symbol_name, EIP) && starts_with(get(Symbol_name, EIP), "$watch-"))
     89   Watch_this_effective_address = get(Symbol_name, EIP);
     90 :(after "Found effective_address(addr)")
     91 if (!Watch_this_effective_address.empty()) {
     92   dbg << "now watching " << HEXWORD << addr << " for " << Watch_this_effective_address << end();
     93   put(Watch_points, Watch_this_effective_address, addr);
     94 }
     95
     96 //: If a label starts with '$dump-stack', dump out to the trace n bytes on
     97 //: either side of ESP.
     98
     99 :(after "Run One Instruction")
    100 if (contains_key(Symbol_name, EIP) && starts_with(get(Symbol_name, EIP), "$dump-stack")) {
    101   dump_stack(64);
    102 }
    103 :(code)
    104 void dump_stack(int n) {
    105   uint32_t stack_pointer = Reg[ESP].u;
    106   uint32_t start = ((stack_pointer-n)&0xfffffff0);
    107   dbg << "stack:" << end();
    108   for (uint32_t addr = start;  addr < start+n*2;  addr+=16) {
    109     if (addr >= AFTER_STACK) break;
    110     ostringstream out;
    111     out << HEXWORD << addr << ":";
    112     for (int i = 0;  i < 16;  i+=4) {
    113       out << ' ';
    114       out << ((addr+i == stack_pointer) ? '[' : ' ');
    115       out << HEXWORD << read_mem_u32(addr+i);
    116       out << ((addr+i == stack_pointer) ? ']' : ' ');
    117     }
    118     dbg << out.str() << end();
    119   }
    120 }
    121
    122 //: Special label that dumps regions of memory.
    123 //: Not a general mechanism; by the time you get here you're willing to hack
    124 //: on the emulator.
    125 :(after "Run One Instruction")
    126 if (contains_key(Symbol_name, EIP) && get(Symbol_name, EIP) == "$dump-stream-at-EAX")
    127   dump_stream_at(Reg[EAX].u);
    128 :(code)
    129 void dump_stream_at(uint32_t stream_start) {
    130   int32_t stream_length = read_mem_i32(stream_start + 8);
    131   dbg << "stream length: " << std::dec << stream_length << end();
    132   for (int i = 0;  i < stream_length + 12;  ++i)
    133     dbg << "0x" << HEXWORD << (stream_start+i) << ": " << HEXBYTE << NUM(read_mem_u8(stream_start+i)) << end();
    134 }
    135
    136 //: helpers
    137
    138 :(code)
    139 string hacky_squeeze_out_whitespace(const string& s) {
    140   // strip whitespace at start
    141   string::const_iterator first = s.begin();
    142   while (first != s.end() && isspace(*first))
    143     ++first;
    144   if (first == s.end()) return "";
    145
    146   // strip whitespace at end
    147   string::const_iterator last = --s.end();
    148   while (last != s.begin() && isspace(*last))
    149     --last;
    150   ++last;
    151
    152   // replace runs of spaces/dots with single space until comment or string
    153   // TODO:
    154   //   leave alone dots not surrounded by whitespace
    155   //   leave alone '#' within word
    156   //   leave alone '"' within word
    157   //   squeeze spaces after end of string
    158   ostringstream out;
    159   bool previous_was_space = false;
    160   bool in_comment_or_string = false;
    161   for (string::const_iterator curr = first;  curr != last;  ++curr) {
    162     if (in_comment_or_string)
    163       out << *curr;
    164     else if (isspace(*curr) || *curr == '.')
    165       previous_was_space = true;
    166     else {
    167       if (previous_was_space)
    168         out << ' ';
    169       out << *curr;
    170       previous_was_space = false;
    171       if (*curr == '#' || *curr == '"') in_comment_or_string = true;
    172     }
    173   }
    174   return out.str();
    175 }