diff --git a/CMakeLists.txt b/CMakeLists.txt index f001148d..b0ebefd0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -250,6 +250,8 @@ add_library(libbloaty STATIC src/range_map.cc src/range_map.h src/re.h + src/source_map.cc + src/source_map.h src/util.cc src/util.h src/webassembly.cc diff --git a/doc/using.md b/doc/using.md index c05359b5..44a14845 100644 --- a/doc/using.md +++ b/doc/using.md @@ -99,6 +99,11 @@ Options: -c FILE Load configuration from . -d SOURCE,SOURCE Comma-separated list of sources to scan. --debug-file=FILE Use this file for debug symbols and/or symbol table. + --source-map=ID=FILE + Use this source map file for the binary. The ID can be + the build ID (or Wasm sourceMappingURL) or the file path + as specified in the command line. + Currently only supported for Wasm. -C MODE How to demangle symbols. Possible values are: --demangle=MODE --demangle=none no demangling, print raw symbols --demangle=short demangle, but omit arg/return types @@ -845,6 +850,33 @@ $ ./bloaty -c config.bloaty -d bloaty_package,compileunits bloaty 100.0% 29.5Mi 100.0% 6.69Mi TOTAL ``` +# Source maps + +Bloaty provides a `--source-map` option which allows +specifying a source map for the binary. It takes an ID and +source map file name, separated by '='. The ID can be the +build ID or path to the binary file as specified in the +command line. + +For example, to use the `compileunits` and `inlines` data +sources for Wasm, a source map must be provided. For Wasm, +the ID can be the text in the `sourceMappingURL` section of +the binary. + +```cmdoutput +$ ./bloaty -d compileunits --domain=file --source-map=o.wasm=o.wasm.map o.wasm + FILE SIZE + -------------- + 45.0% 49 [section sourceMappingURL] + 19.3% 21 Main.java + 12.8% 14 [section Export] + 7.3% 8 [WASM Header] + 6.4% 7 [section Code] + 5.5% 6 [section Type] + 3.7% 4 [section Function] + 100.0% 109 TOTAL +``` + # Source filter Sometimes, you are only interested in parts of the binary diff --git a/src/bloaty.cc b/src/bloaty.cc index 0f9881e7..e5d1f033 100644 --- a/src/bloaty.cc +++ b/src/bloaty.cc @@ -1468,6 +1468,7 @@ class Bloaty { void AddFilename(const std::string& filename, bool base_file); void AddDebugFilename(const std::string& filename); + void AddSourceMapFilename(const std::string& filename); size_t GetSourceCount() const { return sources_.size(); } @@ -1535,6 +1536,7 @@ class Bloaty { std::vector input_files_; std::vector base_files_; std::map debug_files_; + std::map sourcemap_files_; // For allocating memory, like to decompress compressed sections. std::unique_ptr arena_; @@ -1592,6 +1594,18 @@ void Bloaty::AddDebugFilename(const std::string& filename) { debug_files_[build_id] = filename; } +void Bloaty::AddSourceMapFilename(const std::string& filename) { + std::size_t delimiter = filename.find('='); + if (delimiter == std::string::npos) { + THROWF("Source map filename '$0' must have a build id and file name " + "separated by '='", + filename); + } + std::string sourcemap_build_id = filename.substr(0, delimiter); + std::string sourcemap_filename = filename.substr(delimiter + 1); + sourcemap_files_[sourcemap_build_id] = sourcemap_filename; +} + void Bloaty::DefineCustomDataSource(const CustomDataSource& source) { if (source.base_data_source() == "symbols") { THROW( @@ -1761,6 +1775,22 @@ void Bloaty::ScanAndRollupFile(const std::string& filename, Rollup* rollup, file->set_debug_file(debug_file.get()); out_build_ids->push_back(build_id); } + + // Maybe it's a source map file? + auto sourcemap_iter = sourcemap_files_.find(build_id); + // If the source map by build id isn't found, try the file name. + if (sourcemap_iter == sourcemap_files_.end()) { + sourcemap_iter = sourcemap_files_.find(filename); + } + if (sourcemap_iter != sourcemap_files_.end()) { + std::unique_ptr sourcemap_file( + file_factory_.OpenFile(sourcemap_iter->second)); + // The build id is not present in the source map file itself, so it must + // be passed here. + debug_file = TryOpenSourceMapFile(sourcemap_file, build_id); + file->set_debug_file(debug_file.get()); + out_build_ids->push_back(build_id); + } } int64_t filesize_before = @@ -1943,6 +1973,11 @@ USAGE: bloaty [OPTION]... FILE... [-- BASE_FILE...] -c FILE Load configuration from . -d SOURCE,SOURCE Comma-separated list of sources to scan. --debug-file=FILE Use this file for debug symbols and/or symbol table. + --source-map=ID=FILE + Use this source map file for the binary. The ID can be + the build ID (or Wasm sourceMappingURL) or the file path + as specified in the command line. + Currently only supported for Wasm. -C MODE How to demangle symbols. Possible values are: --demangle=MODE --demangle=none no demangling, print raw symbols --demangle=short demangle, but omit arg/return types @@ -2166,6 +2201,8 @@ bool DoParseOptions(bool skip_unknown, int* argc, char** argv[], } } else if (args.TryParseOption("--source-filter", &option)) { options->set_source_filter(std::string(option)); + } else if (args.TryParseOption("--source-map", &option)) { + options->add_source_map(std::string(option)); } else if (args.TryParseFlag("-v")) { options->set_verbose_level(1); } else if (args.TryParseFlag("-vv")) { @@ -2260,6 +2297,10 @@ void BloatyDoMain(const Options& options, const InputFileFactory& file_factory, bloaty.AddDebugFilename(debug_filename); } + for (auto& sourcemap : options.source_map()) { + bloaty.AddSourceMapFilename(sourcemap); + } + for (const auto& custom_data_source : options.custom_data_source()) { bloaty.DefineCustomDataSource(custom_data_source); } diff --git a/src/bloaty.h b/src/bloaty.h index 91a8d100..f7a49d7f 100644 --- a/src/bloaty.h +++ b/src/bloaty.h @@ -294,6 +294,8 @@ std::unique_ptr TryOpenELFFile(std::unique_ptr& file); std::unique_ptr TryOpenMachOFile(std::unique_ptr& file); std::unique_ptr TryOpenWebAssemblyFile(std::unique_ptr& file); std::unique_ptr TryOpenPEFile(std::unique_ptr& file); +std::unique_ptr TryOpenSourceMapFile( + std::unique_ptr& file, std::string build_id); // Provided by dwarf.cc. To use these, a module should fill in a dwarf::File // and then call these functions. diff --git a/src/bloaty.proto b/src/bloaty.proto index 16793288..5eb4421a 100644 --- a/src/bloaty.proto +++ b/src/bloaty.proto @@ -29,6 +29,9 @@ message Options { // debug_filename will *not* have their file size counted. repeated string debug_filename = 10; + // Build id to source map file names, delimited by '='. + repeated string source_map = 15; + // The data sources to scan in each file. At least one data source must be // specified. If more than one source is specified, the output is // hierarchical. @@ -101,3 +104,4 @@ message Regex { optional string pattern = 1; optional string replacement = 2; } + diff --git a/src/source_map.cc b/src/source_map.cc new file mode 100644 index 00000000..584d3422 --- /dev/null +++ b/src/source_map.cc @@ -0,0 +1,239 @@ +// Copyright 2024 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include "bloaty.h" +#include "source_map.h" +#include "util.h" + +#include "absl/strings/string_view.h" + +namespace bloaty { +namespace sourcemap { + +static bool ReadOpeningBrace(absl::string_view* data) { + return ReadFixed(data) == '{'; +} + +static absl::string_view ReadQuotedString(absl::string_view* data) { + RequireChar(data, '\"'); + // Simply read until the next '\"'. We currently do not handle escaped + // characters. Field names never contain quotes and file names are unlikely to + // contain quotes. + return ReadUntilConsuming(data, '\"'); +} + +// Finds the field with the given name in the source map. Any fields encountered +// before the field are skipped. +static void FindField(absl::string_view* data, const char* name) { + while (!data->empty()) { + SkipWhitespace(data); + auto field_name = ReadQuotedString(data); + if (field_name == name) { + SkipWhitespace(data); + RequireChar(data, ':'); + SkipWhitespace(data); + return; + } + + // Skip until the next quote. We don't expect any structures involving + // quotes in the fields that we skip. + ReadUntil(data, '\"'); + } + THROWF("field \"$0\" not found in source map", name); +} + +static int32_t ReadBase64VLQ(absl::string_view* data) { + uint32_t value = 0; + uint32_t shift = 0; + const char* ptr = data->data(); + const char* limit = ptr + data->size(); + while (ptr < limit) { + auto ch = *(ptr++); + // Base64 characters A-Z, a-f do not have the continuation bit set and are + // the last digit. + if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch < 'g')) { + uint32_t digit = ch < 'a' ? ch - 'A' : ch - 'a' + 26; + value |= digit << shift; + data->remove_prefix(ptr - data->data()); + return value & 1 + ? -static_cast(value >> 1) + : static_cast(value >> 1); + } + if (!(ch >= 'g' && ch <= 'z') && !(ch >= '0' && ch <= '9') && ch != '+' && + ch != '/') { + THROWF("Invalid Base64VLQ digit $0", ch); + } + // Base64 characters g-z, 0-9, + and / have the continuation bit set and + // must be followed by another digit. + uint32_t digit = + ch > '9' ? ch - 'g' : (ch >= '0' ? ch - '0' + 20 : (ch == '+' ? 30 : 31)); + value |= digit << shift; + shift += 5; + } + + THROW("Unterminated Base64VLQ"); +} + +static bool IsBase64Digit(char ch) { + return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || + (ch >= '0' && ch <= '9') || ch == '+' || ch == '/'; +} + +static int ReadBase64VLQSegment(absl::string_view* data, int32_t (&values)[5]) { + for (int i = 0; i < 5; i++) { + values[i] = ReadBase64VLQ(data); + if (data->empty() || !IsBase64Digit(data->front())) { + if (i != 0 && i != 3 && i != 4) { + THROWF("Invalid source map VLQ segment length $0", i + 1); + } + return i + 1; + } + } + THROW("Unterminated Base64VLQ segment"); +} + +class VlqSegment { + public: + int32_t col; + int32_t length; + + std::string source_file; + int32_t source_line; + int32_t source_col; + + VlqSegment(int32_t col, int32_t length, + absl::string_view source_file, + int32_t source_line, int32_t source_col) + : col(col), length(length), + source_file(source_file), + source_line(source_line), source_col(source_col) {} + + void addToSink(RangeSink* sink) const { + auto name = sink->data_source() == DataSource::kInlines + ? source_file + ":" + std::to_string(source_line) + : source_file; + sink->AddFileRange("sourcemap", name, col, length); + } +}; + +template +void ForEachVLQSegment(absl::string_view* data, + const std::vector& sources, + Func&& segment_func) { + if (data->empty() || data->front() == '\"') { + return; + } + + // Read the first segment. We don't generate the `VlqSegment` until the next + // one is encountered. This one only points to a particular byte. The next + // segment is required to determine the length. + int32_t values[5]; + int values_count = ReadBase64VLQSegment(data, values); + if (values_count < 4) { + THROW("Source file info expected in first VLQ segment"); + } + int32_t col = values[0]; + int32_t source_file = values[1]; + int32_t source_line = values[2]; + int32_t source_col = values[3]; + + while (!data->empty() && data->front() != '\"') { + if (data->front() == ',') { + data->remove_prefix(1); + continue; + } + + // We don't support line separators in the source map for now. + if (data->front() == ';') { + THROW("Unsupported line separator in source map"); + } + + int new_values_count = ReadBase64VLQSegment(data, values); + if (values_count >= 4) { + segment_func(VlqSegment(col, values[0], + sources[source_file], source_line, source_col)); + } + values_count = new_values_count; + col += values[0]; + if (values_count >= 4) { + source_file += values[1]; + source_line += values[2]; + source_col += values[3]; + } + } +} + +static void ProcessToSink(absl::string_view data, RangeSink* sink) { + ReadOpeningBrace(&data); + + std::vector sources; + FindField(&data, "sources"); + RequireChar(&data, '['); + while (!data.empty()) { + SkipWhitespace(&data); + if (data.empty()) { + break; + } + if (data.front() == ']') { + data.remove_prefix(1); + break; + } + if (data.front() == ',') { + data.remove_prefix(1); + } + auto source = ReadQuotedString(&data); + sources.push_back(source); + } + SkipWhitespace(&data); + RequireChar(&data, ','); + + FindField(&data, "mappings"); + RequireChar(&data, '\"'); + + ForEachVLQSegment(&data, sources, [&sink](const VlqSegment& segment) { + segment.addToSink(sink); + }); + + RequireChar(&data, '\"'); +} + +void SourceMapObjectFile::ProcessFileToSink(RangeSink* sink) const { + if (sink->data_source() != DataSource::kCompileUnits + && sink->data_source() != DataSource::kInlines) { + THROW("Source map doesn't support this data source"); + } + + ProcessToSink(file_data().data(), sink); +} + +} // namespace sourcemap + +std::unique_ptr TryOpenSourceMapFile( + std::unique_ptr& file, std::string build_id) { + absl::string_view data = file->data(); + if (sourcemap::ReadOpeningBrace(&data)) { + return std::unique_ptr( + new sourcemap::SourceMapObjectFile(std::move(file), build_id)); + } + + return nullptr; +} + +} // namespace bloaty + diff --git a/src/source_map.h b/src/source_map.h new file mode 100644 index 00000000..ad4b4762 --- /dev/null +++ b/src/source_map.h @@ -0,0 +1,61 @@ +// Copyright 2024 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef BLOATY_SOURCE_MAP_H_ +#define BLOATY_SOURCE_MAP_H_ + +#include +#include +#include +#include +#include "bloaty.h" +#include "util.h" + +#include "absl/strings/string_view.h" + +namespace bloaty { +namespace sourcemap { + +class SourceMapObjectFile : public ObjectFile { + public: + SourceMapObjectFile(std::unique_ptr file_data, + std::string build_id) + : ObjectFile(std::move(file_data)), build_id_(build_id) {} + + std::string GetBuildId() const override { + return build_id_; + } + + void ProcessFile(const std::vector& sinks) const override { + WARN("General processing not supported for source map files"); + } + + bool GetDisassemblyInfo(absl::string_view /*symbol*/, + DataSource /*symbol_source*/, + DisassemblyInfo* /*info*/) const override { + WARN("Disassembly not supported for source map files"); + return false; + } + + void ProcessFileToSink(RangeSink* sink) const; + + private: + std::string build_id_; +}; + +} // namespace sourcemap +} // namespace bloaty + +#endif // BLOATY_SOURCE_MAP_H_ + diff --git a/src/util.cc b/src/util.cc index b85378ba..2ca22cc7 100644 --- a/src/util.cc +++ b/src/util.cc @@ -23,19 +23,42 @@ void Throw(const char *str, int line) { throw bloaty::Error(str, __FILE__, line); } -absl::string_view ReadNullTerminated(absl::string_view* data) { - const char* nullz = - static_cast(memchr(data->data(), '\0', data->size())); +absl::string_view ReadUntilConsuming(absl::string_view* data, char c) { + absl::string_view ret = ReadUntil(data, c); - // Return false if not NULL-terminated. - if (nullz == NULL) { - THROW("DWARF string was not NULL-terminated"); + if (data->empty() || data->front() != c) { + // Nothing left, meaning we didn't find the terminating character. + if (c == '\0') { + THROW("string is not NULL-terminated"); + } + THROWF("could not find terminating character '$0'", c); } - size_t len = nullz - data->data(); + data->remove_prefix(1); // Remove the terminating character also. + return ret; +} + +absl::string_view ReadUntil(absl::string_view* data, char c) { + const char* found = + static_cast(memchr(data->data(), c, data->size())); + + size_t len = (found == NULL) ? data->size() : (found - data->data()); absl::string_view val = data->substr(0, len); - data->remove_prefix(len + 1); // Remove NULL also. + data->remove_prefix(len); return val; } +void SkipWhitespace(absl::string_view* data) { + const char* c = data->data(); + const char* limit = c + data->size(); + while (c < limit) { + if (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r') { + c++; + } else { + break; + } + } + data->remove_prefix(c - data->data()); +} + } // namespace bloaty diff --git a/src/util.h b/src/util.h index 4db4b8b3..a12b439d 100644 --- a/src/util.h +++ b/src/util.h @@ -160,7 +160,13 @@ template T ReadBigEndian(absl::string_view *data) { // General data reading /////////////////////////////////////////////////////// -absl::string_view ReadNullTerminated(absl::string_view* data); +absl::string_view ReadUntil(absl::string_view* data, char c); + +absl::string_view ReadUntilConsuming(absl::string_view* data, char c); + +inline absl::string_view ReadNullTerminated(absl::string_view* data) { + return ReadUntilConsuming(data, '\0'); +} inline absl::string_view ReadBytes(size_t bytes, absl::string_view* data) { if (data->size() < bytes) { @@ -171,10 +177,20 @@ inline absl::string_view ReadBytes(size_t bytes, absl::string_view* data) { return ret; } +inline void RequireChar(absl::string_view* data, char c) { + if (data->empty() || data->front() != c) { + THROWF("unexpected '$0', expected '$1'", + data->empty() ? "EOF" : data->substr(0, 1), c); + } + data->remove_prefix(1); +} + inline void SkipBytes(size_t bytes, absl::string_view* data) { ReadBytes(bytes, data); // Discard result. } +void SkipWhitespace(absl::string_view* data); + } // namespace bloaty #endif // BLOATY_UTIL_H_ diff --git a/src/webassembly.cc b/src/webassembly.cc index d5dbeac0..d1eea7b6 100644 --- a/src/webassembly.cc +++ b/src/webassembly.cc @@ -13,6 +13,7 @@ // limitations under the License. #include "bloaty.h" +#include "source_map.h" #include "util.h" #include "absl/strings/substitute.h" @@ -176,6 +177,20 @@ void ForEachSection(string_view file, Func&& section_func) { } } +template +void FindSection(string_view file, std::string name, Func&& section_func) { + string_view data = file; + ReadMagic(&data); + + while (!data.empty()) { + Section section = Section::Read(&data); + if (section.name == name) { + section_func(section); + break; + } + } +} + void ParseSections(RangeSink* sink) { ForEachSection(sink->input_file().data(), [sink](const Section& section) { sink->AddFileRange("wasm_sections", section.name, section.data); @@ -412,8 +427,19 @@ class WebAssemblyObjectFile : public ObjectFile { : ObjectFile(std::move(file_data)) {} std::string GetBuildId() const override { - // TODO(haberman): does WebAssembly support this? - return std::string(); + // Use the sourceMappingURL as the build ID to be able to match to the + // source map. + std::string id; + + FindSection(file_data().data(), "sourceMappingURL", + [&id](Section& section) { + uint32_t size = ReadVarUInt32(§ion.contents); + string_view source_mapping_url = + ReadPiece(size, §ion.contents); + id.assign(source_mapping_url); + }); + + return id; } void ProcessFile(const std::vector& sinks) const override { @@ -429,9 +455,17 @@ class WebAssemblyObjectFile : public ObjectFile { case DataSource::kFullSymbols: ParseSymbols(sink); break; - case DataSource::kArchiveMembers: case DataSource::kCompileUnits: case DataSource::kInlines: + if (const sourcemap::SourceMapObjectFile* source_map = + dynamic_cast( + &debug_file())) { + source_map->ProcessFileToSink(sink); + } else { + THROW("Data source requires a source map"); + } + break; + case DataSource::kArchiveMembers: default: THROW("WebAssembly doesn't support this data source"); } @@ -461,3 +495,4 @@ std::unique_ptr TryOpenWebAssemblyFile( } } // namespace bloaty + diff --git a/tests/wasm/sourcemap_compileunits.test b/tests/wasm/sourcemap_compileunits.test new file mode 100644 index 00000000..7bc04862 --- /dev/null +++ b/tests/wasm/sourcemap_compileunits.test @@ -0,0 +1,31 @@ +# RUN: %yaml2obj %s -o %t.obj +# RUN: echo "{\"version\": 3, \"sources\": [\"test1.java\",\"test2.java\"], \"names\": [], \"mappings\": \"QAEI,Y,QACF,Y,oBACA,Y,QCFE,Y\"}" > %t.map +# RUN: %bloaty --raw-map %t.obj --source-map=./sourcemap.wasm.map=%t.map -d compileunits | %FileCheck %s + +--- !WASM +FileHeader: + Version: 0x1 +Sections: + - Type: CODE + Functions: + - Index: 0 + Locals: + - Type: I32 + Count: 13 + Body: 41002100200028028880808000210141052102200120026A210341002104200428028C808080002105200320056A2106410021072007280280808080002108200820066A21094100210A200A200936028080808000418480808000210B200B210C200C0F0B + - Type: CUSTOM + Name: sourceMappingURL + # ./sourcemap.wasm.map + Payload: 142E2F736F757263656D61702E7761736D2E6D6170 + +# CHECK: FILE MAP: +# CHECK: 00-08 8 [WASM Header] +# Note: Due to IsShortFallback, the 8 byte fallback (from mapping 'Y' to 'QACF') is considered part of the following file. +# This actually covers 3 segment ranges: QAEI->Y, Y->QACF (short fallback), and QACF->Y. +# CHECK: 08-28 32 test1.java +# CHECK: 28-3c 20 [section Code] +# CHECK: 3c-50 20 test1.java +# CHECK: 50-5c 12 test2.java +# CHECK: 5c-78 28 [section Code] +# CHECK: 78-a4 44 [section sourceMappingURL] + diff --git a/tests/wasm/sourcemap_diff.test b/tests/wasm/sourcemap_diff.test new file mode 100644 index 00000000..238f33e4 --- /dev/null +++ b/tests/wasm/sourcemap_diff.test @@ -0,0 +1,36 @@ +# RUN: %yaml2obj %s -o %t.obj +# Make a copy of the same Wasm binary, with the same sourceMappingURL. This case +# tests that we can handle multiple source maps keyed by file name. +# RUN: cp %t.obj %t2.obj +# RUN: echo "{\"version\": 3, \"sources\": [\"test1.java\"], \"names\": [], \"mappings\": \"QAEI,gC\"}" > %t.map +# RUN: echo "{\"version\": 3, \"sources\": [\"test2.java\"], \"names\": [], \"mappings\": \"QAEI,gC\"}" > %t2.map +# RUN: %bloaty --raw-map %t.obj --source-map=%t.obj=%t.map --source-map=%t2.obj=%t2.map -- %t2.obj -d compileunits | %FileCheck %s + +--- !WASM +FileHeader: + Version: 0x1 +Sections: + - Type: CODE + Functions: + - Index: 0 + Locals: + - Type: I32 + Count: 13 + Body: 41002100200028028880808000210141052102200120026A210341002104200428028C808080002105200320056A2106410021072007280280808080002108200820066A21094100210A200A200936028080808000418480808000210B200B210C200C0F0B + - Type: CUSTOM + Name: sourceMappingURL + # ./sourcemap.wasm.map + Payload: 142E2F736F757263656D61702E7761736D2E6D6170 + +# CHECK: FILE MAP: +# CHECK: 00-08 8 [WASM Header] +# CHECK: 08-28 32 test1.java +# CHECK: 28-78 80 [section Code] +# CHECK: 78-a4 44 [section sourceMappingURL] + +# CHECK: FILE MAP: +# CHECK: 00-08 8 [WASM Header] +# CHECK: 08-28 32 test2.java +# CHECK: 28-78 80 [section Code] +# CHECK: 78-a4 44 [section sourceMappingURL] + diff --git a/tests/wasm/sourcemap_inlines.test b/tests/wasm/sourcemap_inlines.test new file mode 100644 index 00000000..4287944d --- /dev/null +++ b/tests/wasm/sourcemap_inlines.test @@ -0,0 +1,32 @@ +# RUN: %yaml2obj %s -o %t.obj +# RUN: echo "{\"version\": 3, \"sources\": [\"test1.java\",\"test2.java\"], \"names\": [], \"mappings\": \"QAEI,Y,QACF,Y,oBACA,Y,QCFE,Y\"}" > %t.map +# RUN: %bloaty --raw-map %t.obj --source-map=./sourcemap.wasm.map=%t.map -d inlines | %FileCheck %s + +--- !WASM +FileHeader: + Version: 0x1 +Sections: + - Type: CODE + Functions: + - Index: 0 + Locals: + - Type: I32 + Count: 13 + Body: 41002100200028028880808000210141052102200120026A210341002104200428028C808080002105200320056A2106410021072007280280808080002108200820066A21094100210A200A200936028080808000418480808000210B200B210C200C0F0B + - Type: CUSTOM + Name: sourceMappingURL + # ./sourcemap.wasm.map + Payload: 142E2F736F757263656D61702E7761736D2E6D6170 + +# CHECK: FILE MAP: +# CHECK: 00-08 8 [WASM Header] +# Note: Due to IsShortFallback, the 8 byte fallback (from mapping 'Y' to 'QACF') is considered part of the following line. +# This line covers 2 segment ranges: QAEI->Y and Y->QACF (short fallback). +# CHECK: 08-1c 20 test1.java:2 +# CHECK: 1c-28 12 test1.java:3 +# CHECK: 28-3c 20 [section Code] +# CHECK: 3c-50 20 test1.java:4 +# CHECK: 50-5c 12 test2.java:2 +# CHECK: 5c-78 28 [section Code] +# CHECK: 78-a4 44 [section sourceMappingURL] +