]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - gdb/python/lib/gdb/dap/breakpoint.py
Initial implementation of Debugger Adapter Protocol
[thirdparty/binutils-gdb.git] / gdb / python / lib / gdb / dap / breakpoint.py
1 # Copyright 2022 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 import gdb
17 import os
18
19 from .server import request, capability
20 from .startup import send_gdb_with_response, in_gdb_thread
21
22
23 # Map from the breakpoint "kind" (like "function") to a second map, of
24 # breakpoints of that type. The second map uses the breakpoint spec
25 # as a key, and the gdb.Breakpoint itself as a value. This is used to
26 # implement the clearing behavior specified by the protocol, while
27 # allowing for reuse when a breakpoint can be kept.
28 breakpoint_map = {}
29
30
31 @in_gdb_thread
32 def breakpoint_descriptor(bp):
33 "Return the Breakpoint object descriptor given a gdb Breakpoint."
34 if bp.locations:
35 # Just choose the first location, because DAP doesn't allow
36 # multiple locations. See
37 # https://github.com/microsoft/debug-adapter-protocol/issues/13
38 loc = bp.locations[0]
39 (basename, line) = loc.source
40 return {
41 "id": bp.number,
42 "verified": True,
43 "source": {
44 "name": os.path.basename(basename),
45 "path": loc.fullname,
46 # We probably don't need this but it doesn't hurt to
47 # be explicit.
48 "sourceReference": 0,
49 },
50 "line": line,
51 "instructionReference": hex(loc.address),
52 }
53 else:
54 return {
55 "id": bp.number,
56 "verified": False,
57 }
58
59
60 # Helper function to set some breakpoints according to a list of
61 # specifications.
62 @in_gdb_thread
63 def _set_breakpoints(kind, specs):
64 global breakpoint_map
65 # Try to reuse existing breakpoints if possible.
66 if kind in breakpoint_map:
67 saved_map = breakpoint_map[kind]
68 else:
69 saved_map = {}
70 breakpoint_map[kind] = {}
71 result = []
72 for spec in specs:
73 keyspec = frozenset(spec.items())
74 if keyspec in saved_map:
75 bp = saved_map.pop(keyspec)
76 else:
77 # FIXME handle exceptions here
78 bp = gdb.Breakpoint(**spec)
79 breakpoint_map[kind][keyspec] = bp
80 result.append(breakpoint_descriptor(bp))
81 # Delete any breakpoints that were not reused.
82 for entry in saved_map.values():
83 entry.delete()
84 return result
85
86
87 @request("setBreakpoints")
88 def set_breakpoint(source, *, breakpoints=[], **args):
89 if "path" not in source:
90 result = []
91 else:
92 specs = []
93 for obj in breakpoints:
94 specs.append(
95 {
96 "source": source["path"],
97 "line": obj["line"],
98 }
99 )
100 # Be sure to include the path in the key, so that we only
101 # clear out breakpoints coming from this same source.
102 key = "source:" + source["path"]
103 result = send_gdb_with_response(lambda: _set_breakpoints(key, specs))
104 return {
105 "breakpoints": result,
106 }
107
108
109 @request("setFunctionBreakpoints")
110 @capability("supportsFunctionBreakpoints")
111 def set_fn_breakpoint(breakpoints, **args):
112 specs = []
113 for bp in breakpoints:
114 specs.append(
115 {
116 "function": bp["name"],
117 }
118 )
119 result = send_gdb_with_response(lambda: _set_breakpoints("function", specs))
120 return {
121 "breakpoints": result,
122 }
123
124
125 @request("setInstructionBreakpoints")
126 @capability("supportsInstructionBreakpoints")
127 def set_insn_breakpoints(*, breakpoints, offset=None, **args):
128 specs = []
129 for bp in breakpoints:
130 # There's no way to set an explicit address breakpoint
131 # from Python, so we rely on "spec" instead.
132 val = "*" + bp["instructionReference"]
133 if offset is not None:
134 val = val + " + " + str(offset)
135 specs.append(
136 {
137 "spec": val,
138 }
139 )
140 result = send_gdb_with_response(lambda: _set_breakpoints("instruction", specs))
141 return {
142 "breakpoints": result,
143 }