Enable debugging of RS code under GDB
- Add/integrate GDBJITRegistrar support class for interfacing with GDB
-- Once the GDBJITRegistrar is merged into LLVM trunk (and AOSP upgrades LLVM)
all files GDB* should be removed, and replaced with appropriate includes
- Basic [host|target]-side integration tests
-- host-tests: use bcc driver and clang to verify gdb output
-- target-tests: run skeleton apk on target and verify gdb output
- Add support for optimization_level metadata in bcinfo, libbcc
-- Disabled some LTO passes when optimization_level = 0
-- move register allocator registration after metadata inspection
- Initial version of android-commands.py GDB plugin (for test infrastructure)
-- relevant commands: load-android-app, run-android-app, start-android-app
-- tested versions: gdb (7.2, 7.3), python (2.6, 2.7)
- build 'bcc' driver tool by default in eng builds
Change-Id: I99e0c11c8591c6d911632c1dcc82dd8fbe1244a8
diff --git a/lib/ExecutionEngine/GDBJITRegistrar.cpp b/lib/ExecutionEngine/GDBJITRegistrar.cpp
new file mode 100644
index 0000000..5d8428a
--- /dev/null
+++ b/lib/ExecutionEngine/GDBJITRegistrar.cpp
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This file contains portions derived from LLVM, with the original copyright
+// header below:
+//==----- GDBJITRegistrar.cpp - Notify GDB about in-memory object files ---==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the GDBJITRegistrar object which is used by JIT engines to
+// register in-memory object files with GDB for debugging.
+//
+//===----------------------------------------------------------------------===//
+
+#include "GDBJITRegistrar.h"
+#include "llvm/ADT/DenseMap.h"
+#include "GDBJIT.h"
+#include "llvm/Support/Memory.h"
+#include "llvm/Support/MutexGuard.h"
+#include "llvm/Support/Mutex.h"
+#include "llvm/Support/ErrorHandling.h"
+
+#include <fstream>
+
+#ifdef ANDROID_ENGINEERING_BUILD
+// Path to write dump output.
+// It is expected that a debugger (plugin) sets this
+// string to a writeable directory where files (such as JITted object files,
+// IR dumps) are to be written. If this variable is 0, no debug dumps
+// are generated.
+char* gDebugDumpDirectory = 0;
+#endif // ANDROID_ENGINEERING_BUILD
+
+//************************************************************************
+// COPIED/ADAPTED FROM llvm/lib/ExecutionEngine/JIT/JITDebugRegisterer.cpp
+//************************************************************************
+// This must be kept in sync with gdb/gdb/jit.h .
+extern "C" {
+
+ // We put information about the JITed function in this global, which the
+ // debugger reads. Make sure to specify the version statically, because the
+ // debugger checks the version before we can set it during runtime.
+ static struct jit_descriptor __jit_debug_descriptor = { 1, 0, 0, 0 };
+
+}
+//****************************************************************************
+// END COPIED/ADAPTED FROM llvm/lib/ExecutionEngine/JIT/JITDebugRegisterer.cpp
+//****************************************************************************
+
+namespace {
+
+// Buffer for an in-memory object file in executable memory
+typedef llvm::DenseMap< const ObjectBuffer*, std::pair<std::size_t, jit_code_entry*> >
+ RegisteredObjectBufferMap;
+
+/// Global access point for the GDB JIT interface designed for use with a
+/// singleton toolbox. Handles thread-safe registration and deregistration of
+/// object files that are in executable memory managed by the client of this
+/// class.
+class GDBJITRegistrar {
+ /// A map of in-memory object files that have been registered with the GDB JIT interface.
+ RegisteredObjectBufferMap ObjectBufferMap;
+
+public:
+ /// Instantiates the GDB JIT service.
+ GDBJITRegistrar() : ObjectBufferMap() {}
+
+ /// Unregisters each object that was previously registered with GDB, and
+ /// releases all internal resources.
+ ~GDBJITRegistrar();
+
+ /// Creates an entry in the GDB JIT registry for the buffer @p Object,
+ /// which must contain an object file in executable memory with any
+ /// debug information for GDB.
+ void registerObject(const ObjectBuffer* Object, std::size_t Size);
+
+ /// Removes the internal registration of @p Object, and
+ /// frees associated resources.
+ /// Returns true if @p Object was found in ObjectBufferMap.
+ bool deregisterObject(const ObjectBuffer* Object);
+
+private:
+ /// Deregister the debug info for the given object file from the debugger
+ /// and delete any temporary copies. This private method does not remove
+ /// the function from Map so that it can be called while iterating over Map.
+ void deregisterObjectInternal(RegisteredObjectBufferMap::iterator I);
+};
+
+/// Lock used to serialize all gdb-jit registration events, since they
+/// modify global variables.
+llvm::sys::Mutex JITDebugLock;
+
+/// Acquire the lock and do the registration.
+void NotifyGDB(jit_code_entry* JITCodeEntry) {
+ llvm::MutexGuard locked(JITDebugLock);
+ __jit_debug_descriptor.action_flag = JIT_REGISTER_FN;
+
+ // Insert this entry at the head of the list.
+ JITCodeEntry->prev_entry = NULL;
+ jit_code_entry* NextEntry = __jit_debug_descriptor.first_entry;
+ JITCodeEntry->next_entry = NextEntry;
+ if (NextEntry != NULL) {
+ NextEntry->prev_entry = JITCodeEntry;
+ }
+ __jit_debug_descriptor.first_entry = JITCodeEntry;
+ __jit_debug_descriptor.relevant_entry = JITCodeEntry;
+ __jit_debug_register_code();
+}
+
+GDBJITRegistrar* RegistrarSingleton() {
+ static GDBJITRegistrar* sRegistrar = NULL;
+ if (sRegistrar == NULL) {
+ // The mutex is here so that it won't slow down access once the registrar
+ // is instantiated
+ llvm::MutexGuard locked(JITDebugLock);
+ // Check again to be sure another thread didn't create this while we waited
+ if (sRegistrar == NULL) {
+ sRegistrar = new GDBJITRegistrar;
+ }
+ }
+ return sRegistrar;
+}
+
+GDBJITRegistrar::~GDBJITRegistrar() {
+ // Free all registered object files.
+ for (RegisteredObjectBufferMap::iterator I = ObjectBufferMap.begin(), E = ObjectBufferMap.end();
+ I != E; ++I) {
+ // Call the private method that doesn't update the map so our iterator
+ // doesn't break.
+ deregisterObjectInternal(I);
+ }
+ ObjectBufferMap.clear();
+}
+
+void GDBJITRegistrar::registerObject(const ObjectBuffer* Object, std::size_t Size) {
+
+ assert(Object && "Attempt to register a null object with a debugger.");
+ assert(ObjectBufferMap.find(Object) == ObjectBufferMap.end()
+ && "Second attempt to perform debug registration.");
+
+ jit_code_entry* JITCodeEntry = new jit_code_entry();
+
+ if (JITCodeEntry == 0) {
+ llvm::report_fatal_error("Allocation failed when registering a GDB-JIT entry!\n");
+ }
+ else {
+ JITCodeEntry->symfile_addr = Object;
+ JITCodeEntry->symfile_size = Size;
+
+ ObjectBufferMap[Object] = std::make_pair(Size, JITCodeEntry);
+ NotifyGDB(JITCodeEntry);
+
+#ifdef ANDROID_ENGINEERING_BUILD
+ if (0 != gDebugDumpDirectory) {
+ std::string Filename(gDebugDumpDirectory);
+ Filename += "/jit_registered.o";
+
+ std::ofstream outfile(Filename.c_str(), std::ofstream::binary);
+ outfile.write((char*)JITCodeEntry->symfile_addr, JITCodeEntry->symfile_size);
+ outfile.close();
+ }
+#endif
+ }
+}
+
+bool GDBJITRegistrar::deregisterObject(const ObjectBuffer *Object) {
+ RegisteredObjectBufferMap::iterator I = ObjectBufferMap.find(Object);
+
+ if (I != ObjectBufferMap.end()) {
+ deregisterObjectInternal(I);
+ ObjectBufferMap.erase(I);
+ return true;
+ }
+ return false;
+}
+
+void GDBJITRegistrar::deregisterObjectInternal(
+ RegisteredObjectBufferMap::iterator I) {
+
+ jit_code_entry*& JITCodeEntry = I->second.second;
+
+ // Acquire the lock and do the unregistration.
+ {
+ llvm::MutexGuard locked(JITDebugLock);
+ __jit_debug_descriptor.action_flag = JIT_UNREGISTER_FN;
+
+ // Remove the jit_code_entry from the linked list.
+ jit_code_entry* PrevEntry = JITCodeEntry->prev_entry;
+ jit_code_entry* NextEntry = JITCodeEntry->next_entry;
+
+ if (NextEntry) {
+ NextEntry->prev_entry = PrevEntry;
+ }
+ if (PrevEntry) {
+ PrevEntry->next_entry = NextEntry;
+ }
+ else {
+ assert(__jit_debug_descriptor.first_entry == JITCodeEntry);
+ __jit_debug_descriptor.first_entry = NextEntry;
+ }
+
+ // Tell GDB which entry we removed, and unregister the code.
+ __jit_debug_descriptor.relevant_entry = JITCodeEntry;
+ __jit_debug_register_code();
+ }
+
+ delete JITCodeEntry;
+ JITCodeEntry = NULL;
+}
+
+} // end namespace
+
+void registerObjectWithGDB(const ObjectBuffer* Object, std::size_t Size) {
+ GDBJITRegistrar* Registrar = RegistrarSingleton();
+ if (Registrar) {
+ Registrar->registerObject(Object, Size);
+ }
+}
+
+void deregisterObjectWithGDB(const ObjectBuffer* Object) {
+ GDBJITRegistrar* Registrar = RegistrarSingleton();
+ if (Registrar) {
+ Registrar->deregisterObject(Object);
+ }
+}