IntelJITEventListener.cpp revision cd81d94322a39503e4a3e87b6ee03d4fcb3465fb
1//===-- IntelJITEventListener.cpp - Tell Intel profiler about JITed 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 to tell Intel(R) VTune(TM)
11// Amplifier XE 2011 about JITted functions.
12//
13//===----------------------------------------------------------------------===//
14
15#include "llvm/Config/config.h"
16#include "llvm/ExecutionEngine/JITEventListener.h"
17
18#include "llvm/IR/DebugInfo.h"
19#include "llvm/IR/Function.h"
20#include "llvm/IR/Metadata.h"
21#include "llvm/ADT/DenseMap.h"
22#include "llvm/CodeGen/MachineFunction.h"
23#include "llvm/DebugInfo/DIContext.h"
24#include "llvm/ExecutionEngine/ObjectImage.h"
25#include "llvm/Object/ObjectFile.h"
26#include "llvm/Support/Debug.h"
27#include "llvm/Support/raw_ostream.h"
28#include "llvm/Support/Errno.h"
29#include "llvm/IR/ValueHandle.h"
30#include "EventListenerCommon.h"
31#include "IntelJITEventsWrapper.h"
32
33using namespace llvm;
34using namespace llvm::jitprofiling;
35
36#define DEBUG_TYPE "amplifier-jit-event-listener"
37
38namespace {
39
40class IntelJITEventListener : public JITEventListener {
41  typedef DenseMap<void*, unsigned int> MethodIDMap;
42
43  std::unique_ptr<IntelJITEventsWrapper> Wrapper;
44  MethodIDMap MethodIDs;
45  FilenameCache Filenames;
46
47  typedef SmallVector<const void *, 64> MethodAddressVector;
48  typedef DenseMap<const void *, MethodAddressVector>  ObjectMap;
49
50  ObjectMap  LoadedObjectMap;
51
52public:
53  IntelJITEventListener(IntelJITEventsWrapper* libraryWrapper) {
54      Wrapper.reset(libraryWrapper);
55  }
56
57  ~IntelJITEventListener() {
58  }
59
60  virtual void NotifyFunctionEmitted(const Function &F,
61                                     void *FnStart, size_t FnSize,
62                                     const EmittedFunctionDetails &Details);
63
64  virtual void NotifyFreeingMachineCode(void *OldPtr);
65
66  virtual void NotifyObjectEmitted(const ObjectImage &Obj);
67
68  virtual void NotifyFreeingObject(const ObjectImage &Obj);
69};
70
71static LineNumberInfo LineStartToIntelJITFormat(
72    uintptr_t StartAddress,
73    uintptr_t Address,
74    DebugLoc Loc) {
75  LineNumberInfo Result;
76
77  Result.Offset = Address - StartAddress;
78  Result.LineNumber = Loc.getLine();
79
80  return Result;
81}
82
83static LineNumberInfo DILineInfoToIntelJITFormat(uintptr_t StartAddress,
84                                                 uintptr_t Address,
85                                                 DILineInfo Line) {
86  LineNumberInfo Result;
87
88  Result.Offset = Address - StartAddress;
89  Result.LineNumber = Line.Line;
90
91  return Result;
92}
93
94static iJIT_Method_Load FunctionDescToIntelJITFormat(
95    IntelJITEventsWrapper& Wrapper,
96    const char* FnName,
97    uintptr_t FnStart,
98    size_t FnSize) {
99  iJIT_Method_Load Result;
100  memset(&Result, 0, sizeof(iJIT_Method_Load));
101
102  Result.method_id = Wrapper.iJIT_GetNewMethodID();
103  Result.method_name = const_cast<char*>(FnName);
104  Result.method_load_address = reinterpret_cast<void*>(FnStart);
105  Result.method_size = FnSize;
106
107  Result.class_id = 0;
108  Result.class_file_name = NULL;
109  Result.user_data = NULL;
110  Result.user_data_size = 0;
111  Result.env = iJDE_JittingAPI;
112
113  return Result;
114}
115
116// Adds the just-emitted function to the symbol table.
117void IntelJITEventListener::NotifyFunctionEmitted(
118    const Function &F, void *FnStart, size_t FnSize,
119    const EmittedFunctionDetails &Details) {
120  iJIT_Method_Load FunctionMessage = FunctionDescToIntelJITFormat(*Wrapper,
121                                      F.getName().data(),
122                                      reinterpret_cast<uint64_t>(FnStart),
123                                      FnSize);
124
125  std::vector<LineNumberInfo> LineInfo;
126
127  if (!Details.LineStarts.empty()) {
128    // Now convert the line number information from the address/DebugLoc
129    // format in Details to the offset/lineno in Intel JIT API format.
130
131    LineInfo.reserve(Details.LineStarts.size() + 1);
132
133    DebugLoc FirstLoc = Details.LineStarts[0].Loc;
134    assert(!FirstLoc.isUnknown()
135           && "LineStarts should not contain unknown DebugLocs");
136
137    MDNode *FirstLocScope = FirstLoc.getScope(F.getContext());
138    DISubprogram FunctionDI = getDISubprogram(FirstLocScope);
139    if (FunctionDI.Verify()) {
140      FunctionMessage.source_file_name = const_cast<char*>(
141                                          Filenames.getFullPath(FirstLocScope));
142
143      LineNumberInfo FirstLine;
144      FirstLine.Offset = 0;
145      FirstLine.LineNumber = FunctionDI.getLineNumber();
146      LineInfo.push_back(FirstLine);
147    }
148
149    for (std::vector<EmittedFunctionDetails::LineStart>::const_iterator I =
150          Details.LineStarts.begin(), E = Details.LineStarts.end();
151          I != E; ++I) {
152      // This implementation ignores the DebugLoc filename because the Intel
153      // JIT API does not support multiple source files associated with a single
154      // JIT function
155      LineInfo.push_back(LineStartToIntelJITFormat(
156                          reinterpret_cast<uintptr_t>(FnStart),
157                          I->Address,
158                          I->Loc));
159
160      // If we have no file name yet for the function, use the filename from
161      // the first instruction that has one
162      if (FunctionMessage.source_file_name == 0) {
163        MDNode *scope = I->Loc.getScope(
164          Details.MF->getFunction()->getContext());
165        FunctionMessage.source_file_name = const_cast<char*>(
166                                                  Filenames.getFullPath(scope));
167      }
168    }
169
170    FunctionMessage.line_number_size = LineInfo.size();
171    FunctionMessage.line_number_table = &*LineInfo.begin();
172  } else {
173    FunctionMessage.line_number_size = 0;
174    FunctionMessage.line_number_table = 0;
175  }
176
177  Wrapper->iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED,
178                            &FunctionMessage);
179  MethodIDs[FnStart] = FunctionMessage.method_id;
180}
181
182void IntelJITEventListener::NotifyFreeingMachineCode(void *FnStart) {
183  MethodIDMap::iterator I = MethodIDs.find(FnStart);
184  if (I != MethodIDs.end()) {
185    Wrapper->iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_UNLOAD_START, &I->second);
186    MethodIDs.erase(I);
187  }
188}
189
190void IntelJITEventListener::NotifyObjectEmitted(const ObjectImage &Obj) {
191  // Get the address of the object image for use as a unique identifier
192  const void* ObjData = Obj.getData().data();
193  DIContext* Context = DIContext::getDWARFContext(Obj.getObjectFile());
194  MethodAddressVector Functions;
195
196  // Use symbol info to iterate functions in the object.
197  for (object::symbol_iterator I = Obj.begin_symbols(),
198                               E = Obj.end_symbols();
199                        I != E;
200                        ++I) {
201    std::vector<LineNumberInfo> LineInfo;
202    std::string SourceFileName;
203
204    object::SymbolRef::Type SymType;
205    if (I->getType(SymType)) continue;
206    if (SymType == object::SymbolRef::ST_Function) {
207      StringRef  Name;
208      uint64_t   Addr;
209      uint64_t   Size;
210      if (I->getName(Name)) continue;
211      if (I->getAddress(Addr)) continue;
212      if (I->getSize(Size)) continue;
213
214      // Record this address in a local vector
215      Functions.push_back((void*)Addr);
216
217      // Build the function loaded notification message
218      iJIT_Method_Load FunctionMessage = FunctionDescToIntelJITFormat(*Wrapper,
219                                           Name.data(),
220                                           Addr,
221                                           Size);
222      if (Context) {
223        DILineInfoTable  Lines = Context->getLineInfoForAddressRange(Addr, Size);
224        DILineInfoTable::iterator  Begin = Lines.begin();
225        DILineInfoTable::iterator  End = Lines.end();
226        for (DILineInfoTable::iterator It = Begin; It != End; ++It) {
227          LineInfo.push_back(DILineInfoToIntelJITFormat((uintptr_t)Addr,
228                                                        It->first,
229                                                        It->second));
230        }
231        if (LineInfo.size() == 0) {
232          FunctionMessage.source_file_name = 0;
233          FunctionMessage.line_number_size = 0;
234          FunctionMessage.line_number_table = 0;
235        } else {
236          SourceFileName = Lines.front().second.FileName;
237          FunctionMessage.source_file_name = const_cast<char *>(SourceFileName.c_str());
238          FunctionMessage.line_number_size = LineInfo.size();
239          FunctionMessage.line_number_table = &*LineInfo.begin();
240        }
241      } else {
242        FunctionMessage.source_file_name = 0;
243        FunctionMessage.line_number_size = 0;
244        FunctionMessage.line_number_table = 0;
245      }
246
247      Wrapper->iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED,
248                                &FunctionMessage);
249      MethodIDs[(void*)Addr] = FunctionMessage.method_id;
250    }
251  }
252
253  // To support object unload notification, we need to keep a list of
254  // registered function addresses for each loaded object.  We will
255  // use the MethodIDs map to get the registered ID for each function.
256  LoadedObjectMap[ObjData] = Functions;
257}
258
259void IntelJITEventListener::NotifyFreeingObject(const ObjectImage &Obj) {
260  // Get the address of the object image for use as a unique identifier
261  const void* ObjData = Obj.getData().data();
262
263  // Get the object's function list from LoadedObjectMap
264  ObjectMap::iterator OI = LoadedObjectMap.find(ObjData);
265  if (OI == LoadedObjectMap.end())
266    return;
267  MethodAddressVector& Functions = OI->second;
268
269  // Walk the function list, unregistering each function
270  for (MethodAddressVector::iterator FI = Functions.begin(),
271                                     FE = Functions.end();
272       FI != FE;
273       ++FI) {
274    void* FnStart = const_cast<void*>(*FI);
275    MethodIDMap::iterator MI = MethodIDs.find(FnStart);
276    if (MI != MethodIDs.end()) {
277      Wrapper->iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_UNLOAD_START,
278                                &MI->second);
279      MethodIDs.erase(MI);
280    }
281  }
282
283  // Erase the object from LoadedObjectMap
284  LoadedObjectMap.erase(OI);
285}
286
287}  // anonymous namespace.
288
289namespace llvm {
290JITEventListener *JITEventListener::createIntelJITEventListener() {
291  return new IntelJITEventListener(new IntelJITEventsWrapper);
292}
293
294// for testing
295JITEventListener *JITEventListener::createIntelJITEventListener(
296                                      IntelJITEventsWrapper* TestImpl) {
297  return new IntelJITEventListener(TestImpl);
298}
299
300} // namespace llvm
301
302