Skip to content

Commit

Permalink
Add more detailed function meta-data output
Browse files Browse the repository at this point in the history
Summary:
Renames the `offset` command to `function-info` and expands it to include information on: function name, the actual source location, and the origin source location. The latter requires a source-map to be provided on the command line.

Using this command it's now possible to find out where all the functions are in a HBC file and how they map back to the original source.

Reviewed By: kodafb

Differential Revision: D19213728

fbshipit-source-id: 0e6898fb19a1171b82f2a68794a42ba88b6edeb7
  • Loading branch information
jbower-fb authored and facebook-github-bot committed Feb 5, 2020
1 parent be569b3 commit 164af3c
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 24 deletions.
27 changes: 26 additions & 1 deletion tools/hbcdump/ProfileAnalyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -835,7 +835,7 @@ void ProfileAnalyzer::dumpSummary() {
<< "\n";
}

void ProfileAnalyzer::dumpFunctionOffsets(
void ProfileAnalyzer::dumpFunctionInfo(
uint32_t funcId,
StructuredPrinter &printer) {
auto bcProvider = hbcParser_.getBCProvider();
Expand All @@ -852,6 +852,8 @@ void ProfileAnalyzer::dumpFunctionOffsets(
printer.emitKeyValue(
"VirtualOffset", bcProvider->getVirtualOffsetForFunction(funcId));
printer.emitKeyValue("Size", header.bytecodeSizeInBytes());
printer.emitKeyValue(
"Name", bcProvider->getStringRefFromID(header.functionName()));

auto dbg = bcProvider->getDebugOffsets(funcId);
if (dbg) {
Expand All @@ -863,6 +865,29 @@ void ProfileAnalyzer::dumpFunctionOffsets(
}
}

llvm::Optional<SourceMapTextLocation> sourceLocOpt =
bcProvider->getLocationForAddress(funcId, /* offsetInFunction */ 0);
if (sourceLocOpt.hasValue()) {
printer.emitKey("FinalSourceLocation");
printer.openDict();
printer.emitKeyValue("Source", sourceLocOpt->fileName);
printer.emitKeyValue("Line", sourceLocOpt->line);
printer.emitKeyValue("Column", sourceLocOpt->column);
printer.closeDict();
if (sourceMap_) {
auto originalSourceLoc = sourceMap_->getLocationForAddress(
sourceLocOpt->line, sourceLocOpt->column);
if (originalSourceLoc.hasValue()) {
printer.emitKey("OriginalSourceLocation");
printer.openDict();
printer.emitKeyValue("Source", originalSourceLoc->fileName);
printer.emitKeyValue("Line", originalSourceLoc->line);
printer.emitKeyValue("Column", originalSourceLoc->column);
printer.closeDict();
}
}
}

printer.closeDict();
}

Expand Down
20 changes: 13 additions & 7 deletions tools/hbcdump/ProfileAnalyzer.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#include "HBCParser.h"
#include "StructuredPrinter.h"

#include "hermes/SourceMap/SourceMapParser.h"

#include "llvm/Support/raw_ostream.h"

#include <numeric>
Expand Down Expand Up @@ -55,6 +57,7 @@ class ProfileAnalyzer {
std::unordered_map<unsigned, FunctionRuntimeStatistics> funcRuntimeStats_;
// Caches any unused function checksums in profile trace.
std::unordered_set<std::string> unusedChecksumsInTrace_;
std::unique_ptr<SourceMap> sourceMap_;

ProfileData deserializeTrace(
std::unique_ptr<llvm::MemoryBuffer> profileBuffer);
Expand Down Expand Up @@ -115,8 +118,11 @@ class ProfileAnalyzer {
ProfileAnalyzer(
llvm::raw_ostream &os,
std::shared_ptr<hbc::BCProvider> bcProvider,
llvm::Optional<std::unique_ptr<llvm::MemoryBuffer>> profileBufferOpt)
: os_(os), hbcParser_(std::move(bcProvider)) {
llvm::Optional<std::unique_ptr<llvm::MemoryBuffer>> profileBufferOpt,
std::unique_ptr<SourceMap> &&sourceMap)
: os_(os),
hbcParser_(std::move(bcProvider)),
sourceMap_(std::move(sourceMap)) {
if (profileBufferOpt.hasValue()) {
profileDataOpt_ =
deserializeTrace(std::move(profileBufferOpt.getValue()));
Expand Down Expand Up @@ -150,15 +156,15 @@ class ProfileAnalyzer {
void dumpEpilogue();
// Print a high-level summary for the profile trace.
void dumpSummary();
// Print offsets of a function.
void dumpFunctionOffsets(uint32_t funcId, StructuredPrinter &printer);
// Print offsets for all functions in bundle.
void dumpAllFunctionOffsets(StructuredPrinter &printer) {
// Print meta-data for functions e.g. offset, source-location, etc.
void dumpFunctionInfo(uint32_t funcId, StructuredPrinter &printer);
// Print meta-data for all functions in bundle.
void dumpAllFunctionInfo(StructuredPrinter &printer) {
printer.openArray();
for (uint32_t i = 0, e = hbcParser_.getBCProvider()->getFunctionCount();
i < e;
i++) {
dumpFunctionOffsets(i, printer);
dumpFunctionInfo(i, printer);
}
printer.closeArray();
}
Expand Down
56 changes: 40 additions & 16 deletions tools/hbcdump/hbcdump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

#include "hermes/BCGen/HBC/BytecodeDisassembler.h"
#include "hermes/Public/Buffer.h"
#include "hermes/SourceMap/SourceMapGenerator.h"
#include "hermes/SourceMap/SourceMapParser.h"
#include "hermes/Support/MemoryBuffer.h"

#include "llvm/ADT/SmallVector.h"
Expand Down Expand Up @@ -41,6 +41,10 @@ static llvm::cl::opt<std::string> DumpOutputFilename(
"out",
llvm::cl::desc("Output file name"));

static llvm::cl::opt<std::string> SourceMapFilename(
"source-map",
llvm::cl::desc("Optional source-map file name, used by function-info"));

static llvm::cl::opt<std::string> StartupCommands(
"c",
llvm::cl::desc(
Expand Down Expand Up @@ -134,15 +138,14 @@ static void printHelp(llvm::Optional<llvm::StringRef> command = llvm::None) {
"USAGE: block\n"},
{"at-virtual",
"Display information about the function at a given virtual offset.\n\n"
"USAGE: at-virtual <OFFSET> [-json]\n"},
"USAGE: at-virtual <OFFSET>\n"},
{"help",
"Help instructions for hbcdump tool commands.\n\n"
"USAGE: help <COMMAND>\n"
" h <COMMAND>\n"},
{"offsets",
"Display offset, virtual offset, and the size of the function(s)\n\n"
"USAGE: offsets [-json]\n"
" offsets <FUNC_ID> [-json]\n"
{"function-info",
"Display info about a specific function, or all functions\n\n"
"USAGE: function-info [<FUNC_ID>]\n"
"NOTE: Virtual offset is the offset from the beginning of the segment\n"},
};

Expand All @@ -169,6 +172,7 @@ static void enterCommandLoop(
llvm::raw_ostream &os,
std::shared_ptr<hbc::BCProvider> bcProvider,
llvm::Optional<std::unique_ptr<llvm::MemoryBuffer>> profileBufferOpt,
std::unique_ptr<SourceMap> &&sourceMap,
const std::vector<std::string> &startupCommands) {
BytecodeDisassembler disassembler(bcProvider);

Expand All @@ -185,7 +189,8 @@ static void enterCommandLoop(
profileBufferOpt.hasValue()
? llvm::Optional<std::unique_ptr<llvm::MemoryBuffer>>(
std::move(profileBufferOpt.getValue()))
: llvm::None);
: llvm::None,
std::move(sourceMap));

// Process startup commands.
bool terminateLoop = false;
Expand Down Expand Up @@ -312,19 +317,18 @@ static bool executeCommand(
return false;
}
analyzer.dumpFileName(filenameId);
} else if (command == "offset" || command == "offsets") {
bool json = findAndRemoveOne(commandTokens, "-json");
} else if (command == "function-info") {
std::unique_ptr<StructuredPrinter> printer =
StructuredPrinter::create(os, json);
StructuredPrinter::create(os, true);
if (commandTokens.size() == 1) {
analyzer.dumpAllFunctionOffsets(*printer);
analyzer.dumpAllFunctionInfo(*printer);
} else if (commandTokens.size() == 2) {
uint32_t funcId;
if (commandTokens[1].getAsInteger(0, funcId)) {
os << "Error: cannot parse func_id as integer.\n";
return false;
}
analyzer.dumpFunctionOffsets(funcId, *printer);
analyzer.dumpFunctionInfo(funcId, *printer);
} else {
printHelp(command);
return false;
Expand All @@ -336,9 +340,8 @@ static bool executeCommand(
} else if (command == "block") {
analyzer.dumpBasicBlockStats();
} else if (command == "at_virtual" || command == "at-virtual") {
bool json = findAndRemoveOne(commandTokens, "-json");
std::unique_ptr<StructuredPrinter> printer =
StructuredPrinter::create(os, json);
StructuredPrinter::create(os, /* json */ true);
if (commandTokens.size() == 2) {
uint32_t virtualOffset;
if (commandTokens[1].getAsInteger(0, virtualOffset)) {
Expand All @@ -347,7 +350,7 @@ static bool executeCommand(
}
auto funcId = analyzer.getFunctionFromVirtualOffset(virtualOffset);
if (funcId.hasValue()) {
analyzer.dumpFunctionOffsets(*funcId, *printer);
analyzer.dumpFunctionInfo(*funcId, *printer);
} else {
os << "Virtual offset " << virtualOffset << " is invalid.\n";
}
Expand Down Expand Up @@ -425,13 +428,33 @@ int main(int argc, char **argv) {
}
auto &output = fileOS ? *fileOS : llvm::outs();

std::unique_ptr<SourceMap> sourceMap;
if (!SourceMapFilename.empty()) {
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> sourceMapBufOrErr =
llvm::MemoryBuffer::getFile(SourceMapFilename);
if (!sourceMapBufOrErr) {
llvm::errs() << "Error: fail to open file: " << SourceMapFilename << ": "
<< sourceMapBufOrErr.getError().message() << "\n";
return -1;
}
sourceMap = SourceMapParser::parse(*sourceMapBufOrErr.get().get());
if (!sourceMap) {
llvm::errs() << "Error loading source map: " << SourceMapFilename << "\n";
return -1;
}
}

if (ProfileFile.empty()) {
if (ShowSectionRanges) {
BytecodeSectionWalker walker(bytecodeStart, std::move(ret.first), output);
walker.printSectionRanges(HumanizeSectionRanges);
} else {
enterCommandLoop(
output, std::move(ret.first), llvm::None, startupCommands);
output,
std::move(ret.first),
llvm::None,
std::move(sourceMap),
startupCommands);
}
} else {
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> profileBuffer =
Expand All @@ -445,6 +468,7 @@ int main(int argc, char **argv) {
output,
std::move(ret.first),
std::move(profileBuffer.get()),
std::move(sourceMap),
startupCommands);
}

Expand Down

0 comments on commit 164af3c

Please sign in to comment.