void mapped_module::parseFileLineInfo() { /* Determine if we've parsed this file already. */ image * moduleImage = obj()->parse_img(); assert( moduleImage != NULL ); const Object & moduleObject = moduleImage->getObject(); const char * fileName = moduleObject.getFileName(); /* We have not parsed this file already, so wind up libdwarf. */ int fd = open( fileName, O_RDONLY ); if (fd == -1) return; Dwarf_Debug dbg; int status = dwarf_init( fd, DW_DLC_READ, & pd_dwarf_handler, moduleObject.getErrFunc(), & dbg, NULL ); if( status != DW_DLV_OK ) { P_close( fd ); return; } /* Itereate over the CU headers. */ Dwarf_Unsigned header; while( dwarf_next_cu_header( dbg, NULL, NULL, NULL, NULL, & header, NULL ) == DW_DLV_OK ) { /* Acquire the CU DIE. */ Dwarf_Die cuDIE; status = dwarf_siblingof( dbg, NULL, & cuDIE, NULL); if( status != DW_DLV_OK ) { /* If we can get no (more) CUs, we're done. */ break; } /* Acquire this CU's source lines. */ Dwarf_Line * lineBuffer; Dwarf_Signed lineCount; status = dwarf_srclines( cuDIE, & lineBuffer, & lineCount, NULL ); /* See if we can get anything useful out of the next CU if this one is corrupt. */ if( status == DW_DLV_ERROR ) { dwarf_printf( "%s[%d]: dwarf_srclines() error.\n" ); } /* It's OK for a CU not to have line information. */ if( status != DW_DLV_OK ) { /* Free this CU's DIE. */ dwarf_dealloc( dbg, cuDIE, DW_DLA_DIE ); continue; } assert( status == DW_DLV_OK ); /* The 'lines' returned are actually interval markers; the code generated from lineNo runs from lineAddr up to but not including the lineAddr of the next line. */ bool isPreviousValid = false; Dwarf_Unsigned previousLineNo = 0; Dwarf_Addr previousLineAddr = 0x0; char * previousLineSource = NULL; Address baseAddr = obj()->codeBase(); /* Iterate over this CU's source lines. */ for( int i = 0; i < lineCount; i++ ) { /* Acquire the line number, address, source, and end of sequence flag. */ Dwarf_Unsigned lineNo; status = dwarf_lineno( lineBuffer[i], & lineNo, NULL ); if( status != DW_DLV_OK ) { continue; } Dwarf_Addr lineAddr; status = dwarf_lineaddr( lineBuffer[i], & lineAddr, NULL ); if( status != DW_DLV_OK ) { continue; } lineAddr += baseAddr; char * lineSource; status = dwarf_linesrc( lineBuffer[i], & lineSource, NULL ); if( status != DW_DLV_OK ) { continue; } Dwarf_Bool isEndOfSequence; status = dwarf_lineendsequence( lineBuffer[i], & isEndOfSequence, NULL ); if( status != DW_DLV_OK ) { continue; } if( isPreviousValid ) { /* If we're talking about the same (source file, line number) tuple, and it isn't the end of the sequence, we can coalesce the range. (The end of sequence marker marks discontinuities in the ranges.) */ if( lineNo == previousLineNo && strcmp( lineSource, previousLineSource ) == 0 && ! isEndOfSequence ) { /* Don't update the prev* values; just keep going until we hit the end of a sequence or a new sourcefile. */ continue; } /* end if we can coalesce this range */ /* Determine into which mapped_module this line information should be inserted. */ int_function * currentFunction = obj()->findFuncByAddr( previousLineAddr ); if( currentFunction == NULL ) { // /* DEBUG */ fprintf( stderr, "%s[%d]: failed to find function containing address 0x%lx; line number information will be lost.\n", __FILE__, __LINE__, lineAddr ); } else { mapped_module * currentModule = currentFunction->mod(); assert( currentModule != NULL ); char * canonicalLineSource = strrchr( previousLineSource, '/' ); if( canonicalLineSource == NULL ) { canonicalLineSource = previousLineSource; } else { ++canonicalLineSource; } /* The line 'canonicalLineSource:previousLineNo' has an address range of [previousLineAddr, lineAddr). */ currentModule->lineInfo_.addLine( canonicalLineSource, previousLineNo, previousLineAddr, lineAddr ); // /* DEBUG */ fprintf( stderr, "%s[%d]: inserted address range [0x%lx, 0x%lx) for source '%s:%u' into module '%s'.\n", __FILE__, __LINE__, previousLineAddr, lineAddr, canonicalLineSource, previousLineNo, currentModule->fileName().c_str() ); } /* end if we found the function by its address */ } /* end if the previous* variables are valid */ /* If the current line ends the sequence, invalidate previous; otherwise, update. */ if( isEndOfSequence ) { dwarf_dealloc( dbg, lineSource, DW_DLA_STRING ); isPreviousValid = false; } else { if( isPreviousValid ) { dwarf_dealloc( dbg, previousLineSource, DW_DLA_STRING ); } previousLineNo = lineNo; previousLineSource = lineSource; previousLineAddr = lineAddr; isPreviousValid = true; } /* end if line was not the end of a sequence */ } /* end iteration over source line entries. */ /* Free this CU's source lines. */ for( int i = 0; i < lineCount; i++ ) { dwarf_dealloc( dbg, lineBuffer[i], DW_DLA_LINE ); } dwarf_dealloc( dbg, lineBuffer, DW_DLA_LIST ); /* Free this CU's DIE. */ dwarf_dealloc( dbg, cuDIE, DW_DLA_DIE ); } /* end CU header iteration */ /* Wind down libdwarf. */ status = dwarf_finish( dbg, NULL ); if( status != DW_DLV_OK ) { dwarf_printf( "%s[%d]: failed to dwarf_finish()\n" ); } P_close( fd ); /* Note that we've parsed this file. */ } /* end parseFileLineInfo() */
/* get_compiler_info */ int get_compiler_info(void) { Dwarf_Unsigned cu_header_length, abbrev_offset, next_cu_header; Dwarf_Half version, address_size; Dwarf_Signed attrcount, i, language; Dwarf_Die no_die = 0, cu_die; Dwarf_Attribute *attrs; Dwarf_Half attrcode; Dwarf_Debug dbg = 0; Dwarf_Error err; char *compiler = NULL; int fd = -1; OUTPUT_VERBOSE((5, "%s", _BLUE("Extracting DWARF info"))); /* Open the binary */ if (0 > (fd = open(globals.program_full, O_RDONLY))) { OUTPUT(("%s [%s]", _ERROR("opening program binary"), globals.program_full)); return PERFEXPERT_ERROR; } /* Initialize DWARF library */ if (DW_DLV_OK != dwarf_init(fd, DW_DLC_READ, 0, 0, &dbg, &err)) { OUTPUT(("%s", _ERROR("failed DWARF initialization"))); return PERFEXPERT_ERROR; } /* Find compilation unit header */ if (DW_DLV_ERROR == dwarf_next_cu_header(dbg, &cu_header_length, &version, &abbrev_offset, &address_size, &next_cu_header, &err)) { OUTPUT(("%s", _ERROR("reading DWARF CU header"))); return PERFEXPERT_ERROR; } /* Expect the CU to have a single sibling, a DIE */ if (DW_DLV_ERROR == dwarf_siblingof(dbg, no_die, &cu_die, &err)) { OUTPUT(("%s", _ERROR("getting sibling of CU"))); return PERFEXPERT_ERROR; } /* Find the DIEs attributes */ if (DW_DLV_OK != dwarf_attrlist(cu_die, &attrs, &attrcount, &err)) { OUTPUT(("%s", _ERROR("in dwarf_attlist"))); return PERFEXPERT_ERROR; } /* For each attribute... */ for (i = 0; i < attrcount; ++i) { if (DW_DLV_OK != dwarf_whatattr(attrs[i], &attrcode, &err)) { OUTPUT(("%s [%s]", _ERROR("in dwarf_whatattr"))); return PERFEXPERT_ERROR; } if (DW_AT_producer == attrcode) { if (DW_DLV_OK != dwarf_formstring(attrs[i], &compiler, &err)) { OUTPUT(("%s [%s]", _ERROR("in dwarf_formstring"))); return PERFEXPERT_ERROR; } else { OUTPUT_VERBOSE((5, " Compiler: %s", _CYAN(compiler))); } } if (DW_AT_language == attrcode) { if (DW_DLV_OK != dwarf_formsdata(attrs[i], &language, &err)) { OUTPUT(("%s [%s]", _ERROR("in dwarf_formsdata"))); return PERFEXPERT_ERROR; } else { OUTPUT_VERBOSE((5, " Language: %d", language)); } } } if (PERFEXPERT_SUCCESS != database_write(compiler, language)) { OUTPUT(("%s", _ERROR("writing to database"))); return PERFEXPERT_ERROR; } /* Finalize DWARF library */ if (DW_DLV_OK != dwarf_finish(dbg, &err)) { OUTPUT(("%s", _ERROR("failed DWARF finalization"))); return PERFEXPERT_ERROR; } close(fd); return PERFEXPERT_SUCCESS; }
bool FLinuxCrashContext::GetInfoForAddress(void * Address, const char **OutFunctionNamePtr, const char **OutSourceFilePtr, int *OutLineNumberPtr) { if (DebugInfo == NULL) { return false; } Dwarf_Die Die; Dwarf_Unsigned Addr = reinterpret_cast< Dwarf_Unsigned >( Address ), LineNumber = 0; const char * SrcFile = NULL; static_assert(sizeof(Dwarf_Unsigned) >= sizeof(Address), "Dwarf_Unsigned type should be long enough to represent pointers. Check libdwarf bitness."); int ReturnCode = DW_DLV_OK; Dwarf_Error ErrorInfo; bool bExitHeaderLoop = false; int32 MaxCompileUnitsAllowed = 16 * 1024 * 1024; // safeguard to make sure we never get into an infinite loop const int32 kMaxBufferLinesAllowed = 16 * 1024 * 1024; // safeguard to prevent too long line loop for(;;) { if (--MaxCompileUnitsAllowed <= 0) { fprintf(stderr, "Breaking out from what seems to be an infinite loop during DWARF parsing (too many compile units).\n"); ReturnCode = DW_DLE_DIE_NO_CU_CONTEXT; // invalidate break; } if (bExitHeaderLoop) break; ReturnCode = dwarf_next_cu_header(DebugInfo, NULL, NULL, NULL, NULL, NULL, &ErrorInfo); if (ReturnCode != DW_DLV_OK) break; Die = NULL; while(dwarf_siblingof(DebugInfo, Die, &Die, &ErrorInfo) == DW_DLV_OK) { Dwarf_Half Tag; if (dwarf_tag(Die, &Tag, &ErrorInfo) != DW_DLV_OK) { bExitHeaderLoop = true; break; } if (Tag == DW_TAG_compile_unit) { break; } } if (Die == NULL) { break; } // check if address is inside this CU Dwarf_Unsigned LowerPC, HigherPC; if (!dwarf_attrval_unsigned(Die, DW_AT_low_pc, &LowerPC, &ErrorInfo) && !dwarf_attrval_unsigned(Die, DW_AT_high_pc, &HigherPC, &ErrorInfo)) { if (Addr < LowerPC || Addr >= HigherPC) { continue; } } Dwarf_Line * LineBuf; Dwarf_Signed NumLines = kMaxBufferLinesAllowed; if (dwarf_srclines(Die, &LineBuf, &NumLines, &ErrorInfo) != DW_DLV_OK) { // could not get line info for some reason break; } if (NumLines >= kMaxBufferLinesAllowed) { fprintf(stderr, "Number of lines associated with a DIE looks unreasonable (%d), early quitting.\n", static_cast<int32>(NumLines)); ReturnCode = DW_DLE_DIE_NO_CU_CONTEXT; // invalidate break; } // look which line is that Dwarf_Addr LineAddress, PrevLineAddress = ~0ULL; Dwarf_Unsigned PrevLineNumber = 0; const char * PrevSrcFile = NULL; char * SrcFileTemp = NULL; for (int Idx = 0; Idx < NumLines; ++Idx) { if (dwarf_lineaddr(LineBuf[Idx], &LineAddress, &ErrorInfo) != 0 || dwarf_lineno(LineBuf[Idx], &LineNumber, &ErrorInfo) != 0) { bExitHeaderLoop = true; break; } if (!dwarf_linesrc(LineBuf[Idx], &SrcFileTemp, &ErrorInfo)) { SrcFile = SrcFileTemp; } // check if we hit the exact line if (Addr == LineAddress) { bExitHeaderLoop = true; break; } else if (PrevLineAddress < Addr && Addr < LineAddress) { LineNumber = PrevLineNumber; SrcFile = PrevSrcFile; bExitHeaderLoop = true; break; } PrevLineAddress = LineAddress; PrevLineNumber = LineNumber; PrevSrcFile = SrcFile; } } const char * FunctionName = NULL; if (ReturnCode == DW_DLV_OK) { FindFunctionNameInDIEAndChildren(DebugInfo, Die, Addr, &FunctionName); } if (OutFunctionNamePtr != NULL && FunctionName != NULL) { *OutFunctionNamePtr = FunctionName; } if (OutSourceFilePtr != NULL && SrcFile != NULL) { *OutSourceFilePtr = SrcFile; if (OutLineNumberPtr != NULL) { *OutLineNumberPtr = LineNumber; } } // Resets internal CU pointer, so next time we get here it begins from the start while (ReturnCode != DW_DLV_NO_ENTRY) { if (ReturnCode == DW_DLV_ERROR) break; ReturnCode = dwarf_next_cu_header(DebugInfo, NULL, NULL, NULL, NULL, NULL, &ErrorInfo); } // if we weren't able to find a function name, don't trust the source file either return FunctionName != NULL; }
static void translate(Dwarf_Debug dbg, const char* addrstr) { Dwarf_Die die, ret_die; Dwarf_Line *lbuf; Dwarf_Error de; Dwarf_Half tag; Dwarf_Unsigned lopc, hipc, addr, lineno, plineno; Dwarf_Signed lcount; Dwarf_Addr lineaddr, plineaddr; char *funcname; char *file, *file0, *pfile; char demangled[1024]; int i, ret; addr = strtoull(addrstr, NULL, 16); addr += section_base; lineno = 0; file = unknown; die = NULL; lbuf = NULL; lcount = 0; while ((ret = dwarf_next_cu_header(dbg, NULL, NULL, NULL, NULL, NULL, &de)) == DW_DLV_OK) { die = NULL; while (dwarf_siblingof(dbg, die, &ret_die, &de) == DW_DLV_OK) { if (die != NULL) dwarf_dealloc(dbg, die, DW_DLA_DIE); die = ret_die; if (dwarf_tag(die, &tag, &de) != DW_DLV_OK) { warnx("dwarf_tag failed: %s", dwarf_errmsg(de)); goto next_cu; } /* XXX: What about DW_TAG_partial_unit? */ if (tag == DW_TAG_compile_unit) break; } if (ret_die == NULL) { warnx("could not find DW_TAG_compile_unit die"); goto next_cu; } if (!dwarf_attrval_unsigned(die, DW_AT_low_pc, &lopc, &de) && !dwarf_attrval_unsigned(die, DW_AT_high_pc, &hipc, &de)) { /* * Check if the address falls into the PC range of * this CU. */ if (handle_high_pc(die, lopc, &hipc) != DW_DLV_OK) goto next_cu; if (addr < lopc || addr >= hipc) goto next_cu; } switch (dwarf_srclines(die, &lbuf, &lcount, &de)) { case DW_DLV_OK: break; case DW_DLV_NO_ENTRY: /* If a CU lacks debug info, just skip it. */ goto next_cu; default: warnx("dwarf_srclines: %s", dwarf_errmsg(de)); goto out; } plineaddr = ~0ULL; plineno = 0; pfile = unknown; for (i = 0; i < lcount; i++) { if (dwarf_lineaddr(lbuf[i], &lineaddr, &de)) { warnx("dwarf_lineaddr: %s", dwarf_errmsg(de)); goto out; } if (dwarf_lineno(lbuf[i], &lineno, &de)) { warnx("dwarf_lineno: %s", dwarf_errmsg(de)); goto out; } if (dwarf_linesrc(lbuf[i], &file0, &de)) { warnx("dwarf_linesrc: %s", dwarf_errmsg(de)); } else file = file0; if (addr == lineaddr) goto out; else if (addr < lineaddr && addr > plineaddr) { lineno = plineno; file = pfile; goto out; } plineaddr = lineaddr; plineno = lineno; pfile = file; } next_cu: if (die != NULL) { dwarf_dealloc(dbg, die, DW_DLA_DIE); die = NULL; } } out: funcname = NULL; if (ret == DW_DLV_OK && func) { search_func(dbg, die, addr, &funcname); die = NULL; } if (func) { if (funcname == NULL) if ((funcname = strdup(unknown)) == NULL) err(EXIT_FAILURE, "strdup"); if (demangle && !elftc_demangle(funcname, demangled, sizeof(demangled), 0)) printf("%s\n", demangled); else printf("%s\n", funcname); free(funcname); } (void) printf("%s:%ju\n", base ? basename(file) : file, lineno); if (die != NULL) dwarf_dealloc(dbg, die, DW_DLA_DIE); /* * Reset internal CU pointer, so we will start from the first CU * next round. */ while (ret != DW_DLV_NO_ENTRY) { if (ret == DW_DLV_ERROR) errx(EXIT_FAILURE, "dwarf_next_cu_header: %s", dwarf_errmsg(de)); ret = dwarf_next_cu_header(dbg, NULL, NULL, NULL, NULL, NULL, &de); } }