LLVM 20.0.0git
GDBRegistrationListener.cpp
Go to the documentation of this file.
1//===----- GDBRegistrationListener.cpp - Registers objects with GDB -------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10#include "llvm/ADT/DenseMap.h"
16#include "llvm/Support/Mutex.h"
17#include <mutex>
18
19using namespace llvm;
20using namespace llvm::object;
21
22// This must be kept in sync with gdb/gdb/jit.h .
23extern "C" {
24
25 typedef enum {
30
31 struct jit_code_entry {
34 const char *symfile_addr;
36 };
37
38 struct jit_descriptor {
40 // This should be jit_actions_t, but we want to be specific about the
41 // bit-width.
45 };
46
47 // We put information about the JITed function in this global, which the
48 // debugger reads. Make sure to specify the version statically, because the
49 // debugger checks the version before we can set it during runtime.
51
52 // Debuggers puts a breakpoint in this function.
53 extern "C" void __jit_debug_register_code();
54}
55
56namespace {
57
58// FIXME: lli aims to provide both, RuntimeDyld and JITLink, as the dynamic
59// loaders for its JIT implementations. And they both offer debugging via the
60// GDB JIT interface, which builds on the two well-known symbol names below.
61// As these symbols must be unique across the linked executable, we can only
62// define them in one of the libraries and make the other depend on it.
63// OrcTargetProcess is a minimal stub for embedding a JIT client in remote
64// executors. For the moment it seems reasonable to have the definition there
65// and let ExecutionEngine depend on it, until we find a better solution.
66//
67LLVM_ATTRIBUTE_USED void requiredSymbolDefinitionsFromOrcTargetProcess() {
69 << (void *)&__jit_debug_descriptor;
70}
71
72struct RegisteredObjectInfo {
73 RegisteredObjectInfo() = default;
74
75 RegisteredObjectInfo(std::size_t Size, jit_code_entry *Entry,
77 : Size(Size), Entry(Entry), Obj(std::move(Obj)) {}
78
79 std::size_t Size;
82};
83
84// Buffer for an in-memory object file in executable memory
86 RegisteredObjectBufferMap;
87
88/// Global access point for the JIT debugging interface designed for use with a
89/// singleton toolbox. Handles thread-safe registration and deregistration of
90/// object files that are in executable memory managed by the client of this
91/// class.
92class GDBJITRegistrationListener : public JITEventListener {
93 /// Lock used to serialize all jit registration events, since they
94 /// modify global variables.
95 ///
96 /// Only a single instance of GDBJITRegistrationListener is ever created,
97 /// and so the lock can be a member variable of that instance. This ensures
98 /// destructors are run in the correct order.
99 sys::Mutex JITDebugLock;
100
101 /// A map of in-memory object files that have been registered with the
102 /// JIT interface.
103 RegisteredObjectBufferMap ObjectBufferMap;
104
105 /// Instantiates the JIT service.
106 GDBJITRegistrationListener() = default;
107
108 /// Unregisters each object that was previously registered and releases all
109 /// internal resources.
110 ~GDBJITRegistrationListener() override;
111
112public:
113 static GDBJITRegistrationListener &instance() {
114 static GDBJITRegistrationListener Instance;
115 return Instance;
116 }
117
118 /// Creates an entry in the JIT registry for the buffer @p Object,
119 /// which must contain an object file in executable memory with any
120 /// debug information for the debugger.
121 void notifyObjectLoaded(ObjectKey K, const ObjectFile &Obj,
122 const RuntimeDyld::LoadedObjectInfo &L) override;
123
124 /// Removes the internal registration of @p Object, and
125 /// frees associated resources.
126 /// Returns true if @p Object was found in ObjectBufferMap.
127 void notifyFreeingObject(ObjectKey K) override;
128
129private:
130 /// Deregister the debug info for the given object file from the debugger
131 /// and delete any temporary copies. This private method does not remove
132 /// the function from Map so that it can be called while iterating over Map.
133 void deregisterObjectInternal(RegisteredObjectBufferMap::iterator I);
134};
135
136/// Do the registration.
137void NotifyDebugger(jit_code_entry* JITCodeEntry) {
139
140 // Insert this entry at the head of the list.
141 JITCodeEntry->prev_entry = nullptr;
143 JITCodeEntry->next_entry = NextEntry;
144 if (NextEntry) {
145 NextEntry->prev_entry = JITCodeEntry;
146 }
147 __jit_debug_descriptor.first_entry = JITCodeEntry;
150}
151
152GDBJITRegistrationListener::~GDBJITRegistrationListener() {
153 // Free all registered object files.
154 std::lock_guard<llvm::sys::Mutex> locked(JITDebugLock);
155 for (RegisteredObjectBufferMap::iterator I = ObjectBufferMap.begin(),
156 E = ObjectBufferMap.end();
157 I != E; ++I) {
158 // Call the private method that doesn't update the map so our iterator
159 // doesn't break.
160 deregisterObjectInternal(I);
161 }
162 ObjectBufferMap.clear();
163}
164
165void GDBJITRegistrationListener::notifyObjectLoaded(
166 ObjectKey K, const ObjectFile &Obj,
168
169 OwningBinary<ObjectFile> DebugObj = L.getObjectForDebug(Obj);
170
171 // Bail out if debug objects aren't supported.
172 if (!DebugObj.getBinary())
173 return;
174
175 const char *Buffer = DebugObj.getBinary()->getMemoryBufferRef().getBufferStart();
176 size_t Size = DebugObj.getBinary()->getMemoryBufferRef().getBufferSize();
177
178 std::lock_guard<llvm::sys::Mutex> locked(JITDebugLock);
179 assert(!ObjectBufferMap.contains(K) &&
180 "Second attempt to perform debug registration.");
181 jit_code_entry* JITCodeEntry = new jit_code_entry();
182
183 if (!JITCodeEntry) {
185 "Allocation failed when registering a JIT entry!\n");
186 } else {
187 JITCodeEntry->symfile_addr = Buffer;
188 JITCodeEntry->symfile_size = Size;
189
190 ObjectBufferMap[K] =
191 RegisteredObjectInfo(Size, JITCodeEntry, std::move(DebugObj));
192 NotifyDebugger(JITCodeEntry);
193 }
194}
195
196void GDBJITRegistrationListener::notifyFreeingObject(ObjectKey K) {
197 std::lock_guard<llvm::sys::Mutex> locked(JITDebugLock);
198 RegisteredObjectBufferMap::iterator I = ObjectBufferMap.find(K);
199
200 if (I != ObjectBufferMap.end()) {
201 deregisterObjectInternal(I);
202 ObjectBufferMap.erase(I);
203 }
204}
205
206void GDBJITRegistrationListener::deregisterObjectInternal(
207 RegisteredObjectBufferMap::iterator I) {
208
209 jit_code_entry*& JITCodeEntry = I->second.Entry;
210
211 // Do the unregistration.
212 {
214
215 // Remove the jit_code_entry from the linked list.
216 jit_code_entry* PrevEntry = JITCodeEntry->prev_entry;
217 jit_code_entry* NextEntry = JITCodeEntry->next_entry;
218
219 if (NextEntry) {
220 NextEntry->prev_entry = PrevEntry;
221 }
222 if (PrevEntry) {
223 PrevEntry->next_entry = NextEntry;
224 }
225 else {
228 }
229
230 // Tell the debugger which entry we removed, and unregister the code.
233 }
234
235 delete JITCodeEntry;
236 JITCodeEntry = nullptr;
237}
238
239} // end namespace
240
241namespace llvm {
242
244 return &GDBJITRegistrationListener::instance();
245}
246
247} // namespace llvm
248
250{
252}
#define LLVM_ATTRIBUTE_USED
Definition: Compiler.h:147
This file defines the DenseMap class.
uint64_t Size
struct jit_descriptor __jit_debug_descriptor
void __jit_debug_register_code()
#define I(x, y, z)
Definition: MD5.cpp:58
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
JITEventListener - Abstract interface for use by the JIT to notify clients about significant events d...
static JITEventListener * createGDBRegistrationListener()
Information about the loaded object.
Definition: RuntimeDyld.h:69
This class is the base class for all object file types.
Definition: ObjectFile.h:229
LLVMJITEventListenerRef LLVMCreateGDBRegistrationListener(void)
struct LLVMOpaqueJITEventListener * LLVMJITEventListenerRef
Definition: Types.h:165
@ Entry
Definition: COFF.h:826
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
void report_fatal_error(Error Err, bool gen_crash_diag=true)
Report a serious error, calling any installed error handler.
Definition: Error.cpp:167
raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Definition: STLExtras.h:1856
LLVMAttributeRef wrap(Attribute Attr)
Definition: Attributes.h:315
Implement std::hash so that hash_code can be used in STL containers.
Definition: BitVector.h:858
Definition: JITLoaderGDB.h:28
struct jit_code_entry * prev_entry
Definition: JITLoaderGDB.h:30
const char * symfile_addr
Definition: JITLoaderGDB.h:31
uint64_t symfile_size
Definition: JITLoaderGDB.h:32
struct jit_code_entry * next_entry
Definition: JITLoaderGDB.h:29
uint32_t action_flag
Definition: JITLoaderGDB.h:39
struct jit_code_entry * relevant_entry
Definition: JITLoaderGDB.h:40
uint32_t version
Definition: JITLoaderGDB.h:36
struct jit_code_entry * first_entry
Definition: JITLoaderGDB.h:41