mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-22 10:42:39 +01:00
[MCJIT] Profile the code generated by MCJIT engine using Intel VTune profiler
Patch by Elena Kovanova. Thanks Elena! Problem: LLVM already has a feature to profile the JIT-compiled code with VTune. This is done using Intel JIT Profiling API (https://github.com/intel/ittapi). Function information is captured by VTune as soon as the function is JIT-compiled. We tried to use the same approach to report the function information generated by the MCJIT engine – read parsing the debug information for in-memory ELF module and report it using JIT API. As the results, we figured out that it did not work properly for the following cases: inline functions, the functions located in multiple source files, the functions having several bodies (address ranges). Solution: To overcome limitations described above, we have introduced new APIs as a part of Intel ITT APIs to report the entire in-memory ELF module to be further processed as regular ELF binaries with debug information. This patch 1. Switches LLVM to open source version of Intel ITT/JIT APIs (https://github.com/intel/ittapi) to keep it always up to date. 2. Adds support of profiling the code generated by MCJIT engine using Intel VTune profiler Another separate patch will get rid of obsolete Intel ITT APIs stuff, having LLVM already switched to https://github.com/intel/ittapi. Differential Revision: https://reviews.llvm.org/D86435
This commit is contained in:
parent
4b7f9fd6c6
commit
460e0b8ba2
@ -1,5 +1,26 @@
|
||||
include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/.. )
|
||||
|
||||
set(GIT_REPOSITORY https://github.com/intel/ittapi.git)
|
||||
set(GIT_TAG v3.18.8)
|
||||
|
||||
if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/ittapi)
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} clone ${GIT_REPOSITORY}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
RESULT_VARIABLE GIT_CLONE_RESULT)
|
||||
if(NOT GIT_CLONE_RESULT EQUAL "0")
|
||||
message(FATAL_ERROR "git clone ${GIT_REPOSITORY} failed with ${GIT_CLONE_RESULT}, please clone ${GIT_REPOSITORY}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} checkout ${GIT_TAG}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ittapi
|
||||
RESULT_VARIABLE GIT_CHECKOUT_RESULT)
|
||||
if(NOT GIT_CHECKOUT_RESULT EQUAL "0")
|
||||
message(FATAL_ERROR "git checkout ${GIT_TAG} failed with ${GIT_CHECKOUT_RESULT}, please checkout ${GIT_TAG} at ${CMAKE_CURRENT_SOURCE_DIR}/ittapi")
|
||||
endif()
|
||||
|
||||
include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/ittapi/include/ )
|
||||
|
||||
if( HAVE_LIBDL )
|
||||
set(LLVM_INTEL_JIT_LIBS ${CMAKE_DL_LIBS})
|
||||
endif()
|
||||
@ -10,6 +31,7 @@ set(LLVM_INTEL_JIT_LIBS ${LLVM_PTHREAD_LIB} ${LLVM_INTEL_JIT_LIBS})
|
||||
add_llvm_component_library(LLVMIntelJITEvents
|
||||
IntelJITEventListener.cpp
|
||||
jitprofiling.c
|
||||
ittapi/src/ittnotify/ittnotify_static.c
|
||||
|
||||
LINK_LIBS ${LLVM_INTEL_JIT_LIBS}
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "IntelJITEventsWrapper.h"
|
||||
#include "ittnotify.h"
|
||||
#include "llvm-c/ExecutionEngine.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/CodeGen/MachineFunction.h"
|
||||
@ -36,6 +37,86 @@ using namespace llvm::object;
|
||||
|
||||
namespace {
|
||||
|
||||
class IntelIttnotifyInfo {
|
||||
std::string ModuleName;
|
||||
std::vector<std::string> SectionNamesVector;
|
||||
std::vector<__itt_section_info> SectionInfoVector;
|
||||
__itt_module_object *ModuleObject;
|
||||
IntelJITEventsWrapper &WrapperRef;
|
||||
|
||||
public:
|
||||
IntelIttnotifyInfo(IntelJITEventsWrapper &Wrapper)
|
||||
: ModuleObject(NULL), WrapperRef(Wrapper){};
|
||||
~IntelIttnotifyInfo() { delete ModuleObject; };
|
||||
|
||||
void setModuleName(const char *Name) { ModuleName = std::string(Name); }
|
||||
|
||||
const char *getModuleName() { return ModuleName.c_str(); }
|
||||
|
||||
void setModuleObject(__itt_module_object *ModuleObj) {
|
||||
ModuleObject = ModuleObj;
|
||||
}
|
||||
|
||||
__itt_module_object *getModuleObject() { return ModuleObject; }
|
||||
|
||||
__itt_section_info *getSectionInfoVectorBegin() {
|
||||
if (SectionInfoVector.size())
|
||||
return &SectionInfoVector[0];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void reportSection(llvm::IttEventType EventType, const char *SectionName,
|
||||
unsigned int SectionSize) {
|
||||
WrapperRef.iJitIttNotifyInfo(EventType, SectionName, SectionSize);
|
||||
}
|
||||
|
||||
int fillSectionInformation(const ObjectFile &Obj,
|
||||
const RuntimeDyld::LoadedObjectInfo &L) {
|
||||
|
||||
int SectionCounter = 0;
|
||||
|
||||
for (auto &Section : Obj.sections()) {
|
||||
uint64_t SectionLoadAddr = L.getSectionLoadAddress(Section);
|
||||
if (SectionLoadAddr) {
|
||||
object::ELFSectionRef ElfSection(Section);
|
||||
|
||||
__itt_section_info SectionInfo;
|
||||
memset(&SectionInfo, 0, sizeof(SectionInfo));
|
||||
SectionInfo.start_addr = reinterpret_cast<void *>(SectionLoadAddr);
|
||||
SectionInfo.file_offset = ElfSection.getOffset();
|
||||
SectionInfo.flags = ElfSection.getFlags();
|
||||
|
||||
StringRef SectionName("");
|
||||
auto SectionNameOrError = ElfSection.getName();
|
||||
if (SectionNameOrError)
|
||||
SectionName = *SectionNameOrError;
|
||||
|
||||
SectionNamesVector.push_back(SectionName.str());
|
||||
SectionInfo.size = ElfSection.getSize();
|
||||
reportSection(llvm::LoadBinarySection, SectionName.str().c_str(),
|
||||
SectionInfo.size);
|
||||
|
||||
if (ElfSection.isBSS()) {
|
||||
SectionInfo.type = itt_section_type_bss;
|
||||
} else if (ElfSection.isData()) {
|
||||
SectionInfo.type = itt_section_type_data;
|
||||
} else if (ElfSection.isText()) {
|
||||
SectionInfo.type = itt_section_type_text;
|
||||
}
|
||||
SectionInfoVector.push_back(SectionInfo);
|
||||
++SectionCounter;
|
||||
}
|
||||
}
|
||||
// Hereinafter: don't change SectionNamesVector content to avoid vector
|
||||
// reallocation - reallocation invalidates all the references, pointers, and
|
||||
// iterators referring to the elements in the sequence.
|
||||
for (int I = 0; I < SectionCounter; ++I) {
|
||||
SectionInfoVector[I].name = SectionNamesVector[I].c_str();
|
||||
}
|
||||
return SectionCounter;
|
||||
}
|
||||
};
|
||||
|
||||
class IntelJITEventListener : public JITEventListener {
|
||||
typedef DenseMap<void*, unsigned int> MethodIDMap;
|
||||
|
||||
@ -48,6 +129,8 @@ class IntelJITEventListener : public JITEventListener {
|
||||
ObjectMap LoadedObjectMap;
|
||||
std::map<ObjectKey, OwningBinary<ObjectFile>> DebugObjects;
|
||||
|
||||
std::map<ObjectKey, std::unique_ptr<IntelIttnotifyInfo>> KeyToIttnotify;
|
||||
|
||||
public:
|
||||
IntelJITEventListener(IntelJITEventsWrapper* libraryWrapper) {
|
||||
Wrapper.reset(libraryWrapper);
|
||||
@ -95,22 +178,68 @@ static iJIT_Method_Load FunctionDescToIntelJITFormat(
|
||||
return Result;
|
||||
}
|
||||
|
||||
int getBackwardCompatibilityMode() {
|
||||
|
||||
char *BackwardCompatibilityEnv = getenv("INTEL_JIT_BACKWARD_COMPATIBILITY");
|
||||
int BackwardCompatibilityMode = 0;
|
||||
if (BackwardCompatibilityEnv) {
|
||||
StringRef(BackwardCompatibilityEnv)
|
||||
.getAsInteger(10, BackwardCompatibilityMode);
|
||||
}
|
||||
return BackwardCompatibilityMode;
|
||||
}
|
||||
|
||||
void IntelJITEventListener::notifyObjectLoaded(
|
||||
ObjectKey Key, const ObjectFile &Obj,
|
||||
const RuntimeDyld::LoadedObjectInfo &L) {
|
||||
|
||||
int BackwardCompatibilityMode = getBackwardCompatibilityMode();
|
||||
if (BackwardCompatibilityMode == 0) {
|
||||
if (Obj.isELF()) {
|
||||
std::unique_ptr<IntelIttnotifyInfo> ModuleIttnotify =
|
||||
std::make_unique<IntelIttnotifyInfo>(*Wrapper);
|
||||
ModuleIttnotify->setModuleName(
|
||||
StringRef(llvm::utohexstr(
|
||||
MD5Hash(Obj.getMemoryBufferRef().getBuffer()), true))
|
||||
.str()
|
||||
.c_str());
|
||||
|
||||
__itt_module_object *ModuleObject = new __itt_module_object();
|
||||
ModuleObject->module_name = ModuleIttnotify->getModuleName();
|
||||
ModuleObject->module_size = Obj.getMemoryBufferRef().getBufferSize();
|
||||
Wrapper->iJitIttNotifyInfo(llvm::LoadBinaryModule,
|
||||
ModuleObject->module_name,
|
||||
ModuleObject->module_size);
|
||||
ModuleObject->module_type = __itt_module_type_elf;
|
||||
ModuleObject->section_number =
|
||||
ModuleIttnotify->fillSectionInformation(Obj, L);
|
||||
ModuleObject->module_buffer =
|
||||
(void *)const_cast<char *>(Obj.getMemoryBufferRef().getBufferStart());
|
||||
ModuleObject->module_id =
|
||||
__itt_id_make((void *)&(*ModuleObject), ModuleObject->module_size);
|
||||
ModuleObject->section_array =
|
||||
ModuleIttnotify->getSectionInfoVectorBegin();
|
||||
ModuleIttnotify->setModuleObject(ModuleObject);
|
||||
|
||||
__itt_module_load_with_sections(ModuleObject);
|
||||
|
||||
KeyToIttnotify[Key] = std::move(ModuleIttnotify);
|
||||
}
|
||||
} else if (BackwardCompatibilityMode == 1) {
|
||||
|
||||
OwningBinary<ObjectFile> DebugObjOwner = L.getObjectForDebug(Obj);
|
||||
const ObjectFile *DebugObj = DebugObjOwner.getBinary();
|
||||
if (!DebugObj)
|
||||
return;
|
||||
|
||||
// Get the address of the object image for use as a unique identifier
|
||||
const void* ObjData = DebugObj->getData().data();
|
||||
const void *ObjData = DebugObj->getData().data();
|
||||
std::unique_ptr<DIContext> Context = DWARFContext::create(*DebugObj);
|
||||
MethodAddressVector Functions;
|
||||
|
||||
// Use symbol info to iterate functions in the object.
|
||||
for (const std::pair<SymbolRef, uint64_t> &P : computeSymbolSizes(*DebugObj)) {
|
||||
for (const std::pair<SymbolRef, uint64_t> &P :
|
||||
computeSymbolSizes(*DebugObj)) {
|
||||
SymbolRef Sym = P.first;
|
||||
std::vector<LineNumberInfo> LineInfo;
|
||||
std::string SourceFileName;
|
||||
@ -153,7 +282,7 @@ void IntelJITEventListener::notifyObjectLoaded(
|
||||
uint64_t Index = Sec->getIndex();
|
||||
|
||||
// Record this address in a local vector
|
||||
Functions.push_back((void*)Addr);
|
||||
Functions.push_back((void *)Addr);
|
||||
|
||||
// Build the function loaded notification message
|
||||
iJIT_Method_Load FunctionMessage =
|
||||
@ -192,7 +321,7 @@ void IntelJITEventListener::notifyObjectLoaded(
|
||||
|
||||
Wrapper->iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED,
|
||||
&FunctionMessage);
|
||||
MethodIDs[(void*)Addr] = FunctionMessage.method_id;
|
||||
MethodIDs[(void *)Addr] = FunctionMessage.method_id;
|
||||
}
|
||||
|
||||
// To support object unload notification, we need to keep a list of
|
||||
@ -200,9 +329,22 @@ void IntelJITEventListener::notifyObjectLoaded(
|
||||
// use the MethodIDs map to get the registered ID for each function.
|
||||
LoadedObjectMap[ObjData] = Functions;
|
||||
DebugObjects[Key] = std::move(DebugObjOwner);
|
||||
}
|
||||
}
|
||||
|
||||
void IntelJITEventListener::notifyFreeingObject(ObjectKey Key) {
|
||||
|
||||
int BackwardCompatibilityMode = getBackwardCompatibilityMode();
|
||||
if (BackwardCompatibilityMode == 0) {
|
||||
if (KeyToIttnotify.find(Key) == KeyToIttnotify.end())
|
||||
return;
|
||||
__itt_module_unload_with_sections(KeyToIttnotify[Key]->getModuleObject());
|
||||
Wrapper->iJitIttNotifyInfo(
|
||||
llvm::UnloadBinaryModule,
|
||||
KeyToIttnotify[Key]->getModuleObject()->module_name,
|
||||
KeyToIttnotify[Key]->getModuleObject()->module_size);
|
||||
KeyToIttnotify.erase(Key);
|
||||
} else if (BackwardCompatibilityMode == 1) {
|
||||
// This object may not have been registered with the listener. If it wasn't,
|
||||
// bail out.
|
||||
if (DebugObjects.find(Key) == DebugObjects.end())
|
||||
@ -210,20 +352,19 @@ void IntelJITEventListener::notifyFreeingObject(ObjectKey Key) {
|
||||
|
||||
// Get the address of the object image for use as a unique identifier
|
||||
const ObjectFile &DebugObj = *DebugObjects[Key].getBinary();
|
||||
const void* ObjData = DebugObj.getData().data();
|
||||
const void *ObjData = DebugObj.getData().data();
|
||||
|
||||
// Get the object's function list from LoadedObjectMap
|
||||
ObjectMap::iterator OI = LoadedObjectMap.find(ObjData);
|
||||
if (OI == LoadedObjectMap.end())
|
||||
return;
|
||||
MethodAddressVector& Functions = OI->second;
|
||||
MethodAddressVector &Functions = OI->second;
|
||||
|
||||
// Walk the function list, unregistering each function
|
||||
for (MethodAddressVector::iterator FI = Functions.begin(),
|
||||
FE = Functions.end();
|
||||
FI != FE;
|
||||
++FI) {
|
||||
void* FnStart = const_cast<void*>(*FI);
|
||||
FI != FE; ++FI) {
|
||||
void *FnStart = const_cast<void *>(*FI);
|
||||
MethodIDMap::iterator MI = MethodIDs.find(FnStart);
|
||||
if (MI != MethodIDs.end()) {
|
||||
Wrapper->iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_UNLOAD_START,
|
||||
@ -235,6 +376,7 @@ void IntelJITEventListener::notifyFreeingObject(ObjectKey Key) {
|
||||
// Erase the object from LoadedObjectMap
|
||||
LoadedObjectMap.erase(OI);
|
||||
DebugObjects.erase(Key);
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace.
|
||||
|
@ -21,10 +21,18 @@
|
||||
|
||||
namespace llvm {
|
||||
|
||||
typedef enum {
|
||||
LoadBinaryModule,
|
||||
LoadBinarySection,
|
||||
UnloadBinaryModule,
|
||||
UnloadBinarySection
|
||||
} IttEventType;
|
||||
|
||||
class IntelJITEventsWrapper {
|
||||
// Function pointer types for testing implementation of Intel jitprofiling
|
||||
// library
|
||||
typedef int (*NotifyEventPtr)(iJIT_JVM_EVENT, void*);
|
||||
typedef int (*IttnotifyInfoPtr)(IttEventType, const char *, unsigned int);
|
||||
typedef void (*RegisterCallbackExPtr)(void *, iJIT_ModeChangedEx );
|
||||
typedef iJIT_IsProfilingActiveFlags (*IsProfilingActivePtr)(void);
|
||||
typedef void (*FinalizeThreadPtr)(void);
|
||||
@ -32,6 +40,7 @@ class IntelJITEventsWrapper {
|
||||
typedef unsigned int (*GetNewMethodIDPtr)(void);
|
||||
|
||||
NotifyEventPtr NotifyEventFunc;
|
||||
IttnotifyInfoPtr IttnotifyInfoFunc;
|
||||
RegisterCallbackExPtr RegisterCallbackExFunc;
|
||||
IsProfilingActivePtr IsProfilingActiveFunc;
|
||||
GetNewMethodIDPtr GetNewMethodIDFunc;
|
||||
@ -42,23 +51,22 @@ public:
|
||||
}
|
||||
|
||||
IntelJITEventsWrapper()
|
||||
: NotifyEventFunc(::iJIT_NotifyEvent),
|
||||
: NotifyEventFunc(::iJIT_NotifyEvent), IttnotifyInfoFunc(0),
|
||||
RegisterCallbackExFunc(::iJIT_RegisterCallbackEx),
|
||||
IsProfilingActiveFunc(::iJIT_IsProfilingActive),
|
||||
GetNewMethodIDFunc(::iJIT_GetNewMethodID) {
|
||||
}
|
||||
GetNewMethodIDFunc(::iJIT_GetNewMethodID) {}
|
||||
|
||||
IntelJITEventsWrapper(NotifyEventPtr NotifyEventImpl,
|
||||
IttnotifyInfoPtr IttnotifyInfoImpl,
|
||||
RegisterCallbackExPtr RegisterCallbackExImpl,
|
||||
IsProfilingActivePtr IsProfilingActiveImpl,
|
||||
FinalizeThreadPtr FinalizeThreadImpl,
|
||||
FinalizeProcessPtr FinalizeProcessImpl,
|
||||
GetNewMethodIDPtr GetNewMethodIDImpl)
|
||||
: NotifyEventFunc(NotifyEventImpl),
|
||||
: NotifyEventFunc(NotifyEventImpl), IttnotifyInfoFunc(IttnotifyInfoImpl),
|
||||
RegisterCallbackExFunc(RegisterCallbackExImpl),
|
||||
IsProfilingActiveFunc(IsProfilingActiveImpl),
|
||||
GetNewMethodIDFunc(GetNewMethodIDImpl) {
|
||||
}
|
||||
GetNewMethodIDFunc(GetNewMethodIDImpl) {}
|
||||
|
||||
// Sends an event announcing that a function has been emitted
|
||||
// return values are event-specific. See Intel documentation for details.
|
||||
@ -68,6 +76,13 @@ public:
|
||||
return NotifyEventFunc(EventType, EventSpecificData);
|
||||
}
|
||||
|
||||
int iJitIttNotifyInfo(IttEventType EventType, const char *Name,
|
||||
unsigned int Size) {
|
||||
if (!IttnotifyInfoFunc)
|
||||
return -1;
|
||||
return IttnotifyInfoFunc(EventType, Name, Size);
|
||||
}
|
||||
|
||||
// Registers a callback function to receive notice of profiling state changes
|
||||
void iJIT_RegisterCallbackEx(void *UserData,
|
||||
iJIT_ModeChangedEx NewModeCallBackFuncEx) {
|
||||
|
@ -1,2 +1,3 @@
|
||||
if not config.root.llvm_use_intel_jitevents:
|
||||
config.unsupported = True
|
||||
config.environment['INTEL_JIT_BACKWARD_COMPATIBILITY'] = '1'
|
||||
|
@ -88,6 +88,46 @@ int NotifyEvent(iJIT_JVM_EVENT EventType, void *EventSpecificData) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ittNotifyInfo(IttEventType EventType, const char *Name, unsigned int Size) {
|
||||
switch (EventType) {
|
||||
case LoadBinaryModule: {
|
||||
if (!Name) {
|
||||
errs() << "Error: The IttNotify event listener did not provide a module "
|
||||
"name.";
|
||||
return -1;
|
||||
}
|
||||
outs() << "Module loaded : Name = " << Name << ", Size = " << Size << "\n";
|
||||
} break;
|
||||
case LoadBinarySection: {
|
||||
if (!Name) {
|
||||
errs() << "Error: The IttNotify event listener did not provide a section "
|
||||
"name.";
|
||||
return -1;
|
||||
}
|
||||
outs() << "Loaded section : Name = " << Name << ", Size = " << Size << "\n";
|
||||
} break;
|
||||
case UnloadBinaryModule: {
|
||||
if (!Name) {
|
||||
errs() << "Error: The IttNotify event listener did not provide a module "
|
||||
"name.";
|
||||
return -1;
|
||||
}
|
||||
outs() << "Module unloaded : Name = " << Name << ", Size = " << Size
|
||||
<< "\n";
|
||||
} break;
|
||||
case UnloadBinarySection: {
|
||||
if (!Name) {
|
||||
errs() << "Error: The IttNotify event listener did not provide a section "
|
||||
"name.";
|
||||
return -1;
|
||||
}
|
||||
outs() << "Unloaded section : Name = " << Name << ", Size = " << Size
|
||||
<< "\n";
|
||||
} break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
iJIT_IsProfilingActiveFlags IsProfilingActive(void) {
|
||||
// for testing, pretend we have an Intel Parallel Amplifier XE 2011
|
||||
// instance attached
|
||||
@ -155,7 +195,8 @@ public:
|
||||
|
||||
std::unique_ptr<llvm::JITEventListener> Listener(
|
||||
JITEventListener::createIntelJITEventListener(new IntelJITEventsWrapper(
|
||||
NotifyEvent, 0, IsProfilingActive, 0, 0, GetNewMethodID)));
|
||||
NotifyEvent, ittNotifyInfo, 0, IsProfilingActive, 0, 0,
|
||||
GetNewMethodID)));
|
||||
|
||||
TheJIT->RegisterJITEventListener(Listener.get());
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user