Simplify TUI disassembly
[deliverable/binutils-gdb.git] / gdb / tui / tui-disasm.c
1 /* Disassembly display.
2
3 Copyright (C) 1998-2019 Free Software Foundation, Inc.
4
5 Contributed by Hewlett-Packard Company.
6
7 This file is part of GDB.
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>. */
21
22 #include "defs.h"
23 #include "arch-utils.h"
24 #include "symtab.h"
25 #include "breakpoint.h"
26 #include "frame.h"
27 #include "value.h"
28 #include "source.h"
29 #include "disasm.h"
30 #include "tui/tui.h"
31 #include "tui/tui-data.h"
32 #include "tui/tui-win.h"
33 #include "tui/tui-layout.h"
34 #include "tui/tui-winsource.h"
35 #include "tui/tui-stack.h"
36 #include "tui/tui-file.h"
37 #include "tui/tui-disasm.h"
38 #include "tui/tui-source.h"
39 #include "progspace.h"
40 #include "objfiles.h"
41
42 #include "gdb_curses.h"
43
44 struct tui_asm_line
45 {
46 CORE_ADDR addr;
47 std::string addr_string;
48 std::string insn;
49 };
50
51 /* Function to set the disassembly window's content.
52 Disassemble count lines starting at pc.
53 Return address of the count'th instruction after pc. */
54 static CORE_ADDR
55 tui_disassemble (struct gdbarch *gdbarch,
56 std::vector<tui_asm_line> &asm_lines,
57 CORE_ADDR pc, int pos, int count)
58 {
59 string_file gdb_dis_out;
60
61 /* Now construct each line. */
62 for (int i = 0; i < count; ++i)
63 {
64 print_address (gdbarch, pc, &gdb_dis_out);
65 asm_lines[pos + i].addr = pc;
66 asm_lines[pos + i].addr_string = std::move (gdb_dis_out.string ());
67
68 gdb_dis_out.clear ();
69
70 pc = pc + gdb_print_insn (gdbarch, pc, &gdb_dis_out, NULL);
71
72 asm_lines[pos + i].insn = std::move (gdb_dis_out.string ());
73
74 /* Reset the buffer to empty. */
75 gdb_dis_out.clear ();
76 }
77 return pc;
78 }
79
80 /* Find the disassembly address that corresponds to FROM lines above
81 or below the PC. Variable sized instructions are taken into
82 account by the algorithm. */
83 static CORE_ADDR
84 tui_find_disassembly_address (struct gdbarch *gdbarch, CORE_ADDR pc, int from)
85 {
86 CORE_ADDR new_low;
87 int max_lines;
88
89 max_lines = (from > 0) ? from : - from;
90 if (max_lines <= 1)
91 return pc;
92
93 std::vector<tui_asm_line> asm_lines (max_lines);
94
95 new_low = pc;
96 if (from > 0)
97 {
98 tui_disassemble (gdbarch, asm_lines, pc, 0, max_lines);
99 new_low = asm_lines[max_lines - 1].addr;
100 }
101 else
102 {
103 CORE_ADDR last_addr;
104 int pos;
105 struct bound_minimal_symbol msymbol;
106
107 /* Find backward an address which is a symbol and for which
108 disassembling from that address will fill completely the
109 window. */
110 pos = max_lines - 1;
111 do {
112 new_low -= 1 * max_lines;
113 msymbol = lookup_minimal_symbol_by_pc_section (new_low, 0);
114
115 if (msymbol.minsym)
116 new_low = BMSYMBOL_VALUE_ADDRESS (msymbol);
117 else
118 new_low += 1 * max_lines;
119
120 tui_disassemble (gdbarch, asm_lines, new_low, 0, max_lines);
121 last_addr = asm_lines[pos].addr;
122 } while (last_addr > pc && msymbol.minsym);
123
124 /* Scan forward disassembling one instruction at a time until
125 the last visible instruction of the window matches the pc.
126 We keep the disassembled instructions in the 'lines' window
127 and shift it downward (increasing its addresses). */
128 if (last_addr < pc)
129 do
130 {
131 CORE_ADDR next_addr;
132
133 pos++;
134 if (pos >= max_lines)
135 pos = 0;
136
137 next_addr = tui_disassemble (gdbarch, asm_lines,
138 last_addr, pos, 1);
139
140 /* If there are some problems while disassembling exit. */
141 if (next_addr <= last_addr)
142 break;
143 last_addr = next_addr;
144 } while (last_addr <= pc);
145 pos++;
146 if (pos >= max_lines)
147 pos = 0;
148 new_low = asm_lines[pos].addr;
149 }
150 return new_low;
151 }
152
153 /* Function to set the disassembly window's content. */
154 enum tui_status
155 tui_disasm_window::set_contents (struct gdbarch *arch,
156 struct symtab *s,
157 struct tui_line_or_address line_or_addr)
158 {
159 int i;
160 int offset = horizontal_offset;
161 int max_lines, line_width;
162 CORE_ADDR cur_pc;
163 struct tui_locator_window *locator = tui_locator_win_info_ptr ();
164 int tab_len = tui_tab_width;
165 int insn_pos;
166 int addr_size, insn_size;
167
168 gdb_assert (line_or_addr.loa == LOA_ADDRESS);
169 CORE_ADDR pc = line_or_addr.u.addr;
170 if (pc == 0)
171 return TUI_FAILURE;
172
173 gdbarch = arch;
174 start_line_or_addr.loa = LOA_ADDRESS;
175 start_line_or_addr.u.addr = pc;
176 cur_pc = locator->addr;
177
178 /* Window size, excluding highlight box. */
179 max_lines = height - 2;
180 line_width = width - TUI_EXECINFO_SIZE - 2;
181
182 /* Get temporary table that will hold all strings (addr & insn). */
183 std::vector<tui_asm_line> asm_lines (max_lines);
184
185 tui_disassemble (gdbarch, asm_lines, pc, 0, max_lines);
186
187 /* Determine maximum address- and instruction lengths. */
188 addr_size = 0;
189 insn_size = 0;
190 for (i = 0; i < max_lines; i++)
191 {
192 size_t len = asm_lines[i].addr_string.size ();
193
194 if (len > addr_size)
195 addr_size = len;
196
197 len = asm_lines[i].insn.size ();
198 if (len > insn_size)
199 insn_size = len;
200 }
201
202 /* Align instructions to the same column. */
203 insn_pos = (1 + (addr_size / tab_len)) * tab_len;
204
205 /* Now construct each line. */
206 content.resize (max_lines);
207 for (i = 0; i < max_lines; i++)
208 {
209 tui_source_element *src = &content[i];
210
211 std::string line
212 = (asm_lines[i].addr_string
213 + n_spaces (insn_pos
214 - asm_lines[i].addr_string.size ())
215 + asm_lines[i].insn);
216
217 /* Now copy the line taking the offset into account. */
218 if (line.size() > offset)
219 src->line.reset (xstrndup (&line[offset], line_width));
220 else
221 src->line.reset (xstrdup (""));
222
223 src->line_or_addr.loa = LOA_ADDRESS;
224 src->line_or_addr.u.addr = asm_lines[i].addr;
225 src->is_exec_point = asm_lines[i].addr == cur_pc;
226 }
227 return TUI_SUCCESS;
228 }
229
230
231 /* Function to display the disassembly window with disassembled code. */
232 void
233 tui_show_disassem (struct gdbarch *gdbarch, CORE_ADDR start_addr)
234 {
235 struct symtab *s = find_pc_line_symtab (start_addr);
236 struct tui_win_info *win_with_focus = tui_win_with_focus ();
237 struct tui_line_or_address val;
238
239 gdb_assert (TUI_DISASM_WIN != nullptr && TUI_DISASM_WIN->is_visible ());
240
241 val.loa = LOA_ADDRESS;
242 val.u.addr = start_addr;
243 TUI_DISASM_WIN->update_source_window (gdbarch, s, val);
244
245 /* If the focus was in the src win, put it in the asm win, if the
246 source view isn't split. */
247 if (tui_current_layout () != SRC_DISASSEM_COMMAND
248 && win_with_focus == TUI_SRC_WIN)
249 tui_set_win_focus_to (TUI_DISASM_WIN);
250 }
251
252
253 /* Function to display the disassembly window. */
254 void
255 tui_show_disassem_and_update_source (struct gdbarch *gdbarch,
256 CORE_ADDR start_addr)
257 {
258 struct symtab_and_line sal;
259
260 tui_show_disassem (gdbarch, start_addr);
261 if (tui_current_layout () == SRC_DISASSEM_COMMAND)
262 {
263 struct tui_line_or_address val;
264
265 /* Update what is in the source window if it is displayed too,
266 note that it follows what is in the disassembly window and
267 visa-versa. */
268 sal = find_pc_line (start_addr, 0);
269 val.loa = LOA_LINE;
270 val.u.line_no = sal.line;
271 TUI_SRC_WIN->update_source_window (gdbarch, sal.symtab, val);
272 if (sal.symtab)
273 {
274 set_current_source_symtab_and_line (sal);
275 tui_update_locator_fullname (symtab_to_fullname (sal.symtab));
276 }
277 else
278 tui_update_locator_fullname ("?");
279 }
280 }
281
282 void
283 tui_get_begin_asm_address (struct gdbarch **gdbarch_p, CORE_ADDR *addr_p)
284 {
285 struct tui_locator_window *locator;
286 struct gdbarch *gdbarch = get_current_arch ();
287 CORE_ADDR addr;
288
289 locator = tui_locator_win_info_ptr ();
290
291 if (locator->addr == 0)
292 {
293 struct bound_minimal_symbol main_symbol;
294
295 /* Find address of the start of program.
296 Note: this should be language specific. */
297 main_symbol = lookup_minimal_symbol ("main", NULL, NULL);
298 if (main_symbol.minsym == 0)
299 main_symbol = lookup_minimal_symbol ("MAIN", NULL, NULL);
300 if (main_symbol.minsym == 0)
301 main_symbol = lookup_minimal_symbol ("_start", NULL, NULL);
302 if (main_symbol.minsym)
303 addr = BMSYMBOL_VALUE_ADDRESS (main_symbol);
304 else
305 addr = 0;
306 }
307 else /* The target is executing. */
308 {
309 gdbarch = locator->gdbarch;
310 addr = locator->addr;
311 }
312
313 *gdbarch_p = gdbarch;
314 *addr_p = addr;
315 }
316
317 /* Determine what the low address will be to display in the TUI's
318 disassembly window. This may or may not be the same as the low
319 address input. */
320 CORE_ADDR
321 tui_get_low_disassembly_address (struct gdbarch *gdbarch,
322 CORE_ADDR low, CORE_ADDR pc)
323 {
324 int pos;
325
326 /* Determine where to start the disassembly so that the pc is about
327 in the middle of the viewport. */
328 pos = tui_default_win_viewport_height (DISASSEM_WIN, DISASSEM_COMMAND) / 2;
329 pc = tui_find_disassembly_address (gdbarch, pc, -pos);
330
331 if (pc < low)
332 pc = low;
333 return pc;
334 }
335
336 /* Scroll the disassembly forward or backward vertically. */
337 void
338 tui_disasm_window::do_scroll_vertical (int num_to_scroll)
339 {
340 if (!content.empty ())
341 {
342 CORE_ADDR pc;
343 struct tui_line_or_address val;
344
345 pc = content[0].line_or_addr.u.addr;
346 if (num_to_scroll >= 0)
347 num_to_scroll++;
348 else
349 --num_to_scroll;
350
351 val.loa = LOA_ADDRESS;
352 val.u.addr = tui_find_disassembly_address (gdbarch, pc, num_to_scroll);
353 update_source_window_as_is (gdbarch, NULL, val);
354 }
355 }
356
357 bool
358 tui_disasm_window::location_matches_p (struct bp_location *loc, int line_no)
359 {
360 return (content[line_no].line_or_addr.loa == LOA_ADDRESS
361 && content[line_no].line_or_addr.u.addr == loc->address);
362 }
363
364 bool
365 tui_disasm_window::addr_is_displayed (CORE_ADDR addr) const
366 {
367 bool is_displayed = false;
368 int threshold = SCROLL_THRESHOLD;
369
370 int i = 0;
371 while (i < content.size () - threshold && !is_displayed)
372 {
373 is_displayed
374 = (content[i].line_or_addr.loa == LOA_ADDRESS
375 && content[i].line_or_addr.u.addr == addr);
376 i++;
377 }
378
379 return is_displayed;
380 }
381
382 void
383 tui_disasm_window::maybe_update (struct frame_info *fi, symtab_and_line sal,
384 int line_no, CORE_ADDR addr)
385 {
386 CORE_ADDR low;
387
388 if (find_pc_partial_function (get_frame_pc (fi),
389 NULL, &low, NULL) == 0)
390 {
391 /* There is no symbol available for current PC. There is no
392 safe way how to "disassemble backwards". */
393 low = get_frame_pc (fi);
394 }
395 else
396 low = tui_get_low_disassembly_address (get_frame_arch (fi),
397 low, get_frame_pc (fi));
398
399 struct tui_line_or_address a;
400
401 a.loa = LOA_ADDRESS;
402 a.u.addr = low;
403 if (!addr_is_displayed (addr))
404 update_source_window (get_frame_arch (fi), sal.symtab, a);
405 else
406 {
407 a.u.addr = addr;
408 set_is_exec_point_at (a);
409 }
410 }
This page took 0.051434 seconds and 5 git commands to generate.