1 /* TUI status line.
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 "symtab.h"
23 #include "breakpoint.h"
24 #include "frame.h"
25 #include "command.h"
26 #include "inferior.h"
27 #include "target.h"
28 #include "top.h"
29 #include "gdb-demangle.h"
30 #include "source.h"
31 #include "tui/tui.h"
32 #include "tui/tui-data.h"
33 #include "tui/tui-status.h"
34 #include "tui/tui-wingeneral.h"
35 #include "tui/tui-source.h"
36 #include "tui/tui-winsource.h"
37 #include "tui/tui-file.h"
38 #include "tui/tui-location.h"
39 
40 #include "gdb_curses.h"
41 
42 static const std::string PROC_PREFIX = "In: ";
43 static const std::string LINE_PREFIX = "L";
44 static const std::string PC_PREFIX = "PC: ";
45 
46 /* Strings to display in the TUI status line.  */
47 static const std::string SINGLE_KEY = "(SingleKey)";
48 
49 /* Minimum/Maximum length of some fields displayed in the TUI status
50    line.  */
51 #define MIN_LINE_WIDTH     4  /* Use at least 4 digits for line
52                                            numbers.  */
53 #define MIN_PROC_WIDTH    12
54 #define MAX_TARGET_WIDTH  10
55 #define MAX_PID_WIDTH     19
56 
57 
58 
59 std::string
make_status_line()60 tui_status_window::make_status_line () const
61 {
62   char line_buf[50];
63   int status_size;
64   int proc_width;
65   const char *pid_name;
66   int target_width;
67   int pid_width;
68   int line_width;
69 
70   std::string pid_name_holder;
71   if (inferior_ptid == null_ptid)
72     pid_name = "No process";
73   else
74     {
75       pid_name_holder = target_pid_to_str (inferior_ptid);
76       pid_name = pid_name_holder.c_str ();
77     }
78 
79   target_width = strlen (target_shortname ());
80   if (target_width > MAX_TARGET_WIDTH)
81     target_width = MAX_TARGET_WIDTH;
82 
83   pid_width = strlen (pid_name);
84   if (pid_width > MAX_PID_WIDTH)
85     pid_width = MAX_PID_WIDTH;
86 
87   status_size = width;
88 
89   /* Translate line number and obtain its size.  */
90   int line_no = tui_location.line_no ();
91   if (line_no > 0)
92     xsnprintf (line_buf, sizeof (line_buf), "%d", line_no);
93   else
94     strcpy (line_buf, "??");
95   line_width = strlen (line_buf);
96   if (line_width < MIN_LINE_WIDTH)
97     line_width = MIN_LINE_WIDTH;
98 
99   /* Translate PC address.  */
100   struct gdbarch *gdbarch = tui_location.gdbarch ();
101   CORE_ADDR addr = tui_location.addr ();
102   std::string pc_out (gdbarch
103                           ? paddress (gdbarch, addr)
104                           : "??");
105   const char *pc_buf = pc_out.c_str ();
106   int pc_width = pc_out.size ();
107 
108   /* Width of the field showing the window with current focus.  For a window
109      named "src" we show "(src)".  */
110   int focus_width = (tui_win_with_focus () != nullptr
111                          ? 1 + strlen (tui_win_with_focus ()->name ()) + 1
112                          : 0);
113 
114   /* First determine the amount of proc name width we have available.
115      The +1 are for a space separator between fields.  */
116   proc_width = (status_size
117                     - (target_width + 1)
118                     - (pid_width + 1)
119                     - (PROC_PREFIX.size () + 1)
120                     - (LINE_PREFIX.size () + line_width + 1)
121                     - (PC_PREFIX.size () + pc_width + 1)
122                     - (tui_current_key_mode == TUI_SINGLE_KEY_MODE
123                        ? (SINGLE_KEY.size () + 1)
124                        : 0)
125                     - (focus_width > 0
126                        ? focus_width + 1
127                        : 0));
128 
129   /* If there is no room to print the function name, try by removing
130      some fields.  */
131   if (proc_width < MIN_PROC_WIDTH)
132     {
133       proc_width += target_width + 1;
134       target_width = 0;
135       if (proc_width < MIN_PROC_WIDTH)
136           {
137             proc_width += pid_width + 1;
138             pid_width = 0;
139             if (proc_width <= MIN_PROC_WIDTH)
140               {
141                 proc_width += pc_width + PC_PREFIX.size () + 1;
142                 pc_width = 0;
143                 if (proc_width < 0)
144                     {
145                       proc_width += line_width + LINE_PREFIX.size () + 1;
146                       line_width = 0;
147                       if (proc_width < 0)
148                         proc_width = 0;
149                     }
150               }
151           }
152     }
153 
154   /* Now create the status line from the string version of the
155      elements.  */
156   string_file string;
157 
158   if (target_width > 0)
159     string.printf ("%*.*s ", -target_width, target_width, target_shortname ());
160   if (pid_width > 0)
161     string.printf ("%*.*s ", -pid_width, pid_width, pid_name);
162 
163   /* Show whether we are in SingleKey mode.  */
164   if (tui_current_key_mode == TUI_SINGLE_KEY_MODE)
165     {
166       string.puts (SINGLE_KEY.c_str ());
167       string.puts (" ");
168     }
169 
170   if (tui_win_with_focus () != nullptr)
171     {
172       string.puts ("(");
173       string.puts (tui_win_with_focus ()->name ());
174       string.puts (") ");
175     }
176 
177   /* Procedure/class name.  */
178   if (proc_width > 0)
179     {
180       const std::string &proc_name = tui_location.proc_name ();
181       if (proc_name.size () > proc_width)
182           string.printf ("%s%*.*s* ", PROC_PREFIX.c_str (),
183                            1 - proc_width, proc_width - 1, proc_name.c_str ());
184       else
185           string.printf ("%s%*.*s ", PROC_PREFIX.c_str (),
186                            -proc_width, proc_width, proc_name.c_str ());
187     }
188 
189   if (line_width > 0)
190     string.printf ("%s%*.*s ", LINE_PREFIX.c_str (),
191                        -line_width, line_width, line_buf);
192   if (pc_width > 0)
193     {
194       string.puts (PC_PREFIX.c_str ());
195       string.puts (pc_buf);
196     }
197 
198   std::string string_val = string.release ();
199 
200   size_t len = string_val.size ();
201   if (len < status_size)
202     string_val.append (status_size - len, ' ');
203   else if (len > status_size)
204     string_val.erase (status_size, len);
205 
206   gdb_assert (string_val.size () == status_size);
207 
208   return string_val;
209 }
210 
211 /* Get a printable name for the function at the address.  The symbol
212    name is demangled if demangling is turned on.  Returns a pointer to
213    a static area holding the result.  */
214 static char*
tui_get_function_from_frame(const frame_info_ptr & fi)215 tui_get_function_from_frame (const frame_info_ptr &fi)
216 {
217   static char name[256];
218   string_file stream;
219 
220   print_address_symbolic (get_frame_arch (fi), get_frame_pc (fi),
221                                 &stream, demangle, "");
222 
223   /* Use simple heuristics to isolate the function name.  The symbol
224      can be demangled and we can have function parameters.  Remove
225      them because the status line is too short to display them.  */
226   const char *d = stream.c_str ();
227   if (*d == '<')
228     d++;
229   strncpy (name, d, sizeof (name) - 1);
230   name[sizeof (name) - 1] = 0;
231 
232   char *p = strchr (name, '(');
233   if (!p)
234     p = strchr (name, '>');
235   if (p)
236     *p = 0;
237   p = strchr (name, '+');
238   if (p)
239     *p = 0;
240   return name;
241 }
242 
243 void
rerender()244 tui_status_window::rerender ()
245 {
246   gdb_assert (handle != NULL);
247 
248   std::string string = make_status_line ();
249   scrollok (handle.get (), FALSE);
250   wmove (handle.get (), 0, 0);
251   /* We ignore the return value from wstandout and wstandend, casting them
252      to void in order to avoid a compiler warning.  The warning itself was
253      introduced by a patch to ncurses 5.7 dated 2009-08-29, changing these
254      macro to expand to code that causes the compiler to generate an
255      unused-value warning.  */
256   (void) wstandout (handle.get ());
257   waddstr (handle.get (), string.c_str ());
258   wclrtoeol (handle.get ());
259   (void) wstandend (handle.get ());
260   refresh_window ();
261   wmove (handle.get (), 0, 0);
262 }
263 
264 /* Function to print the frame information for the TUI.  The windows
265    are refreshed only if frame information has changed since the last
266    refresh.  */
267 
268 void
tui_show_frame_info(const frame_info_ptr & fi)269 tui_show_frame_info (const frame_info_ptr &fi)
270 {
271   bool status_changed_p;
272 
273   if (fi != nullptr)
274     {
275       symtab_and_line sal = find_frame_sal (fi);
276 
277       const char *func_name;
278       /* find_frame_sal does not always set PC, but we want to ensure
279            that it is available in the SAL.  */
280       if (get_frame_pc_if_available (fi, &sal.pc))
281           func_name = tui_get_function_from_frame (fi);
282       else
283           func_name = _("<unavailable>");
284 
285       status_changed_p
286           = tui_location.set_location (get_frame_arch (fi), sal, func_name);
287 
288       /* If the status information has not changed, then frame information has
289            not changed.  If frame information has not changed, then the windows'
290            contents will not change.  So don't bother refreshing the windows.  */
291       if (!status_changed_p)
292           return;
293 
294       for (struct tui_source_window_base *win_info : tui_source_windows ())
295           {
296             win_info->maybe_update (fi, sal);
297             win_info->update_exec_info ();
298           }
299     }
300   else
301     {
302       symtab_and_line sal {};
303 
304       status_changed_p = tui_location.set_location (NULL, sal, "");
305 
306       if (!status_changed_p)
307           return;
308 
309       for (struct tui_source_window_base *win_info : tui_source_windows ())
310           win_info->erase_source_content ();
311     }
312 }
313 
314 void
tui_show_status_content()315 tui_show_status_content ()
316 {
317   if (tui_is_window_visible (STATUS_WIN))
318     TUI_STATUS_WIN->rerender ();
319 }
320 
321 /* Command to update the display with the current execution point.  */
322 static void
tui_update_command(const char * arg,int from_tty)323 tui_update_command (const char *arg, int from_tty)
324 {
325   execute_command ("frame 0", from_tty);
326 }
327 
328 /* Function to initialize gdb commands, for tui window stack
329    manipulation.  */
330 
331 void _initialize_tui_stack ();
332 void
_initialize_tui_stack()333 _initialize_tui_stack ()
334 {
335   add_com ("update", class_tui, tui_update_command,
336              _("\
337 Update the source window to display the current execution point.\n\
338 Usage: update"));
339 }
340