1/* TUI display source/assembly window. 2 3 Copyright (C) 1998-2020 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 32#include "tui/tui.h" 33#include "tui/tui-data.h" 34#include "tui/tui-io.h" 35#include "tui/tui-stack.h" 36#include "tui/tui-win.h" 37#include "tui/tui-wingeneral.h" 38#include "tui/tui-winsource.h" 39#include "tui/tui-source.h" 40#include "tui/tui-disasm.h" 41#include "gdb_curses.h" 42 43/* Function to display the "main" routine. */ 44void 45tui_display_main () 46{ 47 auto adapter = tui_source_windows (); 48 if (adapter.begin () != adapter.end ()) 49 { 50 struct gdbarch *gdbarch; 51 CORE_ADDR addr; 52 53 tui_get_begin_asm_address (&gdbarch, &addr); 54 if (addr != (CORE_ADDR) 0) 55 { 56 struct symtab *s; 57 58 tui_update_source_windows_with_addr (gdbarch, addr); 59 s = find_pc_line_symtab (addr); 60 tui_update_locator_fullname (s); 61 } 62 } 63} 64 65/* See tui-winsource.h. */ 66 67std::string 68tui_copy_source_line (const char **ptr, int line_no, int first_col, 69 int line_width, int ndigits) 70{ 71 const char *lineptr = *ptr; 72 73 /* Init the line with the line number. */ 74 std::string result; 75 76 if (line_no > 0) 77 { 78 if (ndigits > 0) 79 result = string_printf ("%*d ", ndigits, line_no); 80 else 81 { 82 result = string_printf ("%-6d", line_no); 83 int len = result.size (); 84 len = len - ((len / tui_tab_width) * tui_tab_width); 85 result.append (len, ' '); 86 } 87 } 88 89 int column = 0; 90 char c; 91 do 92 { 93 int skip_bytes; 94 95 c = *lineptr; 96 if (c == '\033' && skip_ansi_escape (lineptr, &skip_bytes)) 97 { 98 /* We always have to preserve escapes. */ 99 result.append (lineptr, lineptr + skip_bytes); 100 lineptr += skip_bytes; 101 continue; 102 } 103 if (c == '\0') 104 break; 105 106 ++lineptr; 107 ++column; 108 109 auto process_tab = [&] () 110 { 111 int max_tab_len = tui_tab_width; 112 113 --column; 114 for (int j = column % max_tab_len; 115 j < max_tab_len && column < first_col + line_width; 116 column++, j++) 117 if (column >= first_col) 118 result.push_back (' '); 119 }; 120 121 /* We have to process all the text in order to pick up all the 122 escapes. */ 123 if (column <= first_col || column > first_col + line_width) 124 { 125 if (c == '\t') 126 process_tab (); 127 continue; 128 } 129 130 if (c == '\n' || c == '\r' || c == '\0') 131 { 132 /* Nothing. */ 133 } 134 else if (c < 040 && c != '\t') 135 { 136 result.push_back ('^'); 137 result.push_back (c + 0100); 138 } 139 else if (c == 0177) 140 { 141 result.push_back ('^'); 142 result.push_back ('?'); 143 } 144 else if (c == '\t') 145 process_tab (); 146 else 147 result.push_back (c); 148 } 149 while (c != '\0' && c != '\n' && c != '\r'); 150 151 if (c == '\r' && *lineptr == '\n') 152 ++lineptr; 153 *ptr = lineptr; 154 155 return result; 156} 157 158void 159tui_source_window_base::style_changed () 160{ 161 if (tui_active && is_visible ()) 162 refill (); 163} 164 165/* Function to display source in the source window. This function 166 initializes the horizontal scroll to 0. */ 167void 168tui_source_window_base::update_source_window 169 (struct gdbarch *gdbarch, 170 const struct symtab_and_line &sal) 171{ 172 m_horizontal_offset = 0; 173 update_source_window_as_is (gdbarch, sal); 174} 175 176 177/* Function to display source in the source/asm window. This function 178 shows the source as specified by the horizontal offset. */ 179void 180tui_source_window_base::update_source_window_as_is 181 (struct gdbarch *gdbarch, 182 const struct symtab_and_line &sal) 183{ 184 bool ret = set_contents (gdbarch, sal); 185 186 if (!ret) 187 erase_source_content (); 188 else 189 { 190 update_breakpoint_info (nullptr, false); 191 show_source_content (); 192 update_exec_info (); 193 } 194} 195 196 197/* Function to ensure that the source and/or disassemly windows 198 reflect the input address. */ 199void 200tui_update_source_windows_with_addr (struct gdbarch *gdbarch, CORE_ADDR addr) 201{ 202 struct symtab_and_line sal {}; 203 if (addr != 0) 204 sal = find_pc_line (addr, 0); 205 206 for (struct tui_source_window_base *win_info : tui_source_windows ()) 207 win_info->update_source_window (gdbarch, sal); 208} 209 210/* Function to ensure that the source and/or disassembly windows 211 reflect the symtab and line. */ 212void 213tui_update_source_windows_with_line (struct symtab_and_line sal) 214{ 215 struct gdbarch *gdbarch = nullptr; 216 if (sal.symtab != nullptr) 217 { 218 find_line_pc (sal.symtab, sal.line, &sal.pc); 219 gdbarch = SYMTAB_OBJFILE (sal.symtab)->arch (); 220 } 221 222 for (struct tui_source_window_base *win_info : tui_source_windows ()) 223 win_info->update_source_window (gdbarch, sal); 224} 225 226void 227tui_source_window_base::do_erase_source_content (const char *str) 228{ 229 int x_pos; 230 int half_width = (width - 2) / 2; 231 232 m_content.clear (); 233 if (handle != NULL) 234 { 235 werase (handle.get ()); 236 check_and_display_highlight_if_needed (); 237 238 if (strlen (str) >= half_width) 239 x_pos = 1; 240 else 241 x_pos = half_width - strlen (str); 242 mvwaddstr (handle.get (), 243 (height / 2), 244 x_pos, 245 (char *) str); 246 247 refresh_window (); 248 } 249} 250 251 252/* Redraw the complete line of a source or disassembly window. */ 253void 254tui_source_window_base::show_source_line (int lineno) 255{ 256 struct tui_source_element *line; 257 int x; 258 259 line = &m_content[lineno - 1]; 260 if (line->is_exec_point) 261 tui_set_reverse_mode (handle.get (), true); 262 263 wmove (handle.get (), lineno, TUI_EXECINFO_SIZE); 264 tui_puts (line->line.c_str (), handle.get ()); 265 if (line->is_exec_point) 266 tui_set_reverse_mode (handle.get (), false); 267 268 /* Clear to end of line but stop before the border. */ 269 x = getcurx (handle.get ()); 270 while (x + 1 < width) 271 { 272 waddch (handle.get (), ' '); 273 x = getcurx (handle.get ()); 274 } 275} 276 277void 278tui_source_window_base::show_source_content () 279{ 280 gdb_assert (!m_content.empty ()); 281 282 for (int lineno = 1; lineno <= m_content.size (); lineno++) 283 show_source_line (lineno); 284 285 check_and_display_highlight_if_needed (); 286 refresh_window (); 287} 288 289tui_source_window_base::tui_source_window_base () 290{ 291 m_start_line_or_addr.loa = LOA_ADDRESS; 292 m_start_line_or_addr.u.addr = 0; 293 294 gdb::observers::source_styling_changed.attach 295 (std::bind (&tui_source_window::style_changed, this), 296 m_observable); 297} 298 299tui_source_window_base::~tui_source_window_base () 300{ 301 gdb::observers::source_styling_changed.detach (m_observable); 302} 303 304/* See tui-data.h. */ 305 306void 307tui_source_window_base::update_tab_width () 308{ 309 werase (handle.get ()); 310 rerender (); 311} 312 313void 314tui_source_window_base::rerender () 315{ 316 if (!m_content.empty ()) 317 { 318 struct symtab_and_line cursal 319 = get_current_source_symtab_and_line (); 320 321 if (m_start_line_or_addr.loa == LOA_LINE) 322 cursal.line = m_start_line_or_addr.u.line_no; 323 else 324 cursal.pc = m_start_line_or_addr.u.addr; 325 update_source_window (m_gdbarch, cursal); 326 } 327 else if (deprecated_safe_get_selected_frame () != NULL) 328 { 329 struct symtab_and_line cursal 330 = get_current_source_symtab_and_line (); 331 struct frame_info *frame = deprecated_safe_get_selected_frame (); 332 struct gdbarch *gdbarch = get_frame_arch (frame); 333 334 struct symtab *s = find_pc_line_symtab (get_frame_pc (frame)); 335 if (this != TUI_SRC_WIN) 336 find_line_pc (s, cursal.line, &cursal.pc); 337 update_source_window (gdbarch, cursal); 338 } 339 else 340 erase_source_content (); 341} 342 343/* See tui-data.h. */ 344 345void 346tui_source_window_base::refill () 347{ 348 symtab_and_line sal {}; 349 350 if (this == TUI_SRC_WIN) 351 { 352 sal = get_current_source_symtab_and_line (); 353 if (sal.symtab == NULL) 354 { 355 struct frame_info *fi = deprecated_safe_get_selected_frame (); 356 if (fi != nullptr) 357 sal = find_pc_line (get_frame_pc (fi), 0); 358 } 359 } 360 361 if (sal.pspace == nullptr) 362 sal.pspace = current_program_space; 363 364 if (m_start_line_or_addr.loa == LOA_LINE) 365 sal.line = m_start_line_or_addr.u.line_no; 366 else 367 sal.pc = m_start_line_or_addr.u.addr; 368 369 update_source_window_as_is (m_gdbarch, sal); 370} 371 372/* Scroll the source forward or backward horizontally. */ 373 374void 375tui_source_window_base::do_scroll_horizontal (int num_to_scroll) 376{ 377 if (!m_content.empty ()) 378 { 379 int offset = m_horizontal_offset + num_to_scroll; 380 if (offset < 0) 381 offset = 0; 382 m_horizontal_offset = offset; 383 refill (); 384 } 385} 386 387 388/* Set or clear the is_exec_point flag in the line whose line is 389 line_no. */ 390 391void 392tui_source_window_base::set_is_exec_point_at (struct tui_line_or_address l) 393{ 394 bool changed = false; 395 int i; 396 397 i = 0; 398 while (i < m_content.size ()) 399 { 400 bool new_state; 401 struct tui_line_or_address content_loa = 402 m_content[i].line_or_addr; 403 404 if (content_loa.loa == l.loa 405 && ((l.loa == LOA_LINE && content_loa.u.line_no == l.u.line_no) 406 || (l.loa == LOA_ADDRESS && content_loa.u.addr == l.u.addr))) 407 new_state = true; 408 else 409 new_state = false; 410 if (new_state != m_content[i].is_exec_point) 411 { 412 changed = true; 413 m_content[i].is_exec_point = new_state; 414 show_source_line (i + 1); 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 iterate_over_breakpoints ([&] (breakpoint *bp) -> bool 461 { 462 struct bp_location *loc; 463 464 if (bp == being_deleted) 465 return false; 466 467 for (loc = bp->loc; loc != NULL; loc = loc->next) 468 { 469 if (location_matches_p (loc, i)) 470 { 471 if (bp->enable_state == bp_disabled) 472 mode |= TUI_BP_DISABLED; 473 else 474 mode |= TUI_BP_ENABLED; 475 if (bp->hit_count) 476 mode |= TUI_BP_HIT; 477 if (bp->loc->cond) 478 mode |= TUI_BP_CONDITIONAL; 479 if (bp->type == bp_hardware_breakpoint) 480 mode |= TUI_BP_HARDWARE; 481 } 482 } 483 return false; 484 }); 485 if (line->break_mode != mode) 486 { 487 line->break_mode = mode; 488 need_refresh = true; 489 } 490 } 491 return need_refresh; 492} 493 494/* Function to initialize the content of the execution info window, 495 based upon the input window which is either the source or 496 disassembly window. */ 497void 498tui_source_window_base::update_exec_info () 499{ 500 update_breakpoint_info (nullptr, true); 501 for (int i = 0; i < m_content.size (); i++) 502 { 503 struct tui_source_element *src_element = &m_content[i]; 504 char element[TUI_EXECINFO_SIZE] = " "; 505 506 /* Now update the exec info content based upon the state 507 of each line as indicated by the source content. */ 508 tui_bp_flags mode = src_element->break_mode; 509 if (mode & TUI_BP_HIT) 510 element[TUI_BP_HIT_POS] = (mode & TUI_BP_HARDWARE) ? 'H' : 'B'; 511 else if (mode & (TUI_BP_ENABLED | TUI_BP_DISABLED)) 512 element[TUI_BP_HIT_POS] = (mode & TUI_BP_HARDWARE) ? 'h' : 'b'; 513 514 if (mode & TUI_BP_ENABLED) 515 element[TUI_BP_BREAK_POS] = '+'; 516 else if (mode & TUI_BP_DISABLED) 517 element[TUI_BP_BREAK_POS] = '-'; 518 519 if (src_element->is_exec_point) 520 element[TUI_EXEC_POS] = '>'; 521 522 mvwaddstr (handle.get (), i + 1, 1, element); 523 } 524 refresh_window (); 525} 526