void jl_getDylibFunctionInfo(const char **name, size_t *line, const char **filename, size_t pointer, int *fromC, int skipC) { #ifdef _OS_WINDOWS_ DWORD fbase = SymGetModuleBase64(GetCurrentProcess(),(DWORD)pointer); char *fname = 0; if (fbase != 0) { #else Dl_info dlinfo; const char *fname = 0; if ((dladdr((void*)pointer, &dlinfo) != 0) && dlinfo.dli_fname) { *fromC = !jl_is_sysimg(dlinfo.dli_fname); if (skipC && *fromC) return; // In case we fail with the debug info lookup, we at least still // have the function name, even if we don't have line numbers *name = dlinfo.dli_sname; *filename = dlinfo.dli_fname; uint64_t fbase = (uint64_t)dlinfo.dli_fbase; #endif obfiletype::iterator it = objfilemap.find(fbase); llvm::object::ObjectFile *obj = NULL; DIContext *context = NULL; int64_t slide = 0; #ifndef _OS_WINDOWS_ fname = dlinfo.dli_fname; #else IMAGEHLP_MODULE64 ModuleInfo; ModuleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULE64); SymGetModuleInfo64(GetCurrentProcess(), (DWORD64)pointer, &ModuleInfo); fname = ModuleInfo.LoadedImageName; *fromC = !jl_is_sysimg(fname); if (skipC && *fromC) return; #endif if (it == objfilemap.end()) { #ifdef _OS_DARWIN_ // First find the uuid of the object file (we'll use this to make sure we find the // correct debug symbol file). uint8_t uuid[16], uuid2[16]; #ifdef LLVM36 std::unique_ptr<MemoryBuffer> membuf = MemoryBuffer::getMemBuffer( StringRef((const char *)fbase, (size_t)(((uint64_t)-1)-fbase)),"",false); auto origerrorobj = llvm::object::ObjectFile::createObjectFile( membuf->getMemBufferRef(), sys::fs::file_magic::unknown); #elif LLVM35 MemoryBuffer *membuf = MemoryBuffer::getMemBuffer( StringRef((const char *)fbase, (size_t)(((uint64_t)-1)-fbase)),"",false); std::unique_ptr<MemoryBuffer> buf(membuf); auto origerrorobj = llvm::object::ObjectFile::createObjectFile( buf, sys::fs::file_magic::unknown); #else MemoryBuffer *membuf = MemoryBuffer::getMemBuffer( StringRef((const char *)fbase, (size_t)(((uint64_t)-1)-fbase)),"",false); llvm::object::ObjectFile *origerrorobj = llvm::object::ObjectFile::createObjectFile( membuf); #endif if (!origerrorobj) { objfileentry_t entry = {obj,context,slide}; objfilemap[fbase] = entry; goto lookup; } #ifdef LLVM36 llvm::object::MachOObjectFile *morigobj = (llvm::object::MachOObjectFile *)origerrorobj.get().release(); #elif LLVM35 llvm::object::MachOObjectFile *morigobj = (llvm::object::MachOObjectFile *)origerrorobj.get(); #else llvm::object::MachOObjectFile *morigobj = (llvm::object::MachOObjectFile *)origerrorobj; #endif if (!getObjUUID(morigobj,uuid)) { objfileentry_t entry = {obj,context,slide}; objfilemap[fbase] = entry; goto lookup; } // On OS X debug symbols are not contained in the dynamic library and that's why // we can't have nice things (easily). For now we only support .dSYM files in the same directory // as the shared library. In the future we may use DBGCopyFullDSYMURLForUUID from CoreFoundation to make // use of spotlight to find the .dSYM file. char dsympath[PATH_MAX]; strlcpy(dsympath, dlinfo.dli_fname, sizeof(dsympath)); strlcat(dsympath, ".dSYM/Contents/Resources/DWARF/", sizeof(dsympath)); strlcat(dsympath, strrchr(dlinfo.dli_fname,'/')+1, sizeof(dsympath)); #ifdef LLVM35 auto errorobj = llvm::object::ObjectFile::createObjectFile(dsympath); #else llvm::object::ObjectFile *errorobj = llvm::object::ObjectFile::createObjectFile(dsympath); #endif #else // On non OS X systems we need to mmap another copy because of the permissions on the mmaped // shared library. #ifdef LLVM35 auto errorobj = llvm::object::ObjectFile::createObjectFile(fname); #else llvm::object::ObjectFile *errorobj = llvm::object::ObjectFile::createObjectFile(fname); #endif #endif #ifdef LLVM36 if (errorobj) { obj = errorobj.get().getBinary().release(); errorobj.get().getBuffer().release(); #elif LLVM35 if (errorobj) { obj = errorobj.get(); #else if (errorobj != NULL) { obj = errorobj; #endif #ifdef _OS_DARWIN_ if (getObjUUID(morigobj,uuid2) && memcmp(uuid,uuid2,sizeof(uuid)) == 0) { #endif #ifdef LLVM36 context = DIContext::getDWARFContext(*obj); #else context = DIContext::getDWARFContext(obj); #endif slide = -(uint64_t)fbase; #ifdef _OS_DARWIN_ } #endif #ifdef _OS_WINDOWS_ assert(obj->isCOFF()); llvm::object::COFFObjectFile *coffobj = (llvm::object::COFFObjectFile *)obj; const llvm::object::pe32plus_header *pe32plus; coffobj->getPE32PlusHeader(pe32plus); if (pe32plus != NULL) { slide = pe32plus->ImageBase-fbase; } else { const llvm::object::pe32_header *pe32; coffobj->getPE32Header(pe32); if (pe32 == NULL) { obj = NULL; context = NULL; } else { slide = pe32->ImageBase-fbase; } } #endif } objfileentry_t entry = {obj,context,slide}; objfilemap[fbase] = entry; } else { obj = it->second.obj; context = it->second.ctx; slide = it->second.slide; } #ifdef _OS_DARWIN_ lookup: #endif lookup_pointer(context, name, line, filename, pointer+slide, jl_is_sysimg(fname), fromC); return; } *fromC = 1; return; } #endif void jl_getFunctionInfo(const char **name, size_t *line, const char **filename, size_t pointer, int *fromC, int skipC) { *name = NULL; *line = -1; *filename = "no file"; *fromC = 0; #if USE_MCJIT // With MCJIT we can get function information directly from the ObjectFile std::map<size_t, ObjectInfo, revcomp> &objmap = jl_jit_events->getObjectMap(); std::map<size_t, ObjectInfo, revcomp>::iterator it = objmap.lower_bound(pointer); if (it == objmap.end()) return jl_getDylibFunctionInfo(name,line,filename,pointer,fromC,skipC); if ((pointer - it->first) > it->second.size) return jl_getDylibFunctionInfo(name,line,filename,pointer,fromC,skipC); #ifdef LLVM36 DIContext *context = DIContext::getDWARFContext(*it->second.object); #else DIContext *context = DIContext::getDWARFContext(it->second.object); #endif lookup_pointer(context, name, line, filename, pointer, 1, fromC); #else // !USE_MCJIT // Without MCJIT we use the FuncInfo structure containing address maps std::map<size_t, FuncInfo, revcomp> &info = jl_jit_events->getMap(); std::map<size_t, FuncInfo, revcomp>::iterator it = info.lower_bound(pointer); if (it != info.end() && (size_t)(*it).first + (*it).second.lengthAdr >= pointer) { // We do this to hide the jlcall wrappers when getting julia backtraces, // but it is still good to have them for regular lookup of C frames. if (skipC && (*it).second.lines.empty()) { // Technically not true, but we don't want them // in julia backtraces, so close enough *fromC = 1; return; } *name = (*it).second.name.c_str(); *filename = (*it).second.filename.c_str(); if ((*it).second.lines.empty()) { *fromC = 1; return; } std::vector<JITEvent_EmittedFunctionDetails::LineStart>::iterator vit = (*it).second.lines.begin(); JITEvent_EmittedFunctionDetails::LineStart prev = *vit; if ((*it).second.func) { DISubprogram debugscope = DISubprogram(prev.Loc.getScope((*it).second.func->getContext())); *filename = debugscope.getFilename().data(); // the DISubprogram has the un-mangled name, so use that if // available. However, if the scope need not be the current // subprogram. if (debugscope.getName().data() != NULL) *name = debugscope.getName().data(); else *name = jl_demangle(*name); } vit++; while (vit != (*it).second.lines.end()) { if (pointer <= (*vit).Address) { *line = prev.Loc.getLine(); break; } prev = *vit; vit++; } if (*line == (size_t) -1) { *line = prev.Loc.getLine(); } } else { jl_getDylibFunctionInfo(name,line,filename,pointer,fromC,skipC); } #endif // USE_MCJIT }
void jl_getFunctionInfo(const char **name, int *line, const char **filename, size_t pointer, int skipC) { *name = NULL; *line = -1; *filename = "no file"; #if USE_MCJIT // With MCJIT we can get function information directly from the ObjectFile std::map<size_t, ObjectInfo, revcomp> &objmap = jl_jit_events->getObjectMap(); std::map<size_t, ObjectInfo, revcomp>::iterator it = objmap.lower_bound(pointer); if (it == objmap.end()) return jl_getDylibFunctionInfo(name,line,filename,pointer,skipC); if ((pointer - it->first) > it->second.size) return jl_getDylibFunctionInfo(name,line,filename,pointer,skipC); DIContext *context = DIContext::getDWARFContext(it->second.object); lookup_pointer(context, name, line, filename, pointer, 1); #else // !USE_MCJIT // Without MCJIT we use the FuncInfo structure containing address maps std::map<size_t, FuncInfo, revcomp> &info = jl_jit_events->getMap(); std::map<size_t, FuncInfo, revcomp>::iterator it = info.lower_bound(pointer); if (it != info.end() && (size_t)(*it).first + (*it).second.lengthAdr >= pointer) { // commenting these lines out skips functions that don't // have explicit debug info. this is useful for hiding // the jlcall wrapper functions we generate. #if LLVM_VERSION_MAJOR == 3 #if LLVM_VERSION_MINOR == 0 //*name = &(*(*it).second.func).getNameStr()[0]; #elif LLVM_VERSION_MINOR >= 1 //*name = (((*(*it).second.func).getName()).data()); #endif #endif std::vector<JITEvent_EmittedFunctionDetails::LineStart>::iterator vit = (*it).second.lines.begin(); JITEvent_EmittedFunctionDetails::LineStart prev = *vit; if ((*it).second.func) { DISubprogram debugscope = DISubprogram(prev.Loc.getScope((*it).second.func->getContext())); *filename = debugscope.getFilename().data(); // the DISubprogram has the un-mangled name, so use that if // available. *name = debugscope.getName().data(); } else { *name = (*it).second.name.c_str(); *filename = (*it).second.filename.c_str(); } vit++; while (vit != (*it).second.lines.end()) { if (pointer <= (*vit).Address) { *line = prev.Loc.getLine(); break; } prev = *vit; vit++; } if (*line == -1) { *line = prev.Loc.getLine(); } } else { jl_getDylibFunctionInfo(name,line,filename,pointer,skipC); } #endif // USE_MCJIT }