return len;
}
-/* Function to set the disassembly window's content.
- Disassemble count lines starting at pc.
- Return address of the count'th instruction after pc. */
+/* Function to disassemble up to COUNT instructions starting from address
+ PC into the ASM_LINES vector (which will be emptied of any previous
+ contents). Return the address of the COUNT'th instruction after pc.
+ When ADDR_SIZE is non-null then place the maximum size of an address and
+ label into the value pointed to by ADDR_SIZE, and set the addr_size
+ field on each item in ASM_LINES, otherwise the addr_size fields within
+ ASM_LINES are undefined.
+
+ It is worth noting that ASM_LINES might not have COUNT entries when this
+ function returns. If the disassembly is truncated for some other
+ reason, for example, we hit invalid memory, then ASM_LINES can have
+ fewer entries than requested. */
static CORE_ADDR
tui_disassemble (struct gdbarch *gdbarch,
std::vector<tui_asm_line> &asm_lines,
- CORE_ADDR pc, int pos, int count,
+ CORE_ADDR pc, int count,
size_t *addr_size = nullptr)
{
bool term_out = source_styling && gdb_stdout->can_emit_style_escape ();
string_file gdb_dis_out (term_out);
+ /* Must start with an empty list. */
+ asm_lines.clear ();
+
/* Now construct each line. */
for (int i = 0; i < count; ++i)
{
- print_address (gdbarch, pc, &gdb_dis_out);
- asm_lines[pos + i].addr = pc;
- asm_lines[pos + i].addr_string = std::move (gdb_dis_out.string ());
+ tui_asm_line tal;
+ CORE_ADDR orig_pc = pc;
+ try
+ {
+ pc = pc + gdb_print_insn (gdbarch, pc, &gdb_dis_out, NULL);
+ }
+ catch (const gdb_exception_error &except)
+ {
+ /* If PC points to an invalid address then we'll catch a
+ MEMORY_ERROR here, this should stop the disassembly, but
+ otherwise is fine. */
+ if (except.error != MEMORY_ERROR)
+ throw;
+ return pc;
+ }
+
+ /* Capture the disassembled instruction. */
+ tal.insn = std::move (gdb_dis_out.string ());
+ gdb_dis_out.clear ();
+
+ /* And capture the address the instruction is at. */
+ tal.addr = orig_pc;
+ print_address (gdbarch, orig_pc, &gdb_dis_out);
+ tal.addr_string = std::move (gdb_dis_out.string ());
gdb_dis_out.clear ();
if (addr_size != nullptr)
size_t new_size;
if (term_out)
- new_size = len_without_escapes (asm_lines[pos + i].addr_string);
+ new_size = len_without_escapes (tal.addr_string);
else
- new_size = asm_lines[pos + i].addr_string.size ();
+ new_size = tal.addr_string.size ();
*addr_size = std::max (*addr_size, new_size);
- asm_lines[pos + i].addr_size = new_size;
+ tal.addr_size = new_size;
}
- pc = pc + gdb_print_insn (gdbarch, pc, &gdb_dis_out, NULL);
-
- asm_lines[pos + i].insn = std::move (gdb_dis_out.string ());
-
- /* Reset the buffer to empty. */
- gdb_dis_out.clear ();
+ asm_lines.push_back (std::move (tal));
}
return pc;
}
+/* Look backward from ADDR for an address from which we can start
+ disassembling, this needs to be something we can be reasonably
+ confident will fall on an instruction boundary. We use msymbol
+ addresses, or the start of a section. */
+
+static CORE_ADDR
+tui_find_backward_disassembly_start_address (CORE_ADDR addr)
+{
+ struct bound_minimal_symbol msym, msym_prev;
+
+ msym = lookup_minimal_symbol_by_pc_section (addr - 1, nullptr,
+ lookup_msym_prefer::TEXT,
+ &msym_prev);
+ if (msym.minsym != nullptr)
+ return BMSYMBOL_VALUE_ADDRESS (msym);
+ else if (msym_prev.minsym != nullptr)
+ return BMSYMBOL_VALUE_ADDRESS (msym_prev);
+
+ /* Find the section that ADDR is in, and look for the start of the
+ section. */
+ struct obj_section *section = find_pc_section (addr);
+ if (section != NULL)
+ return obj_section_addr (section);
+
+ return addr;
+}
+
/* Find the disassembly address that corresponds to FROM lines above
or below the PC. Variable sized instructions are taken into
account by the algorithm. */
int max_lines;
max_lines = (from > 0) ? from : - from;
- if (max_lines <= 1)
+ if (max_lines == 0)
return pc;
- std::vector<tui_asm_line> asm_lines (max_lines);
+ std::vector<tui_asm_line> asm_lines;
new_low = pc;
if (from > 0)
{
- tui_disassemble (gdbarch, asm_lines, pc, 0, max_lines);
- new_low = asm_lines[max_lines - 1].addr;
+ /* Always disassemble 1 extra instruction here, then if the last
+ instruction fails to disassemble we will take the address of the
+ previous instruction that did disassemble as the result. */
+ tui_disassemble (gdbarch, asm_lines, pc, max_lines + 1);
+ new_low = asm_lines.back ().addr;
}
else
{
+ /* In order to disassemble backwards we need to find a suitable
+ address to start disassembling from and then work forward until we
+ re-find the address we're currently at. We can then figure out
+ which address will be at the top of the TUI window after our
+ backward scroll. During our backward disassemble we need to be
+ able to distinguish between the case where the last address we
+ _can_ disassemble is ADDR, and the case where the disassembly
+ just happens to stop at ADDR, for this reason we increase
+ MAX_LINES by one. */
+ max_lines++;
+
+ /* When we disassemble a series of instructions this will hold the
+ address of the last instruction disassembled. */
CORE_ADDR last_addr;
- int pos;
- struct bound_minimal_symbol msymbol;
-
- /* Find backward an address which is a symbol and for which
- disassembling from that address will fill completely the
- window. */
- pos = max_lines - 1;
- do {
- new_low -= 1 * max_lines;
- msymbol = lookup_minimal_symbol_by_pc_section (new_low, 0);
-
- if (msymbol.minsym)
- new_low = BMSYMBOL_VALUE_ADDRESS (msymbol);
- else
- new_low += 1 * max_lines;
-
- tui_disassemble (gdbarch, asm_lines, new_low, 0, max_lines);
- last_addr = asm_lines[pos].addr;
- } while (last_addr > pc && msymbol.minsym);
+
+ /* And this will hold the address of the next instruction that would
+ have been disassembled. */
+ CORE_ADDR next_addr;
+
+ /* As we search backward if we find an address that looks like a
+ promising starting point then we record it in this structure. If
+ the next address we try is not a suitable starting point then we
+ will fall back to the address held here. */
+ gdb::optional<CORE_ADDR> possible_new_low;
+
+ /* The previous value of NEW_LOW so we know if the new value is
+ different or not. */
+ CORE_ADDR prev_low;
+
+ do
+ {
+ /* Find an address from which we can start disassembling. */
+ prev_low = new_low;
+ new_low = tui_find_backward_disassembly_start_address (new_low);
+
+ /* Disassemble forward. */
+ next_addr = tui_disassemble (gdbarch, asm_lines, new_low, max_lines);
+ last_addr = asm_lines.back ().addr;
+
+ /* If disassembling from the current value of NEW_LOW reached PC
+ (or went past it) then this would do as a starting point if we
+ can't find anything better, so remember it. */
+ if (last_addr >= pc && new_low != prev_low
+ && asm_lines.size () >= max_lines)
+ possible_new_low.emplace (new_low);
+
+ /* Continue searching until we find a value of NEW_LOW from which
+ disassembling MAX_LINES instructions doesn't reach PC. We
+ know this means we can find the required number of previous
+ instructions then. */
+ }
+ while ((last_addr > pc
+ || (last_addr == pc && asm_lines.size () < max_lines))
+ && new_low != prev_low);
+
+ /* If we failed to disassemble the required number of lines then the
+ following walk forward is not going to work, it assumes that
+ ASM_LINES contains exactly MAX_LINES entries. Instead we should
+ consider falling back to a previous possible start address in
+ POSSIBLE_NEW_LOW. */
+ if (asm_lines.size () < max_lines)
+ {
+ if (!possible_new_low.has_value ())
+ return pc;
+
+ /* Take the best possible match we have. */
+ new_low = *possible_new_low;
+ next_addr = tui_disassemble (gdbarch, asm_lines, new_low, max_lines);
+ last_addr = asm_lines.back ().addr;
+ gdb_assert (asm_lines.size () >= max_lines);
+ }
/* Scan forward disassembling one instruction at a time until
the last visible instruction of the window matches the pc.
We keep the disassembled instructions in the 'lines' window
and shift it downward (increasing its addresses). */
+ int pos = max_lines - 1;
if (last_addr < pc)
do
{
- CORE_ADDR next_addr;
-
pos++;
if (pos >= max_lines)
pos = 0;
- next_addr = tui_disassemble (gdbarch, asm_lines,
- last_addr, pos, 1);
-
+ CORE_ADDR old_next_addr = next_addr;
+ std::vector<tui_asm_line> single_asm_line;
+ next_addr = tui_disassemble (gdbarch, single_asm_line,
+ next_addr, 1);
/* If there are some problems while disassembling exit. */
- if (next_addr <= last_addr)
- break;
- last_addr = next_addr;
- } while (last_addr <= pc);
+ if (next_addr <= old_next_addr)
+ return pc;
+ gdb_assert (single_asm_line.size () == 1);
+ asm_lines[pos] = single_asm_line[0];
+ } while (next_addr <= pc);
pos++;
if (pos >= max_lines)
pos = 0;
new_low = asm_lines[pos].addr;
+
+ /* When scrolling backward the addresses should move backward, or at
+ the very least stay the same if we are at the first address that
+ can be disassembled. */
+ gdb_assert (new_low <= pc);
}
return new_low;
}
line_width = width - TUI_EXECINFO_SIZE - 2;
/* Get temporary table that will hold all strings (addr & insn). */
- std::vector<tui_asm_line> asm_lines (max_lines);
+ std::vector<tui_asm_line> asm_lines;
size_t addr_size = 0;
- tui_disassemble (gdbarch, asm_lines, pc, 0, max_lines, &addr_size);
+ tui_disassemble (gdbarch, asm_lines, pc, max_lines, &addr_size);
/* Align instructions to the same column. */
insn_pos = (1 + (addr_size / tab_len)) * tab_len;
{
tui_source_element *src = &content[i];
- std::string line
- = (asm_lines[i].addr_string
- + n_spaces (insn_pos - asm_lines[i].addr_size)
- + asm_lines[i].insn);
+ std::string line;
+ CORE_ADDR addr;
+
+ if (i < asm_lines.size ())
+ {
+ line
+ = (asm_lines[i].addr_string
+ + n_spaces (insn_pos - asm_lines[i].addr_size)
+ + asm_lines[i].insn);
+ addr = asm_lines[i].addr;
+ }
+ else
+ {
+ line = "";
+ addr = 0;
+ }
const char *ptr = line.c_str ();
src->line = tui_copy_source_line (&ptr, -1, offset, line_width, 0);
src->line_or_addr.loa = LOA_ADDRESS;
- src->line_or_addr.u.addr = asm_lines[i].addr;
- src->is_exec_point = asm_lines[i].addr == cur_pc;
+ src->line_or_addr.u.addr = addr;
+ src->is_exec_point = (addr == cur_pc && line.size () > 0);
}
return true;
}
CORE_ADDR pc;
pc = start_line_or_addr.u.addr;
- if (num_to_scroll >= 0)
- num_to_scroll++;
- else
- --num_to_scroll;
symtab_and_line sal {};
sal.pspace = current_program_space;