• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===-- OProfileJITEventListener.cpp - Tell OProfile about JITted code ----===//
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 // This file defines a JITEventListener object that uses OProfileWrapper to tell
11 // oprofile about JITted functions, including source line information.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #include "llvm-c/ExecutionEngine.h"
16 #include "llvm/CodeGen/MachineFunction.h"
17 #include "llvm/Config/config.h"
18 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
19 #include "llvm/ExecutionEngine/JITEventListener.h"
20 #include "llvm/ExecutionEngine/OProfileWrapper.h"
21 #include "llvm/ExecutionEngine/RuntimeDyld.h"
22 #include "llvm/IR/DebugInfo.h"
23 #include "llvm/IR/Function.h"
24 #include "llvm/Object/ObjectFile.h"
25 #include "llvm/Object/SymbolSize.h"
26 #include "llvm/Support/Debug.h"
27 #include "llvm/Support/Errno.h"
28 #include "llvm/Support/raw_ostream.h"
29 #include <dirent.h>
30 #include <fcntl.h>
31 
32 using namespace llvm;
33 using namespace llvm::object;
34 
35 #define DEBUG_TYPE "oprofile-jit-event-listener"
36 
37 namespace {
38 
39 class OProfileJITEventListener : public JITEventListener {
40   std::unique_ptr<OProfileWrapper> Wrapper;
41 
42   void initialize();
43   std::map<const char*, OwningBinary<ObjectFile>> DebugObjects;
44 
45 public:
OProfileJITEventListener(std::unique_ptr<OProfileWrapper> LibraryWrapper)46   OProfileJITEventListener(std::unique_ptr<OProfileWrapper> LibraryWrapper)
47     : Wrapper(std::move(LibraryWrapper)) {
48     initialize();
49   }
50 
51   ~OProfileJITEventListener();
52 
53   void NotifyObjectEmitted(const ObjectFile &Obj,
54                            const RuntimeDyld::LoadedObjectInfo &L) override;
55 
56   void NotifyFreeingObject(const ObjectFile &Obj) override;
57 };
58 
initialize()59 void OProfileJITEventListener::initialize() {
60   if (!Wrapper->op_open_agent()) {
61     const std::string err_str = sys::StrError();
62     LLVM_DEBUG(dbgs() << "Failed to connect to OProfile agent: " << err_str
63                       << "\n");
64   } else {
65     LLVM_DEBUG(dbgs() << "Connected to OProfile agent.\n");
66   }
67 }
68 
~OProfileJITEventListener()69 OProfileJITEventListener::~OProfileJITEventListener() {
70   if (Wrapper->isAgentAvailable()) {
71     if (Wrapper->op_close_agent() == -1) {
72       const std::string err_str = sys::StrError();
73       LLVM_DEBUG(dbgs() << "Failed to disconnect from OProfile agent: "
74                         << err_str << "\n");
75     } else {
76       LLVM_DEBUG(dbgs() << "Disconnected from OProfile agent.\n");
77     }
78   }
79 }
80 
NotifyObjectEmitted(const ObjectFile & Obj,const RuntimeDyld::LoadedObjectInfo & L)81 void OProfileJITEventListener::NotifyObjectEmitted(
82                                        const ObjectFile &Obj,
83                                        const RuntimeDyld::LoadedObjectInfo &L) {
84   if (!Wrapper->isAgentAvailable()) {
85     return;
86   }
87 
88   OwningBinary<ObjectFile> DebugObjOwner = L.getObjectForDebug(Obj);
89   const ObjectFile &DebugObj = *DebugObjOwner.getBinary();
90   std::unique_ptr<DIContext> Context = DWARFContext::create(DebugObj);
91 
92   // Use symbol info to iterate functions in the object.
93   for (const std::pair<SymbolRef, uint64_t> &P : computeSymbolSizes(DebugObj)) {
94     SymbolRef Sym = P.first;
95     if (!Sym.getType() || *Sym.getType() != SymbolRef::ST_Function)
96       continue;
97 
98     Expected<StringRef> NameOrErr = Sym.getName();
99     if (!NameOrErr)
100       continue;
101     StringRef Name = *NameOrErr;
102     Expected<uint64_t> AddrOrErr = Sym.getAddress();
103     if (!AddrOrErr)
104       continue;
105     uint64_t Addr = *AddrOrErr;
106     uint64_t Size = P.second;
107 
108     if (Wrapper->op_write_native_code(Name.data(), Addr, (void *)Addr, Size) ==
109         -1) {
110       LLVM_DEBUG(dbgs() << "Failed to tell OProfile about native function "
111                         << Name << " at [" << (void *)Addr << "-"
112                         << ((char *)Addr + Size) << "]\n");
113       continue;
114     }
115 
116     DILineInfoTable Lines = Context->getLineInfoForAddressRange(Addr, Size);
117     size_t i = 0;
118     size_t num_entries = Lines.size();
119     struct debug_line_info *debug_line;
120     debug_line = (struct debug_line_info *)calloc(
121         num_entries, sizeof(struct debug_line_info));
122 
123     for (auto& It : Lines) {
124       debug_line[i].vma = (unsigned long)It.first;
125       debug_line[i].lineno = It.second.Line;
126       debug_line[i].filename =
127           const_cast<char *>(Lines.front().second.FileName.c_str());
128       ++i;
129     }
130 
131     if (Wrapper->op_write_debug_line_info((void *)Addr, num_entries,
132                                           debug_line) == -1) {
133       LLVM_DEBUG(dbgs() << "Failed to tell OProfiler about debug object at ["
134                         << (void *)Addr << "-" << ((char *)Addr + Size)
135                         << "]\n");
136       continue;
137     }
138   }
139 
140   DebugObjects[Obj.getData().data()] = std::move(DebugObjOwner);
141 }
142 
NotifyFreeingObject(const ObjectFile & Obj)143 void OProfileJITEventListener::NotifyFreeingObject(const ObjectFile &Obj) {
144   if (Wrapper->isAgentAvailable()) {
145 
146     // If there was no agent registered when the original object was loaded then
147     // we won't have created a debug object for it, so bail out.
148     if (DebugObjects.find(Obj.getData().data()) == DebugObjects.end())
149       return;
150 
151     const ObjectFile &DebugObj = *DebugObjects[Obj.getData().data()].getBinary();
152 
153     // Use symbol info to iterate functions in the object.
154     for (symbol_iterator I = DebugObj.symbol_begin(),
155                          E = DebugObj.symbol_end();
156          I != E; ++I) {
157       if (I->getType() && *I->getType() == SymbolRef::ST_Function) {
158         Expected<uint64_t> AddrOrErr = I->getAddress();
159         if (!AddrOrErr)
160           continue;
161         uint64_t Addr = *AddrOrErr;
162 
163         if (Wrapper->op_unload_native_code(Addr) == -1) {
164           LLVM_DEBUG(
165               dbgs()
166               << "Failed to tell OProfile about unload of native function at "
167               << (void *)Addr << "\n");
168           continue;
169         }
170       }
171     }
172   }
173 
174   DebugObjects.erase(Obj.getData().data());
175 }
176 
177 }  // anonymous namespace.
178 
179 namespace llvm {
createOProfileJITEventListener()180 JITEventListener *JITEventListener::createOProfileJITEventListener() {
181   return new OProfileJITEventListener(llvm::make_unique<OProfileWrapper>());
182 }
183 
184 } // namespace llvm
185 
LLVMCreateOProfileJITEventListener(void)186 LLVMJITEventListenerRef LLVMCreateOProfileJITEventListener(void)
187 {
188   return wrap(JITEventListener::createOProfileJITEventListener());
189 }
190