tui-winsource.c revision 1.10 1 1.1 christos /* TUI display source/assembly window.
2 1.1 christos
3 1.10 christos Copyright (C) 1998-2023 Free Software Foundation, Inc.
4 1.1 christos
5 1.1 christos Contributed by Hewlett-Packard Company.
6 1.1 christos
7 1.1 christos This file is part of GDB.
8 1.1 christos
9 1.1 christos This program is free software; you can redistribute it and/or modify
10 1.1 christos it under the terms of the GNU General Public License as published by
11 1.1 christos the Free Software Foundation; either version 3 of the License, or
12 1.1 christos (at your option) any later version.
13 1.1 christos
14 1.1 christos This program is distributed in the hope that it will be useful,
15 1.1 christos but WITHOUT ANY WARRANTY; without even the implied warranty of
16 1.1 christos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 1.1 christos GNU General Public License for more details.
18 1.1 christos
19 1.1 christos You should have received a copy of the GNU General Public License
20 1.1 christos along with this program. If not, see <http://www.gnu.org/licenses/>. */
21 1.1 christos
22 1.1 christos #include "defs.h"
23 1.1 christos #include <ctype.h>
24 1.1 christos #include "symtab.h"
25 1.1 christos #include "frame.h"
26 1.1 christos #include "breakpoint.h"
27 1.1 christos #include "value.h"
28 1.1 christos #include "source.h"
29 1.1 christos #include "objfiles.h"
30 1.1 christos #include "filenames.h"
31 1.10 christos #include "safe-ctype.h"
32 1.1 christos
33 1.1 christos #include "tui/tui.h"
34 1.1 christos #include "tui/tui-data.h"
35 1.8 christos #include "tui/tui-io.h"
36 1.1 christos #include "tui/tui-stack.h"
37 1.1 christos #include "tui/tui-win.h"
38 1.1 christos #include "tui/tui-wingeneral.h"
39 1.1 christos #include "tui/tui-winsource.h"
40 1.1 christos #include "tui/tui-source.h"
41 1.1 christos #include "tui/tui-disasm.h"
42 1.10 christos #include "tui/tui-location.h"
43 1.1 christos #include "gdb_curses.h"
44 1.1 christos
45 1.1 christos /* Function to display the "main" routine. */
46 1.1 christos void
47 1.9 christos tui_display_main ()
48 1.1 christos {
49 1.9 christos auto adapter = tui_source_windows ();
50 1.9 christos if (adapter.begin () != adapter.end ())
51 1.1 christos {
52 1.1 christos struct gdbarch *gdbarch;
53 1.1 christos CORE_ADDR addr;
54 1.1 christos
55 1.1 christos tui_get_begin_asm_address (&gdbarch, &addr);
56 1.1 christos if (addr != (CORE_ADDR) 0)
57 1.1 christos {
58 1.3 christos struct symtab *s;
59 1.1 christos
60 1.1 christos tui_update_source_windows_with_addr (gdbarch, addr);
61 1.3 christos s = find_pc_line_symtab (addr);
62 1.10 christos tui_location.set_location (s);
63 1.1 christos }
64 1.1 christos }
65 1.1 christos }
66 1.1 christos
67 1.9 christos /* See tui-winsource.h. */
68 1.1 christos
69 1.9 christos std::string
70 1.10 christos tui_copy_source_line (const char **ptr, int *length)
71 1.9 christos {
72 1.9 christos const char *lineptr = *ptr;
73 1.9 christos
74 1.9 christos /* Init the line with the line number. */
75 1.9 christos std::string result;
76 1.9 christos
77 1.9 christos int column = 0;
78 1.9 christos char c;
79 1.9 christos do
80 1.9 christos {
81 1.9 christos int skip_bytes;
82 1.9 christos
83 1.9 christos c = *lineptr;
84 1.9 christos if (c == '\033' && skip_ansi_escape (lineptr, &skip_bytes))
85 1.9 christos {
86 1.9 christos /* We always have to preserve escapes. */
87 1.9 christos result.append (lineptr, lineptr + skip_bytes);
88 1.9 christos lineptr += skip_bytes;
89 1.9 christos continue;
90 1.9 christos }
91 1.9 christos if (c == '\0')
92 1.9 christos break;
93 1.9 christos
94 1.9 christos ++lineptr;
95 1.9 christos ++column;
96 1.9 christos
97 1.9 christos auto process_tab = [&] ()
98 1.9 christos {
99 1.9 christos int max_tab_len = tui_tab_width;
100 1.9 christos
101 1.9 christos --column;
102 1.9 christos for (int j = column % max_tab_len;
103 1.10 christos j < max_tab_len;
104 1.9 christos column++, j++)
105 1.10 christos result.push_back (' ');
106 1.9 christos };
107 1.9 christos
108 1.9 christos if (c == '\n' || c == '\r' || c == '\0')
109 1.9 christos {
110 1.9 christos /* Nothing. */
111 1.9 christos }
112 1.10 christos else if (c == '\t')
113 1.10 christos process_tab ();
114 1.10 christos else if (ISCNTRL (c))
115 1.9 christos {
116 1.9 christos result.push_back ('^');
117 1.9 christos result.push_back (c + 0100);
118 1.10 christos ++column;
119 1.9 christos }
120 1.9 christos else if (c == 0177)
121 1.9 christos {
122 1.9 christos result.push_back ('^');
123 1.9 christos result.push_back ('?');
124 1.10 christos ++column;
125 1.9 christos }
126 1.9 christos else
127 1.9 christos result.push_back (c);
128 1.9 christos }
129 1.9 christos while (c != '\0' && c != '\n' && c != '\r');
130 1.9 christos
131 1.9 christos if (c == '\r' && *lineptr == '\n')
132 1.9 christos ++lineptr;
133 1.9 christos *ptr = lineptr;
134 1.9 christos
135 1.10 christos if (length != nullptr)
136 1.10 christos *length = column;
137 1.10 christos
138 1.9 christos return result;
139 1.9 christos }
140 1.9 christos
141 1.9 christos void
142 1.9 christos tui_source_window_base::style_changed ()
143 1.9 christos {
144 1.9 christos if (tui_active && is_visible ())
145 1.9 christos refill ();
146 1.9 christos }
147 1.1 christos
148 1.1 christos /* Function to display source in the source window. This function
149 1.1 christos initializes the horizontal scroll to 0. */
150 1.1 christos void
151 1.9 christos tui_source_window_base::update_source_window
152 1.9 christos (struct gdbarch *gdbarch,
153 1.9 christos const struct symtab_and_line &sal)
154 1.1 christos {
155 1.9 christos m_horizontal_offset = 0;
156 1.9 christos update_source_window_as_is (gdbarch, sal);
157 1.1 christos }
158 1.1 christos
159 1.1 christos
160 1.1 christos /* Function to display source in the source/asm window. This function
161 1.1 christos shows the source as specified by the horizontal offset. */
162 1.1 christos void
163 1.9 christos tui_source_window_base::update_source_window_as_is
164 1.9 christos (struct gdbarch *gdbarch,
165 1.9 christos const struct symtab_and_line &sal)
166 1.1 christos {
167 1.9 christos bool ret = set_contents (gdbarch, sal);
168 1.1 christos
169 1.9 christos if (!ret)
170 1.9 christos erase_source_content ();
171 1.1 christos else
172 1.1 christos {
173 1.9 christos update_breakpoint_info (nullptr, false);
174 1.9 christos show_source_content ();
175 1.9 christos update_exec_info ();
176 1.1 christos }
177 1.1 christos }
178 1.1 christos
179 1.1 christos
180 1.1 christos /* Function to ensure that the source and/or disassemly windows
181 1.1 christos reflect the input address. */
182 1.1 christos void
183 1.1 christos tui_update_source_windows_with_addr (struct gdbarch *gdbarch, CORE_ADDR addr)
184 1.1 christos {
185 1.9 christos struct symtab_and_line sal {};
186 1.1 christos if (addr != 0)
187 1.9 christos sal = find_pc_line (addr, 0);
188 1.1 christos
189 1.9 christos for (struct tui_source_window_base *win_info : tui_source_windows ())
190 1.9 christos win_info->update_source_window (gdbarch, sal);
191 1.1 christos }
192 1.1 christos
193 1.9 christos /* Function to ensure that the source and/or disassembly windows
194 1.9 christos reflect the symtab and line. */
195 1.1 christos void
196 1.9 christos tui_update_source_windows_with_line (struct symtab_and_line sal)
197 1.1 christos {
198 1.9 christos struct gdbarch *gdbarch = nullptr;
199 1.9 christos if (sal.symtab != nullptr)
200 1.9 christos {
201 1.9 christos find_line_pc (sal.symtab, sal.line, &sal.pc);
202 1.10 christos gdbarch = sal.symtab->compunit ()->objfile ()->arch ();
203 1.1 christos }
204 1.1 christos
205 1.9 christos for (struct tui_source_window_base *win_info : tui_source_windows ())
206 1.9 christos win_info->update_source_window (gdbarch, sal);
207 1.1 christos }
208 1.1 christos
209 1.1 christos void
210 1.9 christos tui_source_window_base::do_erase_source_content (const char *str)
211 1.1 christos {
212 1.9 christos int x_pos;
213 1.9 christos int half_width = (width - 2) / 2;
214 1.9 christos
215 1.9 christos m_content.clear ();
216 1.9 christos if (handle != NULL)
217 1.1 christos {
218 1.9 christos werase (handle.get ());
219 1.9 christos check_and_display_highlight_if_needed ();
220 1.1 christos
221 1.9 christos if (strlen (str) >= half_width)
222 1.9 christos x_pos = 1;
223 1.9 christos else
224 1.9 christos x_pos = half_width - strlen (str);
225 1.9 christos mvwaddstr (handle.get (),
226 1.9 christos (height / 2),
227 1.9 christos x_pos,
228 1.9 christos (char *) str);
229 1.1 christos
230 1.9 christos refresh_window ();
231 1.1 christos }
232 1.1 christos }
233 1.1 christos
234 1.1 christos
235 1.9 christos /* Redraw the complete line of a source or disassembly window. */
236 1.1 christos void
237 1.9 christos tui_source_window_base::show_source_line (int lineno)
238 1.1 christos {
239 1.9 christos struct tui_source_element *line;
240 1.9 christos
241 1.10 christos line = &m_content[lineno];
242 1.9 christos if (line->is_exec_point)
243 1.10 christos tui_set_reverse_mode (m_pad.get (), true);
244 1.9 christos
245 1.10 christos wmove (m_pad.get (), lineno, 0);
246 1.10 christos tui_puts (line->line.c_str (), m_pad.get ());
247 1.9 christos if (line->is_exec_point)
248 1.10 christos tui_set_reverse_mode (m_pad.get (), false);
249 1.10 christos }
250 1.10 christos
251 1.10 christos /* See tui-winsource.h. */
252 1.1 christos
253 1.10 christos void
254 1.10 christos tui_source_window_base::refresh_window ()
255 1.10 christos {
256 1.10 christos /* tui_win_info::refresh_window would draw the empty background window to
257 1.10 christos the screen, potentially creating a flicker. */
258 1.10 christos wnoutrefresh (handle.get ());
259 1.10 christos
260 1.10 christos int pad_width = std::max (m_max_length, width);
261 1.10 christos int left_margin = 1 + TUI_EXECINFO_SIZE + extra_margin ();
262 1.10 christos int view_width = width - left_margin - 1;
263 1.10 christos int pad_x = std::min (pad_width - view_width, m_horizontal_offset);
264 1.10 christos /* Ensure that an equal number of scrolls will work if the user
265 1.10 christos scrolled beyond where we clip. */
266 1.10 christos m_horizontal_offset = pad_x;
267 1.10 christos prefresh (m_pad.get (), 0, pad_x, y + 1, x + left_margin,
268 1.10 christos y + m_content.size (), x + left_margin + view_width - 1);
269 1.9 christos }
270 1.9 christos
271 1.9 christos void
272 1.9 christos tui_source_window_base::show_source_content ()
273 1.9 christos {
274 1.9 christos gdb_assert (!m_content.empty ());
275 1.1 christos
276 1.10 christos check_and_display_highlight_if_needed ();
277 1.10 christos
278 1.10 christos int pad_width = std::max (m_max_length, width);
279 1.10 christos if (m_pad == nullptr || pad_width > getmaxx (m_pad.get ())
280 1.10 christos || m_content.size () > getmaxy (m_pad.get ()))
281 1.10 christos m_pad.reset (newpad (m_content.size (), pad_width));
282 1.10 christos
283 1.10 christos werase (m_pad.get ());
284 1.10 christos for (int lineno = 0; lineno < m_content.size (); lineno++)
285 1.9 christos show_source_line (lineno);
286 1.1 christos
287 1.9 christos refresh_window ();
288 1.1 christos }
289 1.1 christos
290 1.9 christos tui_source_window_base::tui_source_window_base ()
291 1.9 christos {
292 1.9 christos m_start_line_or_addr.loa = LOA_ADDRESS;
293 1.9 christos m_start_line_or_addr.u.addr = 0;
294 1.9 christos
295 1.10 christos gdb::observers::styling_changed.attach
296 1.9 christos (std::bind (&tui_source_window::style_changed, this),
297 1.10 christos m_observable, "tui-winsource");
298 1.9 christos }
299 1.1 christos
300 1.9 christos tui_source_window_base::~tui_source_window_base ()
301 1.1 christos {
302 1.10 christos gdb::observers::styling_changed.detach (m_observable);
303 1.9 christos }
304 1.1 christos
305 1.9 christos /* See tui-data.h. */
306 1.1 christos
307 1.9 christos void
308 1.9 christos tui_source_window_base::update_tab_width ()
309 1.9 christos {
310 1.9 christos werase (handle.get ());
311 1.9 christos rerender ();
312 1.1 christos }
313 1.1 christos
314 1.1 christos void
315 1.9 christos tui_source_window_base::rerender ()
316 1.1 christos {
317 1.9 christos if (!m_content.empty ())
318 1.1 christos {
319 1.9 christos struct symtab_and_line cursal
320 1.9 christos = get_current_source_symtab_and_line ();
321 1.1 christos
322 1.9 christos if (m_start_line_or_addr.loa == LOA_LINE)
323 1.9 christos cursal.line = m_start_line_or_addr.u.line_no;
324 1.9 christos else
325 1.9 christos cursal.pc = m_start_line_or_addr.u.addr;
326 1.9 christos update_source_window (m_gdbarch, cursal);
327 1.9 christos }
328 1.9 christos else if (deprecated_safe_get_selected_frame () != NULL)
329 1.9 christos {
330 1.9 christos struct symtab_and_line cursal
331 1.9 christos = get_current_source_symtab_and_line ();
332 1.10 christos frame_info_ptr frame = deprecated_safe_get_selected_frame ();
333 1.9 christos struct gdbarch *gdbarch = get_frame_arch (frame);
334 1.9 christos
335 1.9 christos struct symtab *s = find_pc_line_symtab (get_frame_pc (frame));
336 1.9 christos if (this != TUI_SRC_WIN)
337 1.9 christos find_line_pc (s, cursal.line, &cursal.pc);
338 1.9 christos update_source_window (gdbarch, cursal);
339 1.1 christos }
340 1.1 christos else
341 1.9 christos erase_source_content ();
342 1.1 christos }
343 1.1 christos
344 1.9 christos /* See tui-data.h. */
345 1.8 christos
346 1.8 christos void
347 1.9 christos tui_source_window_base::refill ()
348 1.8 christos {
349 1.9 christos symtab_and_line sal {};
350 1.8 christos
351 1.9 christos if (this == TUI_SRC_WIN)
352 1.8 christos {
353 1.9 christos sal = get_current_source_symtab_and_line ();
354 1.9 christos if (sal.symtab == NULL)
355 1.9 christos {
356 1.10 christos frame_info_ptr fi = deprecated_safe_get_selected_frame ();
357 1.9 christos if (fi != nullptr)
358 1.9 christos sal = find_pc_line (get_frame_pc (fi), 0);
359 1.9 christos }
360 1.8 christos }
361 1.8 christos
362 1.9 christos if (sal.pspace == nullptr)
363 1.9 christos sal.pspace = current_program_space;
364 1.9 christos
365 1.9 christos if (m_start_line_or_addr.loa == LOA_LINE)
366 1.9 christos sal.line = m_start_line_or_addr.u.line_no;
367 1.9 christos else
368 1.9 christos sal.pc = m_start_line_or_addr.u.addr;
369 1.9 christos
370 1.9 christos update_source_window_as_is (m_gdbarch, sal);
371 1.8 christos }
372 1.1 christos
373 1.1 christos /* Scroll the source forward or backward horizontally. */
374 1.8 christos
375 1.1 christos void
376 1.9 christos tui_source_window_base::do_scroll_horizontal (int num_to_scroll)
377 1.1 christos {
378 1.9 christos if (!m_content.empty ())
379 1.1 christos {
380 1.9 christos int offset = m_horizontal_offset + num_to_scroll;
381 1.9 christos if (offset < 0)
382 1.9 christos offset = 0;
383 1.9 christos m_horizontal_offset = offset;
384 1.10 christos refresh_window ();
385 1.1 christos }
386 1.1 christos }
387 1.1 christos
388 1.1 christos
389 1.9 christos /* Set or clear the is_exec_point flag in the line whose line is
390 1.1 christos line_no. */
391 1.1 christos
392 1.1 christos void
393 1.9 christos tui_source_window_base::set_is_exec_point_at (struct tui_line_or_address l)
394 1.1 christos {
395 1.9 christos bool changed = false;
396 1.1 christos int i;
397 1.1 christos
398 1.1 christos i = 0;
399 1.9 christos while (i < m_content.size ())
400 1.1 christos {
401 1.9 christos bool new_state;
402 1.1 christos struct tui_line_or_address content_loa =
403 1.9 christos m_content[i].line_or_addr;
404 1.1 christos
405 1.1 christos if (content_loa.loa == l.loa
406 1.1 christos && ((l.loa == LOA_LINE && content_loa.u.line_no == l.u.line_no)
407 1.10 christos || (l.loa == LOA_ADDRESS && content_loa.u.addr == l.u.addr)))
408 1.10 christos new_state = true;
409 1.1 christos else
410 1.9 christos new_state = false;
411 1.9 christos if (new_state != m_content[i].is_exec_point)
412 1.10 christos {
413 1.10 christos changed = true;
414 1.10 christos m_content[i].is_exec_point = new_state;
415 1.10 christos }
416 1.1 christos i++;
417 1.1 christos }
418 1.1 christos if (changed)
419 1.9 christos refill ();
420 1.1 christos }
421 1.1 christos
422 1.9 christos /* See tui-winsource.h. */
423 1.9 christos
424 1.1 christos void
425 1.9 christos tui_update_all_breakpoint_info (struct breakpoint *being_deleted)
426 1.1 christos {
427 1.9 christos for (tui_source_window_base *win : tui_source_windows ())
428 1.1 christos {
429 1.9 christos if (win->update_breakpoint_info (being_deleted, false))
430 1.9 christos win->update_exec_info ();
431 1.1 christos }
432 1.1 christos }
433 1.1 christos
434 1.1 christos
435 1.9 christos /* Scan the source window and the breakpoints to update the break_mode
436 1.1 christos information for each line.
437 1.1 christos
438 1.9 christos Returns true if something changed and the execution window must be
439 1.1 christos refreshed. */
440 1.1 christos
441 1.9 christos bool
442 1.9 christos tui_source_window_base::update_breakpoint_info
443 1.9 christos (struct breakpoint *being_deleted, bool current_only)
444 1.1 christos {
445 1.1 christos int i;
446 1.9 christos bool need_refresh = false;
447 1.1 christos
448 1.9 christos for (i = 0; i < m_content.size (); i++)
449 1.1 christos {
450 1.1 christos struct tui_source_element *line;
451 1.1 christos
452 1.9 christos line = &m_content[i];
453 1.1 christos if (current_only && !line->is_exec_point)
454 1.10 christos continue;
455 1.1 christos
456 1.1 christos /* Scan each breakpoint to see if the current line has something to
457 1.10 christos do with it. Identify enable/disabled breakpoints as well as
458 1.10 christos those that we already hit. */
459 1.9 christos tui_bp_flags mode = 0;
460 1.10 christos for (breakpoint *bp : all_breakpoints ())
461 1.10 christos {
462 1.9 christos if (bp == being_deleted)
463 1.10 christos continue;
464 1.1 christos
465 1.10 christos for (bp_location *loc : bp->locations ())
466 1.1 christos {
467 1.9 christos if (location_matches_p (loc, i))
468 1.1 christos {
469 1.1 christos if (bp->enable_state == bp_disabled)
470 1.1 christos mode |= TUI_BP_DISABLED;
471 1.1 christos else
472 1.1 christos mode |= TUI_BP_ENABLED;
473 1.1 christos if (bp->hit_count)
474 1.1 christos mode |= TUI_BP_HIT;
475 1.1 christos if (bp->loc->cond)
476 1.1 christos mode |= TUI_BP_CONDITIONAL;
477 1.1 christos if (bp->type == bp_hardware_breakpoint)
478 1.1 christos mode |= TUI_BP_HARDWARE;
479 1.1 christos }
480 1.1 christos }
481 1.10 christos }
482 1.10 christos
483 1.9 christos if (line->break_mode != mode)
484 1.10 christos {
485 1.10 christos line->break_mode = mode;
486 1.10 christos need_refresh = true;
487 1.10 christos }
488 1.1 christos }
489 1.1 christos return need_refresh;
490 1.1 christos }
491 1.1 christos
492 1.1 christos /* Function to initialize the content of the execution info window,
493 1.1 christos based upon the input window which is either the source or
494 1.1 christos disassembly window. */
495 1.1 christos void
496 1.9 christos tui_source_window_base::update_exec_info ()
497 1.1 christos {
498 1.9 christos update_breakpoint_info (nullptr, true);
499 1.9 christos for (int i = 0; i < m_content.size (); i++)
500 1.1 christos {
501 1.9 christos struct tui_source_element *src_element = &m_content[i];
502 1.9 christos char element[TUI_EXECINFO_SIZE] = " ";
503 1.1 christos
504 1.9 christos /* Now update the exec info content based upon the state
505 1.9 christos of each line as indicated by the source content. */
506 1.9 christos tui_bp_flags mode = src_element->break_mode;
507 1.9 christos if (mode & TUI_BP_HIT)
508 1.9 christos element[TUI_BP_HIT_POS] = (mode & TUI_BP_HARDWARE) ? 'H' : 'B';
509 1.9 christos else if (mode & (TUI_BP_ENABLED | TUI_BP_DISABLED))
510 1.9 christos element[TUI_BP_HIT_POS] = (mode & TUI_BP_HARDWARE) ? 'h' : 'b';
511 1.1 christos
512 1.9 christos if (mode & TUI_BP_ENABLED)
513 1.9 christos element[TUI_BP_BREAK_POS] = '+';
514 1.9 christos else if (mode & TUI_BP_DISABLED)
515 1.9 christos element[TUI_BP_BREAK_POS] = '-';
516 1.1 christos
517 1.9 christos if (src_element->is_exec_point)
518 1.9 christos element[TUI_EXEC_POS] = '>';
519 1.1 christos
520 1.9 christos mvwaddstr (handle.get (), i + 1, 1, element);
521 1.10 christos
522 1.10 christos show_line_number (i);
523 1.1 christos }
524 1.9 christos refresh_window ();
525 1.1 christos }
526