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