From 648075bf23bb03dacf5c9d5f210576fa51f2387f Mon Sep 17 00:00:00 2001 From: Mason Wu Date: Thu, 29 Aug 2024 23:44:07 +0000 Subject: [PATCH 01/11] Initial support for source maps for Wasm --- src/bloaty.cc | 74 +++++--- src/bloaty.h | 23 +-- src/bloaty.proto | 4 + src/source_map.cc | 239 +++++++++++++++++++++++++ src/source_map.h | 61 +++++++ src/util.cc | 43 +++-- src/util.h | 25 ++- src/webassembly.cc | 50 +++++- tests/wasm/sourcemap_compileunits.test | 30 ++++ tests/wasm/sourcemap_inlines.test | 31 ++++ 10 files changed, 528 insertions(+), 52 deletions(-) create mode 100644 src/source_map.cc create mode 100644 src/source_map.h create mode 100644 tests/wasm/sourcemap_compileunits.test create mode 100644 tests/wasm/sourcemap_inlines.test diff --git a/src/bloaty.cc b/src/bloaty.cc index 0f9881e7..3c49cd57 100644 --- a/src/bloaty.cc +++ b/src/bloaty.cc @@ -49,18 +49,18 @@ typedef size_t z_size_t; #include #include -#include "absl/debugging/internal/demangle.h" -#include "absl/memory/memory.h" -#include "absl/strings/numbers.h" -#include "absl/strings/str_join.h" -#include "absl/strings/string_view.h" -#include "absl/strings/substitute.h" -#include "bloaty.h" -#include "bloaty.pb.h" -#include "google/protobuf/io/zero_copy_stream_impl.h" -#include "google/protobuf/text_format.h" -#include "re.h" -#include "util.h" +#include "third_party/absl/debugging/internal/demangle.h" +#include "third_party/absl/memory/memory.h" +#include "third_party/absl/strings/numbers.h" +#include "third_party/absl/strings/str_join.h" +#include "third_party/absl/strings/string_view.h" +#include "third_party/absl/strings/substitute.h" +#include "third_party/bloaty/src/bloaty.h" +#include "third_party/bloaty/src/bloaty.pb.h" +#include "third_party/protobuf/io/zero_copy_stream_impl.h" +#include "net/proto2/public/text_format.h" +#include "third_party/bloaty/src/re.h" +#include "third_party/bloaty/src/util.h" using absl::string_view; @@ -1079,7 +1079,7 @@ std::unique_ptr MmapInputFileFactory::OpenFile( RangeSink::RangeSink(const InputFile* file, const Options& options, DataSource data_source, const DualMap* translator, - google::protobuf::Arena* arena) + proto2::Arena* arena) : file_(file), options_(options), data_source_(data_source), @@ -1334,10 +1334,7 @@ void RangeSink::AddRange(const char* analyzer, string_view name, if (translator_) { if (!translator_->vm_map.CoversRange(vmaddr, vmsize) || !translator_->file_map.CoversRange(fileoff, filesize)) { - WARN("AddRange($0, $1, $2, $3, $4) will be ignored, because it is not " - "covered by base map.", - name.data(), vmaddr, vmsize, fileoff, filesize); - return; + THROW("Tried to add range that is not covered by base map."); } } @@ -1393,7 +1390,7 @@ absl::string_view RangeSink::ZlibDecompress(absl::string_view data, return absl::string_view(); } unsigned char* dbuf = - arena_->google::protobuf::Arena::CreateArray( + arena_->proto2::Arena::CreateArray( arena_, uncompressed_size); uLongf zliblen = uncompressed_size; if (uncompress(dbuf, &zliblen, (unsigned char*)(data.data()), data.size()) != @@ -1468,6 +1465,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,15 +1533,16 @@ 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_; + std::unique_ptr arena_; }; Bloaty::Bloaty(const InputFileFactory& factory, const Options& options) : file_factory_(factory), options_(options), - arena_(std::make_unique()) { + arena_(std::make_unique()) { AddBuiltInSources(data_sources, options); } @@ -1592,6 +1591,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 +1772,18 @@ 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 (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 = @@ -2103,8 +2126,8 @@ bool DoParseOptions(bool skip_unknown, int* argc, char** argv[], if (!input_file.is_open()) { THROWF("couldn't open file $0", option); } - google::protobuf::io::IstreamInputStream stream(&input_file); - if (!google::protobuf::TextFormat::Merge(&stream, options)) { + proto2::io::IstreamInputStream stream(&input_file); + if (!proto2::TextFormat::Merge(&stream, options)) { THROWF("error parsing configuration out of file $0", option); } } else if (args.TryParseOption("-d", &option)) { @@ -2166,6 +2189,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 +2285,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); } @@ -2296,3 +2325,4 @@ bool BloatyMain(const Options& options, const InputFileFactory& file_factory, } } // namespace bloaty + diff --git a/src/bloaty.h b/src/bloaty.h index 91a8d100..64e28128 100644 --- a/src/bloaty.h +++ b/src/bloaty.h @@ -21,7 +21,7 @@ #include #define __STDC_LIMIT_MACROS -#define __STDC_FORMAT_MACROS +/* #define __STDC_FORMAT_MACROS */ #include #include @@ -31,14 +31,14 @@ #include #include -#include "absl/strings/string_view.h" -#include "absl/strings/strip.h" -#include "capstone/capstone.h" +#include "third_party/absl/strings/string_view.h" +#include "third_party/absl/strings/strip.h" +#include "third_party/capstone/capstone.h" -#include "dwarf/debug_info.h" -#include "bloaty.pb.h" -#include "range_map.h" -#include "re.h" +#include "third_party/bloaty/src/dwarf/debug_info.h" +#include "third_party/bloaty/src/bloaty.pb.h" +#include "third_party/bloaty/src/range_map.h" +#include "third_party/bloaty/src/re.h" namespace bloaty { @@ -113,7 +113,7 @@ class RangeSink { public: RangeSink(const InputFile *file, const Options &options, DataSource data_source, const DualMap *translator, - google::protobuf::Arena *arena); + proto2::Arena *arena); RangeSink(const RangeSink &) = delete; RangeSink &operator=(const RangeSink &) = delete; ~RangeSink(); @@ -230,7 +230,7 @@ class RangeSink { DataSource data_source_; const DualMap* translator_; std::vector> outputs_; - google::protobuf::Arena *arena_; + proto2::Arena *arena_; }; // NameMunger ////////////////////////////////////////////////////////////////// @@ -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. @@ -445,3 +447,4 @@ bool BloatyMain(const Options& options, const InputFileFactory& file_factory, } // namespace bloaty #endif + 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..a15c583f --- /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 "third_party/bloaty/src/bloaty.h" +#include "third_party/bloaty/src/source_map.h" +#include "third_party/bloaty/src/util.h" + +#include "third_party/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..9ddbbb58 --- /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 "third_party/bloaty/src/bloaty.h" +#include "third_party/bloaty/src/util.h" + +#include "third_party/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..b9710dcb 100644 --- a/src/util.cc +++ b/src/util.cc @@ -12,30 +12,53 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "util.h" +#include "third_party/bloaty/src/util.h" using absl::string_view; namespace bloaty { -ABSL_ATTRIBUTE_NORETURN 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..ace8b5a7 100644 --- a/src/util.h +++ b/src/util.h @@ -17,9 +17,9 @@ #include -#include "absl/numeric/int128.h" -#include "absl/strings/string_view.h" -#include "absl/strings/substitute.h" +#include "third_party/absl/numeric/int128.h" +#include "third_party/absl/strings/string_view.h" +#include "third_party/absl/strings/substitute.h" namespace bloaty { @@ -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,21 @@ 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..23464ce1 100644 --- a/src/webassembly.cc +++ b/src/webassembly.cc @@ -12,10 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "bloaty.h" -#include "util.h" +#include +#include +#include "third_party/bloaty/src/bloaty.h" +#include "third_party/bloaty/src/source_map.h" +#include "third_party/bloaty/src/util.h" -#include "absl/strings/substitute.h" +#include "third_party/absl/strings/substitute.h" using absl::string_view; @@ -176,6 +179,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 +429,20 @@ 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.resize(size); + memcpy(&id[0], &source_mapping_url[0], size); + }); + + return id; } void ProcessFile(const std::vector& sinks) const override { @@ -429,9 +458,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 { + THROWF("Data source $0 requires a source map", sink->data_source()); + } + break; + case DataSource::kArchiveMembers: default: THROW("WebAssembly doesn't support this data source"); } @@ -461,3 +498,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..ff29e032 --- /dev/null +++ b/tests/wasm/sourcemap_compileunits.test @@ -0,0 +1,30 @@ +# 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 this file. +# 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_inlines.test b/tests/wasm/sourcemap_inlines.test new file mode 100644 index 00000000..8c094088 --- /dev/null +++ b/tests/wasm/sourcemap_inlines.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 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 this line. +# 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] + From 197470f2b76b7f3f2b726a88cc964ef0023d9530 Mon Sep 17 00:00:00 2001 From: Mason Wu Date: Fri, 30 Aug 2024 00:16:22 +0000 Subject: [PATCH 02/11] Correct a few merge issues --- src/bloaty.cc | 24 ++++++++++++------------ src/bloaty.h | 16 ++++++++-------- src/source_map.cc | 8 ++++---- src/source_map.h | 6 +++--- src/util.cc | 2 +- src/util.h | 6 +++--- src/webassembly.cc | 10 ++++------ 7 files changed, 35 insertions(+), 37 deletions(-) diff --git a/src/bloaty.cc b/src/bloaty.cc index 3c49cd57..a894c230 100644 --- a/src/bloaty.cc +++ b/src/bloaty.cc @@ -49,18 +49,18 @@ typedef size_t z_size_t; #include #include -#include "third_party/absl/debugging/internal/demangle.h" -#include "third_party/absl/memory/memory.h" -#include "third_party/absl/strings/numbers.h" -#include "third_party/absl/strings/str_join.h" -#include "third_party/absl/strings/string_view.h" -#include "third_party/absl/strings/substitute.h" -#include "third_party/bloaty/src/bloaty.h" -#include "third_party/bloaty/src/bloaty.pb.h" -#include "third_party/protobuf/io/zero_copy_stream_impl.h" -#include "net/proto2/public/text_format.h" -#include "third_party/bloaty/src/re.h" -#include "third_party/bloaty/src/util.h" +#include "absl/debugging/internal/demangle.h" +#include "absl/memory/memory.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_join.h" +#include "absl/strings/string_view.h" +#include "absl/strings/substitute.h" +#include "bloaty.h" +#include "bloaty.pb.h" +#include "google/protobuf/io/zero_copy_stream_impl.h" +#include "google/protobuf/text_format.h" +#include "re.h" +#include "util.h" using absl::string_view; diff --git a/src/bloaty.h b/src/bloaty.h index 64e28128..1a59ee0d 100644 --- a/src/bloaty.h +++ b/src/bloaty.h @@ -21,7 +21,7 @@ #include #define __STDC_LIMIT_MACROS -/* #define __STDC_FORMAT_MACROS */ +#define __STDC_FORMAT_MACROS #include #include @@ -31,14 +31,14 @@ #include #include -#include "third_party/absl/strings/string_view.h" -#include "third_party/absl/strings/strip.h" -#include "third_party/capstone/capstone.h" +#include "absl/strings/string_view.h" +#include "absl/strings/strip.h" +#include "capstone/capstone.h" -#include "third_party/bloaty/src/dwarf/debug_info.h" -#include "third_party/bloaty/src/bloaty.pb.h" -#include "third_party/bloaty/src/range_map.h" -#include "third_party/bloaty/src/re.h" +#include "dwarf/debug_info.h" +#include "bloaty.pb.h" +#include "range_map.h" +#include "re.h" namespace bloaty { diff --git a/src/source_map.cc b/src/source_map.cc index a15c583f..584d3422 100644 --- a/src/source_map.cc +++ b/src/source_map.cc @@ -17,11 +17,11 @@ #include #include #include -#include "third_party/bloaty/src/bloaty.h" -#include "third_party/bloaty/src/source_map.h" -#include "third_party/bloaty/src/util.h" +#include "bloaty.h" +#include "source_map.h" +#include "util.h" -#include "third_party/absl/strings/string_view.h" +#include "absl/strings/string_view.h" namespace bloaty { namespace sourcemap { diff --git a/src/source_map.h b/src/source_map.h index 9ddbbb58..ad4b4762 100644 --- a/src/source_map.h +++ b/src/source_map.h @@ -19,10 +19,10 @@ #include #include #include -#include "third_party/bloaty/src/bloaty.h" -#include "third_party/bloaty/src/util.h" +#include "bloaty.h" +#include "util.h" -#include "third_party/absl/strings/string_view.h" +#include "absl/strings/string_view.h" namespace bloaty { namespace sourcemap { diff --git a/src/util.cc b/src/util.cc index b9710dcb..86daec39 100644 --- a/src/util.cc +++ b/src/util.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "third_party/bloaty/src/util.h" +#include "util.h" using absl::string_view; diff --git a/src/util.h b/src/util.h index ace8b5a7..7ddbe221 100644 --- a/src/util.h +++ b/src/util.h @@ -17,9 +17,9 @@ #include -#include "third_party/absl/numeric/int128.h" -#include "third_party/absl/strings/string_view.h" -#include "third_party/absl/strings/substitute.h" +#include "absl/numeric/int128.h" +#include "absl/strings/string_view.h" +#include "absl/strings/substitute.h" namespace bloaty { diff --git a/src/webassembly.cc b/src/webassembly.cc index 23464ce1..62259cb4 100644 --- a/src/webassembly.cc +++ b/src/webassembly.cc @@ -12,13 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include -#include -#include "third_party/bloaty/src/bloaty.h" -#include "third_party/bloaty/src/source_map.h" -#include "third_party/bloaty/src/util.h" +#include "bloaty.h" +#include "source_map.h" +#include "util.h" -#include "third_party/absl/strings/substitute.h" +#include "absl/strings/substitute.h" using absl::string_view; From 0d6cf805f2a8321f606aa05892a1779efd741ab4 Mon Sep 17 00:00:00 2001 From: Mason Wu Date: Fri, 30 Aug 2024 00:26:22 +0000 Subject: [PATCH 03/11] Correct a few merge issues --- src/bloaty.cc | 13 ++++++------- src/bloaty.h | 5 ++--- src/util.cc | 2 +- src/util.h | 1 - 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/bloaty.cc b/src/bloaty.cc index a894c230..7ed3cb43 100644 --- a/src/bloaty.cc +++ b/src/bloaty.cc @@ -1079,7 +1079,7 @@ std::unique_ptr MmapInputFileFactory::OpenFile( RangeSink::RangeSink(const InputFile* file, const Options& options, DataSource data_source, const DualMap* translator, - proto2::Arena* arena) + google::protobuf::Arena* arena) : file_(file), options_(options), data_source_(data_source), @@ -1390,7 +1390,7 @@ absl::string_view RangeSink::ZlibDecompress(absl::string_view data, return absl::string_view(); } unsigned char* dbuf = - arena_->proto2::Arena::CreateArray( + arena_->google::protobuf::Arena::CreateArray( arena_, uncompressed_size); uLongf zliblen = uncompressed_size; if (uncompress(dbuf, &zliblen, (unsigned char*)(data.data()), data.size()) != @@ -1536,13 +1536,13 @@ class Bloaty { std::map sourcemap_files_; // For allocating memory, like to decompress compressed sections. - std::unique_ptr arena_; + std::unique_ptr arena_; }; Bloaty::Bloaty(const InputFileFactory& factory, const Options& options) : file_factory_(factory), options_(options), - arena_(std::make_unique()) { + arena_(std::make_unique()) { AddBuiltInSources(data_sources, options); } @@ -2126,8 +2126,8 @@ bool DoParseOptions(bool skip_unknown, int* argc, char** argv[], if (!input_file.is_open()) { THROWF("couldn't open file $0", option); } - proto2::io::IstreamInputStream stream(&input_file); - if (!proto2::TextFormat::Merge(&stream, options)) { + google::protobuf::io::IstreamInputStream stream(&input_file); + if (!google::protobuf::TextFormat::Merge(&stream, options)) { THROWF("error parsing configuration out of file $0", option); } } else if (args.TryParseOption("-d", &option)) { @@ -2325,4 +2325,3 @@ bool BloatyMain(const Options& options, const InputFileFactory& file_factory, } } // namespace bloaty - diff --git a/src/bloaty.h b/src/bloaty.h index 1a59ee0d..f7a49d7f 100644 --- a/src/bloaty.h +++ b/src/bloaty.h @@ -113,7 +113,7 @@ class RangeSink { public: RangeSink(const InputFile *file, const Options &options, DataSource data_source, const DualMap *translator, - proto2::Arena *arena); + google::protobuf::Arena *arena); RangeSink(const RangeSink &) = delete; RangeSink &operator=(const RangeSink &) = delete; ~RangeSink(); @@ -230,7 +230,7 @@ class RangeSink { DataSource data_source_; const DualMap* translator_; std::vector> outputs_; - proto2::Arena *arena_; + google::protobuf::Arena *arena_; }; // NameMunger ////////////////////////////////////////////////////////////////// @@ -447,4 +447,3 @@ bool BloatyMain(const Options& options, const InputFileFactory& file_factory, } // namespace bloaty #endif - diff --git a/src/util.cc b/src/util.cc index 86daec39..2ca22cc7 100644 --- a/src/util.cc +++ b/src/util.cc @@ -18,6 +18,7 @@ using absl::string_view; namespace bloaty { +ABSL_ATTRIBUTE_NORETURN void Throw(const char *str, int line) { throw bloaty::Error(str, __FILE__, line); } @@ -61,4 +62,3 @@ void SkipWhitespace(absl::string_view* data) { } } // namespace bloaty - diff --git a/src/util.h b/src/util.h index 7ddbe221..a12b439d 100644 --- a/src/util.h +++ b/src/util.h @@ -194,4 +194,3 @@ void SkipWhitespace(absl::string_view* data); } // namespace bloaty #endif // BLOATY_UTIL_H_ - From 959061d764a2866d1d890294ea7b0908e3575fd1 Mon Sep 17 00:00:00 2001 From: Mason Wu Date: Fri, 30 Aug 2024 00:28:32 +0000 Subject: [PATCH 04/11] Correct a few merge issues --- src/bloaty.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/bloaty.cc b/src/bloaty.cc index 7ed3cb43..a4022c1a 100644 --- a/src/bloaty.cc +++ b/src/bloaty.cc @@ -1334,7 +1334,10 @@ void RangeSink::AddRange(const char* analyzer, string_view name, if (translator_) { if (!translator_->vm_map.CoversRange(vmaddr, vmsize) || !translator_->file_map.CoversRange(fileoff, filesize)) { - THROW("Tried to add range that is not covered by base map."); + WARN("AddRange($0, $1, $2, $3, $4) will be ignored, because it is not " + "covered by base map.", + name.data(), vmaddr, vmsize, fileoff, filesize); + return; } } From 99359a7e3f82a90cae8f77dca4b5449eba70d56a Mon Sep 17 00:00:00 2001 From: Mason Wu Date: Fri, 30 Aug 2024 00:57:14 +0000 Subject: [PATCH 05/11] Fix build issues --- CMakeLists.txt | 2 ++ src/webassembly.cc | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) 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/src/webassembly.cc b/src/webassembly.cc index 62259cb4..22ef45bf 100644 --- a/src/webassembly.cc +++ b/src/webassembly.cc @@ -463,7 +463,7 @@ class WebAssemblyObjectFile : public ObjectFile { &debug_file())) { source_map->ProcessFileToSink(sink); } else { - THROWF("Data source $0 requires a source map", sink->data_source()); + THROW("Data source requires a source map"); } break; case DataSource::kArchiveMembers: From 09ae24560397861845304e4ea101221f6213fd2f Mon Sep 17 00:00:00 2001 From: Mason Wu Date: Mon, 16 Sep 2024 21:38:21 +0000 Subject: [PATCH 06/11] Use string::assign in WebAssemblyObjectFile::GetBuildId. --- src/webassembly.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/webassembly.cc b/src/webassembly.cc index 22ef45bf..d1eea7b6 100644 --- a/src/webassembly.cc +++ b/src/webassembly.cc @@ -436,8 +436,7 @@ class WebAssemblyObjectFile : public ObjectFile { uint32_t size = ReadVarUInt32(§ion.contents); string_view source_mapping_url = ReadPiece(size, §ion.contents); - id.resize(size); - memcpy(&id[0], &source_mapping_url[0], size); + id.assign(source_mapping_url); }); return id; From 379fe0c5761f34e86bef21ba3e093b9d32ed593d Mon Sep 17 00:00:00 2001 From: Mason Wu Date: Wed, 18 Sep 2024 18:27:59 +0000 Subject: [PATCH 07/11] Use file name as a fallback for matching file with source map. --- src/bloaty.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/bloaty.cc b/src/bloaty.cc index a4022c1a..1d08090b 100644 --- a/src/bloaty.cc +++ b/src/bloaty.cc @@ -1778,6 +1778,10 @@ void Bloaty::ScanAndRollupFile(const std::string& filename, Rollup* rollup, // 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)); From b74a8ddc35a314baf994312ae896e5d6114353e6 Mon Sep 17 00:00:00 2001 From: Mason Wu Date: Tue, 1 Oct 2024 20:08:09 +0000 Subject: [PATCH 08/11] Add clarification to Wasm source map tests around short fallback. --- tests/wasm/sourcemap_compileunits.test | 3 ++- tests/wasm/sourcemap_inlines.test | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/wasm/sourcemap_compileunits.test b/tests/wasm/sourcemap_compileunits.test index ff29e032..7bc04862 100644 --- a/tests/wasm/sourcemap_compileunits.test +++ b/tests/wasm/sourcemap_compileunits.test @@ -20,7 +20,8 @@ Sections: # 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 this file. +# 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 diff --git a/tests/wasm/sourcemap_inlines.test b/tests/wasm/sourcemap_inlines.test index 8c094088..4287944d 100644 --- a/tests/wasm/sourcemap_inlines.test +++ b/tests/wasm/sourcemap_inlines.test @@ -20,7 +20,8 @@ Sections: # 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 this line. +# 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] From eada8dbf61da6922eebb6290b0eef9b95816a978 Mon Sep 17 00:00:00 2001 From: Mason Wu Date: Tue, 1 Oct 2024 20:11:24 +0000 Subject: [PATCH 09/11] Add testing for 2 different source maps provided for a Wasm diff. --- tests/wasm/sourcemap_diff.test | 36 ++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 tests/wasm/sourcemap_diff.test 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] + From 0fc4a54872fd4d95ade4eba8aa96f387ce3016a1 Mon Sep 17 00:00:00 2001 From: Mason Wu Date: Tue, 1 Oct 2024 20:23:50 +0000 Subject: [PATCH 10/11] Add doc for source-map option to usage string. --- doc/using.md | 5 +++++ src/bloaty.cc | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/doc/using.md b/doc/using.md index c05359b5..7dca1509 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 diff --git a/src/bloaty.cc b/src/bloaty.cc index 1d08090b..e5d1f033 100644 --- a/src/bloaty.cc +++ b/src/bloaty.cc @@ -1973,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 From 4ba799b0da5e056fb5128623715e57820cb6767f Mon Sep 17 00:00:00 2001 From: Mason Wu Date: Tue, 1 Oct 2024 21:15:07 +0000 Subject: [PATCH 11/11] Add doc for source-map option to using.md. --- doc/using.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/doc/using.md b/doc/using.md index 7dca1509..44a14845 100644 --- a/doc/using.md +++ b/doc/using.md @@ -850,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