1
0
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:
Lang Hames 2020-11-16 19:22:20 +11:00
parent 4b7f9fd6c6
commit 460e0b8ba2
5 changed files with 357 additions and 136 deletions

View File

@ -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}

View File

@ -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.

View File

@ -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) {

View File

@ -1,2 +1,3 @@
if not config.root.llvm_use_intel_jitevents:
config.unsupported = True
config.environment['INTEL_JIT_BACKWARD_COMPATIBILITY'] = '1'

View File

@ -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());