1 /* TUI display registers in 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 "arch-utils.h"
23 #include "tui/tui.h"
24 #include "tui/tui-data.h"
25 #include "symtab.h"
26 #include "gdbtypes.h"
27 #include "cli/cli-cmds.h"
28 #include "frame.h"
29 #include "regcache.h"
30 #include "inferior.h"
31 #include "target.h"
32 #include "tui/tui-layout.h"
33 #include "tui/tui-win.h"
34 #include "tui/tui-wingeneral.h"
35 #include "tui/tui-file.h"
36 #include "tui/tui-regs.h"
37 #include "tui/tui-io.h"
38 #include "reggroups.h"
39 #include "valprint.h"
40 #include "completer.h"
41
42 #include "gdb_curses.h"
43
44 /* A subclass of string_file that expands tab characters. */
45 class tab_expansion_file : public string_file
46 {
47 public:
48
49 tab_expansion_file () = default;
50
51 void write (const char *buf, long length_buf) override;
52
53 private:
54
55 int m_column = 0;
56 };
57
58 void
write(const char * buf,long length_buf)59 tab_expansion_file::write (const char *buf, long length_buf)
60 {
61 for (long i = 0; i < length_buf; ++i)
62 {
63 if (buf[i] == '\t')
64 {
65 do
66 {
67 string_file::write (" ", 1);
68 ++m_column;
69 }
70 while ((m_column % 8) != 0);
71 }
72 else
73 {
74 string_file::write (&buf[i], 1);
75 if (buf[i] == '\n')
76 m_column = 0;
77 else
78 ++m_column;
79 }
80 }
81 }
82
83 /* Get the register from the frame and return a printable
84 representation of it. */
85
86 static std::string
tui_register_format(const frame_info_ptr & frame,int regnum)87 tui_register_format (const frame_info_ptr &frame, int regnum)
88 {
89 struct gdbarch *gdbarch = get_frame_arch (frame);
90
91 /* Expand tabs into spaces, since ncurses on MS-Windows doesn't. */
92 tab_expansion_file stream;
93 gdbarch_print_registers_info (gdbarch, &stream, frame, regnum, 1);
94
95 /* Remove the possible \n. */
96 std::string str = stream.release ();
97 if (!str.empty () && str.back () == '\n')
98 str.pop_back ();
99
100 return str;
101 }
102
103 /* Compute the register value from the given frame and format it for
104 the display. update 'content' and set 'highlight' if the contents
105 changed. */
106 void
update(const frame_info_ptr & frame)107 tui_register_info::update (const frame_info_ptr &frame)
108 {
109 std::string new_content = tui_register_format (frame, m_regno);
110 highlight = content != new_content;
111 content = std::move (new_content);
112 }
113
114 /* See tui-regs.h. */
115
116 int
last_regs_line_no()117 tui_data_window::last_regs_line_no () const
118 {
119 int num_lines = m_regs_content.size () / m_regs_column_count;
120 if (m_regs_content.size () % m_regs_column_count)
121 num_lines++;
122 return num_lines;
123 }
124
125 /* See tui-regs.h. */
126
127 int
line_from_reg_element_no(int element_no)128 tui_data_window::line_from_reg_element_no (int element_no) const
129 {
130 if (element_no < m_regs_content.size ())
131 {
132 int i, line = (-1);
133
134 i = 1;
135 while (line == (-1))
136 {
137 if (element_no < m_regs_column_count * i)
138 line = i - 1;
139 else
140 i++;
141 }
142
143 return line;
144 }
145 else
146 return (-1);
147 }
148
149 /* See tui-regs.h. */
150
151 int
first_reg_element_no_inline(int line_no)152 tui_data_window::first_reg_element_no_inline (int line_no) const
153 {
154 if (line_no * m_regs_column_count <= m_regs_content.size ())
155 return ((line_no + 1) * m_regs_column_count) - m_regs_column_count;
156 else
157 return (-1);
158 }
159
160 /* Show the registers of the given group in the data window
161 and refresh the window. */
162 void
set_register_group(const reggroup * group)163 tui_data_window::set_register_group (const reggroup *group)
164 {
165 update_register_data (group);
166 rerender ();
167 }
168
169 /* Set the data window to display the registers of the register group
170 using the given frame. */
171
172 void
update_register_data(const reggroup * group)173 tui_data_window::update_register_data (const reggroup *group)
174 {
175 if (group == nullptr)
176 group = general_reggroup;
177
178 if (!target_has_registers ()
179 || !target_has_stack ()
180 || !target_has_memory ())
181 {
182 set_title (_("Registers"));
183 m_current_group = nullptr;
184 m_gdbarch = nullptr;
185 m_regs_content.clear ();
186 return;
187 }
188
189 frame_info_ptr frame = get_selected_frame (nullptr);
190 struct gdbarch *gdbarch = get_frame_arch (frame);
191
192 if (m_current_group == group && m_gdbarch == gdbarch)
193 {
194 /* Nothing to do here. */
195 return;
196 }
197
198 m_current_group = group;
199 m_gdbarch = gdbarch;
200
201 /* Make a new title showing which group we display. */
202 this->set_title (string_printf ("Register group: %s", group->name ()));
203
204 /* Create the registers. */
205 m_regs_content.clear ();
206
207 for (int regnum = 0;
208 regnum < gdbarch_num_cooked_regs (gdbarch);
209 regnum++)
210 {
211 /* Must be in the group. */
212 if (!gdbarch_register_reggroup_p (gdbarch, regnum, group))
213 continue;
214
215 /* If the register name is empty, it is undefined for this
216 processor, so don't display anything. */
217 const char *name = gdbarch_register_name (gdbarch, regnum);
218 if (*name == '\0')
219 continue;
220
221 m_regs_content.emplace_back (regnum, frame);
222 }
223 }
224
225 /* See tui-regs.h. */
226
227 void
display_registers_from(int start_element_no)228 tui_data_window::display_registers_from (int start_element_no)
229 {
230 werase (handle.get ());
231 check_and_display_highlight_if_needed ();
232
233 /* In case the regs window is not boxed, we'll write the last char in the
234 last line here, causing a scroll, so prevent that. */
235 scrollok (handle.get (), FALSE);
236
237 int max_len = 0;
238 for (auto &&data_item_win : m_regs_content)
239 {
240 int len = data_item_win.content.size ();
241
242 if (len > max_len)
243 max_len = len;
244 }
245 m_item_width = max_len + 1;
246
247 int i;
248 /* Mark register windows above the visible area. */
249 for (i = 0; i < start_element_no; i++)
250 m_regs_content[i].y = 0;
251
252 m_regs_column_count = (width - box_size ()) / m_item_width;
253 if (m_regs_column_count == 0)
254 m_regs_column_count = 1;
255 m_item_width = (width - box_size ()) / m_regs_column_count;
256
257 /* Now create each data "sub" window, and write the display into
258 it. */
259 int cur_y = box_width ();
260 while (i < m_regs_content.size () && cur_y <= height - box_size ())
261 {
262 for (int j = 0;
263 j < m_regs_column_count && i < m_regs_content.size ();
264 j++)
265 {
266 /* Create the window if necessary. */
267 m_regs_content[i].x = box_width () + (m_item_width * j);
268 m_regs_content[i].y = cur_y;
269 m_regs_content[i].rerender (handle.get (), m_item_width);
270 i++; /* Next register. */
271 }
272 cur_y++; /* Next row. */
273 }
274
275 /* Mark register windows below the visible area. */
276 for (; i < m_regs_content.size (); i++)
277 m_regs_content[i].y = 0;
278
279 refresh_window ();
280 }
281
282 /* See tui-regs.h. */
283
284 void
display_reg_element_at_line(int start_element_no,int start_line_no)285 tui_data_window::display_reg_element_at_line (int start_element_no,
286 int start_line_no)
287 {
288 int element_no = start_element_no;
289
290 if (start_element_no != 0 && start_line_no != 0)
291 {
292 int last_line_no, first_line_on_last_page;
293
294 last_line_no = last_regs_line_no ();
295 first_line_on_last_page = last_line_no - (height - box_size ());
296 if (first_line_on_last_page < 0)
297 first_line_on_last_page = 0;
298
299 /* If the element_no causes us to scroll past the end of the
300 registers, adjust what element to really start the
301 display at. */
302 if (start_line_no > first_line_on_last_page)
303 element_no = first_reg_element_no_inline (first_line_on_last_page);
304 }
305 display_registers_from (element_no);
306 }
307
308 /* See tui-regs.h. */
309
310 int
display_registers_from_line(int line_no)311 tui_data_window::display_registers_from_line (int line_no)
312 {
313 int element_no;
314
315 if (line_no < 0)
316 line_no = 0;
317 else
318 {
319 /* Make sure that we don't display off the end of the
320 registers. */
321 if (line_no >= last_regs_line_no ())
322 {
323 line_no = line_from_reg_element_no (m_regs_content.size () - 1);
324 if (line_no < 0)
325 line_no = 0;
326 }
327 }
328
329 element_no = first_reg_element_no_inline (line_no);
330 if (element_no < m_regs_content.size ())
331 display_reg_element_at_line (element_no, line_no);
332 else
333 line_no = (-1);
334
335 return line_no;
336 }
337
338
339 /* Answer the index first element displayed. If none are displayed,
340 then return (-1). */
341 int
first_data_item_displayed()342 tui_data_window::first_data_item_displayed ()
343 {
344 for (int i = 0; i < m_regs_content.size (); i++)
345 {
346 if (m_regs_content[i].visible ())
347 return i;
348 }
349
350 return -1;
351 }
352
353 void
erase_data_content()354 tui_data_window::erase_data_content ()
355 {
356 center_string (_("[ Register Values Unavailable ]"));
357 }
358
359 /* See tui-regs.h. */
360
361 void
rerender()362 tui_data_window::rerender ()
363 {
364 if (m_regs_content.empty ())
365 erase_data_content ();
366 else
367 display_registers_from (0);
368 tui_wrefresh (handle.get ());
369 }
370
371
372 /* Scroll the data window vertically forward or backward. */
373 void
do_scroll_vertical(int num_to_scroll)374 tui_data_window::do_scroll_vertical (int num_to_scroll)
375 {
376 int first_element_no;
377 int first_line = (-1);
378
379 first_element_no = first_data_item_displayed ();
380 if (first_element_no < m_regs_content.size ())
381 first_line = line_from_reg_element_no (first_element_no);
382 else
383 { /* Calculate the first line from the element number which is in
384 the general data content. */
385 }
386
387 if (first_line >= 0)
388 {
389 first_line += num_to_scroll;
390 display_registers_from_line (first_line);
391 }
392 }
393
394 /* This function check all displayed registers for changes in values,
395 given a particular frame. If the values have changed, they are
396 updated with the new value and highlighted. */
397 void
check_register_values(const frame_info_ptr & frame)398 tui_data_window::check_register_values (const frame_info_ptr &frame)
399 {
400 if (frame == nullptr)
401 set_register_group (nullptr);
402 else
403 {
404 /* If the frame architecture changed, we need to reset the
405 register group. */
406 struct gdbarch *gdbarch = get_frame_arch (frame);
407 if (gdbarch != m_gdbarch)
408 set_register_group (nullptr);
409 else
410 {
411 for (tui_register_info &data_item_win : m_regs_content)
412 {
413 bool was_hilighted = data_item_win.highlight;
414
415 data_item_win.update (frame);
416
417 /* Register windows whose y == 0 are outside the visible area. */
418 if ((data_item_win.highlight || was_hilighted)
419 && data_item_win.visible ())
420 data_item_win.rerender (handle.get (), m_item_width);
421 }
422 }
423 tui_wrefresh (handle.get ());
424 }
425 }
426
427 /* Display a register in a window. If hilite is TRUE, then the value
428 will be displayed in reverse video. */
429 void
rerender(WINDOW * handle,int field_width)430 tui_register_info::rerender (WINDOW *handle, int field_width)
431 {
432 if (highlight)
433 /* We ignore the return value, casting it to void in order to avoid
434 a compiler warning. The warning itself was introduced by a patch
435 to ncurses 5.7 dated 2009-08-29, changing this macro to expand
436 to code that causes the compiler to generate an unused-value
437 warning. */
438 (void) wstandout (handle);
439
440 mvwaddnstr (handle, y, x, content.c_str (), field_width - 1);
441 if (content.size () < field_width)
442 waddstr (handle, n_spaces (field_width - content.size ()));
443
444 if (highlight)
445 /* We ignore the return value, casting it to void in order to avoid
446 a compiler warning. The warning itself was introduced by a patch
447 to ncurses 5.7 dated 2009-08-29, changing this macro to expand
448 to code that causes the compiler to generate an unused-value
449 warning. */
450 (void) wstandend (handle);
451 }
452
453 /* Helper for "tui reg next", returns the next register group after
454 CURRENT_GROUP in the register group list for GDBARCH, with wrap around
455 behaviour.
456
457 If CURRENT_GROUP is nullptr (e.g. if the tui register window has only
458 just been displayed and has no current group selected) or the currently
459 selected register group can't be found (e.g. if the architecture has
460 changed since the register window was last updated), then the first
461 register group will be returned. */
462
463 static const reggroup *
tui_reg_next(const reggroup * current_group,struct gdbarch * gdbarch)464 tui_reg_next (const reggroup *current_group, struct gdbarch *gdbarch)
465 {
466 const std::vector<const reggroup *> &groups = gdbarch_reggroups (gdbarch);
467 auto it = std::find (groups.begin (), groups.end (), current_group);
468 if (it != groups.end ())
469 it++;
470 if (it == groups.end ())
471 return groups.front ();
472 return *it;
473 }
474
475 /* Helper for "tui reg prev", returns the register group previous to
476 CURRENT_GROUP in the register group list for GDBARCH, with wrap around
477 behaviour.
478
479 If CURRENT_GROUP is nullptr (e.g. if the tui register window has only
480 just been displayed and has no current group selected) or the currently
481 selected register group can't be found (e.g. if the architecture has
482 changed since the register window was last updated), then the last
483 register group will be returned. */
484
485 static const reggroup *
tui_reg_prev(const reggroup * current_group,struct gdbarch * gdbarch)486 tui_reg_prev (const reggroup *current_group, struct gdbarch *gdbarch)
487 {
488 const std::vector<const reggroup *> &groups = gdbarch_reggroups (gdbarch);
489 auto it = std::find (groups.rbegin (), groups.rend (), current_group);
490 if (it != groups.rend ())
491 it++;
492 if (it == groups.rend ())
493 return groups.back ();
494 return *it;
495 }
496
497 /* Implement the 'tui reg' command. Changes the register group displayed
498 in the tui register window. Displays the tui register window if it is
499 not already on display. */
500
501 static void
tui_reg_command(const char * args,int from_tty)502 tui_reg_command (const char *args, int from_tty)
503 {
504 struct gdbarch *gdbarch = get_current_arch ();
505
506 if (args != NULL)
507 {
508 size_t len = strlen (args);
509
510 /* Make sure the curses mode is enabled. */
511 tui_enable ();
512
513 tui_suppress_output suppress;
514
515 /* Make sure the register window is visible. If not, select an
516 appropriate layout. We need to do this before trying to run the
517 'next' or 'prev' commands. */
518 if (TUI_DATA_WIN == NULL || !TUI_DATA_WIN->is_visible ())
519 tui_regs_layout ();
520
521 const reggroup *match = nullptr;
522 const reggroup *current_group = TUI_DATA_WIN->get_current_group ();
523 if (strncmp (args, "next", len) == 0)
524 match = tui_reg_next (current_group, gdbarch);
525 else if (strncmp (args, "prev", len) == 0)
526 match = tui_reg_prev (current_group, gdbarch);
527 else
528 {
529 /* This loop matches on the initial part of a register group
530 name. If this initial part in ARGS matches only one register
531 group then the switch is made. */
532 for (const struct reggroup *group : gdbarch_reggroups (gdbarch))
533 {
534 if (strncmp (group->name (), args, len) == 0)
535 {
536 if (match != NULL)
537 error (_("ambiguous register group name '%s'"), args);
538 match = group;
539 }
540 }
541 }
542
543 if (match == NULL)
544 error (_("unknown register group '%s'"), args);
545
546 TUI_DATA_WIN->set_register_group (match);
547 }
548 else
549 {
550 gdb_printf (_("\"tui reg\" must be followed by the name of "
551 "either a register group,\nor one of 'next' "
552 "or 'prev'. Known register groups are:\n"));
553
554 bool first = true;
555 for (const struct reggroup *group : gdbarch_reggroups (gdbarch))
556 {
557 if (!first)
558 gdb_printf (", ");
559 first = false;
560 gdb_printf ("%s", group->name ());
561 }
562
563 gdb_printf ("\n");
564 }
565 }
566
567 /* Complete names of register groups, and add the special "prev" and "next"
568 names. */
569
570 static void
tui_reggroup_completer(struct cmd_list_element * ignore,completion_tracker & tracker,const char * text,const char * word)571 tui_reggroup_completer (struct cmd_list_element *ignore,
572 completion_tracker &tracker,
573 const char *text, const char *word)
574 {
575 static const char * const extra[] = { "next", "prev", NULL };
576
577 reggroup_completer (ignore, tracker, text, word);
578
579 complete_on_enum (tracker, extra, text, word);
580 }
581
582 void _initialize_tui_regs ();
583 void
_initialize_tui_regs()584 _initialize_tui_regs ()
585 {
586 struct cmd_list_element **tuicmd, *cmd;
587
588 tuicmd = tui_get_cmd_list ();
589
590 cmd = add_cmd ("reg", class_tui, tui_reg_command, _("\
591 TUI command to control the register window.\n\
592 Usage: tui reg NAME\n\
593 NAME is the name of the register group to display"), tuicmd);
594 set_cmd_completer (cmd, tui_reggroup_completer);
595 }
596