From 19b2ea5d57d41ca77ee76baba8713ab0ab460cb5 Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Thu, 13 Apr 2017 23:52:39 -0700 Subject: [PATCH 1/4] Exports .txtmap file when .wasm is exported from asm2wasm. --- auto_update_tests.py | 4 +++ check.py | 25 +++++++++++++++++++ src/tools/asm2wasm.cpp | 19 +++++++++++--- src/tools/wasm-as.cpp | 3 ++- src/wasm-binary.h | 24 +++++++++++++++++- src/wasm-io.h | 4 +++ src/wasm/wasm-binary.cpp | 11 ++++++++ src/wasm/wasm-io.cpp | 13 ++++++++-- src/wasm/wasm.cpp | 1 + test/debugInfo.fromasm.clamp.no-opts.txtmap | 8 ++++++ test/debugInfo.fromasm.clamp.txtmap | 2 ++ ...debugInfo.fromasm.imprecise.no-opts.txtmap | 8 ++++++ test/debugInfo.fromasm.imprecise.txtmap | 1 + test/debugInfo.fromasm.no-opts.txtmap | 8 ++++++ test/debugInfo.fromasm.txtmap | 2 ++ 15 files changed, 126 insertions(+), 7 deletions(-) create mode 100644 test/debugInfo.fromasm.clamp.no-opts.txtmap create mode 100644 test/debugInfo.fromasm.clamp.txtmap create mode 100644 test/debugInfo.fromasm.imprecise.no-opts.txtmap create mode 100644 test/debugInfo.fromasm.imprecise.txtmap create mode 100644 test/debugInfo.fromasm.no-opts.txtmap create mode 100644 test/debugInfo.fromasm.txtmap diff --git a/auto_update_tests.py b/auto_update_tests.py index d76d087506e..27400eb654e 100755 --- a/auto_update_tests.py +++ b/auto_update_tests.py @@ -41,6 +41,10 @@ print ' '.join(cmd) actual = run_command(cmd) with open(os.path.join('test', wasm), 'w') as o: o.write(actual) + if 'debugInfo' in asm: + cmd += ['--binarymap-file', os.path.join('test', wasm + '.map'), '-o', 'a.wasm'] + run_command(cmd) + for dot_s_dir in ['dot_s', 'llvm_autogenerated']: for s in sorted(os.listdir(os.path.join('test', dot_s_dir))): diff --git a/check.py b/check.py index 05c2d92a6e2..e02795f3222 100755 --- a/check.py +++ b/check.py @@ -189,6 +189,31 @@ def do_asm2wasm_test(): fail_with_error('wasm interpreter error: ' + err) # failed to pretty-print fail_with_error('wasm interpreter error') + # verify debug info + if 'debugInfo' in asm: + txtmap = 'a.wasm.txtmap' + cmd += ['--binarymap-file', txtmap, + '--binarymap-url', txtmap + '.map', + '-o', 'a.wasm'] + run_command(cmd) + if not os.path.isfile(txtmap): + fail_with_error('Debug info map not created: %s' % txtmap) + with open(wasm + '.txtmap', 'rb') as expected: + with open(txtmap, 'rb') as actual: + fail_if_not_identical(actual.read(), expected.read()) + with open('a.wasm', 'rb') as binary: + url_section_name = bytearray([16]) + bytearray('sourceMappingURL') + payload = txtmap + '.map' + assert len(payload) < 256, 'name too long' + url_section_contents = bytearray([len(payload)]) + bytearray(payload) + print url_section_name + binary_contents = bytearray(binary.read()) + if url_section_name not in binary_contents: + fail_with_error('source map url section not found in binary') + if url_section_contents not in binary_contents[binary_contents.index(url_section_name):]: + fail_with_error('source map url not found in url section') + + print '\n[ checking asm2wasm binary reading/writing... ]\n' asmjs = os.path.join(options.binaryen_test, 'hello_world.asm.js') diff --git a/src/tools/asm2wasm.cpp b/src/tools/asm2wasm.cpp index cf0715198ad..77d67f07e64 100644 --- a/src/tools/asm2wasm.cpp +++ b/src/tools/asm2wasm.cpp @@ -36,6 +36,8 @@ int main(int argc, const char *argv[]) { bool legalizeJavaScriptFFI = true; Asm2WasmBuilder::TrapMode trapMode = Asm2WasmBuilder::TrapMode::JS; bool wasmOnly = false; + std::string binaryMapFile; + std::string binaryMapUrl; std::string symbolMap; bool emitBinary = true; @@ -99,9 +101,15 @@ int main(int argc, const char *argv[]) { [&legalizeJavaScriptFFI](Options *o, const std::string &) { legalizeJavaScriptFFI = false; }) - .add("--debuginfo", "-g", "Emit names section and debug info (for debug info you must emit text, -S, for this to work)", + .add("--debuginfo", "-g", "Emit names section in wasm binary (or full debuginfo in wast)", Options::Arguments::Zero, [&](Options *o, const std::string &arguments) { options.passOptions.debugInfo = true; }) + .add("--binarymap-file", "-bm", "Emit binary map (if using binary output) to the specified file", + Options::Arguments::One, + [&binaryMapFile](Options *o, const std::string &argument) { binaryMapFile = argument; }) + .add("--binarymap-url", "-bu", "Use specified string as binary map URL", + Options::Arguments::One, + [&binaryMapUrl](Options *o, const std::string &argument) { binaryMapUrl = argument; }) .add("--symbolmap", "-s", "Emit a symbol map (indexes => names)", Options::Arguments::One, [&](Options *o, const std::string &argument) { symbolMap = argument; }) @@ -136,8 +144,9 @@ int main(int argc, const char *argv[]) { } Asm2WasmPreProcessor pre; - // wasm binaries can contain a names section, but not full debug info - pre.debugInfo = options.passOptions.debugInfo && !emitBinary; + // wasm binaries can contain a names section, but not full debug info -- + // debug info is disabled if a map file is not specified with wasm binary + pre.debugInfo = options.passOptions.debugInfo && (!emitBinary || binaryMapFile.size()); auto input( read_file>(options.extra["infile"], Flags::Text, options.debug ? Flags::Debug : Flags::Release)); char *start = pre.process(input.data()); @@ -204,6 +213,10 @@ int main(int argc, const char *argv[]) { writer.setDebugInfo(options.passOptions.debugInfo); writer.setSymbolMap(symbolMap); writer.setBinary(emitBinary); + if (emitBinary) { + writer.setBinaryMapFilename(binaryMapFile); + writer.setBinaryMapUrl(binaryMapUrl); + } writer.write(wasm, options.extra["output"]); if (options.debug) std::cerr << "done." << std::endl; diff --git a/src/tools/wasm-as.cpp b/src/tools/wasm-as.cpp index 067c0f28df6..14ddb7f5b1d 100644 --- a/src/tools/wasm-as.cpp +++ b/src/tools/wasm-as.cpp @@ -86,7 +86,8 @@ int main(int argc, const char *argv[]) { if (options.debug) std::cerr << "binarification..." << std::endl; BufferWithRandomAccess buffer(options.debug); WasmBinaryWriter writer(&wasm, buffer, options.debug); - writer.setDebugInfo(debugInfo); + // if debug info is used, then we want to emit the names section + writer.setNamesSection(debugInfo); if (symbolMap.size() > 0) writer.setSymbolMap(symbolMap); writer.write(); diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 921f90ac8b5..27ed553a4fa 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -308,6 +308,7 @@ enum EncodedType { namespace UserSections { extern const char* Name; +extern const char* SourceMapUrl; enum Subsection { NameFunction = 1, @@ -534,8 +535,11 @@ inline S32LEB binaryWasmType(WasmType type) { class WasmBinaryWriter : public Visitor { Module* wasm; BufferWithRandomAccess& o; + Function* currFunction = nullptr; bool debug; bool debugInfo = true; + std::ostream* binaryMap = nullptr; + std::string binaryMapUrl; std::string symbolMap; MixedArena allocator; @@ -546,7 +550,11 @@ class WasmBinaryWriter : public Visitor { prepare(); } - void setDebugInfo(bool set) { debugInfo = set; } + void setNamesSection(bool set) { debugInfo = set; } + void setBinaryMap(std::ostream* set, std::string url) { + binaryMap = set; + binaryMapUrl = url; + } void setSymbolMap(std::string set) { symbolMap = set; } void write(); @@ -582,6 +590,7 @@ class WasmBinaryWriter : public Visitor { void writeFunctionTableDeclaration(); void writeTableElements(); void writeNames(); + void writeSourceMapUrl(); void writeSymbolMap(); // helpers @@ -607,6 +616,19 @@ class WasmBinaryWriter : public Visitor { void recurse(Expression*& curr); std::vector breakStack; + void visit(Expression* curr) { + if (binaryMap && currFunction) { + // Dump the binaryMap debug info + auto& debugLocations = currFunction->debugLocations; + auto iter = debugLocations.find(curr); + if (iter != debugLocations.end()) { + auto fileName = wasm->debugInfoFileNames[iter->second.fileIndex]; + *binaryMap << o.size() << ":" << fileName << ":" <second.lineNumber << '\n'; + } + } + Visitor::visit(curr); + } + void visitBlock(Block *curr); // emits a node, but if it is a block with no name, emit a list of its contents void recursePossibleBlockContents(Expression* curr); diff --git a/src/wasm-io.h b/src/wasm-io.h index 803cbaf90c3..3a9d1c9959c 100644 --- a/src/wasm-io.h +++ b/src/wasm-io.h @@ -48,11 +48,15 @@ class ModuleWriter : public ModuleIO { bool binary = true; bool debugInfo = false; std::string symbolMap; + std::string binaryMapFilename; + std::string binaryMapUrl; public: void setBinary(bool binary_) { binary = binary_; } void setDebugInfo(bool debugInfo_) { debugInfo = debugInfo_; } void setSymbolMap(std::string symbolMap_) { symbolMap = symbolMap_; } + void setBinaryMapFilename(std::string binaryMapFilename_) { binaryMapFilename = binaryMapFilename_; } + void setBinaryMapUrl(std::string binaryMapUrl_) { binaryMapUrl = binaryMapUrl_; } // write text void writeText(Module& wasm, std::string filename); diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 65c6f2a2157..af0ce2d3fc2 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -46,6 +46,7 @@ void WasmBinaryWriter::write() { writeFunctions(); writeDataSegments(); if (debugInfo) writeNames(); + if (binaryMap) writeSourceMapUrl(); if (symbolMap.size() > 0) writeSymbolMap(); finishUp(); @@ -236,6 +237,7 @@ void WasmBinaryWriter::writeFunctions() { size_t sizePos = writeU32LEBPlaceholder(); size_t start = o.size(); Function* function = wasm->functions[i].get(); + currFunction = function; mappedLocals.clear(); numLocalsByType.clear(); if (debug) std::cerr << "writing" << function->name << std::endl; @@ -258,6 +260,7 @@ void WasmBinaryWriter::writeFunctions() { if (debug) std::cerr << "body size: " << size << ", writing at " << sizePos << ", next starts at " << o.size() << std::endl; o.writeAt(sizePos, U32LEB(size)); } + currFunction = nullptr; finishSection(start); } @@ -420,6 +423,14 @@ void WasmBinaryWriter::writeNames() { finishSection(start); } +void WasmBinaryWriter::writeSourceMapUrl() { + if (debug) std::cerr << "== writeSourceMapUrl" << std::endl; + auto start = startSection(BinaryConsts::Section::User); + writeInlineString(BinaryConsts::UserSections::SourceMapUrl); + writeInlineString(binaryMapUrl.c_str()); + finishSection(start); +} + void WasmBinaryWriter::writeSymbolMap() { std::ofstream file(symbolMap); for (auto& import : wasm->imports) { diff --git a/src/wasm/wasm-io.cpp b/src/wasm/wasm-io.cpp index c3192efbf91..9d2ce29a356 100644 --- a/src/wasm/wasm-io.cpp +++ b/src/wasm/wasm-io.cpp @@ -72,11 +72,21 @@ void ModuleWriter::writeBinary(Module& wasm, std::string filename) { if (debug) std::cerr << "writing binary to " << filename << "\n"; BufferWithRandomAccess buffer(debug); WasmBinaryWriter writer(&wasm, buffer, debug); - writer.setDebugInfo(debugInfo); + // if debug info is used, then we want to emit the names section + writer.setNamesSection(debugInfo); + std::unique_ptr binaryMapStream; + if (binaryMapFilename.size()) { + binaryMapStream = make_unique(); + binaryMapStream->open(binaryMapFilename); + writer.setBinaryMap(binaryMapStream.get(), binaryMapUrl); + } if (symbolMap.size() > 0) writer.setSymbolMap(symbolMap); writer.write(); Output output(filename, Flags::Binary, debug ? Flags::Debug : Flags::Release); buffer.writeTo(output); + if (binaryMapStream) { + binaryMapStream->close(); + } } void ModuleWriter::write(Module& wasm, std::string filename) { @@ -88,4 +98,3 @@ void ModuleWriter::write(Module& wasm, std::string filename) { } } - diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 834d9e28f54..d2bf4e75cad 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -28,6 +28,7 @@ Name WASM("wasm"), namespace BinaryConsts { namespace UserSections { const char* Name = "name"; +const char* SourceMapUrl = "sourceMappingURL"; } } diff --git a/test/debugInfo.fromasm.clamp.no-opts.txtmap b/test/debugInfo.fromasm.clamp.no-opts.txtmap new file mode 100644 index 00000000000..eec2e7bd354 --- /dev/null +++ b/test/debugInfo.fromasm.clamp.no-opts.txtmap @@ -0,0 +1,8 @@ +164:tests/hello_world.c:5 +168:tests/hello_world.c:6 +172:tests/other_file.cpp:314159 +194:return.cpp:50 +201:return.cpp:100 +241:even-opted.cpp:1 +248:even-opted.cpp:2 +255:even-opted.cpp:3 diff --git a/test/debugInfo.fromasm.clamp.txtmap b/test/debugInfo.fromasm.clamp.txtmap new file mode 100644 index 00000000000..bb25b4d8d30 --- /dev/null +++ b/test/debugInfo.fromasm.clamp.txtmap @@ -0,0 +1,2 @@ +211:even-opted.cpp:2 +223:even-opted.cpp:3 diff --git a/test/debugInfo.fromasm.imprecise.no-opts.txtmap b/test/debugInfo.fromasm.imprecise.no-opts.txtmap new file mode 100644 index 00000000000..00a07f11cd0 --- /dev/null +++ b/test/debugInfo.fromasm.imprecise.no-opts.txtmap @@ -0,0 +1,8 @@ +163:tests/hello_world.c:5 +167:tests/hello_world.c:6 +171:tests/other_file.cpp:314159 +193:return.cpp:50 +200:return.cpp:100 +219:even-opted.cpp:1 +226:even-opted.cpp:2 +233:even-opted.cpp:3 diff --git a/test/debugInfo.fromasm.imprecise.txtmap b/test/debugInfo.fromasm.imprecise.txtmap new file mode 100644 index 00000000000..c9f177cc8be --- /dev/null +++ b/test/debugInfo.fromasm.imprecise.txtmap @@ -0,0 +1 @@ +190:even-opted.cpp:2 diff --git a/test/debugInfo.fromasm.no-opts.txtmap b/test/debugInfo.fromasm.no-opts.txtmap new file mode 100644 index 00000000000..eec2e7bd354 --- /dev/null +++ b/test/debugInfo.fromasm.no-opts.txtmap @@ -0,0 +1,8 @@ +164:tests/hello_world.c:5 +168:tests/hello_world.c:6 +172:tests/other_file.cpp:314159 +194:return.cpp:50 +201:return.cpp:100 +241:even-opted.cpp:1 +248:even-opted.cpp:2 +255:even-opted.cpp:3 diff --git a/test/debugInfo.fromasm.txtmap b/test/debugInfo.fromasm.txtmap new file mode 100644 index 00000000000..bb25b4d8d30 --- /dev/null +++ b/test/debugInfo.fromasm.txtmap @@ -0,0 +1,2 @@ +211:even-opted.cpp:2 +223:even-opted.cpp:3 From da4f6154e41dfac1083aa8962f5cfdd34e71df04 Mon Sep 17 00:00:00 2001 From: Yury Delendik Date: Wed, 26 Apr 2017 10:48:18 -0500 Subject: [PATCH 2/4] Extends wasm-as, wasm-dis and s2wasm to consume debug locations. --- src/asm2wasm.h | 4 +- src/passes/Print.cpp | 9 ++- src/s2wasm.h | 27 +++++++-- src/tools/wasm-as.cpp | 17 ++++++ src/tools/wasm-dis.cpp | 13 ++++ src/wasm-binary.h | 20 ++++++- src/wasm-s-parser.h | 20 ++++++- src/wasm.h | 4 +- src/wasm/wasm-binary.cpp | 52 ++++++++++++++++ src/wasm/wasm-s-parser.cpp | 60 +++++++++++++++++-- test/debugInfo.fromasm | 25 ++++---- test/debugInfo.fromasm.clamp | 25 ++++---- test/debugInfo.fromasm.clamp.no-opts | 29 +++++---- test/debugInfo.fromasm.clamp.no-opts.txtmap | 22 ++++--- test/debugInfo.fromasm.clamp.txtmap | 14 ++++- test/debugInfo.fromasm.imprecise | 25 ++++---- test/debugInfo.fromasm.imprecise.no-opts | 29 +++++---- ...debugInfo.fromasm.imprecise.no-opts.txtmap | 22 ++++--- test/debugInfo.fromasm.imprecise.txtmap | 13 +++- test/debugInfo.fromasm.no-opts | 29 +++++---- test/debugInfo.fromasm.no-opts.txtmap | 22 ++++--- test/debugInfo.fromasm.txtmap | 14 ++++- test/dot_s/debug.wast | 4 ++ 23 files changed, 364 insertions(+), 135 deletions(-) diff --git a/src/asm2wasm.h b/src/asm2wasm.h index 16e7c95e1e9..b8439bdbf8d 100644 --- a/src/asm2wasm.h +++ b/src/asm2wasm.h @@ -1382,13 +1382,13 @@ void Asm2WasmBuilder::processAsm(Ref ast) { while (i < expressionStack.size()) { exp = expressionStack[i]; if (debugLocations.count(exp) == 0) { - debugLocations[exp] = { fileIndex, lineNumber }; + debugLocations[exp] = { fileIndex, lineNumber, 0 }; break; } i++; } } else { - debugLocations[exp] = { fileIndex, lineNumber }; + debugLocations[exp] = { fileIndex, lineNumber, 0 }; } break; } diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 6aebd612bc9..95f60e5e2c3 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -45,6 +45,7 @@ struct PrintSExpression : public Visitor { Module* currModule = nullptr; Function* currFunction = nullptr; + Function::DebugLocation lastPrintedLocation; PrintSExpression(std::ostream& o) : o(o) { setMinify(false); @@ -58,8 +59,11 @@ struct PrintSExpression : public Visitor { auto iter = debugLocations.find(curr); if (iter != debugLocations.end()) { auto fileName = currModule->debugInfoFileNames[iter->second.fileIndex]; - o << ";; " << fileName << ":" << iter->second.lineNumber << '\n'; - doIndent(o, indent); + if (lastPrintedLocation != iter->second) { + lastPrintedLocation = iter->second; + o << ";;@ " << fileName << ":" << iter->second.lineNumber << ":" << iter->second.columnNumber << '\n'; + doIndent(o, indent); + } } } Visitor::visit(curr); @@ -599,6 +603,7 @@ struct PrintSExpression : public Visitor { } void visitFunction(Function *curr) { currFunction = curr; + lastPrintedLocation = { 0, 0, 0 }; printOpening(o, "func ", true); printName(curr->name); if (curr->type.is()) { diff --git a/src/s2wasm.h b/src/s2wasm.h index 6ba4750ec49..efa4ad6013b 100644 --- a/src/s2wasm.h +++ b/src/s2wasm.h @@ -44,6 +44,7 @@ class S2WasmBuilder { MixedArena* allocator; LinkerObject* linkerObj; std::unique_ptr symbolInfo; + std::unordered_map fileIndexMap; public: S2WasmBuilder(const char* input, bool debug) @@ -601,7 +602,9 @@ class S2WasmBuilder { size_t fileId = getInt(); skipWhitespace(); auto quoted = getQuoted(); - WASM_UNUSED(fileId); WASM_UNUSED(quoted); // TODO: use the fileId and quoted + uint32_t index = wasm->debugInfoFileNames.size(); + fileIndexMap[fileId] = index; + wasm->debugInfoFileNames.push_back(std::string(quoted.begin(), quoted.end())); s = strchr(s, '\n'); return; } @@ -665,22 +668,31 @@ class S2WasmBuilder { mustMatch(":"); + Function::DebugLocation debugLocation = { 0, 0, 0 }; + bool useDebugLocation = false; auto recordFile = [&]() { if (debug) dump("file"); size_t fileId = getInt(); skipWhitespace(); auto quoted = getQuoted(); - WASM_UNUSED(fileId); WASM_UNUSED(quoted); // TODO: use the fileId and quoted + uint32_t index = wasm->debugInfoFileNames.size(); + fileIndexMap[fileId] = index; + wasm->debugInfoFileNames.push_back(std::string(quoted.begin(), quoted.end())); s = strchr(s, '\n'); }; auto recordLoc = [&]() { if (debug) dump("loc"); size_t fileId = getInt(); skipWhitespace(); - size_t row = getInt(); + uint32_t row = getInt(); skipWhitespace(); - size_t column = getInt(); - WASM_UNUSED(fileId); WASM_UNUSED(row); WASM_UNUSED(column); // TODO: use the fileId, row and column + uint32_t column = getInt(); + auto iter = fileIndexMap.find(fileId); + if (iter == fileIndexMap.end()) { + abort_on("idx"); + } + useDebugLocation = true; + debugLocation = { iter->second, row, column }; s = strchr(s, '\n'); }; auto recordLabel = [&]() { @@ -746,7 +758,10 @@ class S2WasmBuilder { // parse body func->body = allocator->alloc(); std::vector bstack; - auto addToBlock = [&bstack](Expression* curr) { + auto addToBlock = [&](Expression* curr) { + if (useDebugLocation) { + func->debugLocations[curr] = debugLocation; + } Expression* last = bstack.back(); if (last->is()) { last = last->cast()->body; diff --git a/src/tools/wasm-as.cpp b/src/tools/wasm-as.cpp index 14ddb7f5b1d..c289accfd18 100644 --- a/src/tools/wasm-as.cpp +++ b/src/tools/wasm-as.cpp @@ -30,6 +30,8 @@ using namespace wasm; int main(int argc, const char *argv[]) { bool debugInfo = false; std::string symbolMap; + std::string binaryMapFilename; + std::string binaryMapUrl; Options options("wasm-as", "Assemble a .wast (WebAssembly text format) into a .wasm (WebAssembly binary format)"); options.extra["validate"] = "wasm"; options @@ -51,6 +53,12 @@ int main(int argc, const char *argv[]) { .add("--debuginfo", "-g", "Emit names section and debug info", Options::Arguments::Zero, [&](Options *o, const std::string &arguments) { debugInfo = true; }) + .add("--binarymap-file", "-bm", "Emit binary map to the specified file", + Options::Arguments::One, + [&binaryMapFilename](Options *o, const std::string &argument) { binaryMapFilename = argument; }) + .add("--binarymap-url", "-bu", "Use specified string as binary map URL", + Options::Arguments::One, + [&binaryMapUrl](Options *o, const std::string &argument) { binaryMapUrl = argument; }) .add("--symbolmap", "-s", "Emit a symbol map (indexes => names)", Options::Arguments::One, [&](Options *o, const std::string &argument) { symbolMap = argument; }) @@ -88,12 +96,21 @@ int main(int argc, const char *argv[]) { WasmBinaryWriter writer(&wasm, buffer, options.debug); // if debug info is used, then we want to emit the names section writer.setNamesSection(debugInfo); + std::unique_ptr binaryMapStream = nullptr; + if (binaryMapFilename.size()) { + binaryMapStream = make_unique(); + binaryMapStream->open(binaryMapFilename); + writer.setBinaryMap(binaryMapStream.get(), binaryMapUrl); + } if (symbolMap.size() > 0) writer.setSymbolMap(symbolMap); writer.write(); if (options.debug) std::cerr << "writing to output..." << std::endl; Output output(options.extra["output"], Flags::Binary, options.debug ? Flags::Debug : Flags::Release); buffer.writeTo(output); + if (binaryMapStream) { + binaryMapStream->close(); + } if (options.debug) std::cerr << "Done." << std::endl; } diff --git a/src/tools/wasm-dis.cpp b/src/tools/wasm-dis.cpp index 93c28691362..c33b4d333e0 100644 --- a/src/tools/wasm-dis.cpp +++ b/src/tools/wasm-dis.cpp @@ -28,6 +28,7 @@ using namespace cashew; using namespace wasm; int main(int argc, const char *argv[]) { + std::string binaryMapFilename; Options options("wasm-dis", "Un-assemble a .wasm (WebAssembly binary format) into a .wast (WebAssembly text format)"); options.add("--output", "-o", "Output file (stdout if not specified)", Options::Arguments::One, @@ -35,6 +36,9 @@ int main(int argc, const char *argv[]) { o->extra["output"] = argument; Colors::disable(); }) + .add("--binarymap-file", "-bm", "Consume binary map from the specified file to add location information", + Options::Arguments::One, + [&binaryMapFilename](Options *o, const std::string &argument) { binaryMapFilename = argument; }) .add_positional("INFILE", Options::Arguments::One, [](Options *o, const std::string &argument) { o->extra["infile"] = argument; @@ -46,8 +50,17 @@ int main(int argc, const char *argv[]) { if (options.debug) std::cerr << "parsing binary..." << std::endl; Module wasm; try { + std::unique_ptr binaryMapStream; WasmBinaryBuilder parser(wasm, input, options.debug); + if (binaryMapFilename.size()) { + binaryMapStream = make_unique(); + binaryMapStream->open(binaryMapFilename); + parser.setDebugLocations(binaryMapStream.get()); + } parser.read(); + if (binaryMapStream) { + binaryMapStream->close(); + } } catch (ParseException& p) { p.dump(std::cerr); Fatal() << "error in parsing wasm binary"; diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 27ed553a4fa..b852814cccc 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -615,15 +615,17 @@ class WasmBinaryWriter : public Visitor { void recurse(Expression*& curr); std::vector breakStack; + Function::DebugLocation lastDebugLocation; void visit(Expression* curr) { if (binaryMap && currFunction) { // Dump the binaryMap debug info auto& debugLocations = currFunction->debugLocations; auto iter = debugLocations.find(curr); - if (iter != debugLocations.end()) { + if (iter != debugLocations.end() && iter->second != lastDebugLocation) { + lastDebugLocation = iter->second; auto fileName = wasm->debugInfoFileNames[iter->second.fileIndex]; - *binaryMap << o.size() << ":" << fileName << ":" <second.lineNumber << '\n'; + *binaryMap << o.size() << ":" << fileName << ":" << iter->second.lineNumber << ":" << iter->second.columnNumber << '\n'; } } Visitor::visit(curr); @@ -663,14 +665,17 @@ class WasmBinaryBuilder { MixedArena& allocator; std::vector& input; bool debug; + std::istream* binaryMap; + std::pair nextDebugLocation; size_t pos = 0; Index startIndex = -1; + bool useDebugLocation; std::set seenSections; public: - WasmBinaryBuilder(Module& wasm, std::vector& input, bool debug) : wasm(wasm), allocator(wasm.allocator), input(input), debug(debug) {} + WasmBinaryBuilder(Module& wasm, std::vector& input, bool debug) : wasm(wasm), allocator(wasm.allocator), input(input), debug(debug), binaryMap(nullptr), nextDebugLocation(0, { 0, 0, 0 }), useDebugLocation(false) {} void read(); void readUserSection(size_t payloadLen); @@ -759,6 +764,15 @@ class WasmBinaryBuilder { void readTableElements(); void readNames(size_t); + // Debug information reading helpers + void setDebugLocations(std::istream* binaryMap_) { + binaryMap = binaryMap_; + readNextDebugLocation(); + } + Function::DebugLocation debugLocation; + std::unordered_map debugInfoFileIndices; + void readNextDebugLocation(); + // AST reading int depth = 0; // only for debugging diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index be1f6b6999f..b02b6523bcd 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -28,6 +28,16 @@ namespace wasm { +class SourceLocation +{ +public: + cashew::IString filename; + uint32_t line; + uint32_t column; + SourceLocation(cashew::IString filename_, uint32_t line_, uint32_t column_ = 0) + : filename(filename_), line(line_), column(column_) {} +}; + // // An element in an S-Expression: a list or a string // @@ -41,7 +51,7 @@ class Element { bool quoted_; public: - Element(MixedArena& allocator) : isList_(true), list_(allocator), line(-1), col(-1) {} + Element(MixedArena& allocator) : isList_(true), list_(allocator), line(-1), col(-1), loc(nullptr) {} bool isList() { return isList_; } bool isStr() { return !isList_; } @@ -49,6 +59,7 @@ class Element { bool quoted() { return isStr() && quoted_; } size_t line, col; + SourceLocation* loc; // list methods List& list(); @@ -61,14 +72,13 @@ class Element { cashew::IString str(); const char* c_str(); Element* setString(cashew::IString str__, bool dollared__, bool quoted__); - Element* setMetadata(size_t line_, size_t col_); + Element* setMetadata(size_t line_, size_t col_, SourceLocation* loc_); // printing friend std::ostream& operator<<(std::ostream& o, Element& e); void dump(); }; - // // Generic S-Expression parsing into lists // @@ -76,6 +86,7 @@ class SExpressionParser { char* input; size_t line; char* lineStart; + SourceLocation* loc; MixedArena allocator; @@ -87,6 +98,7 @@ class SExpressionParser { private: Element* parse(); void skipWhitespace(); + void parseDebugLocation(); Element* parseString(); }; @@ -102,6 +114,7 @@ class SExpressionWasmBuilder { int functionCounter; int globalCounter; std::map functionTypes; // we need to know function return types before we parse their contents + std::unordered_map debugInfoFileIndices; public: // Assumes control of and modifies the input. @@ -147,6 +160,7 @@ class SExpressionWasmBuilder { Expression* parseExpression(Element& s); private: + Expression* makeExpression(Element& s); Expression* makeBinary(Element& s, BinaryOp op, WasmType type); Expression* makeUnary(Element& s, UnaryOp op, WasmType type); Expression* makeSelect(Element& s); diff --git a/src/wasm.h b/src/wasm.h index 3caab9dbc52..dba08d92ead 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -527,7 +527,9 @@ class Function { std::map localIndices; struct DebugLocation { - uint32_t fileIndex, lineNumber; + uint32_t fileIndex, lineNumber, columnNumber; + bool operator==(const DebugLocation& other) const { return fileIndex == other.fileIndex && lineNumber == other.lineNumber && columnNumber == other.columnNumber; } + bool operator!=(const DebugLocation& other) const { return !(*this == other); } }; std::unordered_map debugLocations; diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index af0ce2d3fc2..46f64ebf01d 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -238,6 +238,7 @@ void WasmBinaryWriter::writeFunctions() { size_t start = o.size(); Function* function = wasm->functions[i].get(); currFunction = function; + lastDebugLocation = {0, 0, 0}; mappedLocals.clear(); numLocalsByType.clear(); if (debug) std::cerr << "writing" << function->name << std::endl; @@ -1353,6 +1354,7 @@ void WasmBinaryBuilder::readFunctions() { // process the function body if (debug) std::cerr << "processing function: " << i << std::endl; nextLabel = 0; + useDebugLocation = false; breaksToReturn = false; // process body assert(breakStack.empty()); @@ -1400,6 +1402,43 @@ void WasmBinaryBuilder::readExports() { } } +void WasmBinaryBuilder::readNextDebugLocation() { + if (binaryMap) { + std::string line; + while (std::getline(*binaryMap, line)) { + auto pos = line.begin(); + while (pos < line.end() && pos[0] != ':') pos++; + if (pos == line.end()) continue; + uint32_t position = atoi(std::string(line.begin(), pos).c_str()); + auto filenameStart = ++pos; + while (pos < line.end() && pos[0] != ':') pos++; + if (pos == line.end()) continue; + std::string file(filenameStart, pos); + auto iter = debugInfoFileIndices.find(file); + if (iter == debugInfoFileIndices.end()) { + Index index = wasm.debugInfoFileNames.size(); + wasm.debugInfoFileNames.push_back(file); + debugInfoFileIndices[file] = index; + } + uint32_t fileIndex = debugInfoFileIndices[file]; + auto lineNumberStart = ++pos; + while (pos < line.end() && pos[0] != ':') pos++; + if (pos == line.end()) { + // old format + uint32_t lineNumber = atoi(std::string(lineNumberStart, line.end()).c_str()); + nextDebugLocation = { position, { fileIndex, lineNumber, 0 } }; + return; + } + uint32_t lineNumber = atoi(std::string(lineNumberStart, pos).c_str()); + auto columnNumberStart = ++pos; + uint32_t columnNumber = atoi(std::string(columnNumberStart, line.end()).c_str()); + + nextDebugLocation = { position, { fileIndex, lineNumber, columnNumber } }; + return; + } + nextDebugLocation.first = 0; + } +} Expression* WasmBinaryBuilder::readExpression() { assert(depth == 0); processExpressions(); @@ -1638,6 +1677,16 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { throw ParseException("Reached function end without seeing End opcode"); } if (debug) std::cerr << "zz recurse into " << ++depth << " at " << pos << std::endl; + if (nextDebugLocation.first) { + while (nextDebugLocation.first && nextDebugLocation.first <= pos) { + if (nextDebugLocation.first < pos) { + std::cerr << "skipping debug location info for " << nextDebugLocation.first << std::endl; + } + debugLocation = nextDebugLocation.second; + useDebugLocation = currFunction; // using only for function expressions + readNextDebugLocation(); + } + } uint8_t code = getInt8(); if (debug) std::cerr << "readExpression seeing " << (int)code << std::endl; switch (code) { @@ -1672,6 +1721,9 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { throw ParseException("bad node code " + std::to_string(code)); } } + if (useDebugLocation && curr) { + currFunction->debugLocations[curr] = debugLocation; + } if (debug) std::cerr << "zz recurse from " << depth-- << " at " << pos << std::endl; return BinaryConsts::ASTNodes(code); } diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 9b053d5b8ce..1842237a579 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -71,9 +71,10 @@ Element* Element::setString(IString str__, bool dollared__, bool quoted__) { return this; } -Element* Element::setMetadata(size_t line_, size_t col_) { +Element* Element::setMetadata(size_t line_, size_t col_, SourceLocation* loc_) { line = line_; col = col_; + loc = loc_; return this; } @@ -93,7 +94,7 @@ void Element::dump() { } -SExpressionParser::SExpressionParser(char* input) : input(input) { +SExpressionParser::SExpressionParser(char* input) : input(input), loc(nullptr) { root = nullptr; line = 1; lineStart = input; @@ -104,6 +105,7 @@ SExpressionParser::SExpressionParser(char* input) : input(input) { Element* SExpressionParser::parse() { std::vector stack; + std::vector stackLocs; Element *curr = allocator.alloc(); while (1) { skipWhitespace(); @@ -111,7 +113,9 @@ Element* SExpressionParser::parse() { if (input[0] == '(') { input++; stack.push_back(curr); - curr = allocator.alloc()->setMetadata(line, input - lineStart - 1); + curr = allocator.alloc()->setMetadata(line, input - lineStart - 1, loc); + stackLocs.push_back(loc); + assert(stack.size() == stackLocs.size()); } else if (input[0] == ')') { input++; auto last = curr; @@ -119,7 +123,10 @@ Element* SExpressionParser::parse() { throw ParseException("s-expr stack empty"); } curr = stack.back(); + assert(stack.size() == stackLocs.size()); stack.pop_back(); + loc = stackLocs.back(); + stackLocs.pop_back(); curr->list().push_back(last); } else { curr->list().push_back(parseString()); @@ -129,6 +136,29 @@ Element* SExpressionParser::parse() { return curr; } +void SExpressionParser::parseDebugLocation() { + // Extracting debug location (if valid) + char* debugLoc = input + 3; // skipping ";;@" + while (debugLoc[0] && debugLoc[0] == ' ') debugLoc++; + char* debugLocEnd = debugLoc; + while (debugLocEnd[0] && debugLocEnd[0] != '\n') debugLocEnd++; + char* pos = debugLoc; + while (pos < debugLocEnd && pos[0] != ':') pos++; + if (pos >= debugLocEnd) { + return; // no line number + } + std::string name(debugLoc, pos); + char* lineStart = ++pos; + while (pos < debugLocEnd && pos[0] != ':') pos++; + std::string lineStr(lineStart, pos); + if (pos >= debugLocEnd) { + return; // no column number + } + std::string colStr(++pos, debugLocEnd); + void* buf = allocator.allocSpace(sizeof(SourceLocation)); + loc = new (buf) SourceLocation(IString(name.c_str(), false), atoi(lineStr.c_str()), atoi(colStr.c_str())); +} + void SExpressionParser::skipWhitespace() { while (1) { while (isspace(input[0])) { @@ -139,6 +169,9 @@ void SExpressionParser::skipWhitespace() { input++; } if (input[0] == ';' && input[1] == ';') { + if (input[2] == '@') { + parseDebugLocation(); + } while (input[0] && input[0] != '\n') input++; line++; lineStart = ++input; @@ -198,13 +231,13 @@ Element* SExpressionParser::parseString() { input++; } input++; - return allocator.alloc()->setString(IString(str.c_str(), false), dollared, true)->setMetadata(line, start - lineStart); + return allocator.alloc()->setString(IString(str.c_str(), false), dollared, true)->setMetadata(line, start - lineStart, loc); } while (input[0] && !isspace(input[0]) && input[0] != ')' && input[0] != '(' && input[0] != ';') input++; if (start == input) throw ParseException("expected string", line, input - lineStart); char temp = input[0]; input[0] = 0; - auto ret = allocator.alloc()->setString(IString(start, false), dollared, false)->setMetadata(line, start - lineStart); + auto ret = allocator.alloc()->setString(IString(start, false), dollared, false)->setMetadata(line, start - lineStart, loc); input[0] = temp; return ret; } @@ -583,6 +616,23 @@ WasmType SExpressionWasmBuilder::stringToWasmType(const char* str, bool allowErr } Expression* SExpressionWasmBuilder::parseExpression(Element& s) { + Expression* result = makeExpression(s); + if (s.loc) { + IString file = s.loc->filename; + auto& debugInfoFileNames = wasm.debugInfoFileNames; + auto iter = debugInfoFileIndices.find(file); + if (iter == debugInfoFileIndices.end()) { + Index index = debugInfoFileNames.size(); + debugInfoFileNames.push_back(file.c_str()); + debugInfoFileIndices[file] = index; + } + uint32_t fileIndex = debugInfoFileIndices[file]; + currFunction->debugLocations[result] = {fileIndex, s.loc->line, s.loc->column}; + } + return result; +} + +Expression* SExpressionWasmBuilder::makeExpression(Element& s) { IString id = s[0]->str(); const char *str = id.str; const char *dot = strchr(str, '.'); diff --git a/test/debugInfo.fromasm b/test/debugInfo.fromasm index 84fb4aece99..7aa584c4371 100644 --- a/test/debugInfo.fromasm +++ b/test/debugInfo.fromasm @@ -11,21 +11,21 @@ (export "fib" (func $fib)) (export "switch_reach" (func $switch_reach)) (func $add (param $0 i32) (param $1 i32) (result i32) - ;; tests/other_file.cpp:314159 + ;;@ tests/other_file.cpp:314159:0 (i32.add (get_local $1) (get_local $1) ) ) (func $ret (param $0 i32) (result i32) - ;; return.cpp:50 + ;;@ return.cpp:50:0 (set_local $0 (i32.shl (get_local $0) (i32.const 1) ) ) - ;; return.cpp:100 + ;;@ return.cpp:100:0 (i32.add (get_local $0) (i32.const 1) @@ -42,21 +42,21 @@ ) ) (func $opts (param $0 i32) (param $1 i32) (result i32) - ;; even-opted.cpp:1 + ;;@ even-opted.cpp:1:0 (set_local $0 (i32.add (get_local $0) (get_local $1) ) ) - ;; even-opted.cpp:2 + ;;@ even-opted.cpp:2:0 (set_local $1 (i32.shr_s (get_local $1) (get_local $0) ) ) - ;; even-opted.cpp:3 + ;;@ even-opted.cpp:3:0 (i32.add (call $i32s-rem (get_local $0) @@ -71,7 +71,7 @@ (local $3 i32) (local $4 i32) (if - ;; fib.c:3 + ;;@ fib.c:3:0 (i32.gt_s (get_local $0) (i32.const 0) @@ -91,21 +91,21 @@ (set_local $1 (i32.const 1) ) - ;; fib.c:8 + ;;@ fib.c:8:0 (return (get_local $1) ) ) ) (loop $while-in - ;; fib.c:4 + ;;@ fib.c:4:0 (set_local $1 (i32.add (get_local $3) (get_local $4) ) ) - ;; fib.c:3 + ;;@ fib.c:3:0 (set_local $2 (i32.add (get_local $2) @@ -113,7 +113,6 @@ ) ) (if - ;; fib.c:3 (i32.ne (get_local $2) (get_local $0) @@ -129,7 +128,7 @@ ) ) ) - ;; fib.c:8 + ;;@ fib.c:8:0 (get_local $1) ) (func $switch_reach (param $0 i32) (result i32) @@ -189,7 +188,7 @@ (get_local $0) ) ) - ;; /tmp/emscripten_test_binaryen2_28hnAe/src.c:59950 + ;;@ /tmp/emscripten_test_binaryen2_28hnAe/src.c:59950:0 (get_local $1) ) ) diff --git a/test/debugInfo.fromasm.clamp b/test/debugInfo.fromasm.clamp index 84fb4aece99..7aa584c4371 100644 --- a/test/debugInfo.fromasm.clamp +++ b/test/debugInfo.fromasm.clamp @@ -11,21 +11,21 @@ (export "fib" (func $fib)) (export "switch_reach" (func $switch_reach)) (func $add (param $0 i32) (param $1 i32) (result i32) - ;; tests/other_file.cpp:314159 + ;;@ tests/other_file.cpp:314159:0 (i32.add (get_local $1) (get_local $1) ) ) (func $ret (param $0 i32) (result i32) - ;; return.cpp:50 + ;;@ return.cpp:50:0 (set_local $0 (i32.shl (get_local $0) (i32.const 1) ) ) - ;; return.cpp:100 + ;;@ return.cpp:100:0 (i32.add (get_local $0) (i32.const 1) @@ -42,21 +42,21 @@ ) ) (func $opts (param $0 i32) (param $1 i32) (result i32) - ;; even-opted.cpp:1 + ;;@ even-opted.cpp:1:0 (set_local $0 (i32.add (get_local $0) (get_local $1) ) ) - ;; even-opted.cpp:2 + ;;@ even-opted.cpp:2:0 (set_local $1 (i32.shr_s (get_local $1) (get_local $0) ) ) - ;; even-opted.cpp:3 + ;;@ even-opted.cpp:3:0 (i32.add (call $i32s-rem (get_local $0) @@ -71,7 +71,7 @@ (local $3 i32) (local $4 i32) (if - ;; fib.c:3 + ;;@ fib.c:3:0 (i32.gt_s (get_local $0) (i32.const 0) @@ -91,21 +91,21 @@ (set_local $1 (i32.const 1) ) - ;; fib.c:8 + ;;@ fib.c:8:0 (return (get_local $1) ) ) ) (loop $while-in - ;; fib.c:4 + ;;@ fib.c:4:0 (set_local $1 (i32.add (get_local $3) (get_local $4) ) ) - ;; fib.c:3 + ;;@ fib.c:3:0 (set_local $2 (i32.add (get_local $2) @@ -113,7 +113,6 @@ ) ) (if - ;; fib.c:3 (i32.ne (get_local $2) (get_local $0) @@ -129,7 +128,7 @@ ) ) ) - ;; fib.c:8 + ;;@ fib.c:8:0 (get_local $1) ) (func $switch_reach (param $0 i32) (result i32) @@ -189,7 +188,7 @@ (get_local $0) ) ) - ;; /tmp/emscripten_test_binaryen2_28hnAe/src.c:59950 + ;;@ /tmp/emscripten_test_binaryen2_28hnAe/src.c:59950:0 (get_local $1) ) ) diff --git a/test/debugInfo.fromasm.clamp.no-opts b/test/debugInfo.fromasm.clamp.no-opts index 9e3050695ed..054226a3a3c 100644 --- a/test/debugInfo.fromasm.clamp.no-opts +++ b/test/debugInfo.fromasm.clamp.no-opts @@ -11,15 +11,15 @@ (export "fib" (func $fib)) (export "switch_reach" (func $switch_reach)) (func $add (param $x i32) (param $y i32) (result i32) - ;; tests/hello_world.c:5 + ;;@ tests/hello_world.c:5:0 (set_local $x (get_local $x) ) - ;; tests/hello_world.c:6 + ;;@ tests/hello_world.c:6:0 (set_local $y (get_local $y) ) - ;; tests/other_file.cpp:314159 + ;;@ tests/other_file.cpp:314159:0 (set_local $x (get_local $y) ) @@ -31,14 +31,14 @@ ) ) (func $ret (param $x i32) (result i32) - ;; return.cpp:50 + ;;@ return.cpp:50:0 (set_local $x (i32.shl (get_local $x) (i32.const 1) ) ) - ;; return.cpp:100 + ;;@ return.cpp:100:0 (return (i32.add (get_local $x) @@ -59,21 +59,21 @@ ) ) (func $opts (param $x i32) (param $y i32) (result i32) - ;; even-opted.cpp:1 + ;;@ even-opted.cpp:1:0 (set_local $x (i32.add (get_local $x) (get_local $y) ) ) - ;; even-opted.cpp:2 + ;;@ even-opted.cpp:2:0 (set_local $y (i32.shr_s (get_local $y) (get_local $x) ) ) - ;; even-opted.cpp:3 + ;;@ even-opted.cpp:3:0 (set_local $x (call $i32s-rem (get_local $x) @@ -102,7 +102,7 @@ (set_local $sp (get_global $STACKTOP) ) - ;; fib.c:3 + ;;@ fib.c:3:0 (set_local $$1 (i32.gt_s (get_local $$0) @@ -126,7 +126,7 @@ (set_local $$$0$lcssa (i32.const 1) ) - ;; fib.c:8 + ;;@ fib.c:8:0 (return (get_local $$$0$lcssa) ) @@ -134,21 +134,20 @@ ) (loop $while-in (block $while-out - ;; fib.c:4 + ;;@ fib.c:4:0 (set_local $$2 (i32.add (get_local $$$019) (get_local $$$01518) ) ) - ;; fib.c:3 + ;;@ fib.c:3:0 (set_local $$3 (i32.add (get_local $$$01617) (i32.const 1) ) ) - ;; fib.c:3 (set_local $$exitcond (i32.eq (get_local $$3) @@ -181,7 +180,7 @@ (br $while-in) ) ) - ;; fib.c:8 + ;;@ fib.c:8:0 (return (get_local $$$0$lcssa) ) @@ -278,7 +277,7 @@ (get_local $$p) ) ) - ;; /tmp/emscripten_test_binaryen2_28hnAe/src.c:59950 + ;;@ /tmp/emscripten_test_binaryen2_28hnAe/src.c:59950:0 (return (get_local $$rc$0) ) diff --git a/test/debugInfo.fromasm.clamp.no-opts.txtmap b/test/debugInfo.fromasm.clamp.no-opts.txtmap index eec2e7bd354..f7b8c313154 100644 --- a/test/debugInfo.fromasm.clamp.no-opts.txtmap +++ b/test/debugInfo.fromasm.clamp.no-opts.txtmap @@ -1,8 +1,14 @@ -164:tests/hello_world.c:5 -168:tests/hello_world.c:6 -172:tests/other_file.cpp:314159 -194:return.cpp:50 -201:return.cpp:100 -241:even-opted.cpp:1 -248:even-opted.cpp:2 -255:even-opted.cpp:3 +199:tests/hello_world.c:5:0 +203:tests/hello_world.c:6:0 +207:tests/other_file.cpp:314159:0 +229:return.cpp:50:0 +236:return.cpp:100:0 +275:even-opted.cpp:1:0 +282:even-opted.cpp:2:0 +289:even-opted.cpp:3:0 +321:fib.c:3:0 +349:fib.c:8:0 +357:fib.c:4:0 +364:fib.c:3:0 +410:fib.c:8:0 +556:/tmp/emscripten_test_binaryen2_28hnAe/src.c:59950:0 diff --git a/test/debugInfo.fromasm.clamp.txtmap b/test/debugInfo.fromasm.clamp.txtmap index bb25b4d8d30..5ab814a0d4a 100644 --- a/test/debugInfo.fromasm.clamp.txtmap +++ b/test/debugInfo.fromasm.clamp.txtmap @@ -1,2 +1,12 @@ -211:even-opted.cpp:2 -223:even-opted.cpp:3 +185:tests/other_file.cpp:314159:0 +199:return.cpp:50:0 +206:return.cpp:100:0 +241:even-opted.cpp:1:0 +248:even-opted.cpp:2:0 +255:even-opted.cpp:3:0 +276:fib.c:3:0 +300:fib.c:8:0 +306:fib.c:4:0 +313:fib.c:3:0 +339:fib.c:8:0 +436:/tmp/emscripten_test_binaryen2_28hnAe/src.c:59950:0 diff --git a/test/debugInfo.fromasm.imprecise b/test/debugInfo.fromasm.imprecise index 4919d624dab..638911cfc1c 100644 --- a/test/debugInfo.fromasm.imprecise +++ b/test/debugInfo.fromasm.imprecise @@ -10,42 +10,42 @@ (export "fib" (func $fib)) (export "switch_reach" (func $switch_reach)) (func $add (param $0 i32) (param $1 i32) (result i32) - ;; tests/other_file.cpp:314159 + ;;@ tests/other_file.cpp:314159:0 (i32.add (get_local $1) (get_local $1) ) ) (func $ret (param $0 i32) (result i32) - ;; return.cpp:50 + ;;@ return.cpp:50:0 (set_local $0 (i32.shl (get_local $0) (i32.const 1) ) ) - ;; return.cpp:100 + ;;@ return.cpp:100:0 (i32.add (get_local $0) (i32.const 1) ) ) (func $opts (param $0 i32) (param $1 i32) (result i32) - ;; even-opted.cpp:1 + ;;@ even-opted.cpp:1:0 (set_local $0 (i32.add (get_local $0) (get_local $1) ) ) - ;; even-opted.cpp:2 + ;;@ even-opted.cpp:2:0 (set_local $1 (i32.shr_s (get_local $1) (get_local $0) ) ) - ;; even-opted.cpp:3 + ;;@ even-opted.cpp:3:0 (i32.add (i32.rem_s (get_local $0) @@ -60,7 +60,7 @@ (local $3 i32) (local $4 i32) (if - ;; fib.c:3 + ;;@ fib.c:3:0 (i32.gt_s (get_local $0) (i32.const 0) @@ -80,21 +80,21 @@ (set_local $1 (i32.const 1) ) - ;; fib.c:8 + ;;@ fib.c:8:0 (return (get_local $1) ) ) ) (loop $while-in - ;; fib.c:4 + ;;@ fib.c:4:0 (set_local $1 (i32.add (get_local $3) (get_local $4) ) ) - ;; fib.c:3 + ;;@ fib.c:3:0 (set_local $2 (i32.add (get_local $2) @@ -102,7 +102,6 @@ ) ) (if - ;; fib.c:3 (i32.ne (get_local $2) (get_local $0) @@ -118,7 +117,7 @@ ) ) ) - ;; fib.c:8 + ;;@ fib.c:8:0 (get_local $1) ) (func $switch_reach (param $0 i32) (result i32) @@ -178,7 +177,7 @@ (get_local $0) ) ) - ;; /tmp/emscripten_test_binaryen2_28hnAe/src.c:59950 + ;;@ /tmp/emscripten_test_binaryen2_28hnAe/src.c:59950:0 (get_local $1) ) ) diff --git a/test/debugInfo.fromasm.imprecise.no-opts b/test/debugInfo.fromasm.imprecise.no-opts index 4bea2c35520..1ec751ef4d0 100644 --- a/test/debugInfo.fromasm.imprecise.no-opts +++ b/test/debugInfo.fromasm.imprecise.no-opts @@ -11,15 +11,15 @@ (export "fib" (func $fib)) (export "switch_reach" (func $switch_reach)) (func $add (param $x i32) (param $y i32) (result i32) - ;; tests/hello_world.c:5 + ;;@ tests/hello_world.c:5:0 (set_local $x (get_local $x) ) - ;; tests/hello_world.c:6 + ;;@ tests/hello_world.c:6:0 (set_local $y (get_local $y) ) - ;; tests/other_file.cpp:314159 + ;;@ tests/other_file.cpp:314159:0 (set_local $x (get_local $y) ) @@ -31,14 +31,14 @@ ) ) (func $ret (param $x i32) (result i32) - ;; return.cpp:50 + ;;@ return.cpp:50:0 (set_local $x (i32.shl (get_local $x) (i32.const 1) ) ) - ;; return.cpp:100 + ;;@ return.cpp:100:0 (return (i32.add (get_local $x) @@ -47,21 +47,21 @@ ) ) (func $opts (param $x i32) (param $y i32) (result i32) - ;; even-opted.cpp:1 + ;;@ even-opted.cpp:1:0 (set_local $x (i32.add (get_local $x) (get_local $y) ) ) - ;; even-opted.cpp:2 + ;;@ even-opted.cpp:2:0 (set_local $y (i32.shr_s (get_local $y) (get_local $x) ) ) - ;; even-opted.cpp:3 + ;;@ even-opted.cpp:3:0 (set_local $x (i32.rem_s (get_local $x) @@ -90,7 +90,7 @@ (set_local $sp (get_global $STACKTOP) ) - ;; fib.c:3 + ;;@ fib.c:3:0 (set_local $$1 (i32.gt_s (get_local $$0) @@ -114,7 +114,7 @@ (set_local $$$0$lcssa (i32.const 1) ) - ;; fib.c:8 + ;;@ fib.c:8:0 (return (get_local $$$0$lcssa) ) @@ -122,21 +122,20 @@ ) (loop $while-in (block $while-out - ;; fib.c:4 + ;;@ fib.c:4:0 (set_local $$2 (i32.add (get_local $$$019) (get_local $$$01518) ) ) - ;; fib.c:3 + ;;@ fib.c:3:0 (set_local $$3 (i32.add (get_local $$$01617) (i32.const 1) ) ) - ;; fib.c:3 (set_local $$exitcond (i32.eq (get_local $$3) @@ -169,7 +168,7 @@ (br $while-in) ) ) - ;; fib.c:8 + ;;@ fib.c:8:0 (return (get_local $$$0$lcssa) ) @@ -266,7 +265,7 @@ (get_local $$p) ) ) - ;; /tmp/emscripten_test_binaryen2_28hnAe/src.c:59950 + ;;@ /tmp/emscripten_test_binaryen2_28hnAe/src.c:59950:0 (return (get_local $$rc$0) ) diff --git a/test/debugInfo.fromasm.imprecise.no-opts.txtmap b/test/debugInfo.fromasm.imprecise.no-opts.txtmap index 00a07f11cd0..25ae6055e50 100644 --- a/test/debugInfo.fromasm.imprecise.no-opts.txtmap +++ b/test/debugInfo.fromasm.imprecise.no-opts.txtmap @@ -1,8 +1,14 @@ -163:tests/hello_world.c:5 -167:tests/hello_world.c:6 -171:tests/other_file.cpp:314159 -193:return.cpp:50 -200:return.cpp:100 -219:even-opted.cpp:1 -226:even-opted.cpp:2 -233:even-opted.cpp:3 +198:tests/hello_world.c:5:0 +202:tests/hello_world.c:6:0 +206:tests/other_file.cpp:314159:0 +228:return.cpp:50:0 +235:return.cpp:100:0 +253:even-opted.cpp:1:0 +260:even-opted.cpp:2:0 +267:even-opted.cpp:3:0 +298:fib.c:3:0 +326:fib.c:8:0 +334:fib.c:4:0 +341:fib.c:3:0 +387:fib.c:8:0 +533:/tmp/emscripten_test_binaryen2_28hnAe/src.c:59950:0 diff --git a/test/debugInfo.fromasm.imprecise.txtmap b/test/debugInfo.fromasm.imprecise.txtmap index c9f177cc8be..89e0f75c13a 100644 --- a/test/debugInfo.fromasm.imprecise.txtmap +++ b/test/debugInfo.fromasm.imprecise.txtmap @@ -1 +1,12 @@ -190:even-opted.cpp:2 +184:tests/other_file.cpp:314159:0 +198:return.cpp:50:0 +205:return.cpp:100:0 +220:even-opted.cpp:1:0 +227:even-opted.cpp:2:0 +234:even-opted.cpp:3:0 +254:fib.c:3:0 +278:fib.c:8:0 +284:fib.c:4:0 +291:fib.c:3:0 +317:fib.c:8:0 +414:/tmp/emscripten_test_binaryen2_28hnAe/src.c:59950:0 diff --git a/test/debugInfo.fromasm.no-opts b/test/debugInfo.fromasm.no-opts index 9e3050695ed..054226a3a3c 100644 --- a/test/debugInfo.fromasm.no-opts +++ b/test/debugInfo.fromasm.no-opts @@ -11,15 +11,15 @@ (export "fib" (func $fib)) (export "switch_reach" (func $switch_reach)) (func $add (param $x i32) (param $y i32) (result i32) - ;; tests/hello_world.c:5 + ;;@ tests/hello_world.c:5:0 (set_local $x (get_local $x) ) - ;; tests/hello_world.c:6 + ;;@ tests/hello_world.c:6:0 (set_local $y (get_local $y) ) - ;; tests/other_file.cpp:314159 + ;;@ tests/other_file.cpp:314159:0 (set_local $x (get_local $y) ) @@ -31,14 +31,14 @@ ) ) (func $ret (param $x i32) (result i32) - ;; return.cpp:50 + ;;@ return.cpp:50:0 (set_local $x (i32.shl (get_local $x) (i32.const 1) ) ) - ;; return.cpp:100 + ;;@ return.cpp:100:0 (return (i32.add (get_local $x) @@ -59,21 +59,21 @@ ) ) (func $opts (param $x i32) (param $y i32) (result i32) - ;; even-opted.cpp:1 + ;;@ even-opted.cpp:1:0 (set_local $x (i32.add (get_local $x) (get_local $y) ) ) - ;; even-opted.cpp:2 + ;;@ even-opted.cpp:2:0 (set_local $y (i32.shr_s (get_local $y) (get_local $x) ) ) - ;; even-opted.cpp:3 + ;;@ even-opted.cpp:3:0 (set_local $x (call $i32s-rem (get_local $x) @@ -102,7 +102,7 @@ (set_local $sp (get_global $STACKTOP) ) - ;; fib.c:3 + ;;@ fib.c:3:0 (set_local $$1 (i32.gt_s (get_local $$0) @@ -126,7 +126,7 @@ (set_local $$$0$lcssa (i32.const 1) ) - ;; fib.c:8 + ;;@ fib.c:8:0 (return (get_local $$$0$lcssa) ) @@ -134,21 +134,20 @@ ) (loop $while-in (block $while-out - ;; fib.c:4 + ;;@ fib.c:4:0 (set_local $$2 (i32.add (get_local $$$019) (get_local $$$01518) ) ) - ;; fib.c:3 + ;;@ fib.c:3:0 (set_local $$3 (i32.add (get_local $$$01617) (i32.const 1) ) ) - ;; fib.c:3 (set_local $$exitcond (i32.eq (get_local $$3) @@ -181,7 +180,7 @@ (br $while-in) ) ) - ;; fib.c:8 + ;;@ fib.c:8:0 (return (get_local $$$0$lcssa) ) @@ -278,7 +277,7 @@ (get_local $$p) ) ) - ;; /tmp/emscripten_test_binaryen2_28hnAe/src.c:59950 + ;;@ /tmp/emscripten_test_binaryen2_28hnAe/src.c:59950:0 (return (get_local $$rc$0) ) diff --git a/test/debugInfo.fromasm.no-opts.txtmap b/test/debugInfo.fromasm.no-opts.txtmap index eec2e7bd354..f7b8c313154 100644 --- a/test/debugInfo.fromasm.no-opts.txtmap +++ b/test/debugInfo.fromasm.no-opts.txtmap @@ -1,8 +1,14 @@ -164:tests/hello_world.c:5 -168:tests/hello_world.c:6 -172:tests/other_file.cpp:314159 -194:return.cpp:50 -201:return.cpp:100 -241:even-opted.cpp:1 -248:even-opted.cpp:2 -255:even-opted.cpp:3 +199:tests/hello_world.c:5:0 +203:tests/hello_world.c:6:0 +207:tests/other_file.cpp:314159:0 +229:return.cpp:50:0 +236:return.cpp:100:0 +275:even-opted.cpp:1:0 +282:even-opted.cpp:2:0 +289:even-opted.cpp:3:0 +321:fib.c:3:0 +349:fib.c:8:0 +357:fib.c:4:0 +364:fib.c:3:0 +410:fib.c:8:0 +556:/tmp/emscripten_test_binaryen2_28hnAe/src.c:59950:0 diff --git a/test/debugInfo.fromasm.txtmap b/test/debugInfo.fromasm.txtmap index bb25b4d8d30..5ab814a0d4a 100644 --- a/test/debugInfo.fromasm.txtmap +++ b/test/debugInfo.fromasm.txtmap @@ -1,2 +1,12 @@ -211:even-opted.cpp:2 -223:even-opted.cpp:3 +185:tests/other_file.cpp:314159:0 +199:return.cpp:50:0 +206:return.cpp:100:0 +241:even-opted.cpp:1:0 +248:even-opted.cpp:2:0 +255:even-opted.cpp:3:0 +276:fib.c:3:0 +300:fib.c:8:0 +306:fib.c:4:0 +313:fib.c:3:0 +339:fib.c:8:0 +436:/tmp/emscripten_test_binaryen2_28hnAe/src.c:59950:0 diff --git a/test/dot_s/debug.wast b/test/dot_s/debug.wast index aa4f34d9e71..0cc0ae20c9e 100644 --- a/test/dot_s/debug.wast +++ b/test/dot_s/debug.wast @@ -18,18 +18,21 @@ ) (block $label$0 (loop $label$1 + ;;@ fib.c:3:17 (set_local $2 (i32.add (get_local $2) (i32.const 1) ) ) + ;;@ fib.c:3:3 (br_if $label$0 (i32.ge_s (get_local $2) (get_local $0) ) ) + ;;@ fib.c:4:11 (set_local $1 (i32.add (get_local $4) @@ -45,6 +48,7 @@ (br $label$1) ) ) + ;;@ fib.c:6:3 (return (get_local $4) ) From 9fcdc20a7db25af63398f3f5fa15f03ab38f0ba8 Mon Sep 17 00:00:00 2001 From: Yury Delendik Date: Mon, 22 May 2017 18:40:22 -0500 Subject: [PATCH 3/4] Output JS source mapping file (instead of .txtmap). --- check.py | 18 +- src/parsing.h | 19 ++ src/tools/wasm-dis.cpp | 3 + src/wasm-binary.h | 11 +- src/wasm/wasm-binary.cpp | 206 +++++++++++++--- test/debugInfo.fromasm.clamp.map | 1 + test/debugInfo.fromasm.clamp.no-opts.map | 1 + test/debugInfo.fromasm.clamp.no-opts.txtmap | 14 -- test/debugInfo.fromasm.clamp.txtmap | 12 - test/debugInfo.fromasm.imprecise.map | 1 + test/debugInfo.fromasm.imprecise.no-opts.map | 1 + ...debugInfo.fromasm.imprecise.no-opts.txtmap | 14 -- test/debugInfo.fromasm.imprecise.txtmap | 12 - test/debugInfo.fromasm.map | 1 + test/debugInfo.fromasm.no-opts.map | 1 + test/debugInfo.fromasm.no-opts.txtmap | 14 -- test/debugInfo.fromasm.txtmap | 12 - test/fib-dbg.wasm | Bin 0 -> 913 bytes test/fib-dbg.wasm.fromBinary | 228 ++++++++++++++++++ test/fib-dbg.wasm.map | 1 + 20 files changed, 448 insertions(+), 122 deletions(-) create mode 100644 test/debugInfo.fromasm.clamp.map create mode 100644 test/debugInfo.fromasm.clamp.no-opts.map delete mode 100644 test/debugInfo.fromasm.clamp.no-opts.txtmap delete mode 100644 test/debugInfo.fromasm.clamp.txtmap create mode 100644 test/debugInfo.fromasm.imprecise.map create mode 100644 test/debugInfo.fromasm.imprecise.no-opts.map delete mode 100644 test/debugInfo.fromasm.imprecise.no-opts.txtmap delete mode 100644 test/debugInfo.fromasm.imprecise.txtmap create mode 100644 test/debugInfo.fromasm.map create mode 100644 test/debugInfo.fromasm.no-opts.map delete mode 100644 test/debugInfo.fromasm.no-opts.txtmap delete mode 100644 test/debugInfo.fromasm.txtmap create mode 100644 test/fib-dbg.wasm create mode 100644 test/fib-dbg.wasm.fromBinary create mode 100644 test/fib-dbg.wasm.map diff --git a/check.py b/check.py index e02795f3222..3a6b1d8491e 100755 --- a/check.py +++ b/check.py @@ -191,19 +191,19 @@ def do_asm2wasm_test(): # verify debug info if 'debugInfo' in asm: - txtmap = 'a.wasm.txtmap' - cmd += ['--binarymap-file', txtmap, - '--binarymap-url', txtmap + '.map', + jsmap = 'a.wasm.map' + cmd += ['--binarymap-file', jsmap, + '--binarymap-url', 'http://example.org/' + jsmap, '-o', 'a.wasm'] run_command(cmd) - if not os.path.isfile(txtmap): - fail_with_error('Debug info map not created: %s' % txtmap) - with open(wasm + '.txtmap', 'rb') as expected: - with open(txtmap, 'rb') as actual: + if not os.path.isfile(jsmap): + fail_with_error('Debug info map not created: %s' % jsmap) + with open(wasm + '.map', 'rb') as expected: + with open(jsmap, 'rb') as actual: fail_if_not_identical(actual.read(), expected.read()) with open('a.wasm', 'rb') as binary: url_section_name = bytearray([16]) + bytearray('sourceMappingURL') - payload = txtmap + '.map' + payload = 'http://example.org/' + jsmap assert len(payload) < 256, 'name too long' url_section_contents = bytearray([len(payload)]) + bytearray(payload) print url_section_name @@ -266,6 +266,8 @@ def do_asm2wasm_test(): print '..', t t = os.path.join(options.binaryen_test, t) cmd = WASM_DIS + [t] + if os.path.isfile(t + '.map'): cmd += ['-bm', t + '.map'] + actual = run_command(cmd) with open(t + '.fromBinary') as f: diff --git a/src/parsing.h b/src/parsing.h index d4df3c1c25b..be3c112f3f0 100644 --- a/src/parsing.h +++ b/src/parsing.h @@ -193,6 +193,25 @@ struct ParseException { } }; +struct MapParseException { + std::string text; + + MapParseException() : text("unknown parse error") {} + MapParseException(std::string text) : text(text) {} + + void dump(std::ostream& o) { + Colors::magenta(o); + o << "["; + Colors::red(o); + o << "map parse exception: "; + Colors::green(o); + o << text; + Colors::magenta(o); + o << "]"; + Colors::normal(o); + } +}; + // Helper for parsers that may not have unique label names. This transforms // the names into unique ones, as required by Binaryen IR. struct UniqueNameMapper { diff --git a/src/tools/wasm-dis.cpp b/src/tools/wasm-dis.cpp index c33b4d333e0..2e5aa4ea2ec 100644 --- a/src/tools/wasm-dis.cpp +++ b/src/tools/wasm-dis.cpp @@ -64,6 +64,9 @@ int main(int argc, const char *argv[]) { } catch (ParseException& p) { p.dump(std::cerr); Fatal() << "error in parsing wasm binary"; + } catch (MapParseException& p) { + p.dump(std::cerr); + Fatal() << "error in parsing wasm source mapping"; } if (options.debug) std::cerr << "Printing..." << std::endl; diff --git a/src/wasm-binary.h b/src/wasm-binary.h index b852814cccc..b6189c548a7 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -593,6 +593,10 @@ class WasmBinaryWriter : public Visitor { void writeSourceMapUrl(); void writeSymbolMap(); + void writeBinaryMapProlog(); + void writeBinaryMapEpilog(); + void writeDebugLocation(size_t offset, const Function::DebugLocation& loc); + // helpers void writeInlineString(const char* name); void writeInlineBuffer(const char* data, size_t size); @@ -616,6 +620,7 @@ class WasmBinaryWriter : public Visitor { void recurse(Expression*& curr); std::vector breakStack; Function::DebugLocation lastDebugLocation; + size_t lastBytecodeOffset; void visit(Expression* curr) { if (binaryMap && currFunction) { @@ -623,9 +628,7 @@ class WasmBinaryWriter : public Visitor { auto& debugLocations = currFunction->debugLocations; auto iter = debugLocations.find(curr); if (iter != debugLocations.end() && iter->second != lastDebugLocation) { - lastDebugLocation = iter->second; - auto fileName = wasm->debugInfoFileNames[iter->second.fileIndex]; - *binaryMap << o.size() << ":" << fileName << ":" << iter->second.lineNumber << ":" << iter->second.columnNumber << '\n'; + writeDebugLocation(o.size(), iter->second); } } Visitor::visit(curr); @@ -767,11 +770,11 @@ class WasmBinaryBuilder { // Debug information reading helpers void setDebugLocations(std::istream* binaryMap_) { binaryMap = binaryMap_; - readNextDebugLocation(); } Function::DebugLocation debugLocation; std::unordered_map debugInfoFileIndices; void readNextDebugLocation(); + void readBinaryMapHeader(); // AST reading int depth = 0; // only for debugging diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 46f64ebf01d..00d0ac4cfd8 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -33,6 +33,9 @@ void WasmBinaryWriter::prepare() { void WasmBinaryWriter::write() { writeHeader(); + if (binaryMap) { + writeBinaryMapProlog(); + } writeTypes(); writeImports(); @@ -49,6 +52,9 @@ void WasmBinaryWriter::write() { if (binaryMap) writeSourceMapUrl(); if (symbolMap.size() > 0) writeSymbolMap(); + if (binaryMap) { + writeBinaryMapEpilog(); + } finishUp(); } @@ -238,7 +244,6 @@ void WasmBinaryWriter::writeFunctions() { size_t start = o.size(); Function* function = wasm->functions[i].get(); currFunction = function; - lastDebugLocation = {0, 0, 0}; mappedLocals.clear(); numLocalsByType.clear(); if (debug) std::cerr << "writing" << function->name << std::endl; @@ -445,6 +450,50 @@ void WasmBinaryWriter::writeSymbolMap() { file.close(); } +void WasmBinaryWriter::writeBinaryMapProlog() { + lastDebugLocation = { 0, /* lineNumber = */ 1, 0 }; + lastBytecodeOffset = 0; + *binaryMap << "{\"version\":3,\"sources\":["; + for (size_t i = 0; i < wasm->debugInfoFileNames.size(); i++) { + if (i > 0) *binaryMap << ","; + // TODO respect JSON string encoding, e.g. quotes and control chars. + *binaryMap << "\"" << wasm->debugInfoFileNames[i] << "\""; + } + *binaryMap << "],\"names\":[],\"mappings\":\""; +} + +void WasmBinaryWriter::writeBinaryMapEpilog() { + *binaryMap << "\"}"; +} + +static void writeBase64VLQ(std::ostream& out, int32_t n) { + uint32_t value = n >= 0 ? n << 1 : ((-n) << 1) | 1; + while (1) { + uint32_t digit = value & 0x1F; + value >>= 5; + if (!value) { + // last VLQ digit -- base64 codes 'A'..'Z', 'a'..'f' + out << char(digit < 26 ? 'A' + digit : 'a' + digit - 26); + break; + } + // more VLG digit will follow -- add continuation bit (0x20), + // base64 codes 'g'..'z', '0'..'9', '+', '/' + out << char(digit < 20 ? 'g' + digit : digit < 30 ? '0' + digit - 20 : digit == 30 ? '+' : '/'); + } +} + +void WasmBinaryWriter::writeDebugLocation(size_t offset, const Function::DebugLocation& loc) { + if (lastBytecodeOffset > 0) { + *binaryMap << ","; + } + writeBase64VLQ(*binaryMap, int32_t(offset - lastBytecodeOffset)); + writeBase64VLQ(*binaryMap, int32_t(loc.fileIndex - lastDebugLocation.fileIndex)); + writeBase64VLQ(*binaryMap, int32_t(loc.lineNumber - lastDebugLocation.lineNumber)); + writeBase64VLQ(*binaryMap, int32_t(loc.columnNumber - lastDebugLocation.columnNumber)); + lastDebugLocation = loc; + lastBytecodeOffset = offset; +} + void WasmBinaryWriter::writeInlineString(const char* name) { int32_t size = strlen(name); o << U32LEB(size); @@ -949,6 +998,7 @@ static Name RETURN_BREAK("binaryen|break-to-return"); void WasmBinaryBuilder::read() { readHeader(); + readBinaryMapHeader(); // read sections until the end while (more()) { @@ -1402,43 +1452,135 @@ void WasmBinaryBuilder::readExports() { } } -void WasmBinaryBuilder::readNextDebugLocation() { - if (binaryMap) { - std::string line; - while (std::getline(*binaryMap, line)) { - auto pos = line.begin(); - while (pos < line.end() && pos[0] != ':') pos++; - if (pos == line.end()) continue; - uint32_t position = atoi(std::string(line.begin(), pos).c_str()); - auto filenameStart = ++pos; - while (pos < line.end() && pos[0] != ':') pos++; - if (pos == line.end()) continue; - std::string file(filenameStart, pos); - auto iter = debugInfoFileIndices.find(file); - if (iter == debugInfoFileIndices.end()) { - Index index = wasm.debugInfoFileNames.size(); - wasm.debugInfoFileNames.push_back(file); - debugInfoFileIndices[file] = index; +static int32_t readBase64VLQ(std::istream& in) { + uint32_t value = 0; + uint32_t shift = 0; + while (1) { + char ch = in.get(); + if (ch == EOF) + throw MapParseException("unexpected EOF in the middle of VLQ"); + if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch < 'g')) { + // last number digit + uint32_t digit = ch < 'a' ? ch - 'A' : ch - 'a' + 26; + value |= digit << shift; + break; + } + if (!(ch >= 'g' && ch <= 'z') && !(ch >= '0' && ch <= '9') && + ch != '+' && ch != '/') { + throw MapParseException("invalid VLQ digit"); + } + uint32_t digit = ch > '9' ? ch - 'g' : (ch >= '0' ? ch - '0' + 20 : (ch == '+' ? 30 : 31)); + value |= digit << shift; + shift += 5; + } + return value & 1 ? -int32_t(value >> 1) : int32_t(value >> 1); +} + +void WasmBinaryBuilder::readBinaryMapHeader() { + if (!binaryMap) return; + + auto maybeReadChar = [&](char expected) { + if (binaryMap->peek() != expected) return false; + binaryMap->get(); + return true; + }; + auto mustReadChar = [&](char expected) { + if (binaryMap->get() != expected) { + throw MapParseException("Unexpected char"); + } + }; + auto findField = [&](const char* name, size_t len) { + bool matching = false; + size_t pos; + while (1) { + int ch = binaryMap->get(); + if (ch == EOF) return false; + if (ch == '\"') { + matching = true; + pos = 0; + } else if (matching && name[pos] == ch) { + ++pos; + if (pos == len) { + if (maybeReadChar('\"')) break; // found field + } + } else { + matching = false; } - uint32_t fileIndex = debugInfoFileIndices[file]; - auto lineNumberStart = ++pos; - while (pos < line.end() && pos[0] != ':') pos++; - if (pos == line.end()) { - // old format - uint32_t lineNumber = atoi(std::string(lineNumberStart, line.end()).c_str()); - nextDebugLocation = { position, { fileIndex, lineNumber, 0 } }; - return; + } + mustReadChar(':'); + return true; + }; + auto readString = [&](std::string& str) { + std::vector vec; + mustReadChar('\"'); + if (!maybeReadChar('\"')) { + while (1) { + int ch = binaryMap->get(); + if (ch == EOF) { + throw MapParseException("unexpected EOF in the middle of string"); + } + if (ch == '\"') break; + vec.push_back(ch); } - uint32_t lineNumber = atoi(std::string(lineNumberStart, pos).c_str()); - auto columnNumberStart = ++pos; - uint32_t columnNumber = atoi(std::string(columnNumberStart, line.end()).c_str()); - - nextDebugLocation = { position, { fileIndex, lineNumber, columnNumber } }; - return; } + str = std::string(vec.begin(), vec.end()); + }; + + if (!findField("sources", strlen("sources"))) { + throw MapParseException("cannot find the sources field in map"); + } + mustReadChar('['); + if (!maybeReadChar(']')) { + do { + std::string file; + readString(file); + Index index = wasm.debugInfoFileNames.size(); + wasm.debugInfoFileNames.push_back(file); + debugInfoFileIndices[file] = index; + } while (maybeReadChar(',')); + mustReadChar(']'); + } + + if (!findField("mappings", strlen("mappings"))) { + throw MapParseException("cannot find the mappings field in map"); + } + mustReadChar('\"'); + if (maybeReadChar('\"')) { // empty mappings nextDebugLocation.first = 0; + return; } + // read first debug location + uint32_t position = readBase64VLQ(*binaryMap); + uint32_t fileIndex = readBase64VLQ(*binaryMap); + uint32_t lineNumber = readBase64VLQ(*binaryMap) + 1; // adjust zero-based line number + uint32_t columnNumber = readBase64VLQ(*binaryMap); + nextDebugLocation = { position, { fileIndex, lineNumber, columnNumber } }; } + +void WasmBinaryBuilder::readNextDebugLocation() { + if (!binaryMap) return; + + char ch; + *binaryMap >> ch; + if (ch == '\"') { // end of records + nextDebugLocation.first = 0; + return; + } + if (ch != ',') { + throw MapParseException("Unexpected delimiter"); + } + int32_t positionDelta = readBase64VLQ(*binaryMap); + uint32_t position = nextDebugLocation.first + positionDelta; + int32_t fileIndexDelta = readBase64VLQ(*binaryMap); + uint32_t fileIndex = nextDebugLocation.second.fileIndex + fileIndexDelta; + int32_t lineNumberDelta = readBase64VLQ(*binaryMap); + uint32_t lineNumber = nextDebugLocation.second.lineNumber + lineNumberDelta; + int32_t columnNumberDelta = readBase64VLQ(*binaryMap); + uint32_t columnNumber = nextDebugLocation.second.columnNumber + columnNumberDelta; + + nextDebugLocation = { position, { fileIndex, lineNumber, columnNumber } }; +} + Expression* WasmBinaryBuilder::readExpression() { assert(depth == 0); processExpressions(); diff --git a/test/debugInfo.fromasm.clamp.map b/test/debugInfo.fromasm.clamp.map new file mode 100644 index 00000000000..5680294cda5 --- /dev/null +++ b/test/debugInfo.fromasm.clamp.map @@ -0,0 +1 @@ +{"version":3,"sources":["tests/hello_world.c","tests/other_file.cpp","return.cpp","even-opted.cpp","fib.c","/tmp/emscripten_test_binaryen2_28hnAe/src.c"],"names":[],"mappings":"yLC8ylTA,cC7vlTA,OAkDA,mCCnGA,OACA,OACA,qBCAA,wBAKA,MAJA,OADA,0BAKA,iGCsi1DA"} \ No newline at end of file diff --git a/test/debugInfo.fromasm.clamp.no-opts.map b/test/debugInfo.fromasm.clamp.no-opts.map new file mode 100644 index 00000000000..82a92c68b2e --- /dev/null +++ b/test/debugInfo.fromasm.clamp.no-opts.map @@ -0,0 +1 @@ +{"version":3,"sources":["tests/hello_world.c","tests/other_file.cpp","return.cpp","even-opted.cpp","fib.c","/tmp/emscripten_test_binaryen2_28hnAe/src.c"],"names":[],"mappings":"uMAIA,IACA,ICyylTA,sBC7vlTA,OAkDA,uCCnGA,OACA,OACA,gCCAA,4BAKA,QAJA,OADA,8CAKA,kJCsi1DA"} \ No newline at end of file diff --git a/test/debugInfo.fromasm.clamp.no-opts.txtmap b/test/debugInfo.fromasm.clamp.no-opts.txtmap deleted file mode 100644 index f7b8c313154..00000000000 --- a/test/debugInfo.fromasm.clamp.no-opts.txtmap +++ /dev/null @@ -1,14 +0,0 @@ -199:tests/hello_world.c:5:0 -203:tests/hello_world.c:6:0 -207:tests/other_file.cpp:314159:0 -229:return.cpp:50:0 -236:return.cpp:100:0 -275:even-opted.cpp:1:0 -282:even-opted.cpp:2:0 -289:even-opted.cpp:3:0 -321:fib.c:3:0 -349:fib.c:8:0 -357:fib.c:4:0 -364:fib.c:3:0 -410:fib.c:8:0 -556:/tmp/emscripten_test_binaryen2_28hnAe/src.c:59950:0 diff --git a/test/debugInfo.fromasm.clamp.txtmap b/test/debugInfo.fromasm.clamp.txtmap deleted file mode 100644 index 5ab814a0d4a..00000000000 --- a/test/debugInfo.fromasm.clamp.txtmap +++ /dev/null @@ -1,12 +0,0 @@ -185:tests/other_file.cpp:314159:0 -199:return.cpp:50:0 -206:return.cpp:100:0 -241:even-opted.cpp:1:0 -248:even-opted.cpp:2:0 -255:even-opted.cpp:3:0 -276:fib.c:3:0 -300:fib.c:8:0 -306:fib.c:4:0 -313:fib.c:3:0 -339:fib.c:8:0 -436:/tmp/emscripten_test_binaryen2_28hnAe/src.c:59950:0 diff --git a/test/debugInfo.fromasm.imprecise.map b/test/debugInfo.fromasm.imprecise.map new file mode 100644 index 00000000000..60f44708269 --- /dev/null +++ b/test/debugInfo.fromasm.imprecise.map @@ -0,0 +1 @@ +{"version":3,"sources":["tests/hello_world.c","tests/other_file.cpp","return.cpp","even-opted.cpp","fib.c","/tmp/emscripten_test_binaryen2_28hnAe/src.c"],"names":[],"mappings":"wLC8ylTA,cC7vlTA,OAkDA,eCnGA,OACA,OACA,oBCAA,wBAKA,MAJA,OADA,0BAKA,iGCsi1DA"} \ No newline at end of file diff --git a/test/debugInfo.fromasm.imprecise.no-opts.map b/test/debugInfo.fromasm.imprecise.no-opts.map new file mode 100644 index 00000000000..2fcda4e2331 --- /dev/null +++ b/test/debugInfo.fromasm.imprecise.no-opts.map @@ -0,0 +1 @@ +{"version":3,"sources":["tests/hello_world.c","tests/other_file.cpp","return.cpp","even-opted.cpp","fib.c","/tmp/emscripten_test_binaryen2_28hnAe/src.c"],"names":[],"mappings":"sMAIA,IACA,ICyylTA,sBC7vlTA,OAkDA,kBCnGA,OACA,OACA,+BCAA,4BAKA,QAJA,OADA,8CAKA,kJCsi1DA"} \ No newline at end of file diff --git a/test/debugInfo.fromasm.imprecise.no-opts.txtmap b/test/debugInfo.fromasm.imprecise.no-opts.txtmap deleted file mode 100644 index 25ae6055e50..00000000000 --- a/test/debugInfo.fromasm.imprecise.no-opts.txtmap +++ /dev/null @@ -1,14 +0,0 @@ -198:tests/hello_world.c:5:0 -202:tests/hello_world.c:6:0 -206:tests/other_file.cpp:314159:0 -228:return.cpp:50:0 -235:return.cpp:100:0 -253:even-opted.cpp:1:0 -260:even-opted.cpp:2:0 -267:even-opted.cpp:3:0 -298:fib.c:3:0 -326:fib.c:8:0 -334:fib.c:4:0 -341:fib.c:3:0 -387:fib.c:8:0 -533:/tmp/emscripten_test_binaryen2_28hnAe/src.c:59950:0 diff --git a/test/debugInfo.fromasm.imprecise.txtmap b/test/debugInfo.fromasm.imprecise.txtmap deleted file mode 100644 index 89e0f75c13a..00000000000 --- a/test/debugInfo.fromasm.imprecise.txtmap +++ /dev/null @@ -1,12 +0,0 @@ -184:tests/other_file.cpp:314159:0 -198:return.cpp:50:0 -205:return.cpp:100:0 -220:even-opted.cpp:1:0 -227:even-opted.cpp:2:0 -234:even-opted.cpp:3:0 -254:fib.c:3:0 -278:fib.c:8:0 -284:fib.c:4:0 -291:fib.c:3:0 -317:fib.c:8:0 -414:/tmp/emscripten_test_binaryen2_28hnAe/src.c:59950:0 diff --git a/test/debugInfo.fromasm.map b/test/debugInfo.fromasm.map new file mode 100644 index 00000000000..5680294cda5 --- /dev/null +++ b/test/debugInfo.fromasm.map @@ -0,0 +1 @@ +{"version":3,"sources":["tests/hello_world.c","tests/other_file.cpp","return.cpp","even-opted.cpp","fib.c","/tmp/emscripten_test_binaryen2_28hnAe/src.c"],"names":[],"mappings":"yLC8ylTA,cC7vlTA,OAkDA,mCCnGA,OACA,OACA,qBCAA,wBAKA,MAJA,OADA,0BAKA,iGCsi1DA"} \ No newline at end of file diff --git a/test/debugInfo.fromasm.no-opts.map b/test/debugInfo.fromasm.no-opts.map new file mode 100644 index 00000000000..82a92c68b2e --- /dev/null +++ b/test/debugInfo.fromasm.no-opts.map @@ -0,0 +1 @@ +{"version":3,"sources":["tests/hello_world.c","tests/other_file.cpp","return.cpp","even-opted.cpp","fib.c","/tmp/emscripten_test_binaryen2_28hnAe/src.c"],"names":[],"mappings":"uMAIA,IACA,ICyylTA,sBC7vlTA,OAkDA,uCCnGA,OACA,OACA,gCCAA,4BAKA,QAJA,OADA,8CAKA,kJCsi1DA"} \ No newline at end of file diff --git a/test/debugInfo.fromasm.no-opts.txtmap b/test/debugInfo.fromasm.no-opts.txtmap deleted file mode 100644 index f7b8c313154..00000000000 --- a/test/debugInfo.fromasm.no-opts.txtmap +++ /dev/null @@ -1,14 +0,0 @@ -199:tests/hello_world.c:5:0 -203:tests/hello_world.c:6:0 -207:tests/other_file.cpp:314159:0 -229:return.cpp:50:0 -236:return.cpp:100:0 -275:even-opted.cpp:1:0 -282:even-opted.cpp:2:0 -289:even-opted.cpp:3:0 -321:fib.c:3:0 -349:fib.c:8:0 -357:fib.c:4:0 -364:fib.c:3:0 -410:fib.c:8:0 -556:/tmp/emscripten_test_binaryen2_28hnAe/src.c:59950:0 diff --git a/test/debugInfo.fromasm.txtmap b/test/debugInfo.fromasm.txtmap deleted file mode 100644 index 5ab814a0d4a..00000000000 --- a/test/debugInfo.fromasm.txtmap +++ /dev/null @@ -1,12 +0,0 @@ -185:tests/other_file.cpp:314159:0 -199:return.cpp:50:0 -206:return.cpp:100:0 -241:even-opted.cpp:1:0 -248:even-opted.cpp:2:0 -255:even-opted.cpp:3:0 -276:fib.c:3:0 -300:fib.c:8:0 -306:fib.c:4:0 -313:fib.c:3:0 -339:fib.c:8:0 -436:/tmp/emscripten_test_binaryen2_28hnAe/src.c:59950:0 diff --git a/test/fib-dbg.wasm b/test/fib-dbg.wasm new file mode 100644 index 0000000000000000000000000000000000000000..8e56773f2c28bd12f16ef059a499611f4811a824 GIT binary patch literal 913 zcmZuwJ#Q015S`ikv^P1Uq@moyt`nCil{c{>1+Zh;MuN&dTkF`tzB_#9M3F3=DL^z( zP|))q_$5?SP|)!!n7!B`37mBA-g`6qF*7;{k{JR3eqOCsVD(v+K_4^B`amy|0ASzV zP;A>!;e}U!*_4fwqwZ;|*Xp*7479tQs##o*7K5p3rSr9E$-~ojcP-pbR~|j3Rcl?y zb*~{`tPOTH*szgi_SrNV1XH6KG>zp(xhLUh5>C=9vh=z3OwFSC6~h%P3BKzG2H zD$=re9X|{bwK+FW*ck71%?>hy>No9=y2^(cmVpzX2ByK#U}~^5*aT9i+<_&Q9Zulh z59E1EtW%Hk=YIU3&~3x{ncvV&RN5WS)g_pGz6e`Ul6F*@fW4~{UC<;MchX?^suKr8 z1xA5P62XN6+{$^I@}ju{X7)yt0a(S{mD6c71SnA5D}dq;UBB~13&f1o%ASv9Pr!M_ zD|@nZF5}k~Phbf+@VEJrvRC5oaweeS@ees=H7x{IN}PYXPTP@iY-)Rl>|M@(YnvLJ zpTPx4c;ef_Hfs`ml;~UZbuA*MFiYI1(Uz9LbKelIq)$N<1RVR0aA;G*H@DCdmd}V< zqTsuN`N+4Bb6N~Hm!*=B!!LcZ83r?j-!*mMRx9vtl`}@@QU^=ET9z3pAtNS#Qy-WHL_hv!-2c3r8`v3p{ literal 0 HcmV?d00001 diff --git a/test/fib-dbg.wasm.fromBinary b/test/fib-dbg.wasm.fromBinary new file mode 100644 index 00000000000..3f940fa8ad6 --- /dev/null +++ b/test/fib-dbg.wasm.fromBinary @@ -0,0 +1,228 @@ +(module + (type $0 (func (param i32 i32))) + (type $1 (func (param i32) (result i32))) + (type $2 (func (result i32))) + (type $3 (func (param i32))) + (type $4 (func)) + (import "env" "DYNAMICTOP_PTR" (global $import$0 i32)) + (import "env" "tempDoublePtr" (global $import$1 i32)) + (import "env" "ABORT" (global $import$2 i32)) + (import "env" "STACKTOP" (global $import$3 i32)) + (import "env" "STACK_MAX" (global $import$4 i32)) + (import "env" "gb" (global $import$5 i32)) + (import "env" "fb" (global $import$6 i32)) + (import "global" "NaN" (global $import$7 f64)) + (import "global" "Infinity" (global $import$8 f64)) + (import "env" "memory" (memory $0 256 256)) + (import "env" "table" (table 0 0 anyfunc)) + (import "env" "memoryBase" (global $import$11 i32)) + (import "env" "tableBase" (global $import$12 i32)) + (global $global$0 (mut i32) (get_global $import$0)) + (global $global$1 (mut i32) (get_global $import$1)) + (global $global$2 (mut i32) (get_global $import$2)) + (global $global$3 (mut i32) (get_global $import$3)) + (global $global$4 (mut i32) (get_global $import$4)) + (global $global$5 (mut i32) (get_global $import$5)) + (global $global$6 (mut i32) (get_global $import$6)) + (global $global$7 (mut i32) (i32.const 0)) + (global $global$8 (mut i32) (i32.const 0)) + (global $global$9 (mut i32) (i32.const 0)) + (global $global$10 (mut i32) (i32.const 0)) + (global $global$11 (mut f64) (get_global $import$7)) + (global $global$12 (mut f64) (get_global $import$8)) + (global $global$13 (mut i32) (i32.const 0)) + (global $global$14 (mut i32) (i32.const 0)) + (global $global$15 (mut i32) (i32.const 0)) + (global $global$16 (mut i32) (i32.const 0)) + (global $global$17 (mut f64) (f64.const 0)) + (global $global$18 (mut i32) (i32.const 0)) + (global $global$19 (mut i32) (i32.const 0)) + (global $global$20 (mut i32) (i32.const 0)) + (global $global$21 (mut f64) (f64.const 0)) + (global $global$22 (mut i32) (i32.const 0)) + (global $global$23 (mut f64) (f64.const 0)) + (export "setThrew" (func $setThrew)) + (export "runPostSets" (func $runPostSets)) + (export "establishStackSpace" (func $establishStackSpace)) + (export "stackSave" (func $stackSave)) + (export "stackRestore" (func $stackRestore)) + (export "_fib" (func $_fib)) + (export "stackAlloc" (func $stackAlloc)) + (func $stackAlloc (type $1) (param $var$0 i32) (result i32) + (local $var$1 i32) + (block $label$0 + (set_local $var$1 + (get_global $global$3) + ) + (set_global $global$3 + (i32.add + (get_global $global$3) + (get_local $var$0) + ) + ) + (set_global $global$3 + (i32.and + (i32.add + (get_global $global$3) + (i32.const 15) + ) + (i32.const -16) + ) + ) + (return + (get_local $var$1) + ) + (unreachable) + ) + (unreachable) + ) + (func $stackSave (type $2) (result i32) + (return + (get_global $global$3) + ) + ) + (func $stackRestore (type $3) (param $var$0 i32) + (set_global $global$3 + (get_local $var$0) + ) + ) + (func $establishStackSpace (type $0) (param $var$0 i32) (param $var$1 i32) + (block $label$0 + (set_global $global$3 + (get_local $var$0) + ) + (set_global $global$4 + (get_local $var$1) + ) + ) + ) + (func $setThrew (type $0) (param $var$0 i32) (param $var$1 i32) + (if + (i32.eq + (get_global $global$7) + (i32.const 0) + ) + (block $label$0 + (set_global $global$7 + (get_local $var$0) + ) + (set_global $global$8 + (get_local $var$1) + ) + ) + ) + ) + (func $_fib (type $1) (param $var$0 i32) (result i32) + (local $var$1 i32) + (local $var$2 i32) + (local $var$3 i32) + (local $var$4 i32) + (local $var$5 i32) + (local $var$6 i32) + (local $var$7 i32) + (local $var$8 i32) + (local $var$9 i32) + (local $var$10 i32) + (local $var$11 i32) + ;;@ fib.c:8:0 + (block $label$0 + (set_local $var$11 + (get_global $global$3) + ) + ;;@ fib.c:3:0 + (set_local $var$6 + (i32.gt_s + (get_local $var$0) + (i32.const 0) + ) + ) + ;;@ fib.c:8:0 + (if + ;;@ fib.c:3:0 + (get_local $var$6) + (block $label$1 + (set_local $var$1 + (i32.const 0) + ) + (set_local $var$5 + (i32.const 1) + ) + (set_local $var$8 + (i32.const 0) + ) + ) + (block $label$2 + (set_local $var$4 + (i32.const 1) + ) + ;;@ fib.c:8:0 + (return + (get_local $var$4) + ) + ) + ) + ;;@ fib.c:3:0 + (loop $label$3 + (block $label$4 + ;;@ fib.c:4:0 + (set_local $var$3 + (i32.add + (get_local $var$5) + (get_local $var$1) + ) + ) + ;;@ fib.c:3:0 + (set_local $var$9 + (i32.add + (get_local $var$8) + (i32.const 1) + ) + ) + (set_local $var$7 + (i32.eq + (get_local $var$9) + (get_local $var$0) + ) + ) + (if + (get_local $var$7) + (block $label$5 + (set_local $var$4 + (get_local $var$3) + ) + (br $label$4) + ) + (block $label$6 + (set_local $var$2 + (get_local $var$5) + ) + (set_local $var$5 + (get_local $var$3) + ) + (set_local $var$8 + (get_local $var$9) + ) + (set_local $var$1 + (get_local $var$2) + ) + ) + ) + (br $label$3) + ) + ) + ;;@ fib.c:8:0 + (return + (get_local $var$4) + ) + (unreachable) + (unreachable) + ) + (unreachable) + ) + (func $runPostSets (type $4) + (local $var$0 i32) + (nop) + ) + ;; custom section "sourceMappingURL", size 35 +) + diff --git a/test/fib-dbg.wasm.map b/test/fib-dbg.wasm.map new file mode 100644 index 00000000000..831f55fbd27 --- /dev/null +++ b/test/fib-dbg.wasm.map @@ -0,0 +1 @@ +{"version":3,"sources":["fib.c"],"names":[],"mappings":"moBAEA,4BAKA,QAJA,OADA,OAAA,uCAKA"} From f7bd8c9b458748808ce7e027d48ea55bd94cdbc9 Mon Sep 17 00:00:00 2001 From: Yury Delendik Date: Tue, 23 May 2017 16:33:37 -0500 Subject: [PATCH 4/4] Renames paramenters from binarymap to sourcemap. --- auto_update_tests.py | 2 +- check.py | 6 ++-- src/tools/asm2wasm.cpp | 18 +++++----- src/tools/wasm-as.cpp | 26 +++++++------- src/tools/wasm-dis.cpp | 20 +++++------ src/wasm-binary.h | 28 +++++++-------- src/wasm-io.h | 8 ++--- src/wasm/wasm-binary.cpp | 73 ++++++++++++++++++++-------------------- src/wasm/wasm-io.cpp | 14 ++++---- 9 files changed, 98 insertions(+), 97 deletions(-) diff --git a/auto_update_tests.py b/auto_update_tests.py index 27400eb654e..c6d376fa043 100755 --- a/auto_update_tests.py +++ b/auto_update_tests.py @@ -42,7 +42,7 @@ actual = run_command(cmd) with open(os.path.join('test', wasm), 'w') as o: o.write(actual) if 'debugInfo' in asm: - cmd += ['--binarymap-file', os.path.join('test', wasm + '.map'), '-o', 'a.wasm'] + cmd += ['--source-map', os.path.join('test', wasm + '.map'), '-o', 'a.wasm'] run_command(cmd) diff --git a/check.py b/check.py index 3a6b1d8491e..9d5b64f1824 100755 --- a/check.py +++ b/check.py @@ -192,8 +192,8 @@ def do_asm2wasm_test(): # verify debug info if 'debugInfo' in asm: jsmap = 'a.wasm.map' - cmd += ['--binarymap-file', jsmap, - '--binarymap-url', 'http://example.org/' + jsmap, + cmd += ['--source-map', jsmap, + '--source-map-url', 'http://example.org/' + jsmap, '-o', 'a.wasm'] run_command(cmd) if not os.path.isfile(jsmap): @@ -266,7 +266,7 @@ def do_asm2wasm_test(): print '..', t t = os.path.join(options.binaryen_test, t) cmd = WASM_DIS + [t] - if os.path.isfile(t + '.map'): cmd += ['-bm', t + '.map'] + if os.path.isfile(t + '.map'): cmd += ['--source-map', t + '.map'] actual = run_command(cmd) diff --git a/src/tools/asm2wasm.cpp b/src/tools/asm2wasm.cpp index 77d67f07e64..3fa97d9815b 100644 --- a/src/tools/asm2wasm.cpp +++ b/src/tools/asm2wasm.cpp @@ -36,8 +36,8 @@ int main(int argc, const char *argv[]) { bool legalizeJavaScriptFFI = true; Asm2WasmBuilder::TrapMode trapMode = Asm2WasmBuilder::TrapMode::JS; bool wasmOnly = false; - std::string binaryMapFile; - std::string binaryMapUrl; + std::string sourceMapFilename; + std::string sourceMapUrl; std::string symbolMap; bool emitBinary = true; @@ -104,12 +104,12 @@ int main(int argc, const char *argv[]) { .add("--debuginfo", "-g", "Emit names section in wasm binary (or full debuginfo in wast)", Options::Arguments::Zero, [&](Options *o, const std::string &arguments) { options.passOptions.debugInfo = true; }) - .add("--binarymap-file", "-bm", "Emit binary map (if using binary output) to the specified file", + .add("--source-map", "-sm", "Emit source map (if using binary output) to the specified file", Options::Arguments::One, - [&binaryMapFile](Options *o, const std::string &argument) { binaryMapFile = argument; }) - .add("--binarymap-url", "-bu", "Use specified string as binary map URL", + [&sourceMapFilename](Options *o, const std::string &argument) { sourceMapFilename = argument; }) + .add("--source-map-url", "-su", "Use specified string as source map URL", Options::Arguments::One, - [&binaryMapUrl](Options *o, const std::string &argument) { binaryMapUrl = argument; }) + [&sourceMapUrl](Options *o, const std::string &argument) { sourceMapUrl = argument; }) .add("--symbolmap", "-s", "Emit a symbol map (indexes => names)", Options::Arguments::One, [&](Options *o, const std::string &argument) { symbolMap = argument; }) @@ -146,7 +146,7 @@ int main(int argc, const char *argv[]) { Asm2WasmPreProcessor pre; // wasm binaries can contain a names section, but not full debug info -- // debug info is disabled if a map file is not specified with wasm binary - pre.debugInfo = options.passOptions.debugInfo && (!emitBinary || binaryMapFile.size()); + pre.debugInfo = options.passOptions.debugInfo && (!emitBinary || sourceMapFilename.size()); auto input( read_file>(options.extra["infile"], Flags::Text, options.debug ? Flags::Debug : Flags::Release)); char *start = pre.process(input.data()); @@ -214,8 +214,8 @@ int main(int argc, const char *argv[]) { writer.setSymbolMap(symbolMap); writer.setBinary(emitBinary); if (emitBinary) { - writer.setBinaryMapFilename(binaryMapFile); - writer.setBinaryMapUrl(binaryMapUrl); + writer.setSourceMapFilename(sourceMapFilename); + writer.setSourceMapUrl(sourceMapUrl); } writer.write(wasm, options.extra["output"]); diff --git a/src/tools/wasm-as.cpp b/src/tools/wasm-as.cpp index c289accfd18..e8003a5ca16 100644 --- a/src/tools/wasm-as.cpp +++ b/src/tools/wasm-as.cpp @@ -30,8 +30,8 @@ using namespace wasm; int main(int argc, const char *argv[]) { bool debugInfo = false; std::string symbolMap; - std::string binaryMapFilename; - std::string binaryMapUrl; + std::string sourceMapFilename; + std::string sourceMapUrl; Options options("wasm-as", "Assemble a .wast (WebAssembly text format) into a .wasm (WebAssembly binary format)"); options.extra["validate"] = "wasm"; options @@ -53,12 +53,12 @@ int main(int argc, const char *argv[]) { .add("--debuginfo", "-g", "Emit names section and debug info", Options::Arguments::Zero, [&](Options *o, const std::string &arguments) { debugInfo = true; }) - .add("--binarymap-file", "-bm", "Emit binary map to the specified file", + .add("--source-map", "-sm", "Emit source map to the specified file", Options::Arguments::One, - [&binaryMapFilename](Options *o, const std::string &argument) { binaryMapFilename = argument; }) - .add("--binarymap-url", "-bu", "Use specified string as binary map URL", + [&sourceMapFilename](Options *o, const std::string &argument) { sourceMapFilename = argument; }) + .add("--source-map-url", "-su", "Use specified string as source map URL", Options::Arguments::One, - [&binaryMapUrl](Options *o, const std::string &argument) { binaryMapUrl = argument; }) + [&sourceMapUrl](Options *o, const std::string &argument) { sourceMapUrl = argument; }) .add("--symbolmap", "-s", "Emit a symbol map (indexes => names)", Options::Arguments::One, [&](Options *o, const std::string &argument) { symbolMap = argument; }) @@ -96,11 +96,11 @@ int main(int argc, const char *argv[]) { WasmBinaryWriter writer(&wasm, buffer, options.debug); // if debug info is used, then we want to emit the names section writer.setNamesSection(debugInfo); - std::unique_ptr binaryMapStream = nullptr; - if (binaryMapFilename.size()) { - binaryMapStream = make_unique(); - binaryMapStream->open(binaryMapFilename); - writer.setBinaryMap(binaryMapStream.get(), binaryMapUrl); + std::unique_ptr sourceMapStream = nullptr; + if (sourceMapFilename.size()) { + sourceMapStream = make_unique(); + sourceMapStream->open(sourceMapFilename); + writer.setSourceMap(sourceMapStream.get(), sourceMapUrl); } if (symbolMap.size() > 0) writer.setSymbolMap(symbolMap); writer.write(); @@ -108,8 +108,8 @@ int main(int argc, const char *argv[]) { if (options.debug) std::cerr << "writing to output..." << std::endl; Output output(options.extra["output"], Flags::Binary, options.debug ? Flags::Debug : Flags::Release); buffer.writeTo(output); - if (binaryMapStream) { - binaryMapStream->close(); + if (sourceMapStream) { + sourceMapStream->close(); } if (options.debug) std::cerr << "Done." << std::endl; diff --git a/src/tools/wasm-dis.cpp b/src/tools/wasm-dis.cpp index 2e5aa4ea2ec..e6fd7baddf9 100644 --- a/src/tools/wasm-dis.cpp +++ b/src/tools/wasm-dis.cpp @@ -28,7 +28,7 @@ using namespace cashew; using namespace wasm; int main(int argc, const char *argv[]) { - std::string binaryMapFilename; + std::string sourceMapFilename; Options options("wasm-dis", "Un-assemble a .wasm (WebAssembly binary format) into a .wast (WebAssembly text format)"); options.add("--output", "-o", "Output file (stdout if not specified)", Options::Arguments::One, @@ -36,9 +36,9 @@ int main(int argc, const char *argv[]) { o->extra["output"] = argument; Colors::disable(); }) - .add("--binarymap-file", "-bm", "Consume binary map from the specified file to add location information", + .add("--source-map", "-sm", "Consume source map from the specified file to add location information", Options::Arguments::One, - [&binaryMapFilename](Options *o, const std::string &argument) { binaryMapFilename = argument; }) + [&sourceMapFilename](Options *o, const std::string &argument) { sourceMapFilename = argument; }) .add_positional("INFILE", Options::Arguments::One, [](Options *o, const std::string &argument) { o->extra["infile"] = argument; @@ -50,16 +50,16 @@ int main(int argc, const char *argv[]) { if (options.debug) std::cerr << "parsing binary..." << std::endl; Module wasm; try { - std::unique_ptr binaryMapStream; + std::unique_ptr sourceMapStream; WasmBinaryBuilder parser(wasm, input, options.debug); - if (binaryMapFilename.size()) { - binaryMapStream = make_unique(); - binaryMapStream->open(binaryMapFilename); - parser.setDebugLocations(binaryMapStream.get()); + if (sourceMapFilename.size()) { + sourceMapStream = make_unique(); + sourceMapStream->open(sourceMapFilename); + parser.setDebugLocations(sourceMapStream.get()); } parser.read(); - if (binaryMapStream) { - binaryMapStream->close(); + if (sourceMapStream) { + sourceMapStream->close(); } } catch (ParseException& p) { p.dump(std::cerr); diff --git a/src/wasm-binary.h b/src/wasm-binary.h index b6189c548a7..889a3aa6c90 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -538,8 +538,8 @@ class WasmBinaryWriter : public Visitor { Function* currFunction = nullptr; bool debug; bool debugInfo = true; - std::ostream* binaryMap = nullptr; - std::string binaryMapUrl; + std::ostream* sourceMap = nullptr; + std::string sourceMapUrl; std::string symbolMap; MixedArena allocator; @@ -551,9 +551,9 @@ class WasmBinaryWriter : public Visitor { } void setNamesSection(bool set) { debugInfo = set; } - void setBinaryMap(std::ostream* set, std::string url) { - binaryMap = set; - binaryMapUrl = url; + void setSourceMap(std::ostream* set, std::string url) { + sourceMap = set; + sourceMapUrl = url; } void setSymbolMap(std::string set) { symbolMap = set; } @@ -593,8 +593,8 @@ class WasmBinaryWriter : public Visitor { void writeSourceMapUrl(); void writeSymbolMap(); - void writeBinaryMapProlog(); - void writeBinaryMapEpilog(); + void writeSourceMapProlog(); + void writeSourceMapEpilog(); void writeDebugLocation(size_t offset, const Function::DebugLocation& loc); // helpers @@ -623,8 +623,8 @@ class WasmBinaryWriter : public Visitor { size_t lastBytecodeOffset; void visit(Expression* curr) { - if (binaryMap && currFunction) { - // Dump the binaryMap debug info + if (sourceMap && currFunction) { + // Dump the sourceMap debug info auto& debugLocations = currFunction->debugLocations; auto iter = debugLocations.find(curr); if (iter != debugLocations.end() && iter->second != lastDebugLocation) { @@ -668,7 +668,7 @@ class WasmBinaryBuilder { MixedArena& allocator; std::vector& input; bool debug; - std::istream* binaryMap; + std::istream* sourceMap; std::pair nextDebugLocation; size_t pos = 0; @@ -678,7 +678,7 @@ class WasmBinaryBuilder { std::set seenSections; public: - WasmBinaryBuilder(Module& wasm, std::vector& input, bool debug) : wasm(wasm), allocator(wasm.allocator), input(input), debug(debug), binaryMap(nullptr), nextDebugLocation(0, { 0, 0, 0 }), useDebugLocation(false) {} + WasmBinaryBuilder(Module& wasm, std::vector& input, bool debug) : wasm(wasm), allocator(wasm.allocator), input(input), debug(debug), sourceMap(nullptr), nextDebugLocation(0, { 0, 0, 0 }), useDebugLocation(false) {} void read(); void readUserSection(size_t payloadLen); @@ -768,13 +768,13 @@ class WasmBinaryBuilder { void readNames(size_t); // Debug information reading helpers - void setDebugLocations(std::istream* binaryMap_) { - binaryMap = binaryMap_; + void setDebugLocations(std::istream* sourceMap_) { + sourceMap = sourceMap_; } Function::DebugLocation debugLocation; std::unordered_map debugInfoFileIndices; void readNextDebugLocation(); - void readBinaryMapHeader(); + void readSourceMapHeader(); // AST reading int depth = 0; // only for debugging diff --git a/src/wasm-io.h b/src/wasm-io.h index 3a9d1c9959c..afdc4503ce2 100644 --- a/src/wasm-io.h +++ b/src/wasm-io.h @@ -48,15 +48,15 @@ class ModuleWriter : public ModuleIO { bool binary = true; bool debugInfo = false; std::string symbolMap; - std::string binaryMapFilename; - std::string binaryMapUrl; + std::string sourceMapFilename; + std::string sourceMapUrl; public: void setBinary(bool binary_) { binary = binary_; } void setDebugInfo(bool debugInfo_) { debugInfo = debugInfo_; } void setSymbolMap(std::string symbolMap_) { symbolMap = symbolMap_; } - void setBinaryMapFilename(std::string binaryMapFilename_) { binaryMapFilename = binaryMapFilename_; } - void setBinaryMapUrl(std::string binaryMapUrl_) { binaryMapUrl = binaryMapUrl_; } + void setSourceMapFilename(std::string sourceMapFilename_) { sourceMapFilename = sourceMapFilename_; } + void setSourceMapUrl(std::string sourceMapUrl_) { sourceMapUrl = sourceMapUrl_; } // write text void writeText(Module& wasm, std::string filename); diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 00d0ac4cfd8..c4bc66f7681 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -33,8 +33,8 @@ void WasmBinaryWriter::prepare() { void WasmBinaryWriter::write() { writeHeader(); - if (binaryMap) { - writeBinaryMapProlog(); + if (sourceMap) { + writeSourceMapProlog(); } writeTypes(); @@ -49,11 +49,11 @@ void WasmBinaryWriter::write() { writeFunctions(); writeDataSegments(); if (debugInfo) writeNames(); - if (binaryMap) writeSourceMapUrl(); + if (sourceMap) writeSourceMapUrl(); if (symbolMap.size() > 0) writeSymbolMap(); - if (binaryMap) { - writeBinaryMapEpilog(); + if (sourceMap) { + writeSourceMapEpilog(); } finishUp(); } @@ -433,7 +433,7 @@ void WasmBinaryWriter::writeSourceMapUrl() { if (debug) std::cerr << "== writeSourceMapUrl" << std::endl; auto start = startSection(BinaryConsts::Section::User); writeInlineString(BinaryConsts::UserSections::SourceMapUrl); - writeInlineString(binaryMapUrl.c_str()); + writeInlineString(sourceMapUrl.c_str()); finishSection(start); } @@ -450,20 +450,20 @@ void WasmBinaryWriter::writeSymbolMap() { file.close(); } -void WasmBinaryWriter::writeBinaryMapProlog() { +void WasmBinaryWriter::writeSourceMapProlog() { lastDebugLocation = { 0, /* lineNumber = */ 1, 0 }; lastBytecodeOffset = 0; - *binaryMap << "{\"version\":3,\"sources\":["; + *sourceMap << "{\"version\":3,\"sources\":["; for (size_t i = 0; i < wasm->debugInfoFileNames.size(); i++) { - if (i > 0) *binaryMap << ","; + if (i > 0) *sourceMap << ","; // TODO respect JSON string encoding, e.g. quotes and control chars. - *binaryMap << "\"" << wasm->debugInfoFileNames[i] << "\""; + *sourceMap << "\"" << wasm->debugInfoFileNames[i] << "\""; } - *binaryMap << "],\"names\":[],\"mappings\":\""; + *sourceMap << "],\"names\":[],\"mappings\":\""; } -void WasmBinaryWriter::writeBinaryMapEpilog() { - *binaryMap << "\"}"; +void WasmBinaryWriter::writeSourceMapEpilog() { + *sourceMap << "\"}"; } static void writeBase64VLQ(std::ostream& out, int32_t n) { @@ -484,12 +484,12 @@ static void writeBase64VLQ(std::ostream& out, int32_t n) { void WasmBinaryWriter::writeDebugLocation(size_t offset, const Function::DebugLocation& loc) { if (lastBytecodeOffset > 0) { - *binaryMap << ","; + *sourceMap << ","; } - writeBase64VLQ(*binaryMap, int32_t(offset - lastBytecodeOffset)); - writeBase64VLQ(*binaryMap, int32_t(loc.fileIndex - lastDebugLocation.fileIndex)); - writeBase64VLQ(*binaryMap, int32_t(loc.lineNumber - lastDebugLocation.lineNumber)); - writeBase64VLQ(*binaryMap, int32_t(loc.columnNumber - lastDebugLocation.columnNumber)); + writeBase64VLQ(*sourceMap, int32_t(offset - lastBytecodeOffset)); + writeBase64VLQ(*sourceMap, int32_t(loc.fileIndex - lastDebugLocation.fileIndex)); + writeBase64VLQ(*sourceMap, int32_t(loc.lineNumber - lastDebugLocation.lineNumber)); + writeBase64VLQ(*sourceMap, int32_t(loc.columnNumber - lastDebugLocation.columnNumber)); lastDebugLocation = loc; lastBytecodeOffset = offset; } @@ -998,7 +998,7 @@ static Name RETURN_BREAK("binaryen|break-to-return"); void WasmBinaryBuilder::read() { readHeader(); - readBinaryMapHeader(); + readSourceMapHeader(); // read sections until the end while (more()) { @@ -1476,16 +1476,16 @@ static int32_t readBase64VLQ(std::istream& in) { return value & 1 ? -int32_t(value >> 1) : int32_t(value >> 1); } -void WasmBinaryBuilder::readBinaryMapHeader() { - if (!binaryMap) return; +void WasmBinaryBuilder::readSourceMapHeader() { + if (!sourceMap) return; auto maybeReadChar = [&](char expected) { - if (binaryMap->peek() != expected) return false; - binaryMap->get(); + if (sourceMap->peek() != expected) return false; + sourceMap->get(); return true; }; auto mustReadChar = [&](char expected) { - if (binaryMap->get() != expected) { + if (sourceMap->get() != expected) { throw MapParseException("Unexpected char"); } }; @@ -1493,7 +1493,7 @@ void WasmBinaryBuilder::readBinaryMapHeader() { bool matching = false; size_t pos; while (1) { - int ch = binaryMap->get(); + int ch = sourceMap->get(); if (ch == EOF) return false; if (ch == '\"') { matching = true; @@ -1515,7 +1515,7 @@ void WasmBinaryBuilder::readBinaryMapHeader() { mustReadChar('\"'); if (!maybeReadChar('\"')) { while (1) { - int ch = binaryMap->get(); + int ch = sourceMap->get(); if (ch == EOF) { throw MapParseException("unexpected EOF in the middle of string"); } @@ -1550,18 +1550,18 @@ void WasmBinaryBuilder::readBinaryMapHeader() { return; } // read first debug location - uint32_t position = readBase64VLQ(*binaryMap); - uint32_t fileIndex = readBase64VLQ(*binaryMap); - uint32_t lineNumber = readBase64VLQ(*binaryMap) + 1; // adjust zero-based line number - uint32_t columnNumber = readBase64VLQ(*binaryMap); + uint32_t position = readBase64VLQ(*sourceMap); + uint32_t fileIndex = readBase64VLQ(*sourceMap); + uint32_t lineNumber = readBase64VLQ(*sourceMap) + 1; // adjust zero-based line number + uint32_t columnNumber = readBase64VLQ(*sourceMap); nextDebugLocation = { position, { fileIndex, lineNumber, columnNumber } }; } void WasmBinaryBuilder::readNextDebugLocation() { - if (!binaryMap) return; + if (!sourceMap) return; char ch; - *binaryMap >> ch; + *sourceMap >> ch; if (ch == '\"') { // end of records nextDebugLocation.first = 0; return; @@ -1569,13 +1569,14 @@ void WasmBinaryBuilder::readNextDebugLocation() { if (ch != ',') { throw MapParseException("Unexpected delimiter"); } - int32_t positionDelta = readBase64VLQ(*binaryMap); + + int32_t positionDelta = readBase64VLQ(*sourceMap); uint32_t position = nextDebugLocation.first + positionDelta; - int32_t fileIndexDelta = readBase64VLQ(*binaryMap); + int32_t fileIndexDelta = readBase64VLQ(*sourceMap); uint32_t fileIndex = nextDebugLocation.second.fileIndex + fileIndexDelta; - int32_t lineNumberDelta = readBase64VLQ(*binaryMap); + int32_t lineNumberDelta = readBase64VLQ(*sourceMap); uint32_t lineNumber = nextDebugLocation.second.lineNumber + lineNumberDelta; - int32_t columnNumberDelta = readBase64VLQ(*binaryMap); + int32_t columnNumberDelta = readBase64VLQ(*sourceMap); uint32_t columnNumber = nextDebugLocation.second.columnNumber + columnNumberDelta; nextDebugLocation = { position, { fileIndex, lineNumber, columnNumber } }; diff --git a/src/wasm/wasm-io.cpp b/src/wasm/wasm-io.cpp index 9d2ce29a356..ebc9af8deba 100644 --- a/src/wasm/wasm-io.cpp +++ b/src/wasm/wasm-io.cpp @@ -74,18 +74,18 @@ void ModuleWriter::writeBinary(Module& wasm, std::string filename) { WasmBinaryWriter writer(&wasm, buffer, debug); // if debug info is used, then we want to emit the names section writer.setNamesSection(debugInfo); - std::unique_ptr binaryMapStream; - if (binaryMapFilename.size()) { - binaryMapStream = make_unique(); - binaryMapStream->open(binaryMapFilename); - writer.setBinaryMap(binaryMapStream.get(), binaryMapUrl); + std::unique_ptr sourceMapStream; + if (sourceMapFilename.size()) { + sourceMapStream = make_unique(); + sourceMapStream->open(sourceMapFilename); + writer.setSourceMap(sourceMapStream.get(), sourceMapUrl); } if (symbolMap.size() > 0) writer.setSymbolMap(symbolMap); writer.write(); Output output(filename, Flags::Binary, debug ? Flags::Debug : Flags::Release); buffer.writeTo(output); - if (binaryMapStream) { - binaryMapStream->close(); + if (sourceMapStream) { + sourceMapStream->close(); } }