GDB (xrefs)
Loading...
Searching...
No Matches
events.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
16import enum
17import gdb
18
19from .server import send_event
20from .startup import exec_and_log, in_gdb_thread, log
21from .modules import is_module, make_module
22
23
24# True when the inferior is thought to be running, False otherwise.
25# This may be accessed from any thread, which can be racy. However,
26# this unimportant because this global is only used for the
27# 'notStopped' response, which itself is inherently racy.
28inferior_running = False
29
30
31@in_gdb_thread
32def _on_exit(event):
33 global inferior_running
34 inferior_running = False
35 code = 0
36 if hasattr(event, "exit_code"):
37 code = event.exit_code
38 send_event(
39 "exited",
40 {
41 "exitCode": code,
42 },
43 )
44 send_event("terminated")
45
46
47@in_gdb_thread
48def thread_event(event, reason):
49 send_event(
50 "thread",
51 {
52 "reason": reason,
53 "threadId": event.inferior_thread.global_num,
54 },
55 )
56
57
58@in_gdb_thread
59def _new_thread(event):
60 global inferior_running
61 inferior_running = True
62 thread_event(event, "started")
63
64
65@in_gdb_thread
66def _thread_exited(event):
67 thread_event(event, "exited")
68
69
70@in_gdb_thread
71def _new_objfile(event):
72 if is_module(event.new_objfile):
73 send_event(
74 "module",
75 {
76 "reason": "new",
77 "module": make_module(event.new_objfile),
78 },
79 )
80
81
82@in_gdb_thread
84 if is_module(event.objfile):
85 send_event(
86 "module",
87 {
88 "reason": "removed",
89 "module": make_module(event.objfile),
90 },
91 )
92
93
94_suppress_cont = False
95
96
97@in_gdb_thread
98def _cont(event):
99 global inferior_running
100 inferior_running = True
101 global _suppress_cont
102 if _suppress_cont:
103 log("_suppress_cont case")
104 _suppress_cont = False
105 else:
106 send_event(
107 "continued",
108 {
109 "threadId": gdb.selected_thread().global_num,
110 "allThreadsContinued": True,
111 },
112 )
113
114
115class StopKinds(enum.Enum):
116 # The values here are chosen to follow the DAP spec.
117 STEP = "step"
118 BREAKPOINT = "breakpoint"
119 PAUSE = "pause"
120 EXCEPTION = "exception"
121
122
123_expected_stop = None
124
125
126@in_gdb_thread
127def exec_and_expect_stop(cmd, reason):
128 """Indicate that a stop is expected, then execute CMD"""
129 global _expected_stop
130 _expected_stop = reason
131 if reason != StopKinds.PAUSE:
132 global _suppress_cont
133 _suppress_cont = True
134 # FIXME if the call fails should we clear _suppress_cont?
135 exec_and_log(cmd)
136
137
138@in_gdb_thread
139def _on_stop(event):
140 global inferior_running
141 inferior_running = False
142 log("entering _on_stop: " + repr(event))
143 global _expected_stop
144 obj = {
145 "threadId": gdb.selected_thread().global_num,
146 "allThreadsStopped": True,
147 }
148 if isinstance(event, gdb.BreakpointEvent):
149 # Ignore the expected stop, we hit a breakpoint instead.
150 _expected_stop = StopKinds.BREAKPOINT
151 obj["hitBreakpointIds"] = [x.number for x in event.breakpoints]
152 elif _expected_stop is None:
153 # FIXME what is even correct here
154 _expected_stop = StopKinds.EXCEPTION
155 obj["reason"] = _expected_stop.value
156 _expected_stop = None
157 send_event("stopped", obj)
158
159
160# This keeps a bit of state between the start of an inferior call and
161# the end. If the inferior was already running when the call started
162# (as can happen if a breakpoint condition calls a function), then we
163# do not want to emit 'continued' or 'stop' events for the call. Note
164# that, for some reason, gdb.events.cont does not fire for an infcall.
165_infcall_was_running = False
166
167
168@in_gdb_thread
170 global _infcall_was_running
171 if isinstance(event, gdb.InferiorCallPreEvent):
172 _infcall_was_running = inferior_running
173 if not _infcall_was_running:
174 _cont(None)
175 else:
176 if not _infcall_was_running:
177 _on_stop(None)
178
179
180gdb.events.stop.connect(_on_stop)
181gdb.events.exited.connect(_on_exit)
182gdb.events.new_thread.connect(_new_thread)
183gdb.events.thread_exited.connect(_thread_exited)
184gdb.events.cont.connect(_cont)
185gdb.events.new_objfile.connect(_new_objfile)
186gdb.events.free_objfile.connect(_objfile_removed)
187gdb.events.inferior_call.connect(_on_inferior_call)
_thread_exited(event)
Definition events.py:66
_objfile_removed(event)
Definition events.py:83
_cont(event)
Definition events.py:98
_new_thread(event)
Definition events.py:59
_on_exit(event)
Definition events.py:32
thread_event(event, reason)
Definition events.py:48
_on_inferior_call(event)
Definition events.py:169
_new_objfile(event)
Definition events.py:71
exec_and_expect_stop(cmd, reason)
Definition events.py:127
_on_stop(event)
Definition events.py:139