LCOV - code coverage report
Current view: top level - lib/ExecutionEngine - GDBRegistrationListener.cpp (source / functions) Hit Total Coverage
Test: llvm-toolchain.info Lines: 56 61 91.8 %
Date: 2018-06-17 00:07:59 Functions: 6 8 75.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13