1 /* TUI display source/assembly window.
3 Copyright (C) 1998-2023 Free Software Foundation, Inc.
5 Contributed by Hewlett-Packard Company.
7 This file is part of GDB.
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.
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.
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/>. */
26 #include "breakpoint.h"
30 #include "filenames.h"
31 #include "gdbsupport/gdb-safe-ctype.h"
34 #include "tui/tui-data.h"
35 #include "tui/tui-io.h"
36 #include "tui/tui-stack.h"
37 #include "tui/tui-win.h"
38 #include "tui/tui-wingeneral.h"
39 #include "tui/tui-winsource.h"
40 #include "tui/tui-source.h"
41 #include "tui/tui-disasm.h"
42 #include "tui/tui-location.h"
43 #include "gdb_curses.h"
45 /* Function to display the "main" routine. */
49 auto adapter
= tui_source_windows ();
50 if (adapter
.begin () != adapter
.end ())
52 struct gdbarch
*gdbarch
;
55 tui_get_begin_asm_address (&gdbarch
, &addr
);
56 if (addr
!= (CORE_ADDR
) 0)
60 tui_update_source_windows_with_addr (gdbarch
, addr
);
61 s
= find_pc_line_symtab (addr
);
62 tui_location
.set_location (s
);
67 /* See tui-winsource.h. */
70 tui_copy_source_line (const char **ptr
, int *length
)
72 const char *lineptr
= *ptr
;
74 /* Init the line with the line number. */
84 if (c
== '\033' && skip_ansi_escape (lineptr
, &skip_bytes
))
86 /* We always have to preserve escapes. */
87 result
.append (lineptr
, lineptr
+ skip_bytes
);
88 lineptr
+= skip_bytes
;
97 auto process_tab
= [&] ()
99 int max_tab_len
= tui_tab_width
;
102 for (int j
= column
% max_tab_len
;
105 result
.push_back (' ');
108 if (c
== '\n' || c
== '\r' || c
== '\0')
114 else if (ISCNTRL (c
))
116 result
.push_back ('^');
117 result
.push_back (c
+ 0100);
122 result
.push_back ('^');
123 result
.push_back ('?');
127 result
.push_back (c
);
129 while (c
!= '\0' && c
!= '\n' && c
!= '\r');
131 if (c
== '\r' && *lineptr
== '\n')
135 if (length
!= nullptr)
142 tui_source_window_base::style_changed ()
144 if (tui_active
&& is_visible ())
148 /* Function to display source in the source window. This function
149 initializes the horizontal scroll to 0. */
151 tui_source_window_base::update_source_window
152 (struct gdbarch
*gdbarch
,
153 const struct symtab_and_line
&sal
)
155 m_horizontal_offset
= 0;
156 update_source_window_as_is (gdbarch
, sal
);
160 /* Function to display source in the source/asm window. This function
161 shows the source as specified by the horizontal offset. */
163 tui_source_window_base::update_source_window_as_is
164 (struct gdbarch
*gdbarch
,
165 const struct symtab_and_line
&sal
)
167 bool ret
= set_contents (gdbarch
, sal
);
170 erase_source_content ();
173 validate_scroll_offsets ();
174 update_breakpoint_info (nullptr, false);
175 update_exec_info (false);
176 show_source_content ();
181 /* Function to ensure that the source and/or disassembly windows
182 reflect the input address. */
184 tui_update_source_windows_with_addr (struct gdbarch
*gdbarch
, CORE_ADDR addr
)
186 struct symtab_and_line sal
{};
188 sal
= find_pc_line (addr
, 0);
190 for (struct tui_source_window_base
*win_info
: tui_source_windows ())
191 win_info
->update_source_window (gdbarch
, sal
);
194 /* Function to ensure that the source and/or disassembly windows
195 reflect the symtab and line. */
197 tui_update_source_windows_with_line (struct symtab_and_line sal
)
199 struct gdbarch
*gdbarch
= nullptr;
200 if (sal
.symtab
!= nullptr)
202 find_line_pc (sal
.symtab
, sal
.line
, &sal
.pc
);
203 gdbarch
= sal
.symtab
->compunit ()->objfile ()->arch ();
206 for (struct tui_source_window_base
*win_info
: tui_source_windows ())
207 win_info
->update_source_window (gdbarch
, sal
);
211 tui_source_window_base::do_erase_source_content (const char *str
)
214 int half_width
= (width
- box_size ()) / 2;
219 werase (handle
.get ());
220 check_and_display_highlight_if_needed ();
222 if (strlen (str
) >= half_width
)
225 x_pos
= half_width
- strlen (str
);
226 mvwaddstr (handle
.get (),
235 /* See tui-winsource.h. */
238 tui_source_window_base::puts_to_pad_with_skip (const char *string
, int skip
)
240 gdb_assert (m_pad
.get () != nullptr);
241 WINDOW
*w
= m_pad
.get ();
245 const char *next
= strpbrk (string
, "\033");
247 /* Print the plain text prefix. */
248 size_t n_chars
= next
== nullptr ? strlen (string
) : next
- string
;
269 std::string
copy (string
, n_chars
);
270 tui_puts (copy
.c_str (), w
);
278 gdb_assert (*next
== '\033');
281 if (skip_ansi_escape (next
, &n_read
))
283 std::string
copy (next
, n_read
);
284 tui_puts (copy
.c_str (), w
);
288 gdb_assert_not_reached ("unhandled escape");
294 tui_puts (string
, w
);
297 /* Redraw the complete line of a source or disassembly window. */
299 tui_source_window_base::show_source_line (int lineno
)
301 struct tui_source_element
*line
;
303 line
= &m_content
[lineno
];
304 if (line
->is_exec_point
)
305 tui_set_reverse_mode (m_pad
.get (), true);
307 wmove (m_pad
.get (), lineno
, 0);
308 puts_to_pad_with_skip (line
->line
.c_str (), m_pad_offset
);
310 if (line
->is_exec_point
)
311 tui_set_reverse_mode (m_pad
.get (), false);
314 /* See tui-winsource.h. */
317 tui_source_window_base::refresh_window ()
319 TUI_SCOPED_DEBUG_START_END ("window `%s`", name ());
321 /* tui_win_info::refresh_window would draw the empty background window to
322 the screen, potentially creating a flicker. */
323 wnoutrefresh (handle
.get ());
325 int pad_width
= getmaxx (m_pad
.get ());
326 int left_margin
= this->left_margin ();
327 int view_width
= this->view_width ();
328 int content_width
= m_max_length
;
329 int pad_x
= m_horizontal_offset
- m_pad_offset
;
331 tui_debug_printf ("pad_width = %d, left_margin = %d, view_width = %d",
332 pad_width
, left_margin
, view_width
);
333 tui_debug_printf ("content_width = %d, pad_x = %d, m_horizontal_offset = %d",
334 content_width
, pad_x
, m_horizontal_offset
);
335 tui_debug_printf ("m_pad_offset = %d", m_pad_offset
);
337 gdb_assert (m_pad_offset
>= 0);
338 gdb_assert (m_horizontal_offset
+ view_width
339 <= std::max (content_width
, view_width
));
340 gdb_assert (pad_x
>= 0);
341 gdb_assert (m_horizontal_offset
>= 0);
343 /* This function can be called before the pad has been allocated, this
344 should only occur during the initial startup. In this case the first
345 condition in the following asserts will not be true, but the nullptr
347 gdb_assert (pad_width
> 0 || m_pad
.get () == nullptr);
348 gdb_assert (pad_x
+ view_width
<= pad_width
|| m_pad
.get () == nullptr);
350 int sminrow
= y
+ box_width ();
351 int smincol
= x
+ box_width () + left_margin
;
352 int smaxrow
= sminrow
+ m_content
.size () - 1;
353 int smaxcol
= smincol
+ view_width
- 1;
354 prefresh (m_pad
.get (), 0, pad_x
, sminrow
, smincol
, smaxrow
, smaxcol
);
358 tui_source_window_base::show_source_content ()
360 TUI_SCOPED_DEBUG_START_END ("window `%s`", name ());
362 gdb_assert (!m_content
.empty ());
364 /* The pad should be at least as wide as the window, but ideally, as wide
365 as the content, however, for some very wide content this might not be
367 int required_pad_width
= std::max (m_max_length
, width
);
368 int required_pad_height
= m_content
.size ();
370 /* If the required pad width is wider than the previously requested pad
371 width, then we might want to grow the pad. */
372 if (required_pad_width
> m_pad_requested_width
373 || required_pad_height
> getmaxy (m_pad
.get ()))
375 /* The current pad width. */
376 int pad_width
= m_pad
== nullptr ? 0 : getmaxx (m_pad
.get ());
378 gdb_assert (pad_width
<= m_pad_requested_width
);
380 /* If the current pad width is smaller than the previously requested
381 pad width, then this means we previously failed to allocate a
382 bigger pad. There's no point asking again, so we'll just make so
383 with the pad we currently have. */
384 if (pad_width
== m_pad_requested_width
385 || required_pad_height
> getmaxy (m_pad
.get ()))
387 pad_width
= required_pad_width
;
391 /* Try to allocate a new pad. */
392 m_pad
.reset (newpad (required_pad_height
, pad_width
));
394 if (m_pad
== nullptr)
396 int reduced_width
= std::max (pad_width
/ 2, width
);
397 if (reduced_width
== pad_width
)
398 error (_("failed to setup source window"));
399 pad_width
= reduced_width
;
402 while (m_pad
== nullptr);
405 m_pad_requested_width
= required_pad_width
;
406 tui_debug_printf ("requested width %d, allocated width %d",
407 required_pad_width
, getmaxx (m_pad
.get ()));
410 gdb_assert (m_pad
!= nullptr);
411 werase (m_pad
.get ());
412 for (int lineno
= 0; lineno
< m_content
.size (); lineno
++)
413 show_source_line (lineno
);
417 /* Calling check_and_display_highlight_if_needed will call refresh_window
418 (so long as the current window can be boxed), which will ensure that
419 the newly loaded window content is copied to the screen. */
420 check_and_display_highlight_if_needed ();
426 tui_source_window_base::tui_source_window_base ()
428 m_start_line_or_addr
.loa
= LOA_ADDRESS
;
429 m_start_line_or_addr
.u
.addr
= 0;
431 gdb::observers::styling_changed
.attach
432 (std::bind (&tui_source_window::style_changed
, this),
433 m_observable
, "tui-winsource");
436 tui_source_window_base::~tui_source_window_base ()
438 gdb::observers::styling_changed
.detach (m_observable
);
441 /* See tui-data.h. */
444 tui_source_window_base::update_tab_width ()
446 werase (handle
.get ());
451 tui_source_window_base::rerender ()
453 TUI_SCOPED_DEBUG_START_END ("window `%s`", name ());
455 if (!m_content
.empty ())
457 struct symtab_and_line cursal
458 = get_current_source_symtab_and_line ();
460 if (m_start_line_or_addr
.loa
== LOA_LINE
)
461 cursal
.line
= m_start_line_or_addr
.u
.line_no
;
463 cursal
.pc
= m_start_line_or_addr
.u
.addr
;
464 update_source_window (m_gdbarch
, cursal
);
466 else if (deprecated_safe_get_selected_frame () != NULL
)
468 struct symtab_and_line cursal
469 = get_current_source_symtab_and_line ();
470 frame_info_ptr frame
= deprecated_safe_get_selected_frame ();
471 struct gdbarch
*gdbarch
= get_frame_arch (frame
);
473 struct symtab
*s
= find_pc_line_symtab (get_frame_pc (frame
));
474 if (this != TUI_SRC_WIN
)
475 find_line_pc (s
, cursal
.line
, &cursal
.pc
);
476 update_source_window (gdbarch
, cursal
);
479 erase_source_content ();
482 /* See tui-data.h. */
485 tui_source_window_base::refill ()
487 symtab_and_line sal
{};
489 if (this == TUI_SRC_WIN
)
491 sal
= get_current_source_symtab_and_line ();
492 if (sal
.symtab
== NULL
)
494 frame_info_ptr fi
= deprecated_safe_get_selected_frame ();
496 sal
= find_pc_line (get_frame_pc (fi
), 0);
500 if (sal
.pspace
== nullptr)
501 sal
.pspace
= current_program_space
;
503 if (m_start_line_or_addr
.loa
== LOA_LINE
)
504 sal
.line
= m_start_line_or_addr
.u
.line_no
;
506 sal
.pc
= m_start_line_or_addr
.u
.addr
;
508 update_source_window_as_is (m_gdbarch
, sal
);
511 /* See tui-winsource.h. */
514 tui_source_window_base::validate_scroll_offsets ()
516 TUI_SCOPED_DEBUG_START_END ("window `%s`", name ());
518 int original_pad_offset
= m_pad_offset
;
520 if (m_horizontal_offset
< 0)
521 m_horizontal_offset
= 0;
523 int content_width
= m_max_length
;
524 int pad_width
= getmaxx (m_pad
.get ());
525 int view_width
= this->view_width ();
527 tui_debug_printf ("pad_width = %d, view_width = %d, content_width = %d",
528 pad_width
, view_width
, content_width
);
529 tui_debug_printf ("original_pad_offset = %d, m_horizontal_offset = %d",
530 original_pad_offset
, m_horizontal_offset
);
532 if (m_horizontal_offset
+ view_width
> content_width
)
533 m_horizontal_offset
= std::max (content_width
- view_width
, 0);
535 if ((m_horizontal_offset
+ view_width
) > (m_pad_offset
+ pad_width
))
537 m_pad_offset
= std::min (m_horizontal_offset
, content_width
- pad_width
);
538 m_pad_offset
= std::max (m_pad_offset
, 0);
540 else if (m_horizontal_offset
< m_pad_offset
)
541 m_pad_offset
= std::max (m_horizontal_offset
+ view_width
- pad_width
, 0);
543 gdb_assert (m_pad_offset
>= 0);
544 return (original_pad_offset
!= m_pad_offset
);
547 /* Scroll the source forward or backward horizontally. */
550 tui_source_window_base::do_scroll_horizontal (int num_to_scroll
)
552 if (!m_content
.empty ())
554 m_horizontal_offset
+= num_to_scroll
;
556 if (validate_scroll_offsets ())
557 show_source_content ();
564 /* Set or clear the is_exec_point flag in the line whose line is
568 tui_source_window_base::set_is_exec_point_at (struct tui_line_or_address l
)
570 bool changed
= false;
574 while (i
< m_content
.size ())
577 struct tui_line_or_address content_loa
=
578 m_content
[i
].line_or_addr
;
580 if (content_loa
.loa
== l
.loa
581 && ((l
.loa
== LOA_LINE
&& content_loa
.u
.line_no
== l
.u
.line_no
)
582 || (l
.loa
== LOA_ADDRESS
&& content_loa
.u
.addr
== l
.u
.addr
)))
586 if (new_state
!= m_content
[i
].is_exec_point
)
589 m_content
[i
].is_exec_point
= new_state
;
597 /* See tui-winsource.h. */
600 tui_update_all_breakpoint_info (struct breakpoint
*being_deleted
)
602 for (tui_source_window_base
*win
: tui_source_windows ())
604 if (win
->update_breakpoint_info (being_deleted
, false))
605 win
->update_exec_info ();
610 /* Scan the source window and the breakpoints to update the break_mode
611 information for each line.
613 Returns true if something changed and the execution window must be
617 tui_source_window_base::update_breakpoint_info
618 (struct breakpoint
*being_deleted
, bool current_only
)
621 bool need_refresh
= false;
623 for (i
= 0; i
< m_content
.size (); i
++)
625 struct tui_source_element
*line
;
627 line
= &m_content
[i
];
628 if (current_only
&& !line
->is_exec_point
)
631 /* Scan each breakpoint to see if the current line has something to
632 do with it. Identify enable/disabled breakpoints as well as
633 those that we already hit. */
634 tui_bp_flags mode
= 0;
635 for (breakpoint
&bp
: all_breakpoints ())
637 if (&bp
== being_deleted
)
640 for (bp_location
&loc
: bp
.locations ())
642 if (location_matches_p (&loc
, i
))
644 if (bp
.enable_state
== bp_disabled
)
645 mode
|= TUI_BP_DISABLED
;
647 mode
|= TUI_BP_ENABLED
;
650 if (bp
.first_loc ().cond
)
651 mode
|= TUI_BP_CONDITIONAL
;
652 if (bp
.type
== bp_hardware_breakpoint
)
653 mode
|= TUI_BP_HARDWARE
;
658 if (line
->break_mode
!= mode
)
660 line
->break_mode
= mode
;
667 /* See tui-winsource.h. */
670 tui_source_window_base::update_exec_info (bool refresh_p
)
672 update_breakpoint_info (nullptr, true);
673 for (int i
= 0; i
< m_content
.size (); i
++)
675 struct tui_source_element
*src_element
= &m_content
[i
];
676 /* Add 1 for '\0'. */
677 char element
[TUI_EXECINFO_SIZE
+ 1];
678 /* Initialize all but last element. */
679 char space
= tui_left_margin_verbose
? '_' : ' ';
680 memset (element
, space
, TUI_EXECINFO_SIZE
);
681 /* Initialize last element. */
682 element
[TUI_EXECINFO_SIZE
] = '\0';
684 /* Now update the exec info content based upon the state
685 of each line as indicated by the source content. */
686 tui_bp_flags mode
= src_element
->break_mode
;
687 if (mode
& TUI_BP_HIT
)
688 element
[TUI_BP_HIT_POS
] = (mode
& TUI_BP_HARDWARE
) ? 'H' : 'B';
689 else if (mode
& (TUI_BP_ENABLED
| TUI_BP_DISABLED
))
690 element
[TUI_BP_HIT_POS
] = (mode
& TUI_BP_HARDWARE
) ? 'h' : 'b';
692 if (mode
& TUI_BP_ENABLED
)
693 element
[TUI_BP_BREAK_POS
] = '+';
694 else if (mode
& TUI_BP_DISABLED
)
695 element
[TUI_BP_BREAK_POS
] = '-';
697 if (src_element
->is_exec_point
)
698 element
[TUI_EXEC_POS
] = '>';
700 mvwaddstr (handle
.get (), i
+ box_width (), box_width (), element
);
702 show_line_number (i
);