1 /* GDB hooks for TUI.
2 
3    Copyright (C) 2001-2024 Free Software Foundation, Inc.
4 
5    This file is part of GDB.
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
19 
20 #include "symtab.h"
21 #include "inferior.h"
22 #include "command.h"
23 #include "bfd.h"
24 #include "symfile.h"
25 #include "objfiles.h"
26 #include "target.h"
27 #include "gdbcore.h"
28 #include "gdbsupport/event-loop.h"
29 #include "event-top.h"
30 #include "frame.h"
31 #include "breakpoint.h"
32 #include "ui-out.h"
33 #include "top.h"
34 #include "observable.h"
35 #include "source.h"
36 #include <unistd.h>
37 #include <fcntl.h>
38 
39 #include "tui/tui.h"
40 #include "tui/tui-hooks.h"
41 #include "tui/tui-data.h"
42 #include "tui/tui-layout.h"
43 #include "tui/tui-io.h"
44 #include "tui/tui-regs.h"
45 #include "tui/tui-win.h"
46 #include "tui/tui-status.h"
47 #include "tui/tui-winsource.h"
48 
49 #include "gdb_curses.h"
50 
51 static void
tui_new_objfile_hook(struct objfile * objfile)52 tui_new_objfile_hook (struct objfile* objfile)
53 {
54   if (tui_active)
55     tui_display_main ();
56 }
57 
58 /* Observer for the register_changed notification.  */
59 
60 static void
tui_register_changed(const frame_info_ptr & frame,int regno)61 tui_register_changed (const frame_info_ptr &frame, int regno)
62 {
63   frame_info_ptr fi;
64 
65   if (!tui_is_window_visible (DATA_WIN))
66     return;
67 
68   /* The frame of the register that was changed may differ from the selected
69      frame, but we only want to show the register values of the selected frame.
70      And even if the frames differ a register change made in one can still show
71      up in the other.  So we always use the selected frame here, and ignore
72      FRAME.  */
73   fi = get_selected_frame (NULL);
74   TUI_DATA_WIN->check_register_values (fi);
75 }
76 
77 /* Breakpoint creation hook.
78    Update the screen to show the new breakpoint.  */
79 static void
tui_event_create_breakpoint(struct breakpoint * b)80 tui_event_create_breakpoint (struct breakpoint *b)
81 {
82   tui_update_all_breakpoint_info (nullptr);
83 }
84 
85 /* Breakpoint deletion hook.
86    Refresh the screen to update the breakpoint marks.  */
87 static void
tui_event_delete_breakpoint(struct breakpoint * b)88 tui_event_delete_breakpoint (struct breakpoint *b)
89 {
90   tui_update_all_breakpoint_info (b);
91 }
92 
93 static void
tui_event_modify_breakpoint(struct breakpoint * b)94 tui_event_modify_breakpoint (struct breakpoint *b)
95 {
96   tui_update_all_breakpoint_info (nullptr);
97 }
98 
99 /* This is set to true if the next window refresh should come from the
100    current stack frame.  */
101 
102 static bool from_stack;
103 
104 /* This is set to true if the next window refresh should come from the
105    current source symtab.  */
106 
107 static bool from_source_symtab;
108 
109 /* Refresh TUI's frame and register information.  This is a hook intended to be
110    used to update the screen after potential frame and register changes.  */
111 
112 static void
tui_refresh_frame_and_register_information()113 tui_refresh_frame_and_register_information ()
114 {
115   if (!from_stack && !from_source_symtab)
116     return;
117 
118   target_terminal::scoped_restore_terminal_state term_state;
119   target_terminal::ours_for_output ();
120 
121   if (from_stack)
122     {
123       frame_info_ptr fi;
124       if (has_stack_frames ())
125           {
126             fi = get_selected_frame (NULL);
127 
128             /* Display the frame position (even if there is no symbols or
129                the PC is not known).  */
130             tui_show_frame_info (fi);
131           }
132 
133       /* Refresh the register window if it's visible.  */
134       if (tui_is_window_visible (DATA_WIN))
135           TUI_DATA_WIN->check_register_values (fi);
136     }
137   else
138     {
139       /* Make sure that the source window is displayed.  */
140       tui_add_win_to_layout (SRC_WIN);
141 
142       struct symtab_and_line sal = get_current_source_symtab_and_line ();
143       tui_update_source_windows_with_line (sal);
144     }
145 }
146 
147 /* Dummy callback for deprecated_print_frame_info_listing_hook which is called
148    from print_frame_info.  */
149 
150 static void
tui_dummy_print_frame_info_listing_hook(struct symtab * s,int line,int stopline,int noerror)151 tui_dummy_print_frame_info_listing_hook (struct symtab *s,
152                                                    int line,
153                                                    int stopline,
154                                                    int noerror)
155 {
156 }
157 
158 /* Perform all necessary cleanups regarding our module's inferior data
159    that is required after the inferior INF just exited.  */
160 
161 static void
tui_inferior_exit(struct inferior * inf)162 tui_inferior_exit (struct inferior *inf)
163 {
164   /* Leave the SingleKey mode to make sure the gdb prompt is visible.  */
165   tui_set_key_mode (TUI_COMMAND_MODE);
166   tui_show_frame_info (0);
167   tui_display_main ();
168   from_stack = true;
169 }
170 
171 /* Observer for the before_prompt notification.  */
172 
173 static void
tui_before_prompt(const char * current_gdb_prompt)174 tui_before_prompt (const char *current_gdb_prompt)
175 {
176   tui_refresh_frame_and_register_information ();
177   from_stack = false;
178   from_source_symtab = false;
179 }
180 
181 /* Observer for the normal_stop notification.  */
182 
183 static void
tui_normal_stop(struct bpstat * bs,int print_frame)184 tui_normal_stop (struct bpstat *bs, int print_frame)
185 {
186   from_stack = true;
187 }
188 
189 /* Observer for user_selected_context_changed.  */
190 
191 static void
tui_context_changed(user_selected_what ignore)192 tui_context_changed (user_selected_what ignore)
193 {
194   from_stack = true;
195 }
196 
197 /* Observer for current_source_symtab_and_line_changed.  */
198 
199 static void
tui_symtab_changed()200 tui_symtab_changed ()
201 {
202   from_source_symtab = true;
203 }
204 
205 /* Token associated with observers registered while TUI hooks are
206    installed.  */
207 static const gdb::observers::token tui_observers_token {};
208 
209 /* Attach or detach a single observer, according to ATTACH.  */
210 
211 template<typename T>
212 static void
attach_or_detach(T & observable,typename T::func_type func,bool attach)213 attach_or_detach (T &observable, typename T::func_type func, bool attach)
214 {
215   if (attach)
216     observable.attach (func, tui_observers_token, "tui-hooks");
217   else
218     observable.detach (tui_observers_token);
219 }
220 
221 /* Attach or detach TUI observers, according to ATTACH.  */
222 
223 static void
tui_attach_detach_observers(bool attach)224 tui_attach_detach_observers (bool attach)
225 {
226   attach_or_detach (gdb::observers::breakpoint_created,
227                         tui_event_create_breakpoint, attach);
228   attach_or_detach (gdb::observers::breakpoint_deleted,
229                         tui_event_delete_breakpoint, attach);
230   attach_or_detach (gdb::observers::breakpoint_modified,
231                         tui_event_modify_breakpoint, attach);
232   attach_or_detach (gdb::observers::inferior_exit,
233                         tui_inferior_exit, attach);
234   attach_or_detach (gdb::observers::before_prompt,
235                         tui_before_prompt, attach);
236   attach_or_detach (gdb::observers::normal_stop,
237                         tui_normal_stop, attach);
238   attach_or_detach (gdb::observers::register_changed,
239                         tui_register_changed, attach);
240   attach_or_detach (gdb::observers::user_selected_context_changed,
241                         tui_context_changed, attach);
242   attach_or_detach (gdb::observers::current_source_symtab_and_line_changed,
243                         tui_symtab_changed, attach);
244 }
245 
246 /* Install the TUI specific hooks.  */
247 void
tui_install_hooks(void)248 tui_install_hooks (void)
249 {
250   /* If this hook is not set to something then print_frame_info will
251      assume that the CLI, not the TUI, is active, and will print the frame info
252      for us in such a way that we are not prepared to handle.  This hook is
253      otherwise effectively obsolete.  */
254   deprecated_print_frame_info_listing_hook
255     = tui_dummy_print_frame_info_listing_hook;
256 
257   /* Install the event hooks.  */
258   tui_attach_detach_observers (true);
259 }
260 
261 /* Remove the TUI specific hooks.  */
262 void
tui_remove_hooks(void)263 tui_remove_hooks (void)
264 {
265   deprecated_print_frame_info_listing_hook = 0;
266 
267   /* Remove our observers.  */
268   tui_attach_detach_observers (false);
269 }
270 
271 void _initialize_tui_hooks ();
272 void
_initialize_tui_hooks()273 _initialize_tui_hooks ()
274 {
275   /* Install the permanent hooks.  */
276   gdb::observers::new_objfile.attach (tui_new_objfile_hook, "tui-hooks");
277 }
278