1/* TUI display source/assembly window. 2 3 Copyright (C) 1998-2023 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 "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-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" 44 45/* Function to display the "main" routine. */ 46void 47tui_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 69std::string 70tui_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 141void 142tui_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. */ 150void 151tui_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. */ 162void 163tui_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 update_breakpoint_info (nullptr, false); 174 show_source_content (); 175 update_exec_info (); 176 } 177} 178 179 180/* Function to ensure that the source and/or disassemly windows 181 reflect the input address. */ 182void 183tui_update_source_windows_with_addr (struct gdbarch *gdbarch, CORE_ADDR addr) 184{ 185 struct symtab_and_line sal {}; 186 if (addr != 0) 187 sal = find_pc_line (addr, 0); 188 189 for (struct tui_source_window_base *win_info : tui_source_windows ()) 190 win_info->update_source_window (gdbarch, sal); 191} 192 193/* Function to ensure that the source and/or disassembly windows 194 reflect the symtab and line. */ 195void 196tui_update_source_windows_with_line (struct symtab_and_line sal) 197{ 198 struct gdbarch *gdbarch = nullptr; 199 if (sal.symtab != nullptr) 200 { 201 find_line_pc (sal.symtab, sal.line, &sal.pc); 202 gdbarch = sal.symtab->compunit ()->objfile ()->arch (); 203 } 204 205 for (struct tui_source_window_base *win_info : tui_source_windows ()) 206 win_info->update_source_window (gdbarch, sal); 207} 208 209void 210tui_source_window_base::do_erase_source_content (const char *str) 211{ 212 int x_pos; 213 int half_width = (width - 2) / 2; 214 215 m_content.clear (); 216 if (handle != NULL) 217 { 218 werase (handle.get ()); 219 check_and_display_highlight_if_needed (); 220 221 if (strlen (str) >= half_width) 222 x_pos = 1; 223 else 224 x_pos = half_width - strlen (str); 225 mvwaddstr (handle.get (), 226 (height / 2), 227 x_pos, 228 (char *) str); 229 230 refresh_window (); 231 } 232} 233 234 235/* Redraw the complete line of a source or disassembly window. */ 236void 237tui_source_window_base::show_source_line (int lineno) 238{ 239 struct tui_source_element *line; 240 241 line = &m_content[lineno]; 242 if (line->is_exec_point) 243 tui_set_reverse_mode (m_pad.get (), true); 244 245 wmove (m_pad.get (), lineno, 0); 246 tui_puts (line->line.c_str (), m_pad.get ()); 247 if (line->is_exec_point) 248 tui_set_reverse_mode (m_pad.get (), false); 249} 250 251/* See tui-winsource.h. */ 252 253void 254tui_source_window_base::refresh_window () 255{ 256 /* tui_win_info::refresh_window would draw the empty background window to 257 the screen, potentially creating a flicker. */ 258 wnoutrefresh (handle.get ()); 259 260 int pad_width = std::max (m_max_length, width); 261 int left_margin = 1 + TUI_EXECINFO_SIZE + extra_margin (); 262 int view_width = width - left_margin - 1; 263 int pad_x = std::min (pad_width - view_width, m_horizontal_offset); 264 /* Ensure that an equal number of scrolls will work if the user 265 scrolled beyond where we clip. */ 266 m_horizontal_offset = pad_x; 267 prefresh (m_pad.get (), 0, pad_x, y + 1, x + left_margin, 268 y + m_content.size (), x + left_margin + view_width - 1); 269} 270 271void 272tui_source_window_base::show_source_content () 273{ 274 gdb_assert (!m_content.empty ()); 275 276 check_and_display_highlight_if_needed (); 277 278 int pad_width = std::max (m_max_length, width); 279 if (m_pad == nullptr || pad_width > getmaxx (m_pad.get ()) 280 || m_content.size () > getmaxy (m_pad.get ())) 281 m_pad.reset (newpad (m_content.size (), pad_width)); 282 283 werase (m_pad.get ()); 284 for (int lineno = 0; lineno < m_content.size (); lineno++) 285 show_source_line (lineno); 286 287 refresh_window (); 288} 289 290tui_source_window_base::tui_source_window_base () 291{ 292 m_start_line_or_addr.loa = LOA_ADDRESS; 293 m_start_line_or_addr.u.addr = 0; 294 295 gdb::observers::styling_changed.attach 296 (std::bind (&tui_source_window::style_changed, this), 297 m_observable, "tui-winsource"); 298} 299 300tui_source_window_base::~tui_source_window_base () 301{ 302 gdb::observers::styling_changed.detach (m_observable); 303} 304 305/* See tui-data.h. */ 306 307void 308tui_source_window_base::update_tab_width () 309{ 310 werase (handle.get ()); 311 rerender (); 312} 313 314void 315tui_source_window_base::rerender () 316{ 317 if (!m_content.empty ()) 318 { 319 struct symtab_and_line cursal 320 = get_current_source_symtab_and_line (); 321 322 if (m_start_line_or_addr.loa == LOA_LINE) 323 cursal.line = m_start_line_or_addr.u.line_no; 324 else 325 cursal.pc = m_start_line_or_addr.u.addr; 326 update_source_window (m_gdbarch, cursal); 327 } 328 else if (deprecated_safe_get_selected_frame () != NULL) 329 { 330 struct symtab_and_line cursal 331 = get_current_source_symtab_and_line (); 332 frame_info_ptr frame = deprecated_safe_get_selected_frame (); 333 struct gdbarch *gdbarch = get_frame_arch (frame); 334 335 struct symtab *s = find_pc_line_symtab (get_frame_pc (frame)); 336 if (this != TUI_SRC_WIN) 337 find_line_pc (s, cursal.line, &cursal.pc); 338 update_source_window (gdbarch, cursal); 339 } 340 else 341 erase_source_content (); 342} 343 344/* See tui-data.h. */ 345 346void 347tui_source_window_base::refill () 348{ 349 symtab_and_line sal {}; 350 351 if (this == TUI_SRC_WIN) 352 { 353 sal = get_current_source_symtab_and_line (); 354 if (sal.symtab == NULL) 355 { 356 frame_info_ptr fi = deprecated_safe_get_selected_frame (); 357 if (fi != nullptr) 358 sal = find_pc_line (get_frame_pc (fi), 0); 359 } 360 } 361 362 if (sal.pspace == nullptr) 363 sal.pspace = current_program_space; 364 365 if (m_start_line_or_addr.loa == LOA_LINE) 366 sal.line = m_start_line_or_addr.u.line_no; 367 else 368 sal.pc = m_start_line_or_addr.u.addr; 369 370 update_source_window_as_is (m_gdbarch, sal); 371} 372 373/* Scroll the source forward or backward horizontally. */ 374 375void 376tui_source_window_base::do_scroll_horizontal (int num_to_scroll) 377{ 378 if (!m_content.empty ()) 379 { 380 int offset = m_horizontal_offset + num_to_scroll; 381 if (offset < 0) 382 offset = 0; 383 m_horizontal_offset = offset; 384 refresh_window (); 385 } 386} 387 388 389/* Set or clear the is_exec_point flag in the line whose line is 390 line_no. */ 391 392void 393tui_source_window_base::set_is_exec_point_at (struct tui_line_or_address l) 394{ 395 bool changed = false; 396 int i; 397 398 i = 0; 399 while (i < m_content.size ()) 400 { 401 bool new_state; 402 struct tui_line_or_address content_loa = 403 m_content[i].line_or_addr; 404 405 if (content_loa.loa == l.loa 406 && ((l.loa == LOA_LINE && content_loa.u.line_no == l.u.line_no) 407 || (l.loa == LOA_ADDRESS && content_loa.u.addr == l.u.addr))) 408 new_state = true; 409 else 410 new_state = false; 411 if (new_state != m_content[i].is_exec_point) 412 { 413 changed = true; 414 m_content[i].is_exec_point = new_state; 415 } 416 i++; 417 } 418 if (changed) 419 refill (); 420} 421 422/* See tui-winsource.h. */ 423 424void 425tui_update_all_breakpoint_info (struct breakpoint *being_deleted) 426{ 427 for (tui_source_window_base *win : tui_source_windows ()) 428 { 429 if (win->update_breakpoint_info (being_deleted, false)) 430 win->update_exec_info (); 431 } 432} 433 434 435/* Scan the source window and the breakpoints to update the break_mode 436 information for each line. 437 438 Returns true if something changed and the execution window must be 439 refreshed. */ 440 441bool 442tui_source_window_base::update_breakpoint_info 443 (struct breakpoint *being_deleted, bool current_only) 444{ 445 int i; 446 bool need_refresh = false; 447 448 for (i = 0; i < m_content.size (); i++) 449 { 450 struct tui_source_element *line; 451 452 line = &m_content[i]; 453 if (current_only && !line->is_exec_point) 454 continue; 455 456 /* Scan each breakpoint to see if the current line has something to 457 do with it. Identify enable/disabled breakpoints as well as 458 those that we already hit. */ 459 tui_bp_flags mode = 0; 460 for (breakpoint *bp : all_breakpoints ()) 461 { 462 if (bp == being_deleted) 463 continue; 464 465 for (bp_location *loc : bp->locations ()) 466 { 467 if (location_matches_p (loc, i)) 468 { 469 if (bp->enable_state == bp_disabled) 470 mode |= TUI_BP_DISABLED; 471 else 472 mode |= TUI_BP_ENABLED; 473 if (bp->hit_count) 474 mode |= TUI_BP_HIT; 475 if (bp->loc->cond) 476 mode |= TUI_BP_CONDITIONAL; 477 if (bp->type == bp_hardware_breakpoint) 478 mode |= TUI_BP_HARDWARE; 479 } 480 } 481 } 482 483 if (line->break_mode != mode) 484 { 485 line->break_mode = mode; 486 need_refresh = true; 487 } 488 } 489 return need_refresh; 490} 491 492/* Function to initialize the content of the execution info window, 493 based upon the input window which is either the source or 494 disassembly window. */ 495void 496tui_source_window_base::update_exec_info () 497{ 498 update_breakpoint_info (nullptr, true); 499 for (int i = 0; i < m_content.size (); i++) 500 { 501 struct tui_source_element *src_element = &m_content[i]; 502 char element[TUI_EXECINFO_SIZE] = " "; 503 504 /* Now update the exec info content based upon the state 505 of each line as indicated by the source content. */ 506 tui_bp_flags mode = src_element->break_mode; 507 if (mode & TUI_BP_HIT) 508 element[TUI_BP_HIT_POS] = (mode & TUI_BP_HARDWARE) ? 'H' : 'B'; 509 else if (mode & (TUI_BP_ENABLED | TUI_BP_DISABLED)) 510 element[TUI_BP_HIT_POS] = (mode & TUI_BP_HARDWARE) ? 'h' : 'b'; 511 512 if (mode & TUI_BP_ENABLED) 513 element[TUI_BP_BREAK_POS] = '+'; 514 else if (mode & TUI_BP_DISABLED) 515 element[TUI_BP_BREAK_POS] = '-'; 516 517 if (src_element->is_exec_point) 518 element[TUI_EXEC_POS] = '>'; 519 520 mvwaddstr (handle.get (), i + 1, 1, element); 521 522 show_line_number (i); 523 } 524 refresh_window (); 525} 526