1 /* TUI display source/assembly window.
3 Copyright (C) 1998-2024 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-status.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 /* See tui-winsource.h. */
183 tui_source_window_base::update_source_window_with_addr (struct gdbarch
*gdbarch
,
186 struct symtab_and_line sal
{};
188 sal
= find_pc_line (addr
, 0);
190 update_source_window (gdbarch
, sal
);
193 /* Function to ensure that the source and/or disassembly windows
194 reflect the input address. */
196 tui_update_source_windows_with_addr (struct gdbarch
*gdbarch
, CORE_ADDR addr
)
198 struct symtab_and_line sal
{};
200 sal
= find_pc_line (addr
, 0);
202 for (struct tui_source_window_base
*win_info
: tui_source_windows ())
203 win_info
->update_source_window (gdbarch
, sal
);
206 /* Function to ensure that the source and/or disassembly windows
207 reflect the symtab and line. */
209 tui_update_source_windows_with_line (struct symtab_and_line sal
)
211 struct gdbarch
*gdbarch
= nullptr;
212 if (sal
.symtab
!= nullptr)
214 find_line_pc (sal
.symtab
, sal
.line
, &sal
.pc
);
215 gdbarch
= sal
.symtab
->compunit ()->objfile ()->arch ();
218 for (struct tui_source_window_base
*win_info
: tui_source_windows ())
219 win_info
->update_source_window (gdbarch
, sal
);
223 tui_source_window_base::do_erase_source_content (const char *str
)
226 int half_width
= (width
- box_size ()) / 2;
231 werase (handle
.get ());
232 check_and_display_highlight_if_needed ();
234 if (strlen (str
) >= half_width
)
237 x_pos
= half_width
- strlen (str
);
238 display_string (height
/ 2, x_pos
, str
);
244 /* See tui-winsource.h. */
247 tui_source_window_base::puts_to_pad_with_skip (const char *string
, int skip
)
249 gdb_assert (m_pad
.get () != nullptr);
250 WINDOW
*w
= m_pad
.get ();
254 const char *next
= strpbrk (string
, "\033");
256 /* Print the plain text prefix. */
257 size_t n_chars
= next
== nullptr ? strlen (string
) : next
- string
;
278 std::string
copy (string
, n_chars
);
279 tui_puts (copy
.c_str (), w
);
287 gdb_assert (*next
== '\033');
290 if (skip_ansi_escape (next
, &n_read
))
292 std::string
copy (next
, n_read
);
293 tui_puts (copy
.c_str (), w
);
297 gdb_assert_not_reached ("unhandled escape");
303 tui_puts (string
, w
);
306 /* Redraw the complete line of a source or disassembly window. */
308 tui_source_window_base::show_source_line (int lineno
)
310 struct tui_source_element
*line
;
312 line
= &m_content
[lineno
];
313 if (line
->is_exec_point
)
314 tui_set_reverse_mode (m_pad
.get (), true);
316 wmove (m_pad
.get (), lineno
, 0);
317 puts_to_pad_with_skip (line
->line
.c_str (), m_pad_offset
);
319 if (line
->is_exec_point
)
320 tui_set_reverse_mode (m_pad
.get (), false);
323 /* See tui-winsource.h. */
326 tui_source_window_base::refresh_window ()
328 TUI_SCOPED_DEBUG_START_END ("window `%s`", name ());
330 /* tui_win_info::refresh_window would draw the empty background window to
331 the screen, potentially creating a flicker. */
332 wnoutrefresh (handle
.get ());
334 int pad_width
= getmaxx (m_pad
.get ());
335 int left_margin
= this->left_margin ();
336 int view_width
= this->view_width ();
337 int content_width
= m_max_length
;
338 int pad_x
= m_horizontal_offset
- m_pad_offset
;
340 tui_debug_printf ("pad_width = %d, left_margin = %d, view_width = %d",
341 pad_width
, left_margin
, view_width
);
342 tui_debug_printf ("content_width = %d, pad_x = %d, m_horizontal_offset = %d",
343 content_width
, pad_x
, m_horizontal_offset
);
344 tui_debug_printf ("m_pad_offset = %d", m_pad_offset
);
346 gdb_assert (m_pad_offset
>= 0);
347 gdb_assert (m_horizontal_offset
+ view_width
348 <= std::max (content_width
, view_width
));
349 gdb_assert (pad_x
>= 0);
350 gdb_assert (m_horizontal_offset
>= 0);
352 /* This function can be called before the pad has been allocated, this
353 should only occur during the initial startup. In this case the first
354 condition in the following asserts will not be true, but the nullptr
356 gdb_assert (pad_width
> 0 || m_pad
.get () == nullptr);
357 gdb_assert (pad_x
+ view_width
<= pad_width
|| m_pad
.get () == nullptr);
359 int sminrow
= y
+ box_width ();
360 int smincol
= x
+ box_width () + left_margin
;
361 int smaxrow
= sminrow
+ m_content
.size () - 1;
362 int smaxcol
= smincol
+ view_width
- 1;
363 prefresh (m_pad
.get (), 0, pad_x
, sminrow
, smincol
, smaxrow
, smaxcol
);
367 tui_source_window_base::show_source_content ()
369 TUI_SCOPED_DEBUG_START_END ("window `%s`", name ());
371 gdb_assert (!m_content
.empty ());
373 /* The pad should be at least as wide as the window, but ideally, as wide
374 as the content, however, for some very wide content this might not be
376 int required_pad_width
= std::max (m_max_length
, width
);
377 int required_pad_height
= m_content
.size ();
379 /* If the required pad width is wider than the previously requested pad
380 width, then we might want to grow the pad. */
381 if (required_pad_width
> m_pad_requested_width
382 || required_pad_height
> getmaxy (m_pad
.get ()))
384 /* The current pad width. */
385 int pad_width
= m_pad
== nullptr ? 0 : getmaxx (m_pad
.get ());
387 gdb_assert (pad_width
<= m_pad_requested_width
);
389 /* If the current pad width is smaller than the previously requested
390 pad width, then this means we previously failed to allocate a
391 bigger pad. There's no point asking again, so we'll just make so
392 with the pad we currently have. */
393 if (pad_width
== m_pad_requested_width
394 || required_pad_height
> getmaxy (m_pad
.get ()))
396 pad_width
= required_pad_width
;
400 /* Try to allocate a new pad. */
401 m_pad
.reset (newpad (required_pad_height
, pad_width
));
403 if (m_pad
== nullptr)
405 int reduced_width
= std::max (pad_width
/ 2, width
);
406 if (reduced_width
== pad_width
)
407 error (_("failed to setup source window"));
408 pad_width
= reduced_width
;
411 while (m_pad
== nullptr);
414 m_pad_requested_width
= required_pad_width
;
415 tui_debug_printf ("requested width %d, allocated width %d",
416 required_pad_width
, getmaxx (m_pad
.get ()));
419 gdb_assert (m_pad
!= nullptr);
420 werase (m_pad
.get ());
421 for (int lineno
= 0; lineno
< m_content
.size (); lineno
++)
422 show_source_line (lineno
);
426 /* Calling check_and_display_highlight_if_needed will call refresh_window
427 (so long as the current window can be boxed), which will ensure that
428 the newly loaded window content is copied to the screen. */
429 check_and_display_highlight_if_needed ();
435 tui_source_window_base::tui_source_window_base ()
437 m_start_line_or_addr
.loa
= LOA_ADDRESS
;
438 m_start_line_or_addr
.u
.addr
= 0;
440 gdb::observers::styling_changed
.attach
441 (std::bind (&tui_source_window::style_changed
, this),
442 m_observable
, "tui-winsource");
445 tui_source_window_base::~tui_source_window_base ()
447 gdb::observers::styling_changed
.detach (m_observable
);
450 /* See tui-data.h. */
453 tui_source_window_base::update_tab_width ()
455 werase (handle
.get ());
460 tui_source_window_base::rerender ()
462 TUI_SCOPED_DEBUG_START_END ("window `%s`", name ());
464 if (!m_content
.empty ())
466 struct symtab_and_line cursal
467 = get_current_source_symtab_and_line ();
469 if (m_start_line_or_addr
.loa
== LOA_LINE
)
470 cursal
.line
= m_start_line_or_addr
.u
.line_no
;
472 cursal
.pc
= m_start_line_or_addr
.u
.addr
;
473 update_source_window (m_gdbarch
, cursal
);
475 else if (deprecated_safe_get_selected_frame () != NULL
)
477 struct symtab_and_line cursal
478 = get_current_source_symtab_and_line ();
479 frame_info_ptr frame
= deprecated_safe_get_selected_frame ();
480 struct gdbarch
*gdbarch
= get_frame_arch (frame
);
482 struct symtab
*s
= find_pc_line_symtab (get_frame_pc (frame
));
483 if (this != TUI_SRC_WIN
)
484 find_line_pc (s
, cursal
.line
, &cursal
.pc
);
485 update_source_window (gdbarch
, cursal
);
490 struct gdbarch
*gdbarch
;
491 tui_get_begin_asm_address (&gdbarch
, &addr
);
493 erase_source_content ();
495 update_source_window_with_addr (gdbarch
, addr
);
499 /* See tui-data.h. */
502 tui_source_window_base::refill ()
504 symtab_and_line sal
{};
506 if (this == TUI_SRC_WIN
)
508 sal
= get_current_source_symtab_and_line ();
509 if (sal
.symtab
== NULL
)
511 frame_info_ptr fi
= deprecated_safe_get_selected_frame ();
513 sal
= find_pc_line (get_frame_pc (fi
), 0);
517 if (sal
.pspace
== nullptr)
518 sal
.pspace
= current_program_space
;
520 if (m_start_line_or_addr
.loa
== LOA_LINE
)
521 sal
.line
= m_start_line_or_addr
.u
.line_no
;
523 sal
.pc
= m_start_line_or_addr
.u
.addr
;
525 update_source_window_as_is (m_gdbarch
, sal
);
528 /* See tui-winsource.h. */
531 tui_source_window_base::validate_scroll_offsets ()
533 TUI_SCOPED_DEBUG_START_END ("window `%s`", name ());
535 int original_pad_offset
= m_pad_offset
;
537 if (m_horizontal_offset
< 0)
538 m_horizontal_offset
= 0;
540 int content_width
= m_max_length
;
541 int pad_width
= getmaxx (m_pad
.get ());
542 int view_width
= this->view_width ();
544 tui_debug_printf ("pad_width = %d, view_width = %d, content_width = %d",
545 pad_width
, view_width
, content_width
);
546 tui_debug_printf ("original_pad_offset = %d, m_horizontal_offset = %d",
547 original_pad_offset
, m_horizontal_offset
);
549 if (m_horizontal_offset
+ view_width
> content_width
)
550 m_horizontal_offset
= std::max (content_width
- view_width
, 0);
552 if ((m_horizontal_offset
+ view_width
) > (m_pad_offset
+ pad_width
))
554 m_pad_offset
= std::min (m_horizontal_offset
, content_width
- pad_width
);
555 m_pad_offset
= std::max (m_pad_offset
, 0);
557 else if (m_horizontal_offset
< m_pad_offset
)
558 m_pad_offset
= std::max (m_horizontal_offset
+ view_width
- pad_width
, 0);
560 gdb_assert (m_pad_offset
>= 0);
561 return (original_pad_offset
!= m_pad_offset
);
564 /* Scroll the source forward or backward horizontally. */
567 tui_source_window_base::do_scroll_horizontal (int num_to_scroll
)
569 if (!m_content
.empty ())
571 m_horizontal_offset
+= num_to_scroll
;
573 if (validate_scroll_offsets ())
574 show_source_content ();
581 /* Set or clear the is_exec_point flag in the line whose line is
585 tui_source_window_base::set_is_exec_point_at (struct tui_line_or_address l
)
587 bool changed
= false;
591 while (i
< m_content
.size ())
594 struct tui_line_or_address content_loa
=
595 m_content
[i
].line_or_addr
;
597 if (content_loa
.loa
== l
.loa
598 && ((l
.loa
== LOA_LINE
&& content_loa
.u
.line_no
== l
.u
.line_no
)
599 || (l
.loa
== LOA_ADDRESS
&& content_loa
.u
.addr
== l
.u
.addr
)))
603 if (new_state
!= m_content
[i
].is_exec_point
)
606 m_content
[i
].is_exec_point
= new_state
;
614 /* See tui-winsource.h. */
617 tui_update_all_breakpoint_info (struct breakpoint
*being_deleted
)
619 for (tui_source_window_base
*win
: tui_source_windows ())
621 if (win
->update_breakpoint_info (being_deleted
, false))
622 win
->update_exec_info ();
627 /* Scan the source window and the breakpoints to update the break_mode
628 information for each line.
630 Returns true if something changed and the execution window must be
634 tui_source_window_base::update_breakpoint_info
635 (struct breakpoint
*being_deleted
, bool current_only
)
638 bool need_refresh
= false;
640 for (i
= 0; i
< m_content
.size (); i
++)
642 struct tui_source_element
*line
;
644 line
= &m_content
[i
];
645 if (current_only
&& !line
->is_exec_point
)
648 /* Scan each breakpoint to see if the current line has something to
649 do with it. Identify enable/disabled breakpoints as well as
650 those that we already hit. */
651 tui_bp_flags mode
= 0;
652 for (breakpoint
&bp
: all_breakpoints ())
654 if (&bp
== being_deleted
)
657 for (bp_location
&loc
: bp
.locations ())
659 if (location_matches_p (&loc
, i
))
661 if (bp
.enable_state
== bp_disabled
)
662 mode
|= TUI_BP_DISABLED
;
664 mode
|= TUI_BP_ENABLED
;
667 if (bp
.first_loc ().cond
)
668 mode
|= TUI_BP_CONDITIONAL
;
669 if (bp
.type
== bp_hardware_breakpoint
)
670 mode
|= TUI_BP_HARDWARE
;
675 if (line
->break_mode
!= mode
)
677 line
->break_mode
= mode
;
684 /* See tui-winsource.h. */
687 tui_source_window_base::update_exec_info (bool refresh_p
)
689 update_breakpoint_info (nullptr, true);
690 for (int i
= 0; i
< m_content
.size (); i
++)
692 struct tui_source_element
*src_element
= &m_content
[i
];
693 /* Add 1 for '\0'. */
694 char element
[TUI_EXECINFO_SIZE
+ 1];
695 /* Initialize all but last element. */
696 char space
= tui_left_margin_verbose
? '_' : ' ';
697 memset (element
, space
, TUI_EXECINFO_SIZE
);
698 /* Initialize last element. */
699 element
[TUI_EXECINFO_SIZE
] = '\0';
701 /* Now update the exec info content based upon the state
702 of each line as indicated by the source content. */
703 tui_bp_flags mode
= src_element
->break_mode
;
704 if (mode
& TUI_BP_HIT
)
705 element
[TUI_BP_HIT_POS
] = (mode
& TUI_BP_HARDWARE
) ? 'H' : 'B';
706 else if (mode
& (TUI_BP_ENABLED
| TUI_BP_DISABLED
))
707 element
[TUI_BP_HIT_POS
] = (mode
& TUI_BP_HARDWARE
) ? 'h' : 'b';
709 if (mode
& TUI_BP_ENABLED
)
710 element
[TUI_BP_BREAK_POS
] = '+';
711 else if (mode
& TUI_BP_DISABLED
)
712 element
[TUI_BP_BREAK_POS
] = '-';
714 if (src_element
->is_exec_point
)
715 element
[TUI_EXEC_POS
] = '>';
717 display_string (i
+ box_width (), box_width (), element
);
719 show_line_number (i
);