Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exporting/importing debug location information from .wast/.asm.js/.s formats #1017

Merged
merged 4 commits into from
Jun 1, 2017

Conversation

yurydelendik
Copy link
Contributor

@yurydelendik yurydelendik commented May 19, 2017

This change allows (based on @dschuff 's debuginfo branch):

@yurydelendik
Copy link
Contributor Author

See also WebAssembly/wabt#432

@@ -599,6 +603,7 @@ struct PrintSExpression : public Visitor<PrintSExpression> {
}
void visitFunction(Function *curr) {
currFunction = curr;
lastPrintedLocation = {0, 0, 0};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think space after { and before } is what we have more commonly in the codebase.

.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",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does this do?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This parameter defines the sourceMappingURL property that will be written in the .wasm custom section.

@@ -132,8 +140,7 @@ int main(int argc, const char *argv[]) {
}

Asm2WasmPreProcessor pre;
// wasm binaries can contain a names section, but not full debug info
pre.debugInfo = passOptions.debugInfo && !emitBinary;
pre.debugInfo = passOptions.debugInfo;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we are emitting binary, and not emitting a binary map, we can drop the debug info, I think? I.e. replace !emitBinary with something like (!emitBinary || !binaryMapFile)

@@ -200,7 +207,11 @@ int main(int argc, const char *argv[]) {
writer.setDebugInfo(passOptions.debugInfo);
writer.setSymbolMap(symbolMap);
writer.setBinary(emitBinary);
writer.write(wasm, options.extra["output"]);
if (emitBinary && binaryMapFile.size()) {
writer.writeBinary(wasm, options.extra["output"], binaryMapFile, binaryMapUrl);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think a more consistent api might be to have, like we have setBinary now (used a few lines above), setBinaryMapFile and setBinaryMapURL methods that we call. then we can avoid calling writeBinary here.

@@ -86,13 +94,24 @@ 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);
writer.setNamesSection(debugInfo);
std::ofstream* binaryMapStream = nullptr;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::unique_ptr

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';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spaces before and after each <<

src/wasm.h Outdated
uint32_t fileIndex, lineNumber;
uint32_t fileIndex, lineNumber, columnNumber;
bool operator==(const DebugLocation& other) { return fileIndex == other.fileIndex && lineNumber == other.lineNumber && columnNumber == other.columnNumber; }
bool operator!=(const DebugLocation& other) { return !this->operator==(other); }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about using the operator, i.e. !(other == *this)?

@kripken
Copy link
Member

kripken commented May 19, 2017

General questions, maybe I don't understand how debug info is meant to work yet:

  • What is the URL?
  • Looks like there is both code to write out a separate binary map file, but also read and write inside the binary?

@yurydelendik
Copy link
Contributor Author

What is the URL?

The URL is the final JavaScript JSON source maps resource (as defined in the WebAssembly/design#1051) and intended to be read (URL and then source maps) by devtools.
.txtmap-file will be further transformed into/from JSON source maps as defined by the design PR.

Looks like there is both code to write out a separate binary map file, but also read and write inside the binary?

At the moment .txtmap is used to store debug information, but final "destination" is JSON map file. The .wasm and .txtmap (later .map) file go side-by-side (similar to JS source maps). The .wasm binary file does not contain any debug location information, only the sourceMappingURL (which points to .map). To import the location information, the .txtmap (later .map) must be read at the same time as .wasm by wasm-dis to place that in AST.

@kripken
Copy link
Member

kripken commented May 19, 2017

Thanks, but what does "later map" mean in all those cases? Is that an expected spec change, or something happening now in the toolchain?

@yurydelendik
Copy link
Contributor Author

Thanks, but what does "later map" mean in all those cases? Is that an expected spec change, or something happening now in the toolchain?

For emscripten, there is emscripten-core/emscripten@e8dd1c8 which is planning to use regular tools/source-maps/sourcemapper.js, however the design PR recommends to use columns instead of lines. We can further modify https://github.com/kripken/emscripten/blob/master/tools/source-maps/sourcemapper.js#L130 to use byteoffset as a column. For prototyping, I'm using txt2map4wasm package (https://github.com/yurydelendik/txt2map4wasm).

@yurydelendik
Copy link
Contributor Author

@kripken Would you recommend to add JavaScript source map generation to this PR?

@kripken
Copy link
Member

kripken commented May 22, 2017

Ok, I think I see, so this emits some map file that is later translated to a full source map by the sourcemapper tool in emscripten? And you're asking if we should do the full translation here?

If that translation is trivial, it might make sense to duplicate that functionality here, since then the code here would be useful by itself. But I'm not sure how simple it is, what do you think?

@yurydelendik
Copy link
Contributor Author

But I'm not sure how simple it is, what do you think?

About 200 SLOC, I'll submit that shortly

@yurydelendik
Copy link
Contributor Author

Now asm2wasm and friends are exporting valid JS source map directly. For sake of simplicity of this PR and sake of re-import (for wasm-dis), I limited it to the following format:

{"version":3,"sources":[…source file names…],"names":[],"mappings":"…VLQ encoded entries…"}

notice that the "sources" field must precede the "mappings" field in the JSON.

check.py Outdated
# verify debug info
if 'debugInfo' in asm:
jsmap = 'a.wasm.map'
cmd += ['--binarymap-file', jsmap,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since the tools now emit the source map directly, do we need binarymap here?

Copy link
Contributor Author

@yurydelendik yurydelendik May 23, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the file is still a standalone file, I just changed its output format -- binary wasm just contains the reference

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so --binarymap-file emits a source map? what i am asking is, should we change the parameter names to --source-map or something similar, if they just emit a source map?

or am I missing something here? is there still a binary file, with a "reference"? if so where is that documented, i don't think i see it in t he design pr?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there still a binary file, with a "reference"? if so where is that documented, i don't think i see it in t he design pr?

https://github.com/WebAssembly/design/pull/1051/files#diff-8e85308ab5cc1e83e91ef59233648be2R219

"For wasm, a custom section named "sourceMappingURL" contains the URL."

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what i am asking is, should we change the parameter names to --source-map or something similar

I can change that, yes. For JSON file name it will be --source-map, and for url (embedded in the wasm) --source-map-url. Is it correct?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, those names sound good.

to make sure i understand, the current state is now that we emit just 2 files, the wasm binary (which has a section that has the URL) and the JSON source map file (which that URL should point to)?

Copy link
Contributor Author

@yurydelendik yurydelendik May 23, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is correct. If --source-map is specified, asm2wasm and wasm-as emit additional JSON source map file. If --source-map-url is specified, the optional custom section section "sourceMappingURL" will be added to the wasm file.

If --source-map is specified for wasm-dis, this JSON source map file is used to read location information and to add these to the wast output.

@yurydelendik yurydelendik force-pushed the debuginfo branch 2 times, most recently from 9facd16 to a2203ec Compare May 23, 2017 22:21
run_command(cmd)
if not os.path.isfile(jsmap):
fail_with_error('Debug info map not created: %s' % jsmap)
with open(wasm + '.map', 'rb') as expected:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like this uses some new test output files in the test dir. please also update auto_update_tests.py which should generate all the test output files in the test folder

src/s2wasm.h Outdated
@@ -665,22 +668,31 @@ class S2WasmBuilder {

mustMatch(":");

Function::DebugLocation debugLocation = {0,0,0};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here and in a few more places, spaces after { and before } would be more consistent with the rest of the code

[&sourceMapFilename](Options *o, const std::string &argument) { sourceMapFilename = argument; })
.add("--source-map-url", "-su", "Use specified string as source map URL",
Options::Arguments::One,
[&sourceMapUrl](Options *o, const std::string &argument) { sourceMapUrl = argument; })
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these are duplicated in two files. i am currently getting rid of most of the existing duplication in #1023. it would be good to do the same thing for these options, however, i understand if you want to land this first and leave the refactoring to followup work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather move this out of scope this PR -- --source-map and --source-map-url are not really optimization options and have different meaning in some cases, e.g. for asm2wasm --source-map means output file, but for wasm-dis -- input file.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, good point, we'll need more refactoring here. Definitely let's leave for later.

@@ -86,13 +94,22 @@ 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);
writer.setNamesSection(debugInfo);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks weird, it used to be the same name, but now it is different. perhaps add a comment, "if debug info is used, then we want to emit the names section" (is that correct?)


if (!findField("sources", strlen("sources")))
throw MapParseException("cannot find the sources field in map");
mustReadChar('[');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation looks wrong here?

str = std::string(vec.begin(), vec.end());
};

if (!findField("sources", strlen("sources")))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if body of if is on another line, please use {,}

@@ -1627,6 +1818,15 @@ 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) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation looks larger than the 2 it should be

@@ -1661,6 +1861,8 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) {
throw ParseException("bad node code " + std::to_string(code));
}
}
if (useDebugLocation && curr)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if identation is too big, and need { }

while (debugLocEnd[0] && debugLocEnd[0] != '\n') debugLocEnd++;
char* pos = debugLoc;
while (pos < debugLocEnd && pos[0] != ':') pos++;
if (pos >= debugLocEnd)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also here and later down, need {}

@yurydelendik yurydelendik force-pushed the debuginfo branch 2 times, most recently from 2dcf466 to 8062530 Compare May 24, 2017 18:03
@@ -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 += ['--source-map', os.path.join('test', wasm + '.map'), '-o', 'a.wasm']
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is a.wasm the right place for this? it should be stored where check.py looks for it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the code above generates proper data for test;a.wasm is just a file we can delete -- we only need source map from this run.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, thanks. Sorry for my confusion.

@kripken
Copy link
Member

kripken commented May 30, 2017

Ok, this lgtm. Did anyone else want to take a look?

@kripken
Copy link
Member

kripken commented Jun 1, 2017

Looks like no concerns, so merging. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants