]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - gdb/tui/tui-winsource.c
Update copyright year range in header of all files managed by GDB
[thirdparty/binutils-gdb.git] / gdb / tui / tui-winsource.c
1 /* TUI display source/assembly window.
2
3 Copyright (C) 1998-2024 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 <ctype.h>
24 #include "symtab.h"
25 #include "frame.h"
26 #include "breakpoint.h"
27 #include "value.h"
28 #include "source.h"
29 #include "objfiles.h"
30 #include "filenames.h"
31 #include "gdbsupport/gdb-safe-ctype.h"
32
33 #include "tui/tui.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"
44
45 /* Function to display the "main" routine. */
46 void
47 tui_display_main ()
48 {
49 auto adapter = tui_source_windows ();
50 if (adapter.begin () != adapter.end ())
51 {
52 struct gdbarch *gdbarch;
53 CORE_ADDR addr;
54
55 tui_get_begin_asm_address (&gdbarch, &addr);
56 if (addr != (CORE_ADDR) 0)
57 {
58 struct symtab *s;
59
60 tui_update_source_windows_with_addr (gdbarch, addr);
61 s = find_pc_line_symtab (addr);
62 tui_location.set_location (s);
63 }
64 }
65 }
66
67 /* See tui-winsource.h. */
68
69 std::string
70 tui_copy_source_line (const char **ptr, int *length)
71 {
72 const char *lineptr = *ptr;
73
74 /* Init the line with the line number. */
75 std::string result;
76
77 int column = 0;
78 char c;
79 do
80 {
81 int skip_bytes;
82
83 c = *lineptr;
84 if (c == '\033' && skip_ansi_escape (lineptr, &skip_bytes))
85 {
86 /* We always have to preserve escapes. */
87 result.append (lineptr, lineptr + skip_bytes);
88 lineptr += skip_bytes;
89 continue;
90 }
91 if (c == '\0')
92 break;
93
94 ++lineptr;
95 ++column;
96
97 auto process_tab = [&] ()
98 {
99 int max_tab_len = tui_tab_width;
100
101 --column;
102 for (int j = column % max_tab_len;
103 j < max_tab_len;
104 column++, j++)
105 result.push_back (' ');
106 };
107
108 if (c == '\n' || c == '\r' || c == '\0')
109 {
110 /* Nothing. */
111 }
112 else if (c == '\t')
113 process_tab ();
114 else if (ISCNTRL (c))
115 {
116 result.push_back ('^');
117 result.push_back (c + 0100);
118 ++column;
119 }
120 else if (c == 0177)
121 {
122 result.push_back ('^');
123 result.push_back ('?');
124 ++column;
125 }
126 else
127 result.push_back (c);
128 }
129 while (c != '\0' && c != '\n' && c != '\r');
130
131 if (c == '\r' && *lineptr == '\n')
132 ++lineptr;
133 *ptr = lineptr;
134
135 if (length != nullptr)
136 *length = column;
137
138 return result;
139 }
140
141 void
142 tui_source_window_base::style_changed ()
143 {
144 if (tui_active && is_visible ())
145 refill ();
146 }
147
148 /* Function to display source in the source window. This function
149 initializes the horizontal scroll to 0. */
150 void
151 tui_source_window_base::update_source_window
152 (struct gdbarch *gdbarch,
153 const struct symtab_and_line &sal)
154 {
155 m_horizontal_offset = 0;
156 update_source_window_as_is (gdbarch, sal);
157 }
158
159
160 /* Function to display source in the source/asm window. This function
161 shows the source as specified by the horizontal offset. */
162 void
163 tui_source_window_base::update_source_window_as_is
164 (struct gdbarch *gdbarch,
165 const struct symtab_and_line &sal)
166 {
167 bool ret = set_contents (gdbarch, sal);
168
169 if (!ret)
170 erase_source_content ();
171 else
172 {
173 validate_scroll_offsets ();
174 update_breakpoint_info (nullptr, false);
175 update_exec_info (false);
176 show_source_content ();
177 }
178 }
179
180
181 /* See tui-winsource.h. */
182 void
183 tui_source_window_base::update_source_window_with_addr (struct gdbarch *gdbarch,
184 CORE_ADDR addr)
185 {
186 struct symtab_and_line sal {};
187 if (addr != 0)
188 sal = find_pc_line (addr, 0);
189
190 update_source_window (gdbarch, sal);
191 }
192
193 /* Function to ensure that the source and/or disassembly windows
194 reflect the input address. */
195 void
196 tui_update_source_windows_with_addr (struct gdbarch *gdbarch, CORE_ADDR addr)
197 {
198 struct symtab_and_line sal {};
199 if (addr != 0)
200 sal = find_pc_line (addr, 0);
201
202 for (struct tui_source_window_base *win_info : tui_source_windows ())
203 win_info->update_source_window (gdbarch, sal);
204 }
205
206 /* Function to ensure that the source and/or disassembly windows
207 reflect the symtab and line. */
208 void
209 tui_update_source_windows_with_line (struct symtab_and_line sal)
210 {
211 struct gdbarch *gdbarch = nullptr;
212 if (sal.symtab != nullptr)
213 {
214 find_line_pc (sal.symtab, sal.line, &sal.pc);
215 gdbarch = sal.symtab->compunit ()->objfile ()->arch ();
216 }
217
218 for (struct tui_source_window_base *win_info : tui_source_windows ())
219 win_info->update_source_window (gdbarch, sal);
220 }
221
222 void
223 tui_source_window_base::do_erase_source_content (const char *str)
224 {
225 int x_pos;
226 int half_width = (width - box_size ()) / 2;
227
228 m_content.clear ();
229 if (handle != NULL)
230 {
231 werase (handle.get ());
232 check_and_display_highlight_if_needed ();
233
234 if (strlen (str) >= half_width)
235 x_pos = 1;
236 else
237 x_pos = half_width - strlen (str);
238 display_string (height / 2, x_pos, str);
239
240 refresh_window ();
241 }
242 }
243
244 /* See tui-winsource.h. */
245
246 void
247 tui_source_window_base::puts_to_pad_with_skip (const char *string, int skip)
248 {
249 gdb_assert (m_pad.get () != nullptr);
250 WINDOW *w = m_pad.get ();
251
252 while (skip > 0)
253 {
254 const char *next = strpbrk (string, "\033");
255
256 /* Print the plain text prefix. */
257 size_t n_chars = next == nullptr ? strlen (string) : next - string;
258 if (n_chars > 0)
259 {
260 if (skip > 0)
261 {
262 if (skip < n_chars)
263 {
264 string += skip;
265 n_chars -= skip;
266 skip = 0;
267 }
268 else
269 {
270 skip -= n_chars;
271 string += n_chars;
272 n_chars = 0;
273 }
274 }
275
276 if (n_chars > 0)
277 {
278 std::string copy (string, n_chars);
279 tui_puts (copy.c_str (), w);
280 }
281 }
282
283 /* We finished. */
284 if (next == nullptr)
285 break;
286
287 gdb_assert (*next == '\033');
288
289 int n_read;
290 if (skip_ansi_escape (next, &n_read))
291 {
292 std::string copy (next, n_read);
293 tui_puts (copy.c_str (), w);
294 next += n_read;
295 }
296 else
297 gdb_assert_not_reached ("unhandled escape");
298
299 string = next;
300 }
301
302 if (*string != '\0')
303 tui_puts (string, w);
304 }
305
306 /* Redraw the complete line of a source or disassembly window. */
307 void
308 tui_source_window_base::show_source_line (int lineno)
309 {
310 struct tui_source_element *line;
311
312 line = &m_content[lineno];
313 if (line->is_exec_point)
314 tui_set_reverse_mode (m_pad.get (), true);
315
316 wmove (m_pad.get (), lineno, 0);
317 puts_to_pad_with_skip (line->line.c_str (), m_pad_offset);
318
319 if (line->is_exec_point)
320 tui_set_reverse_mode (m_pad.get (), false);
321 }
322
323 /* See tui-winsource.h. */
324
325 void
326 tui_source_window_base::refresh_window ()
327 {
328 TUI_SCOPED_DEBUG_START_END ("window `%s`", name ());
329
330 /* tui_win_info::refresh_window would draw the empty background window to
331 the screen, potentially creating a flicker. */
332 wnoutrefresh (handle.get ());
333
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;
339
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);
345
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);
351
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
355 check will. */
356 gdb_assert (pad_width > 0 || m_pad.get () == nullptr);
357 gdb_assert (pad_x + view_width <= pad_width || m_pad.get () == nullptr);
358
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);
364 }
365
366 void
367 tui_source_window_base::show_source_content ()
368 {
369 TUI_SCOPED_DEBUG_START_END ("window `%s`", name ());
370
371 gdb_assert (!m_content.empty ());
372
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
375 possible. */
376 int required_pad_width = std::max (m_max_length, width);
377 int required_pad_height = m_content.size ();
378
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 ()))
383 {
384 /* The current pad width. */
385 int pad_width = m_pad == nullptr ? 0 : getmaxx (m_pad.get ());
386
387 gdb_assert (pad_width <= m_pad_requested_width);
388
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 ()))
395 {
396 pad_width = required_pad_width;
397
398 do
399 {
400 /* Try to allocate a new pad. */
401 m_pad.reset (newpad (required_pad_height, pad_width));
402
403 if (m_pad == nullptr)
404 {
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;
409 }
410 }
411 while (m_pad == nullptr);
412 }
413
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 ()));
417 }
418
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);
423
424 if (can_box ())
425 {
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 ();
430 }
431 else
432 refresh_window ();
433 }
434
435 tui_source_window_base::tui_source_window_base ()
436 {
437 m_start_line_or_addr.loa = LOA_ADDRESS;
438 m_start_line_or_addr.u.addr = 0;
439
440 gdb::observers::styling_changed.attach
441 (std::bind (&tui_source_window::style_changed, this),
442 m_observable, "tui-winsource");
443 }
444
445 tui_source_window_base::~tui_source_window_base ()
446 {
447 gdb::observers::styling_changed.detach (m_observable);
448 }
449
450 /* See tui-data.h. */
451
452 void
453 tui_source_window_base::update_tab_width ()
454 {
455 werase (handle.get ());
456 rerender ();
457 }
458
459 void
460 tui_source_window_base::rerender ()
461 {
462 TUI_SCOPED_DEBUG_START_END ("window `%s`", name ());
463
464 if (!m_content.empty ())
465 {
466 struct symtab_and_line cursal
467 = get_current_source_symtab_and_line ();
468
469 if (m_start_line_or_addr.loa == LOA_LINE)
470 cursal.line = m_start_line_or_addr.u.line_no;
471 else
472 cursal.pc = m_start_line_or_addr.u.addr;
473 update_source_window (m_gdbarch, cursal);
474 }
475 else if (deprecated_safe_get_selected_frame () != NULL)
476 {
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);
481
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);
486 }
487 else
488 {
489 CORE_ADDR addr;
490 struct gdbarch *gdbarch;
491 tui_get_begin_asm_address (&gdbarch, &addr);
492 if (addr == 0)
493 erase_source_content ();
494 else
495 update_source_window_with_addr (gdbarch, addr);
496 }
497 }
498
499 /* See tui-data.h. */
500
501 void
502 tui_source_window_base::refill ()
503 {
504 symtab_and_line sal {};
505
506 if (this == TUI_SRC_WIN)
507 {
508 sal = get_current_source_symtab_and_line ();
509 if (sal.symtab == NULL)
510 {
511 frame_info_ptr fi = deprecated_safe_get_selected_frame ();
512 if (fi != nullptr)
513 sal = find_pc_line (get_frame_pc (fi), 0);
514 }
515 }
516
517 if (sal.pspace == nullptr)
518 sal.pspace = current_program_space;
519
520 if (m_start_line_or_addr.loa == LOA_LINE)
521 sal.line = m_start_line_or_addr.u.line_no;
522 else
523 sal.pc = m_start_line_or_addr.u.addr;
524
525 update_source_window_as_is (m_gdbarch, sal);
526 }
527
528 /* See tui-winsource.h. */
529
530 bool
531 tui_source_window_base::validate_scroll_offsets ()
532 {
533 TUI_SCOPED_DEBUG_START_END ("window `%s`", name ());
534
535 int original_pad_offset = m_pad_offset;
536
537 if (m_horizontal_offset < 0)
538 m_horizontal_offset = 0;
539
540 int content_width = m_max_length;
541 int pad_width = getmaxx (m_pad.get ());
542 int view_width = this->view_width ();
543
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);
548
549 if (m_horizontal_offset + view_width > content_width)
550 m_horizontal_offset = std::max (content_width - view_width, 0);
551
552 if ((m_horizontal_offset + view_width) > (m_pad_offset + pad_width))
553 {
554 m_pad_offset = std::min (m_horizontal_offset, content_width - pad_width);
555 m_pad_offset = std::max (m_pad_offset, 0);
556 }
557 else if (m_horizontal_offset < m_pad_offset)
558 m_pad_offset = std::max (m_horizontal_offset + view_width - pad_width, 0);
559
560 gdb_assert (m_pad_offset >= 0);
561 return (original_pad_offset != m_pad_offset);
562 }
563
564 /* Scroll the source forward or backward horizontally. */
565
566 void
567 tui_source_window_base::do_scroll_horizontal (int num_to_scroll)
568 {
569 if (!m_content.empty ())
570 {
571 m_horizontal_offset += num_to_scroll;
572
573 if (validate_scroll_offsets ())
574 show_source_content ();
575
576 refresh_window ();
577 }
578 }
579
580
581 /* Set or clear the is_exec_point flag in the line whose line is
582 line_no. */
583
584 void
585 tui_source_window_base::set_is_exec_point_at (struct tui_line_or_address l)
586 {
587 bool changed = false;
588 int i;
589
590 i = 0;
591 while (i < m_content.size ())
592 {
593 bool new_state;
594 struct tui_line_or_address content_loa =
595 m_content[i].line_or_addr;
596
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)))
600 new_state = true;
601 else
602 new_state = false;
603 if (new_state != m_content[i].is_exec_point)
604 {
605 changed = true;
606 m_content[i].is_exec_point = new_state;
607 }
608 i++;
609 }
610 if (changed)
611 refill ();
612 }
613
614 /* See tui-winsource.h. */
615
616 void
617 tui_update_all_breakpoint_info (struct breakpoint *being_deleted)
618 {
619 for (tui_source_window_base *win : tui_source_windows ())
620 {
621 if (win->update_breakpoint_info (being_deleted, false))
622 win->update_exec_info ();
623 }
624 }
625
626
627 /* Scan the source window and the breakpoints to update the break_mode
628 information for each line.
629
630 Returns true if something changed and the execution window must be
631 refreshed. */
632
633 bool
634 tui_source_window_base::update_breakpoint_info
635 (struct breakpoint *being_deleted, bool current_only)
636 {
637 int i;
638 bool need_refresh = false;
639
640 for (i = 0; i < m_content.size (); i++)
641 {
642 struct tui_source_element *line;
643
644 line = &m_content[i];
645 if (current_only && !line->is_exec_point)
646 continue;
647
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 ())
653 {
654 if (&bp == being_deleted)
655 continue;
656
657 for (bp_location &loc : bp.locations ())
658 {
659 if (location_matches_p (&loc, i))
660 {
661 if (bp.enable_state == bp_disabled)
662 mode |= TUI_BP_DISABLED;
663 else
664 mode |= TUI_BP_ENABLED;
665 if (bp.hit_count)
666 mode |= TUI_BP_HIT;
667 if (bp.first_loc ().cond)
668 mode |= TUI_BP_CONDITIONAL;
669 if (bp.type == bp_hardware_breakpoint)
670 mode |= TUI_BP_HARDWARE;
671 }
672 }
673 }
674
675 if (line->break_mode != mode)
676 {
677 line->break_mode = mode;
678 need_refresh = true;
679 }
680 }
681 return need_refresh;
682 }
683
684 /* See tui-winsource.h. */
685
686 void
687 tui_source_window_base::update_exec_info (bool refresh_p)
688 {
689 update_breakpoint_info (nullptr, true);
690 for (int i = 0; i < m_content.size (); i++)
691 {
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';
700
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';
708
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] = '-';
713
714 if (src_element->is_exec_point)
715 element[TUI_EXEC_POS] = '>';
716
717 display_string (i + box_width (), box_width (), element);
718
719 show_line_number (i);
720 }
721 if (refresh_p)
722 refresh_window ();
723 }