GDB (xrefs)
Loading...
Searching...
No Matches
startup.py
Go to the documentation of this file.
1# Copyright 2022-2023 Free Software Foundation, Inc.
2
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; either version 3 of the License, or
6# (at your option) any later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program. If not, see <http://www.gnu.org/licenses/>.
15
16# Do not import other gdbdap modules here -- this module must come
17# first.
18import functools
19import gdb
20import queue
21import threading
22import traceback
23import sys
24
25
26# The GDB thread, aka the main thread.
27_gdb_thread = threading.current_thread()
28
29
30# The DAP thread.
31_dap_thread = None
32
33
34def start_thread(name, target, args=()):
35 """Start a new thread, invoking TARGET with *ARGS there.
36 This is a helper function that ensures that any GDB signals are
37 correctly blocked."""
38 result = gdb.Thread(name=name, target=target, args=args, daemon=True)
39 result.start()
40
41
42def start_dap(target):
43 """Start the DAP thread and invoke TARGET there."""
44 exec_and_log("set breakpoint pending on")
45
46 # Functions in this thread contain assertions that check for this
47 # global, so we must set it before letting these functions run.
48 def really_start_dap():
49 global _dap_thread
50 _dap_thread = threading.current_thread()
51 target()
52
53 start_thread("DAP", really_start_dap)
54
55
56def in_gdb_thread(func):
57 """A decorator that asserts that FUNC must be run in the GDB thread."""
58
59 @functools.wraps(func)
60 def ensure_gdb_thread(*args, **kwargs):
61 assert threading.current_thread() is _gdb_thread
62 return func(*args, **kwargs)
63
64 return ensure_gdb_thread
65
66
67def in_dap_thread(func):
68 """A decorator that asserts that FUNC must be run in the DAP thread."""
69
70 @functools.wraps(func)
71 def ensure_dap_thread(*args, **kwargs):
72 assert threading.current_thread() is _dap_thread
73 return func(*args, **kwargs)
74
75 return ensure_dap_thread
76
77
78class LoggingParam(gdb.Parameter):
79 """Whether DAP logging is enabled."""
80
81 set_doc = "Set the DAP logging status."
82 show_doc = "Show the DAP logging status."
83
84 log_file = None
85
86 def __init__(self):
87 super().__init__(
88 "debug dap-log-file", gdb.COMMAND_MAINTENANCE, gdb.PARAM_OPTIONAL_FILENAME
89 )
90 self.value = None
91
92 def get_set_string(self):
93 # Close any existing log file, no matter what.
94 if self.log_file is not None:
95 self.log_file.close()
96 self.log_file = None
97 if self.value is not None:
98 self.log_file = open(self.value, "w")
99 return ""
100
101
102dap_log = LoggingParam()
103
104
105def log(something):
106 """Log SOMETHING to the log file, if logging is enabled."""
107 if dap_log.log_file is not None:
108 print(something, file=dap_log.log_file)
109 dap_log.log_file.flush()
110
111
113 """Log a stack trace to the log file, if logging is enabled."""
114 if dap_log.log_file is not None:
115 traceback.print_exc(file=dap_log.log_file)
116
117
118@in_gdb_thread
120 """Execute the gdb command CMD.
121 If logging is enabled, log the command and its output."""
122 log("+++ " + cmd)
123 try:
124 output = gdb.execute(cmd, from_tty=True, to_string=True)
125 if output != "":
126 log(">>> " + output)
127 except gdb.error:
128 log_stack()
129
130
131class Invoker(object):
132 """A simple class that can invoke a gdb command."""
133
134 def __init__(self, cmd):
135 self.cmd = cmd
136
137 # This is invoked in the gdb thread to run the command.
138 @in_gdb_thread
139 def __call__(self):
140 exec_and_log(self.cmd)
141
142
143def send_gdb(cmd):
144 """Send CMD to the gdb thread.
145 CMD can be either a function or a string.
146 If it is a string, it is passed to gdb.execute."""
147 if isinstance(cmd, str):
148 cmd = Invoker(cmd)
149 gdb.post_event(cmd)
150
151
153 """Send FN to the gdb thread and return its result.
154 If FN is a string, it is passed to gdb.execute and None is
155 returned as the result.
156 If FN throws an exception, this function will throw the
157 same exception in the calling thread.
158 """
159 if isinstance(fn, str):
160 fn = Invoker(fn)
161 if sys.version_info[0] == 3 and sys.version_info[1] <= 6:
162 result_q = queue.Queue()
163 else:
164 result_q = queue.SimpleQueue()
165
166 def message():
167 try:
168 val = fn()
169 result_q.put(val)
170 except Exception as e:
171 result_q.put(e)
172
173 send_gdb(message)
174 val = result_q.get()
175 if isinstance(val, Exception):
176 raise val
177 return val
in_gdb_thread(func)
Definition startup.py:56
send_gdb_with_response(fn)
Definition startup.py:152
exec_and_log(cmd)
Definition startup.py:119
log(something)
Definition startup.py:105
start_dap(target)
Definition startup.py:42
start_thread(name, target, args=())
Definition startup.py:34
in_dap_thread(func)
Definition startup.py:67
void(* func)(remote_target *remote, char *)