]>
Commit | Line | Data |
---|---|---|
85aaf69f | 1 | //===----- GDBRegistrationListener.cpp - Registers objects with GDB -------===// |
223e47cc LB |
2 | // |
3 | // The LLVM Compiler Infrastructure | |
4 | // | |
5 | // This file is distributed under the University of Illinois Open Source | |
6 | // License. See LICENSE.TXT for details. | |
7 | // | |
8 | //===----------------------------------------------------------------------===// | |
9 | ||
223e47cc | 10 | #include "llvm/ADT/DenseMap.h" |
85aaf69f SL |
11 | #include "llvm/ExecutionEngine/JITEventListener.h" |
12 | #include "llvm/Object/ObjectFile.h" | |
223e47cc | 13 | #include "llvm/Support/Compiler.h" |
970d7e83 | 14 | #include "llvm/Support/ErrorHandling.h" |
85aaf69f | 15 | #include "llvm/Support/ManagedStatic.h" |
970d7e83 LB |
16 | #include "llvm/Support/Mutex.h" |
17 | #include "llvm/Support/MutexGuard.h" | |
223e47cc LB |
18 | |
19 | using namespace llvm; | |
85aaf69f | 20 | using namespace llvm::object; |
223e47cc LB |
21 | |
22 | // This must be kept in sync with gdb/gdb/jit.h . | |
23 | extern "C" { | |
24 | ||
25 | typedef enum { | |
26 | JIT_NOACTION = 0, | |
27 | JIT_REGISTER_FN, | |
28 | JIT_UNREGISTER_FN | |
29 | } jit_actions_t; | |
30 | ||
31 | struct jit_code_entry { | |
32 | struct jit_code_entry *next_entry; | |
33 | struct jit_code_entry *prev_entry; | |
34 | const char *symfile_addr; | |
35 | uint64_t symfile_size; | |
36 | }; | |
37 | ||
38 | struct jit_descriptor { | |
39 | uint32_t version; | |
40 | // This should be jit_actions_t, but we want to be specific about the | |
41 | // bit-width. | |
42 | uint32_t action_flag; | |
43 | struct jit_code_entry *relevant_entry; | |
44 | struct jit_code_entry *first_entry; | |
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. | |
1a4d82fc | 50 | struct jit_descriptor __jit_debug_descriptor = { 1, 0, nullptr, nullptr }; |
223e47cc LB |
51 | |
52 | // Debuggers puts a breakpoint in this function. | |
1a4d82fc JJ |
53 | LLVM_ATTRIBUTE_NOINLINE void __jit_debug_register_code() { |
54 | // The noinline and the asm prevent calls to this function from being | |
55 | // optimized out. | |
56 | #if !defined(_MSC_VER) | |
57 | asm volatile("":::"memory"); | |
58 | #endif | |
59 | } | |
223e47cc LB |
60 | |
61 | } | |
62 | ||
63 | namespace { | |
64 | ||
85aaf69f SL |
65 | struct RegisteredObjectInfo { |
66 | RegisteredObjectInfo() {} | |
67 | ||
68 | RegisteredObjectInfo(std::size_t Size, jit_code_entry *Entry, | |
69 | OwningBinary<ObjectFile> Obj) | |
70 | : Size(Size), Entry(Entry), Obj(std::move(Obj)) {} | |
71 | ||
72 | RegisteredObjectInfo(RegisteredObjectInfo &&Other) | |
73 | : Size(Other.Size), Entry(Other.Entry), Obj(std::move(Other.Obj)) {} | |
74 | ||
75 | RegisteredObjectInfo& operator=(RegisteredObjectInfo &&Other) { | |
76 | Size = Other.Size; | |
77 | Entry = Other.Entry; | |
78 | Obj = std::move(Other.Obj); | |
79 | return *this; | |
80 | } | |
81 | ||
82 | std::size_t Size; | |
83 | jit_code_entry *Entry; | |
84 | OwningBinary<ObjectFile> Obj; | |
85 | }; | |
86 | ||
223e47cc | 87 | // Buffer for an in-memory object file in executable memory |
85aaf69f | 88 | typedef llvm::DenseMap< const char*, RegisteredObjectInfo> |
223e47cc LB |
89 | RegisteredObjectBufferMap; |
90 | ||
91 | /// Global access point for the JIT debugging interface designed for use with a | |
92 | /// singleton toolbox. Handles thread-safe registration and deregistration of | |
93 | /// object files that are in executable memory managed by the client of this | |
94 | /// class. | |
85aaf69f | 95 | class GDBJITRegistrationListener : public JITEventListener { |
223e47cc LB |
96 | /// A map of in-memory object files that have been registered with the |
97 | /// JIT interface. | |
98 | RegisteredObjectBufferMap ObjectBufferMap; | |
99 | ||
100 | public: | |
101 | /// Instantiates the JIT service. | |
85aaf69f | 102 | GDBJITRegistrationListener() : ObjectBufferMap() {} |
223e47cc LB |
103 | |
104 | /// Unregisters each object that was previously registered and releases all | |
105 | /// internal resources. | |
85aaf69f | 106 | virtual ~GDBJITRegistrationListener(); |
223e47cc LB |
107 | |
108 | /// Creates an entry in the JIT registry for the buffer @p Object, | |
109 | /// which must contain an object file in executable memory with any | |
110 | /// debug information for the debugger. | |
85aaf69f SL |
111 | void NotifyObjectEmitted(const ObjectFile &Object, |
112 | const RuntimeDyld::LoadedObjectInfo &L) override; | |
223e47cc LB |
113 | |
114 | /// Removes the internal registration of @p Object, and | |
115 | /// frees associated resources. | |
116 | /// Returns true if @p Object was found in ObjectBufferMap. | |
85aaf69f | 117 | void NotifyFreeingObject(const ObjectFile &Object) override; |
223e47cc LB |
118 | |
119 | private: | |
120 | /// Deregister the debug info for the given object file from the debugger | |
121 | /// and delete any temporary copies. This private method does not remove | |
122 | /// the function from Map so that it can be called while iterating over Map. | |
123 | void deregisterObjectInternal(RegisteredObjectBufferMap::iterator I); | |
124 | }; | |
125 | ||
126 | /// Lock used to serialize all jit registration events, since they | |
127 | /// modify global variables. | |
1a4d82fc | 128 | ManagedStatic<sys::Mutex> JITDebugLock; |
223e47cc | 129 | |
1a4d82fc | 130 | /// Do the registration. |
223e47cc | 131 | void NotifyDebugger(jit_code_entry* JITCodeEntry) { |
223e47cc LB |
132 | __jit_debug_descriptor.action_flag = JIT_REGISTER_FN; |
133 | ||
134 | // Insert this entry at the head of the list. | |
1a4d82fc | 135 | JITCodeEntry->prev_entry = nullptr; |
223e47cc LB |
136 | jit_code_entry* NextEntry = __jit_debug_descriptor.first_entry; |
137 | JITCodeEntry->next_entry = NextEntry; | |
1a4d82fc | 138 | if (NextEntry) { |
223e47cc LB |
139 | NextEntry->prev_entry = JITCodeEntry; |
140 | } | |
141 | __jit_debug_descriptor.first_entry = JITCodeEntry; | |
142 | __jit_debug_descriptor.relevant_entry = JITCodeEntry; | |
143 | __jit_debug_register_code(); | |
144 | } | |
145 | ||
85aaf69f | 146 | GDBJITRegistrationListener::~GDBJITRegistrationListener() { |
223e47cc | 147 | // Free all registered object files. |
1a4d82fc | 148 | llvm::MutexGuard locked(*JITDebugLock); |
85aaf69f SL |
149 | for (RegisteredObjectBufferMap::iterator I = ObjectBufferMap.begin(), |
150 | E = ObjectBufferMap.end(); | |
223e47cc LB |
151 | I != E; ++I) { |
152 | // Call the private method that doesn't update the map so our iterator | |
153 | // doesn't break. | |
154 | deregisterObjectInternal(I); | |
155 | } | |
156 | ObjectBufferMap.clear(); | |
157 | } | |
158 | ||
85aaf69f SL |
159 | void GDBJITRegistrationListener::NotifyObjectEmitted( |
160 | const ObjectFile &Object, | |
161 | const RuntimeDyld::LoadedObjectInfo &L) { | |
162 | ||
163 | OwningBinary<ObjectFile> DebugObj = L.getObjectForDebug(Object); | |
164 | ||
165 | // Bail out if debug objects aren't supported. | |
166 | if (!DebugObj.getBinary()) | |
167 | return; | |
168 | ||
169 | const char *Buffer = DebugObj.getBinary()->getMemoryBufferRef().getBufferStart(); | |
170 | size_t Size = DebugObj.getBinary()->getMemoryBufferRef().getBufferSize(); | |
223e47cc | 171 | |
85aaf69f | 172 | const char *Key = Object.getMemoryBufferRef().getBufferStart(); |
223e47cc | 173 | |
85aaf69f | 174 | assert(Key && "Attempt to register a null object with a debugger."); |
1a4d82fc | 175 | llvm::MutexGuard locked(*JITDebugLock); |
85aaf69f | 176 | assert(ObjectBufferMap.find(Key) == ObjectBufferMap.end() && |
223e47cc LB |
177 | "Second attempt to perform debug registration."); |
178 | jit_code_entry* JITCodeEntry = new jit_code_entry(); | |
179 | ||
1a4d82fc | 180 | if (!JITCodeEntry) { |
223e47cc LB |
181 | llvm::report_fatal_error( |
182 | "Allocation failed when registering a JIT entry!\n"); | |
1a4d82fc | 183 | } else { |
223e47cc LB |
184 | JITCodeEntry->symfile_addr = Buffer; |
185 | JITCodeEntry->symfile_size = Size; | |
186 | ||
85aaf69f SL |
187 | ObjectBufferMap[Key] = RegisteredObjectInfo(Size, JITCodeEntry, |
188 | std::move(DebugObj)); | |
223e47cc LB |
189 | NotifyDebugger(JITCodeEntry); |
190 | } | |
191 | } | |
192 | ||
85aaf69f SL |
193 | void GDBJITRegistrationListener::NotifyFreeingObject(const ObjectFile& Object) { |
194 | const char *Key = Object.getMemoryBufferRef().getBufferStart(); | |
1a4d82fc | 195 | llvm::MutexGuard locked(*JITDebugLock); |
85aaf69f | 196 | RegisteredObjectBufferMap::iterator I = ObjectBufferMap.find(Key); |
223e47cc LB |
197 | |
198 | if (I != ObjectBufferMap.end()) { | |
199 | deregisterObjectInternal(I); | |
200 | ObjectBufferMap.erase(I); | |
223e47cc | 201 | } |
223e47cc LB |
202 | } |
203 | ||
85aaf69f | 204 | void GDBJITRegistrationListener::deregisterObjectInternal( |
223e47cc LB |
205 | RegisteredObjectBufferMap::iterator I) { |
206 | ||
85aaf69f | 207 | jit_code_entry*& JITCodeEntry = I->second.Entry; |
223e47cc | 208 | |
1a4d82fc | 209 | // Do the unregistration. |
223e47cc | 210 | { |
223e47cc LB |
211 | __jit_debug_descriptor.action_flag = JIT_UNREGISTER_FN; |
212 | ||
213 | // Remove the jit_code_entry from the linked list. | |
214 | jit_code_entry* PrevEntry = JITCodeEntry->prev_entry; | |
215 | jit_code_entry* NextEntry = JITCodeEntry->next_entry; | |
216 | ||
217 | if (NextEntry) { | |
218 | NextEntry->prev_entry = PrevEntry; | |
219 | } | |
220 | if (PrevEntry) { | |
221 | PrevEntry->next_entry = NextEntry; | |
222 | } | |
223 | else { | |
224 | assert(__jit_debug_descriptor.first_entry == JITCodeEntry); | |
225 | __jit_debug_descriptor.first_entry = NextEntry; | |
226 | } | |
227 | ||
228 | // Tell the debugger which entry we removed, and unregister the code. | |
229 | __jit_debug_descriptor.relevant_entry = JITCodeEntry; | |
230 | __jit_debug_register_code(); | |
231 | } | |
232 | ||
233 | delete JITCodeEntry; | |
1a4d82fc | 234 | JITCodeEntry = nullptr; |
223e47cc LB |
235 | } |
236 | ||
85aaf69f | 237 | llvm::ManagedStatic<GDBJITRegistrationListener> GDBRegListener; |
1a4d82fc | 238 | |
223e47cc LB |
239 | } // end namespace |
240 | ||
241 | namespace llvm { | |
242 | ||
85aaf69f SL |
243 | JITEventListener* JITEventListener::createGDBRegistrationListener() { |
244 | return &*GDBRegListener; | |
223e47cc LB |
245 | } |
246 | ||
247 | } // namespace llvm |