1 //===-- IntelJITEventListener.cpp - Tell Intel profiler about JITed code --===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file defines a JITEventListener object to tell Intel(R) VTune(TM)
10 // Amplifier XE 2011 about JITted functions.
12 //===----------------------------------------------------------------------===//
14 #include "IntelJITEventsWrapper.h"
15 #include "llvm-c/ExecutionEngine.h"
16 #include "llvm/ADT/DenseMap.h"
17 #include "llvm/CodeGen/MachineFunction.h"
18 #include "llvm/Config/config.h"
19 #include "llvm/DebugInfo/DIContext.h"
20 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
21 #include "llvm/ExecutionEngine/JITEventListener.h"
22 #include "llvm/IR/DebugInfo.h"
23 #include "llvm/IR/Function.h"
24 #include "llvm/IR/Metadata.h"
25 #include "llvm/IR/ValueHandle.h"
26 #include "llvm/Object/ObjectFile.h"
27 #include "llvm/Object/SymbolSize.h"
28 #include "llvm/Support/Debug.h"
29 #include "llvm/Support/Errno.h"
30 #include "llvm/Support/raw_ostream.h"
33 using namespace llvm::object
;
35 #define DEBUG_TYPE "amplifier-jit-event-listener"
39 class IntelJITEventListener
: public JITEventListener
{
40 typedef DenseMap
<void*, unsigned int> MethodIDMap
;
42 std::unique_ptr
<IntelJITEventsWrapper
> Wrapper
;
43 MethodIDMap MethodIDs
;
45 typedef SmallVector
<const void *, 64> MethodAddressVector
;
46 typedef DenseMap
<const void *, MethodAddressVector
> ObjectMap
;
48 ObjectMap LoadedObjectMap
;
49 std::map
<ObjectKey
, OwningBinary
<ObjectFile
>> DebugObjects
;
52 IntelJITEventListener(IntelJITEventsWrapper
* libraryWrapper
) {
53 Wrapper
.reset(libraryWrapper
);
56 ~IntelJITEventListener() {
59 void notifyObjectLoaded(ObjectKey Key
, const ObjectFile
&Obj
,
60 const RuntimeDyld::LoadedObjectInfo
&L
) override
;
62 void notifyFreeingObject(ObjectKey Key
) override
;
65 static LineNumberInfo
DILineInfoToIntelJITFormat(uintptr_t StartAddress
,
68 LineNumberInfo Result
;
70 Result
.Offset
= Address
- StartAddress
;
71 Result
.LineNumber
= Line
.Line
;
76 static iJIT_Method_Load
FunctionDescToIntelJITFormat(
77 IntelJITEventsWrapper
& Wrapper
,
81 iJIT_Method_Load Result
;
82 memset(&Result
, 0, sizeof(iJIT_Method_Load
));
84 Result
.method_id
= Wrapper
.iJIT_GetNewMethodID();
85 Result
.method_name
= const_cast<char*>(FnName
);
86 Result
.method_load_address
= reinterpret_cast<void*>(FnStart
);
87 Result
.method_size
= FnSize
;
90 Result
.class_file_name
= NULL
;
91 Result
.user_data
= NULL
;
92 Result
.user_data_size
= 0;
93 Result
.env
= iJDE_JittingAPI
;
98 void IntelJITEventListener::notifyObjectLoaded(
99 ObjectKey Key
, const ObjectFile
&Obj
,
100 const RuntimeDyld::LoadedObjectInfo
&L
) {
102 OwningBinary
<ObjectFile
> DebugObjOwner
= L
.getObjectForDebug(Obj
);
103 const ObjectFile
*DebugObj
= DebugObjOwner
.getBinary();
107 // Get the address of the object image for use as a unique identifier
108 const void* ObjData
= DebugObj
->getData().data();
109 std::unique_ptr
<DIContext
> Context
= DWARFContext::create(*DebugObj
);
110 MethodAddressVector Functions
;
112 // Use symbol info to iterate functions in the object.
113 for (const std::pair
<SymbolRef
, uint64_t> &P
: computeSymbolSizes(*DebugObj
)) {
114 SymbolRef Sym
= P
.first
;
115 std::vector
<LineNumberInfo
> LineInfo
;
116 std::string SourceFileName
;
118 Expected
<SymbolRef::Type
> SymTypeOrErr
= Sym
.getType();
120 // TODO: Actually report errors helpfully.
121 consumeError(SymTypeOrErr
.takeError());
124 SymbolRef::Type SymType
= *SymTypeOrErr
;
125 if (SymType
!= SymbolRef::ST_Function
)
128 Expected
<StringRef
> Name
= Sym
.getName();
130 // TODO: Actually report errors helpfully.
131 consumeError(Name
.takeError());
135 Expected
<uint64_t> AddrOrErr
= Sym
.getAddress();
137 // TODO: Actually report errors helpfully.
138 consumeError(AddrOrErr
.takeError());
141 uint64_t Addr
= *AddrOrErr
;
142 uint64_t Size
= P
.second
;
144 auto SecOrErr
= Sym
.getSection();
146 // TODO: Actually report errors helpfully.
147 consumeError(SecOrErr
.takeError());
150 object::section_iterator Sec
= *SecOrErr
;
151 if (Sec
== Obj
.section_end())
153 uint64_t Index
= Sec
->getIndex();
155 // Record this address in a local vector
156 Functions
.push_back((void*)Addr
);
158 // Build the function loaded notification message
159 iJIT_Method_Load FunctionMessage
=
160 FunctionDescToIntelJITFormat(*Wrapper
, Name
->data(), Addr
, Size
);
161 DILineInfoTable Lines
=
162 Context
->getLineInfoForAddressRange({Addr
, Index
}, Size
);
163 DILineInfoTable::iterator Begin
= Lines
.begin();
164 DILineInfoTable::iterator End
= Lines
.end();
165 for (DILineInfoTable::iterator It
= Begin
; It
!= End
; ++It
) {
167 DILineInfoToIntelJITFormat((uintptr_t)Addr
, It
->first
, It
->second
));
169 if (LineInfo
.size() == 0) {
170 FunctionMessage
.source_file_name
= 0;
171 FunctionMessage
.line_number_size
= 0;
172 FunctionMessage
.line_number_table
= 0;
174 // Source line information for the address range is provided as
175 // a code offset for the start of the corresponding sub-range and
176 // a source line. JIT API treats offsets in LineNumberInfo structures
177 // as the end of the corresponding code region. The start of the code
178 // is taken from the previous element. Need to shift the elements.
180 LineNumberInfo last
= LineInfo
.back();
181 last
.Offset
= FunctionMessage
.method_size
;
182 LineInfo
.push_back(last
);
183 for (size_t i
= LineInfo
.size() - 2; i
> 0; --i
)
184 LineInfo
[i
].LineNumber
= LineInfo
[i
- 1].LineNumber
;
186 SourceFileName
= Lines
.front().second
.FileName
;
187 FunctionMessage
.source_file_name
=
188 const_cast<char *>(SourceFileName
.c_str());
189 FunctionMessage
.line_number_size
= LineInfo
.size();
190 FunctionMessage
.line_number_table
= &*LineInfo
.begin();
193 Wrapper
->iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED
,
195 MethodIDs
[(void*)Addr
] = FunctionMessage
.method_id
;
198 // To support object unload notification, we need to keep a list of
199 // registered function addresses for each loaded object. We will
200 // use the MethodIDs map to get the registered ID for each function.
201 LoadedObjectMap
[ObjData
] = Functions
;
202 DebugObjects
[Key
] = std::move(DebugObjOwner
);
205 void IntelJITEventListener::notifyFreeingObject(ObjectKey Key
) {
206 // This object may not have been registered with the listener. If it wasn't,
208 if (DebugObjects
.find(Key
) == DebugObjects
.end())
211 // Get the address of the object image for use as a unique identifier
212 const ObjectFile
&DebugObj
= *DebugObjects
[Key
].getBinary();
213 const void* ObjData
= DebugObj
.getData().data();
215 // Get the object's function list from LoadedObjectMap
216 ObjectMap::iterator OI
= LoadedObjectMap
.find(ObjData
);
217 if (OI
== LoadedObjectMap
.end())
219 MethodAddressVector
& Functions
= OI
->second
;
221 // Walk the function list, unregistering each function
222 for (MethodAddressVector::iterator FI
= Functions
.begin(),
223 FE
= Functions
.end();
226 void* FnStart
= const_cast<void*>(*FI
);
227 MethodIDMap::iterator MI
= MethodIDs
.find(FnStart
);
228 if (MI
!= MethodIDs
.end()) {
229 Wrapper
->iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_UNLOAD_START
,
235 // Erase the object from LoadedObjectMap
236 LoadedObjectMap
.erase(OI
);
237 DebugObjects
.erase(Key
);
240 } // anonymous namespace.
243 JITEventListener
*JITEventListener::createIntelJITEventListener() {
244 return new IntelJITEventListener(new IntelJITEventsWrapper
);
248 JITEventListener
*JITEventListener::createIntelJITEventListener(
249 IntelJITEventsWrapper
* TestImpl
) {
250 return new IntelJITEventListener(TestImpl
);
255 LLVMJITEventListenerRef
LLVMCreateIntelJITEventListener(void)
257 return wrap(JITEventListener::createIntelJITEventListener());