1 | //===-- IntelJITEventListener.cpp - Tell Intel profiler about JITed code --===// |
2 | // |
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 |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file defines a JITEventListener object to tell Intel(R) VTune(TM) |
10 | // Amplifier XE 2011 about JITted functions. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "IntelJITProfiling/IntelJITEventsWrapper.h" |
15 | #include "ittnotify.h" |
16 | #include "llvm-c/ExecutionEngine.h" |
17 | #include "llvm/ADT/DenseMap.h" |
18 | #include "llvm/CodeGen/MachineFunction.h" |
19 | #include "llvm/Config/config.h" |
20 | #include "llvm/DebugInfo/DIContext.h" |
21 | #include "llvm/DebugInfo/DWARF/DWARFContext.h" |
22 | #include "llvm/ExecutionEngine/JITEventListener.h" |
23 | #include "llvm/IR/DebugInfo.h" |
24 | #include "llvm/IR/Function.h" |
25 | #include "llvm/IR/Metadata.h" |
26 | #include "llvm/IR/ValueHandle.h" |
27 | #include "llvm/Object/ELFObjectFile.h" |
28 | #include "llvm/Object/ObjectFile.h" |
29 | #include "llvm/Object/SymbolSize.h" |
30 | #include "llvm/Support/Debug.h" |
31 | #include "llvm/Support/Errno.h" |
32 | #include "llvm/Support/raw_ostream.h" |
33 | |
34 | using namespace llvm; |
35 | using namespace llvm::object; |
36 | |
37 | #define DEBUG_TYPE "amplifier-jit-event-listener" |
38 | |
39 | namespace { |
40 | |
41 | class IntelIttnotifyInfo { |
42 | std::string ModuleName; |
43 | std::vector<std::string> SectionNamesVector; |
44 | std::vector<__itt_section_info> SectionInfoVector; |
45 | __itt_module_object *ModuleObject; |
46 | IntelJITEventsWrapper &WrapperRef; |
47 | |
48 | public: |
49 | IntelIttnotifyInfo(IntelJITEventsWrapper &Wrapper) |
50 | : ModuleObject(NULL), WrapperRef(Wrapper){}; |
51 | ~IntelIttnotifyInfo() { delete ModuleObject; }; |
52 | |
53 | void setModuleName(const char *Name) { ModuleName = std::string(Name); } |
54 | |
55 | const char *getModuleName() { return ModuleName.c_str(); } |
56 | |
57 | void setModuleObject(__itt_module_object *ModuleObj) { |
58 | ModuleObject = ModuleObj; |
59 | } |
60 | |
61 | __itt_module_object *getModuleObject() { return ModuleObject; } |
62 | |
63 | __itt_section_info *getSectionInfoVectorBegin() { |
64 | if (SectionInfoVector.size()) |
65 | return &SectionInfoVector[0]; |
66 | return NULL; |
67 | } |
68 | |
69 | void reportSection(llvm::IttEventType EventType, const char *SectionName, |
70 | unsigned int SectionSize) { |
71 | WrapperRef.iJitIttNotifyInfo(EventType, SectionName, SectionSize); |
72 | } |
73 | |
74 | int fillSectionInformation(const ObjectFile &Obj, |
75 | const RuntimeDyld::LoadedObjectInfo &L) { |
76 | |
77 | int SectionCounter = 0; |
78 | |
79 | for (auto &Section : Obj.sections()) { |
80 | uint64_t SectionLoadAddr = L.getSectionLoadAddress(Sec: Section); |
81 | if (SectionLoadAddr) { |
82 | object::ELFSectionRef ElfSection(Section); |
83 | |
84 | __itt_section_info SectionInfo; |
85 | memset(&SectionInfo, 0, sizeof(SectionInfo)); |
86 | SectionInfo.start_addr = reinterpret_cast<void *>(SectionLoadAddr); |
87 | SectionInfo.file_offset = ElfSection.getOffset(); |
88 | SectionInfo.flags = ElfSection.getFlags(); |
89 | |
90 | StringRef SectionName("" ); |
91 | auto SectionNameOrError = ElfSection.getName(); |
92 | if (SectionNameOrError) |
93 | SectionName = *SectionNameOrError; |
94 | |
95 | SectionNamesVector.push_back(x: SectionName.str()); |
96 | SectionInfo.size = ElfSection.getSize(); |
97 | reportSection(llvm::LoadBinarySection, SectionName.str().c_str(), |
98 | SectionInfo.size); |
99 | |
100 | if (ElfSection.isBSS()) { |
101 | SectionInfo.type = itt_section_type_bss; |
102 | } else if (ElfSection.isData()) { |
103 | SectionInfo.type = itt_section_type_data; |
104 | } else if (ElfSection.isText()) { |
105 | SectionInfo.type = itt_section_type_text; |
106 | } |
107 | SectionInfoVector.push_back(SectionInfo); |
108 | ++SectionCounter; |
109 | } |
110 | } |
111 | // Hereinafter: don't change SectionNamesVector content to avoid vector |
112 | // reallocation - reallocation invalidates all the references, pointers, and |
113 | // iterators referring to the elements in the sequence. |
114 | for (int I = 0; I < SectionCounter; ++I) { |
115 | SectionInfoVector[I].name = SectionNamesVector[I].c_str(); |
116 | } |
117 | return SectionCounter; |
118 | } |
119 | }; |
120 | |
121 | class IntelJITEventListener : public JITEventListener { |
122 | typedef DenseMap<void*, unsigned int> MethodIDMap; |
123 | |
124 | std::unique_ptr<IntelJITEventsWrapper> Wrapper; |
125 | MethodIDMap MethodIDs; |
126 | |
127 | typedef SmallVector<const void *, 64> MethodAddressVector; |
128 | typedef DenseMap<const void *, MethodAddressVector> ObjectMap; |
129 | |
130 | ObjectMap LoadedObjectMap; |
131 | std::map<ObjectKey, OwningBinary<ObjectFile>> DebugObjects; |
132 | |
133 | std::map<ObjectKey, std::unique_ptr<IntelIttnotifyInfo>> KeyToIttnotify; |
134 | |
135 | public: |
136 | IntelJITEventListener(IntelJITEventsWrapper* libraryWrapper) { |
137 | Wrapper.reset(p: libraryWrapper); |
138 | } |
139 | |
140 | ~IntelJITEventListener() { |
141 | } |
142 | |
143 | void notifyObjectLoaded(ObjectKey Key, const ObjectFile &Obj, |
144 | const RuntimeDyld::LoadedObjectInfo &L) override; |
145 | |
146 | void notifyFreeingObject(ObjectKey Key) override; |
147 | }; |
148 | |
149 | static LineNumberInfo DILineInfoToIntelJITFormat(uintptr_t StartAddress, |
150 | uintptr_t Address, |
151 | DILineInfo Line) { |
152 | LineNumberInfo Result; |
153 | |
154 | Result.Offset = Address - StartAddress; |
155 | Result.LineNumber = Line.Line; |
156 | |
157 | return Result; |
158 | } |
159 | |
160 | static iJIT_Method_Load FunctionDescToIntelJITFormat( |
161 | IntelJITEventsWrapper& Wrapper, |
162 | const char* FnName, |
163 | uintptr_t FnStart, |
164 | size_t FnSize) { |
165 | iJIT_Method_Load Result; |
166 | memset(&Result, 0, sizeof(iJIT_Method_Load)); |
167 | |
168 | Result.method_id = Wrapper.iJIT_GetNewMethodID(); |
169 | Result.method_name = const_cast<char*>(FnName); |
170 | Result.method_load_address = reinterpret_cast<void*>(FnStart); |
171 | Result.method_size = FnSize; |
172 | |
173 | Result.class_id = 0; |
174 | Result.class_file_name = NULL; |
175 | Result.user_data = NULL; |
176 | Result.user_data_size = 0; |
177 | Result.env = iJDE_JittingAPI; |
178 | |
179 | return Result; |
180 | } |
181 | |
182 | int getBackwardCompatibilityMode() { |
183 | |
184 | char *BackwardCompatibilityEnv = getenv(name: "INTEL_JIT_BACKWARD_COMPATIBILITY" ); |
185 | int BackwardCompatibilityMode = 0; |
186 | if (BackwardCompatibilityEnv) { |
187 | StringRef(BackwardCompatibilityEnv) |
188 | .getAsInteger(Radix: 10, Result&: BackwardCompatibilityMode); |
189 | } |
190 | return BackwardCompatibilityMode; |
191 | } |
192 | |
193 | void IntelJITEventListener::notifyObjectLoaded( |
194 | ObjectKey Key, const ObjectFile &Obj, |
195 | const RuntimeDyld::LoadedObjectInfo &L) { |
196 | |
197 | int BackwardCompatibilityMode = getBackwardCompatibilityMode(); |
198 | if (BackwardCompatibilityMode == 0) { |
199 | if (Obj.isELF()) { |
200 | std::unique_ptr<IntelIttnotifyInfo> ModuleIttnotify = |
201 | std::make_unique<IntelIttnotifyInfo>(args&: *Wrapper); |
202 | ModuleIttnotify->setModuleName( |
203 | StringRef(llvm::utohexstr( |
204 | X: MD5Hash(Str: Obj.getMemoryBufferRef().getBuffer()), LowerCase: true)) |
205 | .str() |
206 | .c_str()); |
207 | |
208 | __itt_module_object *ModuleObject = new __itt_module_object(); |
209 | ModuleObject->module_name = ModuleIttnotify->getModuleName(); |
210 | ModuleObject->module_size = Obj.getMemoryBufferRef().getBufferSize(); |
211 | Wrapper->iJitIttNotifyInfo(llvm::LoadBinaryModule, |
212 | ModuleObject->module_name, |
213 | ModuleObject->module_size); |
214 | ModuleObject->module_type = __itt_module_type_elf; |
215 | ModuleObject->section_number = |
216 | ModuleIttnotify->fillSectionInformation(Obj, L); |
217 | ModuleObject->module_buffer = |
218 | (void *)const_cast<char *>(Obj.getMemoryBufferRef().getBufferStart()); |
219 | ModuleObject->module_id = |
220 | __itt_id_make((void *)&(*ModuleObject), ModuleObject->module_size); |
221 | ModuleObject->section_array = |
222 | ModuleIttnotify->getSectionInfoVectorBegin(); |
223 | ModuleIttnotify->setModuleObject(ModuleObject); |
224 | |
225 | __itt_module_load_with_sections(ModuleObject); |
226 | |
227 | KeyToIttnotify[Key] = std::move(ModuleIttnotify); |
228 | } |
229 | } else if (BackwardCompatibilityMode == 1) { |
230 | |
231 | OwningBinary<ObjectFile> DebugObjOwner = L.getObjectForDebug(Obj); |
232 | const ObjectFile *DebugObj = DebugObjOwner.getBinary(); |
233 | if (!DebugObj) |
234 | return; |
235 | |
236 | // Get the address of the object image for use as a unique identifier |
237 | const void *ObjData = DebugObj->getData().data(); |
238 | std::unique_ptr<DIContext> Context = DWARFContext::create(Obj: *DebugObj); |
239 | MethodAddressVector Functions; |
240 | |
241 | // Use symbol info to iterate functions in the object. |
242 | for (const std::pair<SymbolRef, uint64_t> &P : |
243 | computeSymbolSizes(O: *DebugObj)) { |
244 | SymbolRef Sym = P.first; |
245 | std::vector<LineNumberInfo> LineInfo; |
246 | std::string SourceFileName; |
247 | |
248 | Expected<SymbolRef::Type> SymTypeOrErr = Sym.getType(); |
249 | if (!SymTypeOrErr) { |
250 | // TODO: Actually report errors helpfully. |
251 | consumeError(Err: SymTypeOrErr.takeError()); |
252 | continue; |
253 | } |
254 | SymbolRef::Type SymType = *SymTypeOrErr; |
255 | if (SymType != SymbolRef::ST_Function) |
256 | continue; |
257 | |
258 | Expected<StringRef> Name = Sym.getName(); |
259 | if (!Name) { |
260 | // TODO: Actually report errors helpfully. |
261 | consumeError(Err: Name.takeError()); |
262 | continue; |
263 | } |
264 | |
265 | Expected<uint64_t> AddrOrErr = Sym.getAddress(); |
266 | if (!AddrOrErr) { |
267 | // TODO: Actually report errors helpfully. |
268 | consumeError(Err: AddrOrErr.takeError()); |
269 | continue; |
270 | } |
271 | uint64_t Addr = *AddrOrErr; |
272 | uint64_t Size = P.second; |
273 | |
274 | auto SecOrErr = Sym.getSection(); |
275 | if (!SecOrErr) { |
276 | // TODO: Actually report errors helpfully. |
277 | consumeError(Err: SecOrErr.takeError()); |
278 | continue; |
279 | } |
280 | object::section_iterator Sec = *SecOrErr; |
281 | if (Sec == Obj.section_end()) |
282 | continue; |
283 | uint64_t Index = Sec->getIndex(); |
284 | |
285 | // Record this address in a local vector |
286 | Functions.push_back(Elt: (void *)Addr); |
287 | |
288 | // Build the function loaded notification message |
289 | iJIT_Method_Load FunctionMessage = |
290 | FunctionDescToIntelJITFormat(*Wrapper, Name->data(), Addr, Size); |
291 | DILineInfoTable Lines = |
292 | Context->getLineInfoForAddressRange(Address: {.Address: Addr, .SectionIndex: Index}, Size); |
293 | DILineInfoTable::iterator Begin = Lines.begin(); |
294 | DILineInfoTable::iterator End = Lines.end(); |
295 | for (DILineInfoTable::iterator It = Begin; It != End; ++It) { |
296 | LineInfo.push_back( |
297 | DILineInfoToIntelJITFormat((uintptr_t)Addr, It->first, It->second)); |
298 | } |
299 | if (LineInfo.size() == 0) { |
300 | FunctionMessage.source_file_name = 0; |
301 | FunctionMessage.line_number_size = 0; |
302 | FunctionMessage.line_number_table = 0; |
303 | } else { |
304 | // Source line information for the address range is provided as |
305 | // a code offset for the start of the corresponding sub-range and |
306 | // a source line. JIT API treats offsets in LineNumberInfo structures |
307 | // as the end of the corresponding code region. The start of the code |
308 | // is taken from the previous element. Need to shift the elements. |
309 | |
310 | LineNumberInfo last = LineInfo.back(); |
311 | last.Offset = FunctionMessage.method_size; |
312 | LineInfo.push_back(last); |
313 | for (size_t i = LineInfo.size() - 2; i > 0; --i) |
314 | LineInfo[i].LineNumber = LineInfo[i - 1].LineNumber; |
315 | |
316 | SourceFileName = Lines.front().second.FileName; |
317 | FunctionMessage.source_file_name = |
318 | const_cast<char *>(SourceFileName.c_str()); |
319 | FunctionMessage.line_number_size = LineInfo.size(); |
320 | FunctionMessage.line_number_table = &*LineInfo.begin(); |
321 | } |
322 | |
323 | Wrapper->iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, |
324 | &FunctionMessage); |
325 | MethodIDs[(void *)Addr] = FunctionMessage.method_id; |
326 | } |
327 | |
328 | // To support object unload notification, we need to keep a list of |
329 | // registered function addresses for each loaded object. We will |
330 | // use the MethodIDs map to get the registered ID for each function. |
331 | LoadedObjectMap[ObjData] = Functions; |
332 | DebugObjects[Key] = std::move(DebugObjOwner); |
333 | } |
334 | } |
335 | |
336 | void IntelJITEventListener::notifyFreeingObject(ObjectKey Key) { |
337 | |
338 | int BackwardCompatibilityMode = getBackwardCompatibilityMode(); |
339 | if (BackwardCompatibilityMode == 0) { |
340 | if (KeyToIttnotify.find(x: Key) == KeyToIttnotify.end()) |
341 | return; |
342 | __itt_module_unload_with_sections(KeyToIttnotify[Key]->getModuleObject()); |
343 | Wrapper->iJitIttNotifyInfo( |
344 | llvm::UnloadBinaryModule, |
345 | KeyToIttnotify[Key]->getModuleObject()->module_name, |
346 | KeyToIttnotify[Key]->getModuleObject()->module_size); |
347 | KeyToIttnotify.erase(x: Key); |
348 | } else if (BackwardCompatibilityMode == 1) { |
349 | // This object may not have been registered with the listener. If it wasn't, |
350 | // bail out. |
351 | if (DebugObjects.find(x: Key) == DebugObjects.end()) |
352 | return; |
353 | |
354 | // Get the address of the object image for use as a unique identifier |
355 | const ObjectFile &DebugObj = *DebugObjects[Key].getBinary(); |
356 | const void *ObjData = DebugObj.getData().data(); |
357 | |
358 | // Get the object's function list from LoadedObjectMap |
359 | ObjectMap::iterator OI = LoadedObjectMap.find(Val: ObjData); |
360 | if (OI == LoadedObjectMap.end()) |
361 | return; |
362 | MethodAddressVector &Functions = OI->second; |
363 | |
364 | // Walk the function list, unregistering each function |
365 | for (MethodAddressVector::iterator FI = Functions.begin(), |
366 | FE = Functions.end(); |
367 | FI != FE; ++FI) { |
368 | void *FnStart = const_cast<void *>(*FI); |
369 | MethodIDMap::iterator MI = MethodIDs.find(Val: FnStart); |
370 | if (MI != MethodIDs.end()) { |
371 | Wrapper->iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_UNLOAD_START, |
372 | &MI->second); |
373 | MethodIDs.erase(I: MI); |
374 | } |
375 | } |
376 | |
377 | // Erase the object from LoadedObjectMap |
378 | LoadedObjectMap.erase(I: OI); |
379 | DebugObjects.erase(x: Key); |
380 | } |
381 | } |
382 | |
383 | } // anonymous namespace. |
384 | |
385 | namespace llvm { |
386 | JITEventListener *JITEventListener::createIntelJITEventListener() { |
387 | return new IntelJITEventListener(new IntelJITEventsWrapper); |
388 | } |
389 | |
390 | // for testing |
391 | JITEventListener *JITEventListener::createIntelJITEventListener( |
392 | IntelJITEventsWrapper* TestImpl) { |
393 | return new IntelJITEventListener(TestImpl); |
394 | } |
395 | |
396 | } // namespace llvm |
397 | |
398 | LLVMJITEventListenerRef LLVMCreateIntelJITEventListener(void) |
399 | { |
400 | return wrap(P: JITEventListener::createIntelJITEventListener()); |
401 | } |
402 | |