bool DwarfImpl::findLocation()

in folly/experimental/symbolizer/DwarfImpl.cpp [57:296]


bool DwarfImpl::findLocation(
    uintptr_t address,
    const LocationInfoMode mode,
    LocationInfo& locationInfo,
    folly::Range<SymbolizedFrame*> inlineFrames,
    folly::FunctionRef<void(folly::StringPiece)> eachParameterName,
    bool checkAddress) const {
  Die die = getDieAtOffset(cu_, cu_.firstDie);
  // Partial compilation unit (DW_TAG_partial_unit) is not supported.
  FOLLY_SAFE_CHECK(
      die.abbr.tag == DW_TAG_compile_unit ||
          die.abbr.tag == DW_TAG_skeleton_unit,
      die.abbr.tag);

  // Offset in .debug_line for the line number VM program for this CU
  folly::Optional<uint64_t> lineOffset;
  folly::StringPiece compilationDirectory;
  folly::Optional<folly::StringPiece> mainFileName;
  folly::Optional<uint64_t> baseAddrCU;
  folly::Optional<uint64_t> rangesOffset;
  bool seenLowPC = false;
  bool seenHighPC = false;
  enum : unsigned {
    kStmtList = 1U << 0,
    kCompDir = 1U << 1,
    kName = 1U << 2,
    kLowPC = 1U << 3,
    kHighPCOrRanges = 1U << 4,
  };
  unsigned expectedAttributes = kStmtList | kCompDir | kName | kLowPC;
  bool foundAddress = !checkAddress;
  if (!foundAddress) {
    expectedAttributes |= kHighPCOrRanges;
  }
  forEachAttribute(cu_, die, [&](const Attribute& attr) {
    switch (attr.spec.name) {
      case DW_AT_stmt_list:
        expectedAttributes &= ~kStmtList;
        // Offset in .debug_line for the line number VM program for this
        // compilation unit
        lineOffset = boost::get<uint64_t>(attr.attrValue);
        break;
      case DW_AT_comp_dir:
        expectedAttributes &= ~kCompDir;
        // Compilation directory
        compilationDirectory = boost::get<folly::StringPiece>(attr.attrValue);
        break;
      case DW_AT_name:
        expectedAttributes &= ~kName;
        // File name of main file being compiled
        mainFileName = boost::get<folly::StringPiece>(attr.attrValue);
        break;
      case DW_AT_low_pc:
        expectedAttributes &= ~kLowPC;
        baseAddrCU = boost::get<uint64_t>(attr.attrValue);
        if (!foundAddress) {
          if (address < *baseAddrCU) {
            return false;
          }
          seenLowPC = true;
          if (seenHighPC) {
            foundAddress = true;
          } else if (rangesOffset) {
            if (!isAddrInRangeList(
                    address, baseAddrCU, *rangesOffset, cu_.addrSize)) {
              return false;
            }
            foundAddress = true;
          }
        }
        break;
      case DW_AT_high_pc:
        expectedAttributes &= ~kHighPCOrRanges;
        if (!foundAddress) {
          if (address >= boost::get<uint64_t>(attr.attrValue)) {
            return false;
          }
          seenHighPC = true;
          foundAddress = seenLowPC;
        }
        break;
      case DW_AT_ranges:
        // 3.1.1: CU entries have:
        // - either DW_AT_low_pc and DW_AT_high_pc
        // OR
        // - DW_AT_ranges and optional DW_AT_low_pc
        expectedAttributes &= ~kHighPCOrRanges;
        if (!foundAddress) {
          rangesOffset = boost::get<uint64_t>(attr.attrValue);
          if (seenLowPC) {
            if (!isAddrInRangeList(
                    address, baseAddrCU, *rangesOffset, cu_.addrSize)) {
              return false;
            }
            foundAddress = true;
          }
        }
        break;
    }
    return (expectedAttributes != 0); // continue forEachAttribute
  });

  if (!foundAddress || !lineOffset) {
    return false;
  }

  if (mainFileName) {
    locationInfo.hasMainFile = true;
    locationInfo.mainFile = Path(compilationDirectory, "", *mainFileName);
  }

  folly::StringPiece lineSection(cu_.debugSections.debugLine);
  lineSection.advance(*lineOffset);
  DwarfLineNumberVM lineVM(
      lineSection, compilationDirectory, cu_.debugSections);

  // Execute line number VM program to find file and line
  locationInfo.hasFileAndLine =
      lineVM.findAddress(address, locationInfo.file, locationInfo.line);
  if (!locationInfo.hasFileAndLine) {
    return false;
  }

  // NOTE: locationInfo was found, so findLocation returns success bellow.
  // Missing inline function / parameter name is not a failure (best effort).
  bool checkInline =
      (mode == LocationInfoMode::FULL_WITH_INLINE && !inlineFrames.empty());
  if (!checkInline && !eachParameterName) {
    return true;
  }

  // Cache abbreviation.
  std::array<DIEAbbreviation, kMaxAbbreviationEntries> abbrs;
  cu_.abbrCache = folly::range(abbrs);
  folly::StringPiece abbrev = cu_.debugSections.debugAbbrev;
  abbrev.advance(cu_.abbrevOffset);
  DIEAbbreviation abbr;
  while (readAbbreviation(abbrev, abbr)) {
    // Abbreviation code 0 is reserved for null debugging information entries.
    if (abbr.code != 0 && abbr.code <= kMaxAbbreviationEntries) {
      cu_.abbrCache.data()[abbr.code - 1] = abbr;
    }
  }

  // Find the subprogram that matches the given address.
  Die subprogram;
  if (!findSubProgramDieForAddress(die, address, baseAddrCU, subprogram)) {
    // Even though @cu contains @address, it's possible
    // that the corresponding DW_TAG_subprogram DIE is missing.
    return true;
  }

  if (eachParameterName) {
    forEachChild(subprogram, [&](const Die& child) {
      if (child.abbr.tag == DW_TAG_formal_parameter) {
        if (auto name = getAttribute<folly::StringPiece>(child, DW_AT_name)) {
          eachParameterName(*name);
        }
      }
      return true; // continue forEachChild
    });
  }

  if (!checkInline || !subprogram.abbr.hasChildren) {
    return true;
  }

  // NOTE: @subprogram is the DIE of caller function.

  // Use an extra location and get its call file and call line, so that
  // they can be used for the second last location when we don't have
  // enough inline frames for all inline functions call stack.
  size_t size =
      std::min<size_t>(kMaxInlineLocationInfoPerFrame, inlineFrames.size()) + 1;
  CallLocation callLocations[kMaxInlineLocationInfoPerFrame + 1];
  size_t numFound = 0;
  findInlinedSubroutineDieForAddress(
      subprogram,
      lineVM,
      address,
      baseAddrCU,
      folly::Range<CallLocation*>(callLocations, size),
      numFound);
  if (numFound == 0) {
    return true;
  }

  folly::Range<CallLocation*> inlineLocations(callLocations, numFound);
  const auto innerMostFile = locationInfo.file;
  const auto innerMostLine = locationInfo.line;

  // Earlier we filled in locationInfo:
  // - mainFile: the path to the CU -- the file where the non-inlined
  //   call is made from.
  // - file + line: the location of the inner-most inlined call.
  // Here we already find inlined info so mainFile would be redundant.
  locationInfo.hasMainFile = false;
  locationInfo.mainFile = Path{};
  // @findInlinedSubroutineDieForAddress fills inlineLocations[0] with the
  // file+line of the non-inlined outer function making the call.
  // locationInfo.name is already set by the caller by looking up the
  // non-inlined function @address belongs to.
  locationInfo.hasFileAndLine = true;
  locationInfo.file = inlineLocations[0].file;
  locationInfo.line = inlineLocations[0].line;

  // The next inlined subroutine's call file and call line is the current
  // caller's location.
  for (size_t i = 0; i < numFound - 1; i++) {
    inlineLocations[i].file = inlineLocations[i + 1].file;
    inlineLocations[i].line = inlineLocations[i + 1].line;
  }
  // CallLocation for the inner-most inlined function:
  // - will be computed if enough space was available in the passed
  //   buffer.
  // - will have a .name, but no !.file && !.line
  // - its corresponding file+line is the one returned by LineVM based
  //   on @address.
  // Use the inner-most inlined file+line info we got from the LineVM.
  inlineLocations[numFound - 1].file = innerMostFile;
  inlineLocations[numFound - 1].line = innerMostLine;

  // Skip the extra location when actual inline function calls are more
  // than provided frames.
  inlineLocations =
      inlineLocations.subpiece(0, std::min(numFound, inlineFrames.size()));

  // Fill in inline frames in reverse order (as
  // expected by the caller).
  std::reverse(inlineLocations.begin(), inlineLocations.end());
  for (size_t i = 0; i < inlineLocations.size(); i++) {
    inlineFrames[i].found = true;
    inlineFrames[i].addr = address;
    inlineFrames[i].name = inlineLocations[i].name.data();
    inlineFrames[i].location.hasFileAndLine = true;
    inlineFrames[i].location.file = inlineLocations[i].file;
    inlineFrames[i].location.line = inlineLocations[i].line;
  }
  return true;
}