1/*
2 * Copyright 2012, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17// This file contains portions derived from LLVM, with the original copyright
18// header below:
19//==----- GDBJITRegistrar.cpp - Notify GDB about in-memory object files  ---==//
20//
21//                     The LLVM Compiler Infrastructure
22//
23// This file is distributed under the University of Illinois Open Source
24// License. See LICENSE.TXT for details.
25//
26//===----------------------------------------------------------------------===//
27//
28// This file defines the GDBJITRegistrar object which is used by JIT engines to
29// register in-memory object files with GDB for debugging.
30//
31//===----------------------------------------------------------------------===//
32
33#include "bcc/ExecutionEngine/GDBJITRegistrar.h"
34
35#include <llvm/ADT/DenseMap.h>
36#include <llvm/Support/ErrorHandling.h>
37#include <llvm/Support/Memory.h>
38#include <llvm/Support/Mutex.h>
39#include <llvm/Support/MutexGuard.h>
40
41#include "bcc/ExecutionEngine/GDBJIT.h"
42
43#include <fstream>
44
45#ifdef ANDROID_ENGINEERING_BUILD
46// Path to write dump output.
47// It is expected that a debugger (plugin) sets this
48// string to a writeable directory where files (such as JITted object files,
49// IR dumps) are to be written. If this variable is 0, no debug dumps
50// are generated.
51char* gDebugDumpDirectory = 0;
52#endif // ANDROID_ENGINEERING_BUILD
53
54//************************************************************************
55// COPIED/ADAPTED FROM llvm/lib/ExecutionEngine/JIT/JITDebugRegisterer.cpp
56//************************************************************************
57// This must be kept in sync with gdb/gdb/jit.h .
58extern "C" {
59
60  // We put information about the JITed function in this global, which the
61  // debugger reads.  Make sure to specify the version statically, because the
62  // debugger checks the version before we can set it during runtime.
63  static struct jit_descriptor __jit_debug_descriptor = { 1, 0, 0, 0 };
64
65}
66//****************************************************************************
67// END COPIED/ADAPTED FROM llvm/lib/ExecutionEngine/JIT/JITDebugRegisterer.cpp
68//****************************************************************************
69
70namespace {
71
72// Buffer for an in-memory object file in executable memory
73typedef llvm::DenseMap< const ObjectBuffer*, std::pair<std::size_t, jit_code_entry*> >
74  RegisteredObjectBufferMap;
75
76/// Global access point for the GDB JIT interface designed for use with a
77/// singleton toolbox. Handles thread-safe registration and deregistration of
78/// object files that are in executable memory managed by the client of this
79/// class.
80class GDBJITRegistrar {
81  /// A map of in-memory object files that have been registered with the GDB JIT interface.
82  RegisteredObjectBufferMap ObjectBufferMap;
83
84public:
85  /// Instantiates the GDB JIT service.
86  GDBJITRegistrar() : ObjectBufferMap() {}
87
88  /// Unregisters each object that was previously registered with GDB, and
89  /// releases all internal resources.
90  ~GDBJITRegistrar();
91
92  /// Creates an entry in the GDB JIT registry for the buffer @p Object,
93  /// which must contain an object file in executable memory with any
94  /// debug information for GDB.
95  void registerObject(const ObjectBuffer* Object, std::size_t Size);
96
97  /// Removes the internal registration of @p Object, and
98  /// frees associated resources.
99  /// Returns true if @p Object was found in ObjectBufferMap.
100  bool deregisterObject(const ObjectBuffer* Object);
101
102private:
103  /// Deregister the debug info for the given object file from the debugger
104  /// and delete any temporary copies.  This private method does not remove
105  /// the function from Map so that it can be called while iterating over Map.
106  void deregisterObjectInternal(RegisteredObjectBufferMap::iterator I);
107};
108
109/// Lock used to serialize all gdb-jit registration events, since they
110/// modify global variables.
111llvm::sys::Mutex JITDebugLock;
112
113/// Acquire the lock and do the registration.
114void NotifyGDB(jit_code_entry* JITCodeEntry) {
115  llvm::MutexGuard locked(JITDebugLock);
116  __jit_debug_descriptor.action_flag = JIT_REGISTER_FN;
117
118  // Insert this entry at the head of the list.
119  JITCodeEntry->prev_entry = NULL;
120  jit_code_entry* NextEntry = __jit_debug_descriptor.first_entry;
121  JITCodeEntry->next_entry = NextEntry;
122  if (NextEntry != NULL) {
123    NextEntry->prev_entry = JITCodeEntry;
124  }
125  __jit_debug_descriptor.first_entry = JITCodeEntry;
126  __jit_debug_descriptor.relevant_entry = JITCodeEntry;
127  __jit_debug_register_code();
128}
129
130GDBJITRegistrar* RegistrarSingleton() {
131  static GDBJITRegistrar* sRegistrar = NULL;
132  if (sRegistrar == NULL) {
133    // The mutex is here so that it won't slow down access once the registrar
134    //   is instantiated
135    llvm::MutexGuard locked(JITDebugLock);
136    // Check again to be sure another thread didn't create this while we waited
137    if (sRegistrar == NULL) {
138      sRegistrar = new GDBJITRegistrar;
139    }
140  }
141  return sRegistrar;
142}
143
144GDBJITRegistrar::~GDBJITRegistrar() {
145  // Free all registered object files.
146 for (RegisteredObjectBufferMap::iterator I = ObjectBufferMap.begin(), E = ObjectBufferMap.end();
147       I != E; ++I) {
148    // Call the private method that doesn't update the map so our iterator
149    // doesn't break.
150    deregisterObjectInternal(I);
151  }
152  ObjectBufferMap.clear();
153}
154
155void GDBJITRegistrar::registerObject(const ObjectBuffer* Object, std::size_t Size) {
156
157  assert(Object && "Attempt to register a null object with a debugger.");
158  assert(ObjectBufferMap.find(Object) == ObjectBufferMap.end()
159    && "Second attempt to perform debug registration.");
160
161  jit_code_entry* JITCodeEntry = new jit_code_entry();
162
163  if (JITCodeEntry == 0) {
164    llvm::report_fatal_error("Allocation failed when registering a GDB-JIT entry!\n");
165  }
166  else {
167    JITCodeEntry->symfile_addr = Object;
168    JITCodeEntry->symfile_size = Size;
169
170    ObjectBufferMap[Object] = std::make_pair(Size, JITCodeEntry);
171    NotifyGDB(JITCodeEntry);
172
173#ifdef ANDROID_ENGINEERING_BUILD
174    if (0 != gDebugDumpDirectory) {
175      std::string Filename(gDebugDumpDirectory);
176      Filename += "/jit_registered.o";
177
178      std::ofstream outfile(Filename.c_str(), std::ofstream::binary);
179      outfile.write((char*)JITCodeEntry->symfile_addr, JITCodeEntry->symfile_size);
180      outfile.close();
181    }
182#endif
183  }
184}
185
186bool GDBJITRegistrar::deregisterObject(const ObjectBuffer *Object) {
187  RegisteredObjectBufferMap::iterator I = ObjectBufferMap.find(Object);
188
189  if (I != ObjectBufferMap.end()) {
190    deregisterObjectInternal(I);
191    ObjectBufferMap.erase(I);
192    return true;
193  }
194  return false;
195}
196
197void GDBJITRegistrar::deregisterObjectInternal(
198    RegisteredObjectBufferMap::iterator I) {
199
200  jit_code_entry*& JITCodeEntry = I->second.second;
201
202  // Acquire the lock and do the unregistration.
203  {
204    llvm::MutexGuard locked(JITDebugLock);
205    __jit_debug_descriptor.action_flag = JIT_UNREGISTER_FN;
206
207    // Remove the jit_code_entry from the linked list.
208    jit_code_entry* PrevEntry = JITCodeEntry->prev_entry;
209    jit_code_entry* NextEntry = JITCodeEntry->next_entry;
210
211    if (NextEntry) {
212      NextEntry->prev_entry = PrevEntry;
213    }
214    if (PrevEntry) {
215      PrevEntry->next_entry = NextEntry;
216    }
217    else {
218      assert(__jit_debug_descriptor.first_entry == JITCodeEntry);
219      __jit_debug_descriptor.first_entry = NextEntry;
220    }
221
222    // Tell GDB which entry we removed, and unregister the code.
223    __jit_debug_descriptor.relevant_entry = JITCodeEntry;
224    __jit_debug_register_code();
225  }
226
227  delete JITCodeEntry;
228  JITCodeEntry = NULL;
229}
230
231} // end namespace
232
233void registerObjectWithGDB(const ObjectBuffer* Object, std::size_t Size) {
234  GDBJITRegistrar* Registrar = RegistrarSingleton();
235  if (Registrar) {
236    Registrar->registerObject(Object, Size);
237  }
238}
239
240void deregisterObjectWithGDB(const ObjectBuffer* Object) {
241  GDBJITRegistrar* Registrar = RegistrarSingleton();
242  if (Registrar) {
243    Registrar->deregisterObject(Object);
244  }
245}
246