bool PDBSourceLineWriter::PrintFunctions()

in src/common/windows/pdb_source_line_writer.cc [441:566]


bool PDBSourceLineWriter::PrintFunctions() {
  ULONG count = 0;
  DWORD rva = 0;
  CComPtr<IDiaSymbol> global;
  HRESULT hr;

  if (FAILED(session_->get_globalScope(&global))) {
    fprintf(stderr, "get_globalScope failed\n");
    return false;
  }

  CComPtr<IDiaEnumSymbols> symbols = NULL;

  // Find all function symbols first.
  SymbolMap rva_symbol;
  hr = global->findChildren(SymTagFunction, NULL, nsNone, &symbols);

  if (SUCCEEDED(hr)) {
    CComPtr<IDiaSymbol> symbol = NULL;

    while (SUCCEEDED(symbols->Next(1, &symbol, &count)) && count == 1) {
      if (SUCCEEDED(symbol->get_relativeVirtualAddress(&rva))) {
        // Potentially record this as the canonical symbol for this rva.
        MaybeRecordSymbol(rva, symbol, false, &rva_symbol);
      } else {
        fprintf(stderr, "get_relativeVirtualAddress failed on the symbol\n");
        return false;
      }

      symbol.Release();
    }

    symbols.Release();
  }

  // Find all public symbols and record public symbols that are not also private
  // symbols.
  hr = global->findChildren(SymTagPublicSymbol, NULL, nsNone, &symbols);

  if (SUCCEEDED(hr)) {
    CComPtr<IDiaSymbol> symbol = NULL;

    while (SUCCEEDED(symbols->Next(1, &symbol, &count)) && count == 1) {
      if (SUCCEEDED(symbol->get_relativeVirtualAddress(&rva))) {
        // Potentially record this as the canonical symbol for this rva.
        MaybeRecordSymbol(rva, symbol, true, &rva_symbol);
      } else {
        fprintf(stderr, "get_relativeVirtualAddress failed on the symbol\n");
        return false;
      }

      symbol.Release();
    }

    symbols.Release();
  }

  // For each rva, dump the selected symbol at the address.
  SymbolMap::iterator it;
  for (it = rva_symbol.begin(); it != rva_symbol.end(); ++it) {
    CComPtr<IDiaSymbol> symbol = it->second.symbol;
    // Only print public symbols if there is no function symbol for the address.
    if (!it->second.is_public) {
      if (!PrintFunction(symbol, symbol, it->second.is_multiple))
        return false;
    } else {
      if (!PrintCodePublicSymbol(symbol, it->second.is_multiple))
        return false;
    }
  }

  // When building with PGO, the compiler can split functions into
  // "hot" and "cold" blocks, and move the "cold" blocks out to separate
  // pages, so the function can be noncontiguous. To find these blocks,
  // we have to iterate over all the compilands, and then find blocks
  // that are children of them. We can then find the lexical parents
  // of those blocks and print out an extra FUNC line for blocks
  // that are not contained in their parent functions.
  CComPtr<IDiaEnumSymbols> compilands;
  if (FAILED(global->findChildren(SymTagCompiland, NULL,
                                  nsNone, &compilands))) {
    fprintf(stderr, "findChildren failed on the global\n");
    return false;
  }

  CComPtr<IDiaSymbol> compiland;
  while (SUCCEEDED(compilands->Next(1, &compiland, &count)) && count == 1) {
    CComPtr<IDiaEnumSymbols> blocks;
    if (FAILED(compiland->findChildren(SymTagBlock, NULL,
                                       nsNone, &blocks))) {
      fprintf(stderr, "findChildren failed on a compiland\n");
      return false;
    }

    CComPtr<IDiaSymbol> block;
    while (SUCCEEDED(blocks->Next(1, &block, &count)) && count == 1) {
      // find this block's lexical parent function
      CComPtr<IDiaSymbol> parent;
      DWORD tag;
      if (SUCCEEDED(block->get_lexicalParent(&parent)) &&
          SUCCEEDED(parent->get_symTag(&tag)) &&
          tag == SymTagFunction) {
        // now get the block's offset and the function's offset and size,
        // and determine if the block is outside of the function
        DWORD func_rva, block_rva;
        ULONGLONG func_length;
        if (SUCCEEDED(block->get_relativeVirtualAddress(&block_rva)) &&
            SUCCEEDED(parent->get_relativeVirtualAddress(&func_rva)) &&
            SUCCEEDED(parent->get_length(&func_length))) {
          if (block_rva < func_rva || block_rva > (func_rva + func_length)) {
            if (!PrintFunction(parent, block, false)) {
              return false;
            }
          }
        }
      }
      parent.Release();
      block.Release();
    }
    blocks.Release();
    compiland.Release();
  }

  global.Release();
  return true;
}