Introduce tui_source_window_base::set_contents method
[external/binutils.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   char *addr_string;
48   char *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, struct tui_asm_line *asm_lines,
56                  CORE_ADDR pc, int count)
57 {
58   string_file gdb_dis_out;
59
60   /* Now construct each line.  */
61   for (; count > 0; count--, asm_lines++)
62     {
63       xfree (asm_lines->addr_string);
64       xfree (asm_lines->insn);
65       
66       print_address (gdbarch, pc, &gdb_dis_out);
67       asm_lines->addr = pc;
68       asm_lines->addr_string = xstrdup (gdb_dis_out.c_str ());
69
70       gdb_dis_out.clear ();
71
72       pc = pc + gdb_print_insn (gdbarch, pc, &gdb_dis_out, NULL);
73
74       asm_lines->insn = xstrdup (gdb_dis_out.c_str ());
75
76       /* Reset the buffer to empty.  */
77       gdb_dis_out.clear ();
78     }
79   return pc;
80 }
81
82 /* Find the disassembly address that corresponds to FROM lines above
83    or below the PC.  Variable sized instructions are taken into
84    account by the algorithm.  */
85 static CORE_ADDR
86 tui_find_disassembly_address (struct gdbarch *gdbarch, CORE_ADDR pc, int from)
87 {
88   CORE_ADDR new_low;
89   int max_lines;
90   int i;
91   struct tui_asm_line *asm_lines;
92
93   max_lines = (from > 0) ? from : - from;
94   if (max_lines <= 1)
95      return pc;
96
97   asm_lines = XALLOCAVEC (struct tui_asm_line, max_lines);
98   memset (asm_lines, 0, sizeof (struct tui_asm_line) * max_lines);
99
100   new_low = pc;
101   if (from > 0)
102     {
103       tui_disassemble (gdbarch, asm_lines, pc, max_lines);
104       new_low = asm_lines[max_lines - 1].addr;
105     }
106   else
107     {
108       CORE_ADDR last_addr;
109       int pos;
110       struct bound_minimal_symbol msymbol;
111               
112       /* Find backward an address which is a symbol and for which
113          disassembling from that address will fill completely the
114          window.  */
115       pos = max_lines - 1;
116       do {
117          new_low -= 1 * max_lines;
118          msymbol = lookup_minimal_symbol_by_pc_section (new_low, 0);
119
120          if (msymbol.minsym)
121             new_low = BMSYMBOL_VALUE_ADDRESS (msymbol);
122          else
123             new_low += 1 * max_lines;
124
125          tui_disassemble (gdbarch, asm_lines, new_low, max_lines);
126          last_addr = asm_lines[pos].addr;
127       } while (last_addr > pc && msymbol.minsym);
128
129       /* Scan forward disassembling one instruction at a time until
130          the last visible instruction of the window matches the pc.
131          We keep the disassembled instructions in the 'lines' window
132          and shift it downward (increasing its addresses).  */
133       if (last_addr < pc)
134         do
135           {
136             CORE_ADDR next_addr;
137                  
138             pos++;
139             if (pos >= max_lines)
140               pos = 0;
141
142             next_addr = tui_disassemble (gdbarch, &asm_lines[pos],
143                                          last_addr, 1);
144
145             /* If there are some problems while disassembling exit.  */
146             if (next_addr <= last_addr)
147               break;
148             last_addr = next_addr;
149           } while (last_addr <= pc);
150       pos++;
151       if (pos >= max_lines)
152          pos = 0;
153       new_low = asm_lines[pos].addr;
154     }
155   for (i = 0; i < max_lines; i++)
156     {
157       xfree (asm_lines[i].addr_string);
158       xfree (asm_lines[i].insn);
159     }
160   return new_low;
161 }
162
163 /* Function to set the disassembly window's content.  */
164 enum tui_status
165 tui_disasm_window::set_contents (struct gdbarch *arch,
166                                  struct symtab *s,
167                                  struct tui_line_or_address line_or_addr)
168 {
169   int i;
170   int offset = horizontal_offset;
171   int max_lines, line_width;
172   CORE_ADDR cur_pc;
173   struct tui_locator_window *locator = tui_locator_win_info_ptr ();
174   int tab_len = tui_tab_width;
175   struct tui_asm_line *asm_lines;
176   int insn_pos;
177   int addr_size, insn_size;
178   char *line;
179   
180   gdb_assert (line_or_addr.loa == LOA_ADDRESS);
181   CORE_ADDR pc = line_or_addr.u.addr;
182   if (pc == 0)
183     return TUI_FAILURE;
184
185   gdbarch = arch;
186   start_line_or_addr.loa = LOA_ADDRESS;
187   start_line_or_addr.u.addr = pc;
188   cur_pc = locator->addr;
189
190   /* Window size, excluding highlight box.  */
191   max_lines = height - 2;
192   line_width = width - 2;
193
194   /* Get temporary table that will hold all strings (addr & insn).  */
195   asm_lines = XALLOCAVEC (struct tui_asm_line, max_lines);
196   memset (asm_lines, 0, sizeof (struct tui_asm_line) * max_lines);
197
198   tui_disassemble (gdbarch, asm_lines, pc, max_lines);
199
200   /* Determine maximum address- and instruction lengths.  */
201   addr_size = 0;
202   insn_size = 0;
203   for (i = 0; i < max_lines; i++)
204     {
205       size_t len = strlen (asm_lines[i].addr_string);
206
207       if (len > addr_size)
208         addr_size = len;
209
210       len = strlen (asm_lines[i].insn);
211       if (len > insn_size)
212         insn_size = len;
213     }
214
215   /* Align instructions to the same column.  */
216   insn_pos = (1 + (addr_size / tab_len)) * tab_len;
217
218   /* Allocate memory to create each line.  */
219   line = (char*) alloca (insn_pos + insn_size + 1);
220
221   /* Now construct each line.  */
222   content.resize (max_lines);
223   for (i = 0; i < max_lines; i++)
224     {
225       int cur_len;
226
227       tui_source_element *src = &content[i];
228       strcpy (line, asm_lines[i].addr_string);
229       cur_len = strlen (line);
230       memset (line + cur_len, ' ', insn_pos - cur_len);
231       strcpy (line + insn_pos, asm_lines[i].insn);
232
233       /* Now copy the line taking the offset into account.  */
234       xfree (src->line);
235       if (strlen (line) > offset)
236         src->line = xstrndup (&line[offset], line_width);
237       else
238         src->line = xstrdup ("");
239
240       src->line_or_addr.loa = LOA_ADDRESS;
241       src->line_or_addr.u.addr = asm_lines[i].addr;
242       src->is_exec_point = asm_lines[i].addr == cur_pc;
243
244       xfree (asm_lines[i].addr_string);
245       xfree (asm_lines[i].insn);
246     }
247   return TUI_SUCCESS;
248 }
249
250
251 /* Function to display the disassembly window with disassembled code.  */
252 void
253 tui_show_disassem (struct gdbarch *gdbarch, CORE_ADDR start_addr)
254 {
255   struct symtab *s = find_pc_line_symtab (start_addr);
256   struct tui_win_info *win_with_focus = tui_win_with_focus ();
257   struct tui_line_or_address val;
258
259   gdb_assert (TUI_DISASM_WIN != nullptr && TUI_DISASM_WIN->is_visible ());
260
261   val.loa = LOA_ADDRESS;
262   val.u.addr = start_addr;
263   TUI_DISASM_WIN->update_source_window (gdbarch, s, val);
264
265   /* If the focus was in the src win, put it in the asm win, if the
266      source view isn't split.  */
267   if (tui_current_layout () != SRC_DISASSEM_COMMAND 
268       && win_with_focus == TUI_SRC_WIN)
269     tui_set_win_focus_to (TUI_DISASM_WIN);
270 }
271
272
273 /* Function to display the disassembly window.  */
274 void
275 tui_show_disassem_and_update_source (struct gdbarch *gdbarch,
276                                      CORE_ADDR start_addr)
277 {
278   struct symtab_and_line sal;
279
280   tui_show_disassem (gdbarch, start_addr);
281   if (tui_current_layout () == SRC_DISASSEM_COMMAND)
282     {
283       struct tui_line_or_address val;
284
285       /* Update what is in the source window if it is displayed too,
286          note that it follows what is in the disassembly window and
287          visa-versa.  */
288       sal = find_pc_line (start_addr, 0);
289       val.loa = LOA_LINE;
290       val.u.line_no = sal.line;
291       TUI_SRC_WIN->update_source_window (gdbarch, sal.symtab, val);
292       if (sal.symtab)
293         {
294           set_current_source_symtab_and_line (sal);
295           tui_update_locator_fullname (symtab_to_fullname (sal.symtab));
296         }
297       else
298         tui_update_locator_fullname ("?");
299     }
300 }
301
302 void
303 tui_get_begin_asm_address (struct gdbarch **gdbarch_p, CORE_ADDR *addr_p)
304 {
305   struct tui_locator_window *locator;
306   struct gdbarch *gdbarch = get_current_arch ();
307   CORE_ADDR addr;
308
309   locator = tui_locator_win_info_ptr ();
310
311   if (locator->addr == 0)
312     {
313       struct bound_minimal_symbol main_symbol;
314
315       /* Find address of the start of program.
316          Note: this should be language specific.  */
317       main_symbol = lookup_minimal_symbol ("main", NULL, NULL);
318       if (main_symbol.minsym == 0)
319         main_symbol = lookup_minimal_symbol ("MAIN", NULL, NULL);
320       if (main_symbol.minsym == 0)
321         main_symbol = lookup_minimal_symbol ("_start", NULL, NULL);
322       if (main_symbol.minsym)
323         addr = BMSYMBOL_VALUE_ADDRESS (main_symbol);
324       else
325         addr = 0;
326     }
327   else                          /* The target is executing.  */
328     {
329       gdbarch = locator->gdbarch;
330       addr = locator->addr;
331     }
332
333   *gdbarch_p = gdbarch;
334   *addr_p = addr;
335 }
336
337 /* Determine what the low address will be to display in the TUI's
338    disassembly window.  This may or may not be the same as the low
339    address input.  */
340 CORE_ADDR
341 tui_get_low_disassembly_address (struct gdbarch *gdbarch,
342                                  CORE_ADDR low, CORE_ADDR pc)
343 {
344   int pos;
345
346   /* Determine where to start the disassembly so that the pc is about
347      in the middle of the viewport.  */
348   pos = tui_default_win_viewport_height (DISASSEM_WIN, DISASSEM_COMMAND) / 2;
349   pc = tui_find_disassembly_address (gdbarch, pc, -pos);
350
351   if (pc < low)
352     pc = low;
353   return pc;
354 }
355
356 /* Scroll the disassembly forward or backward vertically.  */
357 void
358 tui_disasm_window::do_scroll_vertical (int num_to_scroll)
359 {
360   if (!content.empty ())
361     {
362       CORE_ADDR pc;
363       struct tui_line_or_address val;
364
365       pc = content[0].line_or_addr.u.addr;
366       if (num_to_scroll >= 0)
367         num_to_scroll++;
368       else
369         --num_to_scroll;
370
371       val.loa = LOA_ADDRESS;
372       val.u.addr = tui_find_disassembly_address (gdbarch, pc, num_to_scroll);
373       update_source_window_as_is (gdbarch, NULL, val);
374     }
375 }
376
377 bool
378 tui_disasm_window::location_matches_p (struct bp_location *loc, int line_no)
379 {
380   return (content[line_no].line_or_addr.loa == LOA_ADDRESS
381           && content[line_no].line_or_addr.u.addr == loc->address);
382 }
383
384 bool
385 tui_disasm_window::addr_is_displayed (CORE_ADDR addr) const
386 {
387   bool is_displayed = false;
388   int threshold = SCROLL_THRESHOLD;
389
390   int i = 0;
391   while (i < content.size () - threshold && !is_displayed)
392     {
393       is_displayed
394         = (content[i].line_or_addr.loa == LOA_ADDRESS
395            && content[i].line_or_addr.u.addr == addr);
396       i++;
397     }
398
399   return is_displayed;
400 }
401
402 void
403 tui_disasm_window::maybe_update (struct frame_info *fi, symtab_and_line sal,
404                                  int line_no, CORE_ADDR addr)
405 {
406   CORE_ADDR low;
407
408   if (find_pc_partial_function (get_frame_pc (fi),
409                                 NULL, &low, NULL) == 0)
410     {
411       /* There is no symbol available for current PC.  There is no
412          safe way how to "disassemble backwards".  */
413       low = get_frame_pc (fi);
414     }
415   else
416     low = tui_get_low_disassembly_address (get_frame_arch (fi),
417                                            low, get_frame_pc (fi));
418
419   struct tui_line_or_address a;
420
421   a.loa = LOA_ADDRESS;
422   a.u.addr = low;
423   if (!addr_is_displayed (addr))
424     update_source_window (get_frame_arch (fi), sal.symtab, a);
425   else
426     {
427       a.u.addr = addr;
428       set_is_exec_point_at (a);
429     }
430 }