diff --git a/src/bloaty.cc b/src/bloaty.cc index f489c60..a655715 100644 --- a/src/bloaty.cc +++ b/src/bloaty.cc @@ -1107,12 +1107,13 @@ std::unique_ptr MmapInputFileFactory::OpenFile( RangeSink::RangeSink(const InputFile* file, const Options& options, DataSource data_source, const DualMap* translator, - google::protobuf::Arena* arena) + google::protobuf::Arena* arena, int segment_id) : file_(file), options_(options), data_source_(data_source), translator_(translator), - arena_(arena) {} + arena_(arena), + segment_id_(segment_id) {} RangeSink::~RangeSink() {} @@ -1148,11 +1149,11 @@ bool RangeSink::IsVerboseForVMRange(uint64_t vmaddr, uint64_t vmsize) { RangeMap vm_map; RangeMap file_map; bool contains = false; - vm_map.AddRangeWithTranslation(vmaddr, vmsize, "", translator_->vm_map, + vm_map.AddRangeWithTranslation(VMAddr(segment_id_, vmaddr), vmsize, "", translator_->vm_map, false, &file_map); file_map.ForEachRange( - [this, &contains](uint64_t fileoff, uint64_t filesize) { - if (ContainsVerboseFileOffset(fileoff, filesize)) { + [this, &contains](VMAddr fileoff, uint64_t filesize) { + if (ContainsVerboseFileOffset(fileoff.address, filesize)) { contains = true; } }); @@ -1180,10 +1181,10 @@ bool RangeSink::IsVerboseForFileRange(uint64_t fileoff, uint64_t filesize) { RangeMap vm_map; RangeMap file_map; bool contains = false; - file_map.AddRangeWithTranslation(fileoff, filesize, "", + file_map.AddRangeWithTranslation(VMAddr(fileoff), filesize, "", translator_->file_map, false, &vm_map); - vm_map.ForEachRange([this, &contains](uint64_t vmaddr, uint64_t vmsize) { - if (ContainsVerboseVMAddr(vmaddr, vmsize)) { + vm_map.ForEachRange([this, &contains](VMAddr vmaddr, uint64_t vmsize) { + if (ContainsVerboseVMAddr(vmaddr.address, vmsize)) { contains = true; } }); @@ -1209,14 +1210,14 @@ void RangeSink::AddFileRange(const char* analyzer, string_view name, const std::string label = pair.second->Munge(name); if (translator_) { bool ok = pair.first->file_map.AddRangeWithTranslation( - fileoff, filesize, label, translator_->file_map, verbose, + VMAddr(fileoff), filesize, label, translator_->file_map, verbose, &pair.first->vm_map); if (!ok) { WARN("File range ($0, $1) for label $2 extends beyond base map", fileoff, filesize, name); } } else { - pair.first->file_map.AddRange(fileoff, filesize, label); + pair.first->file_map.AddRange(VMAddr(fileoff), filesize, label); } } } @@ -1234,9 +1235,9 @@ void RangeSink::AddFileRangeForVMAddr(const char* analyzer, assert(translator_); for (auto& pair : outputs_) { std::string label; - if (pair.first->vm_map.TryGetLabel(label_from_vmaddr, &label)) { + if (pair.first->vm_map.TryGetLabel(VMAddr(segment_id_, label_from_vmaddr), &label)) { bool ok = pair.first->file_map.AddRangeWithTranslation( - file_offset, file_range.size(), label, translator_->file_map, verbose, + VMAddr(file_offset), file_range.size(), label, translator_->file_map, verbose, &pair.first->vm_map); if (!ok) { WARN("File range ($0, $1) for label $2 extends beyond base map", @@ -1264,9 +1265,9 @@ void RangeSink::AddFileRangeForFileRange(const char* analyzer, for (auto& pair : outputs_) { std::string label; if (pair.first->file_map.TryGetLabelForRange( - from_file_offset, from_file_range.size(), &label)) { + VMAddr(from_file_offset), from_file_range.size(), &label)) { bool ok = pair.first->file_map.AddRangeWithTranslation( - file_offset, file_range.size(), label, translator_->file_map, verbose, + VMAddr(file_offset), file_range.size(), label, translator_->file_map, verbose, &pair.first->vm_map); if (!ok) { WARN("File range ($0, $1) for label $2 extends beyond base map", @@ -1284,17 +1285,17 @@ void RangeSink::AddVMRangeForVMAddr(const char* analyzer, uint64_t size) { bool verbose = IsVerboseForVMRange(addr, size); if (verbose) { - printf("[%s, %s] AddVMRangeForVMAddr(%" PRIx64 ", [%" PRIx64 ", %" PRIx64 - "])\n", - GetDataSourceLabel(data_source_), analyzer, label_from_vmaddr, addr, - size); + printf("[%s, %s] AddVMRangeForVMAddr(seg=%d label_from=%" PRIx64 ", seg=%d addr=%" PRIx64 ", %" PRIx64 + ")\n", + GetDataSourceLabel(data_source_), analyzer, segment_id_, label_from_vmaddr, + segment_id_, addr, size); } assert(translator_); for (auto& pair : outputs_) { std::string label; - if (pair.first->vm_map.TryGetLabel(label_from_vmaddr, &label)) { + if (pair.first->vm_map.TryGetLabel(VMAddr(segment_id_, label_from_vmaddr), &label)) { bool ok = pair.first->vm_map.AddRangeWithTranslation( - addr, size, label, translator_->vm_map, verbose, + VMAddr(segment_id_, addr), size, label, translator_->vm_map, verbose, &pair.first->file_map); if (!ok && verbose_level > 1) { WARN("VM range ($0, $1) for label $2 extends beyond base map", addr, @@ -1310,15 +1311,15 @@ void RangeSink::AddVMRange(const char* analyzer, uint64_t vmaddr, uint64_t vmsize, const std::string& name) { bool verbose = IsVerboseForVMRange(vmaddr, vmsize); if (verbose) { - printf("[%s, %s] AddVMRange(%.*s, %" PRIx64 ", %" PRIx64 ")\n", + printf("[%s, %s] AddVMRange(%.*s, seg=%d vmaddr=%" PRIx64 ", %" PRIx64 ")\n", GetDataSourceLabel(data_source_), analyzer, (int)name.size(), - name.data(), vmaddr, vmsize); + name.data(), segment_id_, vmaddr, vmsize); } assert(translator_); for (auto& pair : outputs_) { const std::string label = pair.second->Munge(name); bool ok = pair.first->vm_map.AddRangeWithTranslation( - vmaddr, vmsize, label, translator_->vm_map, verbose, + VMAddr(segment_id_, vmaddr), vmsize, label, translator_->vm_map, verbose, &pair.first->file_map); if (!ok) { WARN("VM range ($0, $1) for label $2 extends beyond base map", vmaddr, @@ -1353,15 +1354,15 @@ void RangeSink::AddRange(const char* analyzer, string_view name, if (IsVerboseForVMRange(vmaddr, vmsize) || IsVerboseForFileRange(fileoff, filesize)) { - printf("[%s, %s] AddRange(%.*s, %" PRIx64 ", %" PRIx64 ", %" PRIx64 + printf("[%s, %s] AddRange(%.*s, seg=%d vmaddr=%" PRIx64 ", %" PRIx64 ", %" PRIx64 ", %" PRIx64 ")\n", GetDataSourceLabel(data_source_), analyzer, (int)name.size(), - name.data(), vmaddr, vmsize, fileoff, filesize); + name.data(), segment_id_, vmaddr, vmsize, fileoff, filesize); } if (translator_) { - if (!translator_->vm_map.CoversRange(vmaddr, vmsize) || - !translator_->file_map.CoversRange(fileoff, filesize)) { + if (!translator_->vm_map.CoversRange(VMAddr(segment_id_, vmaddr), vmsize) || + !translator_->file_map.CoversRange(VMAddr(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); @@ -1373,11 +1374,11 @@ void RangeSink::AddRange(const char* analyzer, string_view name, const std::string label = pair.second->Munge(name); uint64_t common = std::min(vmsize, filesize); - pair.first->vm_map.AddDualRange(vmaddr, common, fileoff, label); - pair.first->file_map.AddDualRange(fileoff, common, vmaddr, label); + pair.first->vm_map.AddDualRange(VMAddr(segment_id_, vmaddr), common, VMAddr(fileoff), label); + pair.first->file_map.AddDualRange(VMAddr(fileoff), common, VMAddr(segment_id_, vmaddr), label); - pair.first->vm_map.AddRange(vmaddr + common, vmsize - common, label); - pair.first->file_map.AddRange(fileoff + common, filesize - common, label); + pair.first->vm_map.AddRange(VMAddr(segment_id_, vmaddr + common), vmsize - common, label); + pair.first->file_map.AddRange(VMAddr(fileoff + common), filesize - common, label); } } @@ -1386,7 +1387,7 @@ uint64_t RangeSink::TranslateFileToVM(const char* ptr) { uint64_t offset = ptr - file_->data().data(); uint64_t translated; if (!FileContainsPointer(ptr) || - !translator_->file_map.Translate(offset, &translated)) { + !translator_->file_map.Translate(VMAddr(0, offset), &translated)) { THROWF("Can't translate file offset ($0) to VM, contains: $1, map:\n$2", offset, FileContainsPointer(ptr), translator_->file_map.DebugString().c_str()); @@ -1397,7 +1398,7 @@ uint64_t RangeSink::TranslateFileToVM(const char* ptr) { std::string_view RangeSink::TranslateVMToFile(uint64_t address) { assert(translator_); uint64_t translated; - if (!translator_->vm_map.Translate(address, &translated) || + if (!translator_->vm_map.Translate(VMAddr(segment_id_, address), &translated) || translated > file_->data().size()) { THROWF("Can't translate VM pointer ($0) to file", address); @@ -1868,26 +1869,36 @@ struct DualMaps { map->file_map.Compress(); } RangeMap::ComputeRollup(VmMaps(), [=](const std::vector& keys, - uint64_t addr, uint64_t end) { + VMAddr addr, VMAddr end) { return rollup->AddSizes(keys, end - addr, true); }); RangeMap::ComputeRollup( FileMaps(), - [=](const std::vector& keys, uint64_t addr, uint64_t end) { + [=](const std::vector& keys, VMAddr addr, VMAddr end) { return rollup->AddSizes(keys, end - addr, false); }); } void PrintMaps(const std::vector maps) { - uint64_t last = 0; - uint64_t max = maps[0]->GetMaxAddress(); - int hex_digits = max > 0 ? std::ceil(std::log2(max) / 4) : 0; + VMAddr last(0, 0); + int last_segment = -1; + VMAddr max = maps[0]->GetMaxAddress(); + int hex_digits = max.address > 0 ? std::ceil(std::log2(max.address) / 4) : 0; RangeMap::ComputeRollup(maps, [&](const std::vector& keys, - uint64_t addr, uint64_t end) { - if (addr > last) { - PrintMapRow("[-- Nothing mapped --]", last, addr, hex_digits); + VMAddr addr, VMAddr end) { + if (addr.segment != last_segment) { + if (last_segment != -1) { + printf("\n"); + } + printf("SEGMENT %d:\n", addr.segment); + last = VMAddr(addr.segment, 0); + last_segment = addr.segment; + } + + if (addr.address > last.address) { + PrintMapRow("[-- Nothing mapped --]", last.address, addr.address, hex_digits); } - PrintMapRow(KeysToString(keys), addr, end, hex_digits); + PrintMapRow(KeysToString(keys), addr.address, end.address, hex_digits); last = end; }); printf("\n"); @@ -2004,14 +2015,14 @@ void Bloaty::ScanAndRollupFile(const std::string& filename, Rollup* rollup, // kInputFile source: Copy the base map to the filename sink(s). for (auto sink : filename_sink_ptrs) { maps.base_map()->vm_map.ForEachRange( - [sink](uint64_t start, uint64_t length) { - sink->AddVMRange("inputfile_vmcopier", start, length, + [sink](VMAddr start, uint64_t length) { + sink->AddVMRange("inputfile_vmcopier", start.address, length, sink->input_file().filename()); }); maps.base_map()->file_map.ForEachRange( - [sink](uint64_t start, uint64_t length) { + [sink](VMAddr start, uint64_t length) { sink->AddFileRange("inputfile_filecopier", - sink->input_file().filename(), start, length); + sink->input_file().filename(), start.address, length); }); } diff --git a/src/bloaty.h b/src/bloaty.h index 85515fc..9b99822 100644 --- a/src/bloaty.h +++ b/src/bloaty.h @@ -114,7 +114,7 @@ class RangeSink { public: RangeSink(const InputFile *file, const Options &options, DataSource data_source, const DualMap *translator, - google::protobuf::Arena *arena); + google::protobuf::Arena *arena, int segment_id = 0); RangeSink(const RangeSink &) = delete; RangeSink &operator=(const RangeSink &) = delete; ~RangeSink(); @@ -126,6 +126,8 @@ class RangeSink { DataSource data_source() const { return data_source_; } const InputFile &input_file() const { return *file_; } bool IsBaseMap() const { return translator_ == nullptr; } + int segment_id() const { return segment_id_; } + void set_segment_id(int segment_id) { segment_id_ = segment_id; } // If vmsize or filesize is zero, this mapping is presumed not to exist in // that domain. For example, .bss mappings don't exist in the file, and @@ -238,6 +240,7 @@ class RangeSink { const DualMap* translator_; std::vector> outputs_; google::protobuf::Arena *arena_; + int segment_id_; }; // NameMunger ////////////////////////////////////////////////////////////////// diff --git a/src/disassemble.cc b/src/disassemble.cc index 4cf8839..befbed9 100644 --- a/src/disassemble.cc +++ b/src/disassemble.cc @@ -212,7 +212,7 @@ std::string DisassembleFunction(const DisassemblyInfo& info) { } else { op_str = "<" + std::to_string(iter->second); } - } else if (info.symbol_map.vm_map.TryGetLabel(target, &label)) { + } else if (info.symbol_map.vm_map.TryGetLabel(VMAddr(target), &label)) { op_str = label; } } diff --git a/src/dwarf.cc b/src/dwarf.cc index 04bca7b..6fa1a4a 100644 --- a/src/dwarf.cc +++ b/src/dwarf.cc @@ -440,7 +440,7 @@ void AddDIE(const dwarf::CU& cu, const GeneralDIE& die, // Unfortunately the location doesn't include a size, so we look that part // up in the symbol map. uint64_t size; - if (symbol_map.vm_map.TryGetSize(addr, &size)) { + if (symbol_map.vm_map.TryGetSize(VMAddr(addr), &size)) { sink->AddVMRangeIgnoreDuplicate("dwarf_location", addr, size, cu.unit_name()); } else { diff --git a/src/elf.cc b/src/elf.cc index ac0f610..a648c7d 100644 --- a/src/elf.cc +++ b/src/elf.cc @@ -950,7 +950,7 @@ static void ReadELFSymbols(const InputFile& file, RangeSink* sink, } // TODO(brandonvu) Continue if VM pointer cannot be translated. Issue #315 uint64_t unused; - if (!sink->Translator()->vm_map.Translate(full_addr, &unused)) { + if (!sink->Translator()->vm_map.Translate(VMAddr(full_addr), &unused)) { WARN("Can't translate VM pointer ($0) to file", full_addr); continue; } @@ -1462,7 +1462,7 @@ class ElfObjectFile : public ObjectFile { // symbolized. uint64_t fileoff; - if (!base_map.vm_map.Translate(vmaddr, &fileoff)) { + if (!base_map.vm_map.Translate(VMAddr(vmaddr), &fileoff)) { THROWF("Couldn't translate VM address for function $0", symbol); } diff --git a/src/macho.cc b/src/macho.cc index cd826fe..dd6438e 100644 --- a/src/macho.cc +++ b/src/macho.cc @@ -74,6 +74,7 @@ struct LoadCommand { uint32_t cmd; string_view command_data; string_view file_data; + int segment_id; // Architecture index for universal binaries }; template @@ -84,7 +85,7 @@ bool Is64Bit() { return true; } template void ParseMachOHeaderImpl(string_view macho_data, RangeSink* overhead_sink, - Func&& loadcmd_func) { + int segment_id, Func&& loadcmd_func) { string_view header_data = macho_data; auto header = GetStructPointerAndAdvance(&header_data); MaybeAddOverhead(overhead_sink, @@ -107,6 +108,7 @@ void ParseMachOHeaderImpl(string_view macho_data, RangeSink* overhead_sink, data.cmd = command->cmd; data.command_data = StrictSubstr(header_data, 0, command->cmdsize); data.file_data = macho_data; + data.segment_id = segment_id; std::forward(loadcmd_func)(data); MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", data.command_data); @@ -116,7 +118,7 @@ void ParseMachOHeaderImpl(string_view macho_data, RangeSink* overhead_sink, template void ParseMachOHeader(string_view macho_file, RangeSink* overhead_sink, - Func&& loadcmd_func) { + int segment_id, Func&& loadcmd_func) { uint32_t magic = ReadMagic(macho_file); switch (magic) { case MH_MAGIC: @@ -127,12 +129,12 @@ void ParseMachOHeader(string_view macho_file, RangeSink* overhead_sink, // Still, you can build 32-bit binaries as of this writing, and // there are existing 32-bit binaries floating around, so we might // as well support them. - ParseMachOHeaderImpl(macho_file, overhead_sink, + ParseMachOHeaderImpl(macho_file, overhead_sink, segment_id, std::forward(loadcmd_func)); break; case MH_MAGIC_64: ParseMachOHeaderImpl( - macho_file, overhead_sink, std::forward(loadcmd_func)); + macho_file, overhead_sink, segment_id, std::forward(loadcmd_func)); break; case MH_CIGAM: case MH_CIGAM_64: @@ -167,7 +169,8 @@ void ParseFatHeader(string_view fat_file, RangeSink* overhead_sink, auto arch = GetStructPointerAndAdvance(&header_data); string_view macho_data = StrictSubstr( fat_file, ByteSwap(arch->offset), ByteSwap(arch->size)); - ParseMachOHeader(macho_data, overhead_sink, + // Pass architecture index as segment_id for multi-arch binaries + ParseMachOHeader(macho_data, overhead_sink, i, std::forward(loadcmd_func)); } } @@ -181,7 +184,8 @@ void ForEachLoadCommand(string_view maybe_fat_file, RangeSink* overhead_sink, case MH_MAGIC_64: case MH_CIGAM: case MH_CIGAM_64: - ParseMachOHeader(maybe_fat_file, overhead_sink, + // Single-architecture binary uses segment_id = 0 + ParseMachOHeader(maybe_fat_file, overhead_sink, 0, std::forward(loadcmd_func)); break; case FAT_CIGAM: @@ -417,7 +421,10 @@ void ParseLoadCommand(const LoadCommand& cmd, RangeSink* sink) { void ParseLoadCommands(RangeSink* sink) { ForEachLoadCommand( sink->input_file().data(), sink, - [sink](const LoadCommand& cmd) { ParseLoadCommand(cmd, sink); }); + [sink](const LoadCommand& cmd) { + sink->set_segment_id(cmd.segment_id); + ParseLoadCommand(cmd, sink); + }); } template diff --git a/src/range_map.cc b/src/range_map.cc index 71e4f0b..dad480a 100644 --- a/src/range_map.cc +++ b/src/range_map.cc @@ -21,16 +21,17 @@ namespace bloaty { constexpr uint64_t RangeMap::kUnknownSize; template -uint64_t RangeMap::TranslateWithEntry(T iter, uint64_t addr) const { +VMAddr RangeMap::TranslateWithEntry(T iter, VMAddr addr) const { assert(EntryContains(iter, addr)); assert(iter->second.HasTranslation()); - return addr - iter->first + iter->second.other_start; + uint64_t offset = addr.address - iter->first.address; + return iter->second.other_start + offset; } template -bool RangeMap::TranslateAndTrimRangeWithEntry(T iter, uint64_t addr, - uint64_t size, uint64_t* trimmed_addr, - uint64_t* translated_addr, +bool RangeMap::TranslateAndTrimRangeWithEntry(T iter, VMAddr addr, + uint64_t size, VMAddr* trimmed_addr, + VMAddr* translated_addr, uint64_t* trimmed_size) const { addr = std::max(addr, iter->first); *trimmed_addr = addr; @@ -38,7 +39,7 @@ bool RangeMap::TranslateAndTrimRangeWithEntry(T iter, uint64_t addr, if (size == kUnknownSize) { *trimmed_size = kUnknownSize; } else { - uint64_t end = std::min(addr + size, iter->first + iter->second.size); + VMAddr end = std::min(addr + size, iter->first + iter->second.size); if (addr >= end) { *trimmed_size = 0; return false; @@ -54,7 +55,7 @@ bool RangeMap::TranslateAndTrimRangeWithEntry(T iter, uint64_t addr, return true; } -RangeMap::Map::const_iterator RangeMap::FindContaining(uint64_t addr) const { +RangeMap::Map::const_iterator RangeMap::FindContaining(VMAddr addr) const { auto it = mappings_.upper_bound(addr); // Entry directly after. if (it == mappings_.begin() || (--it, !EntryContains(it, addr))) { return mappings_.end(); @@ -63,7 +64,7 @@ RangeMap::Map::const_iterator RangeMap::FindContaining(uint64_t addr) const { } } -RangeMap::Map::iterator RangeMap::FindContainingOrAfter(uint64_t addr) { +RangeMap::Map::iterator RangeMap::FindContainingOrAfter(VMAddr addr) { auto after = mappings_.upper_bound(addr); auto it = after; if (it != mappings_.begin() && (--it, EntryContains(it, addr))) { @@ -74,7 +75,7 @@ RangeMap::Map::iterator RangeMap::FindContainingOrAfter(uint64_t addr) { } RangeMap::Map::const_iterator RangeMap::FindContainingOrAfter( - uint64_t addr) const { + VMAddr addr) const { auto after = mappings_.upper_bound(addr); auto it = after; if (it != mappings_.begin() && (--it, EntryContains(it, addr))) { @@ -84,17 +85,18 @@ RangeMap::Map::const_iterator RangeMap::FindContainingOrAfter( } } -bool RangeMap::Translate(uint64_t addr, uint64_t* translated) const { +bool RangeMap::Translate(VMAddr addr, uint64_t* translated) const { auto iter = FindContaining(addr); if (iter == mappings_.end() || !iter->second.HasTranslation()) { return false; } else { - *translated = TranslateWithEntry(iter, addr); + VMAddr translated_addr = TranslateWithEntry(iter, addr); + *translated = translated_addr.address; return true; } } -bool RangeMap::TryGetLabel(uint64_t addr, std::string* label) const { +bool RangeMap::TryGetLabel(VMAddr addr, std::string* label) const { auto iter = FindContaining(addr); if (iter == mappings_.end()) { return false; @@ -104,9 +106,9 @@ bool RangeMap::TryGetLabel(uint64_t addr, std::string* label) const { } } -bool RangeMap::TryGetLabelForRange(uint64_t addr, uint64_t size, +bool RangeMap::TryGetLabelForRange(VMAddr addr, uint64_t size, std::string* label) const { - uint64_t end = addr + size; + VMAddr end = addr + size; if (end < addr) { return false; } @@ -125,7 +127,7 @@ bool RangeMap::TryGetLabelForRange(uint64_t addr, uint64_t size, } } -bool RangeMap::TryGetSize(uint64_t addr, uint64_t* size) const { +bool RangeMap::TryGetSize(VMAddr addr, uint64_t* size) const { auto iter = mappings_.find(addr); if (iter == mappings_.end()) { return false; @@ -143,31 +145,32 @@ std::string RangeMap::DebugString() const { return ret; } -void RangeMap::AddRange(uint64_t addr, uint64_t size, const std::string& val) { - AddDualRange(addr, size, kNoTranslation, val); +void RangeMap::AddRange(VMAddr addr, uint64_t size, const std::string& val) { + AddDualRange(addr, size, kNoTranslation(), val); } template -void RangeMap::MaybeSetLabel(T iter, const std::string& label, uint64_t addr, - uint64_t size) { +void RangeMap::MaybeSetLabel(T iter, const std::string& label, VMAddr addr, + VMAddr end) { assert(EntryContains(iter, addr)); + uint64_t size = (end > addr) ? (end - addr) : kUnknownSize; if (iter->second.size == kUnknownSize && size != kUnknownSize) { - assert(addr + size >= addr); - assert(addr + size >= iter->first); + assert(end >= addr); + assert(end >= iter->first); assert(addr >= iter->first); if (addr == iter->first) { T next = std::next(iter); - uint64_t end = addr + size; + VMAddr actual_end = end; if (!IterIsEnd(next)) { - end = std::min(end, next->first); + actual_end = std::min(end, next->first); } - uint64_t new_size = end - iter->first; + uint64_t new_size = actual_end - iter->first; if (verbose_level > 2) { printf(" updating mapping (%s) with new size %" PRIx64 "\n", - EntryDebugString(addr, size, UINT64_MAX, label).c_str(), + EntryDebugString(addr, end - addr, kNoTranslation(), label).c_str(), new_size); } - // This new defined range encompassess all of the unknown-length range, so + // This new defined range encompasses all of the unknown-length range, so // just define the range to have our end. iter->second.size = new_size; CheckConsistency(iter); @@ -178,11 +181,12 @@ void RangeMap::MaybeSetLabel(T iter, const std::string& label, uint64_t addr, } } -void RangeMap::AddDualRange(uint64_t addr, uint64_t size, uint64_t otheraddr, +void RangeMap::AddDualRange(VMAddr addr, uint64_t size, VMAddr otheraddr, const std::string& label) { if (verbose_level > 2) { - printf("%p AddDualRange([%" PRIx64 ", %" PRIx64 "], %" PRIx64 ", %s)\n", - this, addr, size, otheraddr, label.c_str()); + printf("%p AddDualRange([%d:%" PRIx64 ", %" PRIx64 "], [%d:%" PRIx64 "], %s)\n", + this, addr.segment, addr.address, size, + otheraddr.segment, otheraddr.address, label.c_str()); } if (size == 0) return; @@ -190,12 +194,12 @@ void RangeMap::AddDualRange(uint64_t addr, uint64_t size, uint64_t otheraddr, auto it = FindContainingOrAfter(addr); if (size == kUnknownSize) { - assert(otheraddr == kNoTranslation); - if (it != mappings_.end() && EntryContainsStrict(it, addr)) { - MaybeSetLabel(it, label, addr, kUnknownSize); - } else { + assert(otheraddr == kNoTranslation()); + // Only create a new entry if one doesn't already exist at this address. + // If an entry exists, leave it unchanged + if (it == mappings_.end() || !EntryContainsStrict(it, addr)) { auto iter = mappings_.emplace_hint( - it, std::make_pair(addr, Entry(label, kUnknownSize, kNoTranslation))); + it, std::make_pair(addr, Entry(label, kUnknownSize, kNoTranslation()))); if (verbose_level > 2) { printf(" added entry: %s\n", EntryDebugString(iter).c_str()); } @@ -203,8 +207,8 @@ void RangeMap::AddDualRange(uint64_t addr, uint64_t size, uint64_t otheraddr, return; } - const uint64_t base = addr; - uint64_t end = addr + size; + const VMAddr base = addr; + VMAddr end = addr + size; assert(end >= addr); while (1) { @@ -212,7 +216,7 @@ void RangeMap::AddDualRange(uint64_t addr, uint64_t size, uint64_t otheraddr, // gap. while (addr < end && !IterIsEnd(it) && EntryContains(it, addr)) { assert(end >= addr); - MaybeSetLabel(it, label, addr, end - addr); + MaybeSetLabel(it, label, addr, end); addr = RangeEndUnknownLimit(it, addr); ++it; } @@ -223,14 +227,15 @@ void RangeMap::AddDualRange(uint64_t addr, uint64_t size, uint64_t otheraddr, // We found a gap and need to create an entry. Need to make sure the new // entry doesn't extend into a range that was previously defined. - uint64_t this_end = end; + VMAddr this_end = end; if (it != mappings_.end() && end > it->first) { assert(it->first >= addr); this_end = std::min(end, it->first); } - uint64_t other = (otheraddr == kNoTranslation) ? kNoTranslation - : addr - base + otheraddr; + VMAddr other = (otheraddr == kNoTranslation()) + ? kNoTranslation() + : otheraddr + (addr.address - base.address); assert(this_end >= addr); auto iter = mappings_.emplace_hint( it, std::make_pair(addr, Entry(label, this_end - addr, other))); @@ -250,13 +255,13 @@ void RangeMap::AddDualRange(uint64_t addr, uint64_t size, uint64_t otheraddr, // span several section mappings. If we really wanted to get particular here, // we could pass a parameter indicating whether such spanning is expected, and // warn if not. -bool RangeMap::AddRangeWithTranslation(uint64_t addr, uint64_t size, +bool RangeMap::AddRangeWithTranslation(VMAddr addr, uint64_t size, const std::string& val, const RangeMap& translator, bool verbose, RangeMap* other) { auto it = translator.FindContaining(addr); - uint64_t end; + VMAddr end; if (size == kUnknownSize) { end = addr + 1; } else { @@ -269,14 +274,14 @@ bool RangeMap::AddRangeWithTranslation(uint64_t addr, uint64_t size, // cases this would be a bug (ie. symbols VM->file). In other cases it's // totally normal (ie. archive members file->VM). while (!translator.IterIsEnd(it) && it->first < end) { - uint64_t translated_addr; - uint64_t trimmed_addr; + VMAddr translated_addr; + VMAddr trimmed_addr; uint64_t trimmed_size; if (translator.TranslateAndTrimRangeWithEntry( it, addr, size, &trimmed_addr, &translated_addr, &trimmed_size)) { if (verbose_level > 2 || verbose) { - printf(" -> translates to: [%" PRIx64 " %" PRIx64 "]\n", translated_addr, - trimmed_size); + printf(" -> translates to: [%d:%" PRIx64 " %" PRIx64 "]\n", + translated_addr.segment, translated_addr.address, trimmed_size); } other->AddRange(translated_addr, trimmed_size, val); } @@ -304,9 +309,9 @@ void RangeMap::Compress() { } } -bool RangeMap::CoversRange(uint64_t addr, uint64_t size) const { +bool RangeMap::CoversRange(VMAddr addr, uint64_t size) const { auto it = FindContaining(addr); - uint64_t end = addr + size; + VMAddr end = addr + size; assert(end >= addr); while (true) { @@ -320,9 +325,9 @@ bool RangeMap::CoversRange(uint64_t addr, uint64_t size) const { } } -uint64_t RangeMap::GetMaxAddress() const { +VMAddr RangeMap::GetMaxAddress() const { if (mappings_.empty()) { - return 0; + return VMAddr(0, 0); } else { auto& entry = *mappings_.rbegin(); return entry.first + entry.second.size; diff --git a/src/range_map.h b/src/range_map.h index aab0771..d83048c 100644 --- a/src/range_map.h +++ b/src/range_map.h @@ -12,14 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -// RagneMap maps +// RangeMap maps // -// [uint64_t, uint64_t) -> std::string, [optional other range base] +// [VMAddr, uint64_t) -> std::string, [optional other range base] // // where ranges must be non-overlapping. // // This is used to map the address space (either pointer offsets or file -// offsets). +// offsets). VMAddr is a (segment, address) pair that allows multiple +// address spaces to coexist without conflicts. // // The other range base allows us to use this RangeMap to translate addresses // from this domain to another one (like vm_addr -> file_addr or vice versa). @@ -35,6 +36,7 @@ #include #include #include +#include #include #include "absl/strings/str_cat.h" @@ -43,6 +45,53 @@ namespace bloaty { class RangeMapTest; +// VMAddr represents a virtual memory address with an explicit segment. +// This allows multiple address spaces to coexist without conflicts. +struct VMAddr { + int segment; + uint64_t address; + + constexpr VMAddr() : segment(0), address(0) {} + + constexpr VMAddr(int seg, uint64_t addr) : segment(seg), address(addr) {} + + constexpr explicit VMAddr(uint64_t addr) : segment(0), address(addr) {} + + bool operator<(const VMAddr& other) const { + return std::tie(segment, address) < std::tie(other.segment, other.address); + } + + bool operator>(const VMAddr& other) const { + return other < *this; + } + + bool operator<=(const VMAddr& other) const { + return !(other < *this); + } + + bool operator>=(const VMAddr& other) const { + return !(*this < other); + } + + bool operator==(const VMAddr& other) const { + return segment == other.segment && address == other.address; + } + + bool operator!=(const VMAddr& other) const { + return !(*this == other); + } + + VMAddr operator+(uint64_t offset) const { + return VMAddr(segment, address + offset); + } + + uint64_t operator-(const VMAddr& other) const { + assert(segment == other.segment && + "Cannot subtract addresses from different segments"); + return address - other.address; + } +}; + class RangeMap { public: RangeMap() = default; @@ -52,12 +101,12 @@ class RangeMap { RangeMap& operator=(RangeMap& other) = delete; // Adds a range to this map. - void AddRange(uint64_t addr, uint64_t size, const std::string& val); + void AddRange(VMAddr addr, uint64_t size, const std::string& val); // Adds a range to this map (in domain D1) that also corresponds to a // different range in a different map (in domain D2). The correspondance will // be noted to allow us to translate into the other domain later. - void AddDualRange(uint64_t addr, uint64_t size, uint64_t otheraddr, + void AddDualRange(VMAddr addr, uint64_t size, VMAddr otheraddr, const std::string& val); // Adds a range to this map (in domain D1), and also adds corresponding ranges @@ -70,7 +119,7 @@ class RangeMap { // range was actually translated). If the return value is false, then the // contents of |this| and |other| are undefined (Bloaty will bail in this // case). - bool AddRangeWithTranslation(uint64_t addr, uint64_t size, + bool AddRangeWithTranslation(VMAddr addr, uint64_t size, const std::string& val, const RangeMap& translator, bool verbose, RangeMap* other); @@ -85,37 +134,37 @@ class RangeMap { void Compress(); // Returns whether this RangeMap fully covers the given range. - bool CoversRange(uint64_t addr, uint64_t size) const; + bool CoversRange(VMAddr addr, uint64_t size) const; // Returns the maximum address contained in this map. - uint64_t GetMaxAddress() const; + VMAddr GetMaxAddress() const; // Translates |addr| into the other domain, returning |true| if this was // successful. - bool Translate(uint64_t addr, uint64_t *translated) const; + bool Translate(VMAddr addr, uint64_t *translated) const; // Looks for a range within this map that contains |addr|. If found, returns // true and sets |label| to the corresponding label, and |offset| to the // offset from the beginning of this range. - bool TryGetLabel(uint64_t addr, std::string* label) const; - bool TryGetLabelForRange(uint64_t addr, uint64_t size, + bool TryGetLabel(VMAddr addr, std::string* label) const; + bool TryGetLabelForRange(VMAddr addr, uint64_t size, std::string* label) const; // Looks for a range that starts exactly on |addr|. If it exists, returns // true and sets |size| to its size. - bool TryGetSize(uint64_t addr, uint64_t* size) const; + bool TryGetSize(VMAddr addr, uint64_t* size) const; std::string DebugString() const; - static std::string EntryDebugString(uint64_t addr, uint64_t size, - uint64_t other_start, + static std::string EntryDebugString(VMAddr addr, uint64_t size, + VMAddr other_start, const std::string& label) { std::string end = - size == kUnknownSize ? "?" : absl::StrCat(absl::Hex(addr + size)); - std::string ret = absl::StrCat("[", absl::Hex(addr), ", ", end, + size == kUnknownSize ? "?" : absl::StrCat(absl::Hex(addr.address + size)); + std::string ret = absl::StrCat("[", absl::Hex(addr.address), ", ", end, "] (size=", absl::Hex(size), "): ", label); - if (other_start != UINT64_MAX) { - absl::StrAppend(&ret, ", other_start=", absl::Hex(other_start)); + if (other_start != kNoTranslation()) { + absl::StrAppend(&ret, ", other_start=", absl::Hex(other_start.address)); } return ret; } @@ -142,7 +191,7 @@ class RangeMap { } template - void ForEachRangeWithStart(uint64_t start, Func func) const { + void ForEachRangeWithStart(VMAddr start, Func func) const { for (auto iter = FindContaining(start); iter != mappings_.end(); ++iter) { if (!func(iter->second.label, iter->first, RangeEnd(iter) - iter->first)) { @@ -155,16 +204,17 @@ class RangeMap { private: friend class RangeMapTest; - static const uint64_t kNoTranslation = UINT64_MAX; + // Special VMAddr value indicating no translation exists. + static constexpr VMAddr kNoTranslation() { return VMAddr(-1, UINT64_MAX); } struct Entry { - Entry(const std::string& label_, uint64_t size_, uint64_t other_) + Entry(const std::string& label_, uint64_t size_, VMAddr other_) : label(label_), size(size_), other_start(other_) {} std::string label; uint64_t size; - uint64_t other_start; // kNoTranslation if there is no mapping. + VMAddr other_start; // kNoTranslation() if there is no mapping. - bool HasTranslation() const { return other_start != kNoTranslation; } + bool HasTranslation() const { return other_start != kNoTranslation(); } bool HasFallbackLabel() const { return !label.empty() && label[0] == '['; } // We assume that short regions that were unattributed (have fallback @@ -173,7 +223,7 @@ class RangeMap { bool IsShortFallback() const { return size <= 16 && HasFallbackLabel(); } }; - typedef std::map Map; + typedef std::map Map; Map mappings_; template @@ -186,12 +236,12 @@ class RangeMap { } template - bool EntryContains(T iter, uint64_t addr) const { + bool EntryContains(T iter, VMAddr addr) const { return addr >= iter->first && addr < RangeEnd(iter); } template - bool EntryContainsStrict(T iter, uint64_t addr) const { + bool EntryContainsStrict(T iter, VMAddr addr) const { if (iter->second.size == kUnknownSize) { return iter->first == addr; } else { @@ -200,12 +250,12 @@ class RangeMap { } template - void MaybeSetLabel(T iter, const std::string& label, uint64_t addr, - uint64_t end); + void MaybeSetLabel(T iter, const std::string& label, VMAddr addr, + VMAddr end); // When the size is unknown return |unknown| for the end. - uint64_t RangeEndUnknownLimit(Map::const_iterator iter, - uint64_t unknown) const { + VMAddr RangeEndUnknownLimit(Map::const_iterator iter, + VMAddr unknown) const { if (iter->second.size == kUnknownSize) { Map::const_iterator next = std::next(iter); if (IterIsEnd(next) || next->first > unknown) { @@ -214,14 +264,16 @@ class RangeMap { return next->first; } } else { - uint64_t ret = iter->first + iter->second.size; - assert(ret > iter->first); + VMAddr ret = iter->first + iter->second.size; + if (iter->second.size > 0) { + assert(ret > iter->first); + } return ret; } } - uint64_t RangeEnd(Map::const_iterator iter) const { - return RangeEndUnknownLimit(iter, UINT64_MAX); + VMAddr RangeEnd(Map::const_iterator iter) const { + return RangeEndUnknownLimit(iter, VMAddr(iter->first.segment, UINT64_MAX)); } bool IterIsEnd(Map::const_iterator iter) const { @@ -229,22 +281,22 @@ class RangeMap { } template - uint64_t TranslateWithEntry(T iter, uint64_t addr) const; + VMAddr TranslateWithEntry(T iter, VMAddr addr) const; template - bool TranslateAndTrimRangeWithEntry(T iter, uint64_t addr, uint64_t size, - uint64_t* trimmed_addr, - uint64_t* translated_addr, + bool TranslateAndTrimRangeWithEntry(T iter, VMAddr addr, uint64_t size, + VMAddr* trimmed_addr, + VMAddr* translated_addr, uint64_t* trimmed_size) const; // Finds the entry that contains |addr|. If no such mapping exists, returns // mappings_.end(). - Map::const_iterator FindContaining(uint64_t addr) const; + Map::const_iterator FindContaining(VMAddr addr) const; // Finds the entry that contains |addr|, or the very next entry (which may be // mappings_.end()). - Map::iterator FindContainingOrAfter(uint64_t addr); - Map::const_iterator FindContainingOrAfter(uint64_t addr) const; + Map::iterator FindContainingOrAfter(VMAddr addr); + Map::const_iterator FindContainingOrAfter(VMAddr addr) const; }; template @@ -273,6 +325,26 @@ void RangeMap::ComputeRollup(const std::vector& range_maps, iters.push_back(range_map->mappings_.begin()); } + // This algorithm walks through multiple RangeMaps simultaneously + // + // Base Map vs Secondary Maps: + // - range_maps[0] (base map): Defines the address space. Must be continuous within + // each segment. + // - range_maps[1+] (secondary maps): Provide hierarchical detail (sections, symbols). + // Can have gaps, start offsets, or end early + // + // The algorithm allows three relaxations for secondary maps to support cases like + // universal binaries where segments may have padding, sparse content, or incomplete data. + // + // 1. Secondary maps can start after base map starts + // e.g. __TEXT segment at 0x100000000, but first content at 0x100000068 + // + // 2. Secondary maps can start in the middle of a base range + // e.g. Segment starts at 0x100000000, content begins at 0x100000068 + // + // 3. Secondary maps can have gaps or end early + // e.g. One architecture has symbols, another doesn't + // // Iterate over all ranges in parallel to perform this transformation: // // ----- ----- ----- --------------- @@ -293,12 +365,13 @@ void RangeMap::ComputeRollup(const std::vector& range_maps, // C Z 3 C,Z,3 // ----- ----- ----- --------------- // - // All input maps must cover exactly the same domain. - // Outer loop: once per continuous (gapless) region. + // Outer loop: restarts when any map has a discontinuity. + // For the base map, this means a gap in coverage. + // For secondary maps, gaps are OK and handled with placeholders. while (true) { std::vector keys; - uint64_t current = 0; + VMAddr current(0, 0); if (range_maps[0]->IterIsEnd(iters[0])) { // Termination condition: all iterators must be at end. @@ -315,27 +388,39 @@ void RangeMap::ComputeRollup(const std::vector& range_maps, } return; } else { - // Starting a new continuous range: all iterators must start at the same - // place. + // Starting a new continuous range: iterators must start at the same + // place, except secondary iterators which can start after the base current = iters[0]->first; + keys.resize(range_maps.size()); for (int i = 0; i < range_maps.size(); i++) { if (range_maps[i]->IterIsEnd(iters[i])) { + if (i == 0) { + printf("Error, base map ended unexpectedly\n"); + assert(false); + throw std::runtime_error("Base map ended unexpectedly."); + } + // Secondary map ended - use empty placeholder for remaining base ranges + keys[i] = ""; + } else if (iters[i]->first == current) { + keys[i] = iters[i]->second.label; + } else if (iters[i]->first > current) { + if (i == 0) { + printf( + "Error, base range (%s) doesn't start at expected position\n", + range_maps[0]->EntryDebugString(iters[0]).c_str()); + assert(false); + throw std::runtime_error("Base map not at current position."); + } + // Secondary map ahead - use empty placeholder until it catches up + keys[i] = ""; + } else { printf( - "Error, no more ranges for index %d but we need one " - "to match (%s)\n", - i, range_maps[0]->EntryDebugString(iters[0]).c_str()); - assert(false); - throw std::runtime_error("No more ranges."); - } else if (iters[i]->first != current) { - printf( - "Error, range (%s) doesn't match the beginning of base range " - "(%s)\n", + "Error, range (%s) starts before base range (%s)\n", range_maps[i]->EntryDebugString(iters[i]).c_str(), range_maps[0]->EntryDebugString(iters[0]).c_str()); assert(false); - throw std::runtime_error("No more ranges."); + throw std::runtime_error("Range starts before base map."); } - keys.push_back(iters[i]->second.label); } } @@ -343,10 +428,19 @@ void RangeMap::ComputeRollup(const std::vector& range_maps, // Inner loop: once per range within the continuous region. while (continuous) { - uint64_t next_break = UINT64_MAX; + VMAddr next_break(current.segment, UINT64_MAX); for (int i = 0; i < iters.size(); i++) { next_break = std::min(next_break, range_maps[i]->RangeEnd(iters[i])); + + // This handles the case where a secondary map starts in the middle of a + // base range. Without this, we'd create a single range with placeholder + // for the entire base range, when we should split it as: + // [start, content-start] with placeholder, then [content-start, end] + if (i > 0 && !range_maps[i]->IterIsEnd(iters[i]) && + iters[i]->first > current) { + next_break = std::min(next_break, iters[i]->first); + } } func(keys, current, next_break); @@ -355,7 +449,7 @@ void RangeMap::ComputeRollup(const std::vector& range_maps, for (int i = 0; i < iters.size(); i++) { const RangeMap& map = *range_maps[i]; Map::const_iterator& iter = iters[i]; - uint64_t end = continuous ? map.RangeEnd(iter) + VMAddr end = continuous ? map.RangeEnd(iter) : map.RangeEndUnknownLimit(iter, next_break); if (end != next_break) { @@ -365,17 +459,8 @@ void RangeMap::ComputeRollup(const std::vector& range_maps, // Test for discontinuity. if (map.IterIsEnd(iter) || iter->first != next_break) { - if (i > 0 && continuous) { - printf( - "Error, gap between ranges (%s) and (%s) fails to cover base " - "range (%s)\n", - map.EntryDebugString(std::prev(iter)).c_str(), - map.EntryDebugString(iter).c_str(), - range_maps[0]->EntryDebugString(iters[0]).c_str()); - assert(false); - throw std::runtime_error("Entry range extends beyond base range"); - } - assert(i == 0 || !continuous); + // Discontinuity ends the continuous region. + // For secondary maps, gaps are OK and empty placeholders are used. continuous = false; } else { assert(continuous); diff --git a/tests/range_map_test.cc b/tests/range_map_test.cc index fe023ee..485ba26 100644 --- a/tests/range_map_test.cc +++ b/tests/range_map_test.cc @@ -24,7 +24,7 @@ namespace bloaty { class RangeMapTest : public ::testing::Test { protected: void CheckConsistencyFor(const bloaty::RangeMap& map) { - uint64_t last_end = 0; + VMAddr last_end(0, 0); for (auto it = map.mappings_.begin(); it != map.mappings_.end(); ++it) { ASSERT_GE(it->first, last_end); last_end = map.RangeEnd(it); @@ -39,16 +39,16 @@ class RangeMapTest : public ::testing::Test { struct Row { std::vector keys; - uint64_t start; - uint64_t end; + VMAddr start; + VMAddr end; }; void AssertRollupEquals(const std::vector maps, const std::vector& rows) { int i = 0; RangeMap::ComputeRollup( - maps, [&i, &rows](const std::vector& keys, uint64_t start, - uint64_t end) { + maps, [&i, &rows](const std::vector& keys, VMAddr start, + VMAddr end) { ASSERT_LT(i, rows.size()); const auto& row = rows[i]; ASSERT_EQ(row.keys, keys); @@ -60,9 +60,9 @@ class RangeMapTest : public ::testing::Test { } struct Entry { - uint64_t addr; - uint64_t end; - uint64_t other_start; + VMAddr addr; + VMAddr end; + VMAddr other_start; std::string label; }; @@ -84,7 +84,7 @@ class RangeMapTest : public ::testing::Test { i = 0; RangeMap::ComputeRollup({&map}, [&i, &entries](const std::vector& keys, - uint64_t start, uint64_t end) { + VMAddr start, VMAddr end) { ASSERT_LT(i, entries.size()); const auto& entry = entries[i]; ASSERT_EQ(entry.addr, start); @@ -103,7 +103,7 @@ class RangeMapTest : public ::testing::Test { bloaty::RangeMap map2_; bloaty::RangeMap map3_; - const uint64_t kNoTranslation = RangeMap::kNoTranslation; + const VMAddr kNoTranslation = RangeMap::kNoTranslation(); const uint64_t kUnknownSize = RangeMap::kUnknownSize; }; @@ -111,101 +111,101 @@ TEST_F(RangeMapTest, AddRange) { CheckConsistency(); AssertMainMapEquals({}); - map_.AddRange(4, 3, "foo"); + map_.AddRange(VMAddr(4), 3, "foo"); CheckConsistency(); AssertMainMapEquals({ - {4, 7, kNoTranslation, "foo"} + {VMAddr(4), VMAddr(7), kNoTranslation, "foo"} }); - map_.AddRange(30, 5, "bar"); + map_.AddRange(VMAddr(30), 5, "bar"); CheckConsistency(); AssertMainMapEquals({ - {4, 7, kNoTranslation, "foo"}, - {30, 35, kNoTranslation, "bar"} + {VMAddr(4), VMAddr(7), kNoTranslation, "foo"}, + {VMAddr(30), VMAddr(35), kNoTranslation, "bar"} }); - map_.AddRange(50, 0, "baz"); // No-op due to 0 size. + map_.AddRange(VMAddr(50), 0, "baz"); // No-op due to 0 size. CheckConsistency(); AssertMainMapEquals({ - {4, 7, kNoTranslation, "foo"}, - {30, 35, kNoTranslation, "bar"} + {VMAddr(4), VMAddr(7), kNoTranslation, "foo"}, + {VMAddr(30), VMAddr(35), kNoTranslation, "bar"} }); - map_.AddRange(20, 5, "baz"); - map_.AddRange(25, 5, "baz2"); - map_.AddRange(40, 5, "quux"); + map_.AddRange(VMAddr(20), 5, "baz"); + map_.AddRange(VMAddr(25), 5, "baz2"); + map_.AddRange(VMAddr(40), 5, "quux"); CheckConsistency(); AssertMainMapEquals({ - {4, 7, kNoTranslation, "foo"}, - {20, 25, kNoTranslation, "baz"}, - {25, 30, kNoTranslation, "baz2"}, - {30, 35, kNoTranslation, "bar"}, - {40, 45, kNoTranslation, "quux"} + {VMAddr(4), VMAddr(7), kNoTranslation, "foo"}, + {VMAddr(20), VMAddr(25), kNoTranslation, "baz"}, + {VMAddr(25), VMAddr(30), kNoTranslation, "baz2"}, + {VMAddr(30), VMAddr(35), kNoTranslation, "bar"}, + {VMAddr(40), VMAddr(45), kNoTranslation, "quux"} }); - map_.AddRange(21, 25, "overlapping"); + map_.AddRange(VMAddr(21), 25, "overlapping"); CheckConsistency(); AssertMainMapEquals({ - {4, 7, kNoTranslation, "foo"}, - {20, 25, kNoTranslation, "baz"}, - {25, 30, kNoTranslation, "baz2"}, - {30, 35, kNoTranslation, "bar"}, - {35, 40, kNoTranslation, "overlapping"}, - {40, 45, kNoTranslation, "quux"}, - {45, 46, kNoTranslation, "overlapping"} + {VMAddr(4), VMAddr(7), kNoTranslation, "foo"}, + {VMAddr(20), VMAddr(25), kNoTranslation, "baz"}, + {VMAddr(25), VMAddr(30), kNoTranslation, "baz2"}, + {VMAddr(30), VMAddr(35), kNoTranslation, "bar"}, + {VMAddr(35), VMAddr(40), kNoTranslation, "overlapping"}, + {VMAddr(40), VMAddr(45), kNoTranslation, "quux"}, + {VMAddr(45), VMAddr(46), kNoTranslation, "overlapping"} }); - map_.AddRange(21, 25, "overlapping no-op"); + map_.AddRange(VMAddr(21), 25, "overlapping no-op"); CheckConsistency(); AssertMainMapEquals({ - {4, 7, kNoTranslation, "foo"}, - {20, 25, kNoTranslation, "baz"}, - {25, 30, kNoTranslation, "baz2"}, - {30, 35, kNoTranslation, "bar"}, - {35, 40, kNoTranslation, "overlapping"}, - {40, 45, kNoTranslation, "quux"}, - {45, 46, kNoTranslation, "overlapping"} + {VMAddr(4), VMAddr(7), kNoTranslation, "foo"}, + {VMAddr(20), VMAddr(25), kNoTranslation, "baz"}, + {VMAddr(25), VMAddr(30), kNoTranslation, "baz2"}, + {VMAddr(30), VMAddr(35), kNoTranslation, "bar"}, + {VMAddr(35), VMAddr(40), kNoTranslation, "overlapping"}, + {VMAddr(40), VMAddr(45), kNoTranslation, "quux"}, + {VMAddr(45), VMAddr(46), kNoTranslation, "overlapping"} }); - map_.AddRange(0, 100, "overlap everything"); + map_.AddRange(VMAddr(0), 100, "overlap everything"); CheckConsistency(); AssertMainMapEquals({ - {0, 4, kNoTranslation, "overlap everything"}, - {4, 7, kNoTranslation, "foo"}, - {7, 20, kNoTranslation, "overlap everything"}, - {20, 25, kNoTranslation, "baz"}, - {25, 30, kNoTranslation, "baz2"}, - {30, 35, kNoTranslation, "bar"}, - {35, 40, kNoTranslation, "overlapping"}, - {40, 45, kNoTranslation, "quux"}, - {45, 46, kNoTranslation, "overlapping"}, - {46, 100, kNoTranslation, "overlap everything"}, + {VMAddr(0), VMAddr(4), kNoTranslation, "overlap everything"}, + {VMAddr(4), VMAddr(7), kNoTranslation, "foo"}, + {VMAddr(7), VMAddr(20), kNoTranslation, "overlap everything"}, + {VMAddr(20), VMAddr(25), kNoTranslation, "baz"}, + {VMAddr(25), VMAddr(30), kNoTranslation, "baz2"}, + {VMAddr(30), VMAddr(35), kNoTranslation, "bar"}, + {VMAddr(35), VMAddr(40), kNoTranslation, "overlapping"}, + {VMAddr(40), VMAddr(45), kNoTranslation, "quux"}, + {VMAddr(45), VMAddr(46), kNoTranslation, "overlapping"}, + {VMAddr(46), VMAddr(100), kNoTranslation, "overlap everything"}, }); } TEST_F(RangeMapTest, UnknownSize) { - map_.AddRange(5, kUnknownSize, "foo"); + map_.AddRange(VMAddr(5), kUnknownSize, "foo"); CheckConsistency(); AssertMainMapEquals({ - {5, UINT64_MAX, kNoTranslation, "foo"} + {VMAddr(5), VMAddr(UINT64_MAX), kNoTranslation, "foo"} }); - map_.AddRange(100, 15, "bar"); - map_.AddRange(200, kUnknownSize, "baz"); + map_.AddRange(VMAddr(100), 15, "bar"); + map_.AddRange(VMAddr(200), kUnknownSize, "baz"); CheckConsistency(); AssertMainMapEquals({ - {5, 100, kNoTranslation, "foo"}, - {100, 115, kNoTranslation, "bar"}, - {200, UINT64_MAX, kNoTranslation, "baz"} + {VMAddr(5), VMAddr(100), kNoTranslation, "foo"}, + {VMAddr(100), VMAddr(115), kNoTranslation, "bar"}, + {VMAddr(200), VMAddr(UINT64_MAX), kNoTranslation, "baz"} }); - map2_.AddRange(5, 110, "base0"); - map2_.AddRange(200, 50, "base1"); + map2_.AddRange(VMAddr(5), 110, "base0"); + map2_.AddRange(VMAddr(200), 50, "base1"); AssertRollupEquals({&map2_, &map_}, { - {{"base0", "foo"}, 5, 100}, - {{"base0", "bar"}, 100, 115}, - {{"base1", "baz"}, 200, 250}, + {{"base0", "foo"}, VMAddr(5), VMAddr(100)}, + {{"base0", "bar"}, VMAddr(100), VMAddr(115)}, + {{"base1", "baz"}, VMAddr(200), VMAddr(250)}, }); } @@ -213,191 +213,191 @@ TEST_F(RangeMapTest, UnknownSize2) { // This case is slightly weird, but we do consider the "100" below to be a // hard fact even if the size is unknown, so the "[95, 105]: bar" range // doesn't override it. - map_.AddRange(100, kUnknownSize, "foo"); - map_.AddRange(95, 10, "bar"); + map_.AddRange(VMAddr(100), kUnknownSize, "foo"); + map_.AddRange(VMAddr(95), 10, "bar"); AssertMainMapEquals({ - {95, 100, kNoTranslation, "bar"}, - {100, 105, kNoTranslation, "foo"}, + {VMAddr(95), VMAddr(100), kNoTranslation, "bar"}, + {VMAddr(100), VMAddr(105), kNoTranslation, "foo"}, }); } TEST_F(RangeMapTest, UnknownSize3) { - map_.AddRange(100, kUnknownSize, "foo"); - map_.AddRange(150, kUnknownSize, "bar"); + map_.AddRange(VMAddr(100), kUnknownSize, "foo"); + map_.AddRange(VMAddr(150), kUnknownSize, "bar"); // This tells us the ultimate size of "foo", and we keep the "foo" label even // though the new label is "baz". - map_.AddRange(100, 100, "baz"); + map_.AddRange(VMAddr(100), 100, "baz"); AssertMainMapEquals({ - {100, 150, kNoTranslation, "foo"}, - {150, 200, kNoTranslation, "bar"}, + {VMAddr(100), VMAddr(150), kNoTranslation, "foo"}, + {VMAddr(150), VMAddr(200), kNoTranslation, "bar"}, }); } TEST_F(RangeMapTest, UnknownSize4) { - map_.AddRange(100, kUnknownSize, "foo"); - map_.AddRange(150, 100, "bar"); + map_.AddRange(VMAddr(100), kUnknownSize, "foo"); + map_.AddRange(VMAddr(150), 100, "bar"); // This tells us the ultimate size of "foo", and we keep the "foo" label even // though the new label is "baz". - map_.AddRange(100, 100, "baz"); + map_.AddRange(VMAddr(100), 100, "baz"); AssertMainMapEquals({ - {100, 150, kNoTranslation, "foo"}, - {150, 250, kNoTranslation, "bar"}, + {VMAddr(100), VMAddr(150), kNoTranslation, "foo"}, + {VMAddr(150), VMAddr(250), kNoTranslation, "bar"}, }); } TEST_F(RangeMapTest, Bug1) { - map_.AddRange(100, 20, "foo"); - map_.AddRange(120, 20, "bar"); - map_.AddRange(100, 15, "baz"); + map_.AddRange(VMAddr(100), 20, "foo"); + map_.AddRange(VMAddr(120), 20, "bar"); + map_.AddRange(VMAddr(100), 15, "baz"); AssertMainMapEquals({ - {100, 120, kNoTranslation, "foo"}, - {120, 140, kNoTranslation, "bar"}, + {VMAddr(100), VMAddr(120), kNoTranslation, "foo"}, + {VMAddr(120), VMAddr(140), kNoTranslation, "bar"}, }); } TEST_F(RangeMapTest, Bug2) { - map_.AddRange(100, kUnknownSize, "foo"); - map_.AddRange(200, 50, "bar"); - map_.AddRange(150, 10, "baz"); + map_.AddRange(VMAddr(100), kUnknownSize, "foo"); + map_.AddRange(VMAddr(200), 50, "bar"); + map_.AddRange(VMAddr(150), 10, "baz"); AssertMainMapEquals({ - {100, 150, kNoTranslation, "foo"}, - {150, 160, kNoTranslation, "baz"}, - {200, 250, kNoTranslation, "bar"}, + {VMAddr(100), VMAddr(150), kNoTranslation, "foo"}, + {VMAddr(150), VMAddr(160), kNoTranslation, "baz"}, + {VMAddr(200), VMAddr(250), kNoTranslation, "bar"}, }); } TEST_F(RangeMapTest, Bug3) { - map_.AddRange(100, kUnknownSize, "foo"); - map_.AddRange(200, kUnknownSize, "bar"); - map_.AddRange(150, 10, "baz"); + map_.AddRange(VMAddr(100), kUnknownSize, "foo"); + map_.AddRange(VMAddr(200), kUnknownSize, "bar"); + map_.AddRange(VMAddr(150), 10, "baz"); AssertMainMapEquals({ - {100, 150, kNoTranslation, "foo"}, - {150, 160, kNoTranslation, "baz"}, - {200, UINT64_MAX, kNoTranslation, "bar"}, + {VMAddr(100), VMAddr(150), kNoTranslation, "foo"}, + {VMAddr(150), VMAddr(160), kNoTranslation, "baz"}, + {VMAddr(200), VMAddr(UINT64_MAX), kNoTranslation, "bar"}, }); } TEST_F(RangeMapTest, GetLabel) { - map_.AddRange(100, kUnknownSize, "foo"); - map_.AddRange(200, 50, "bar"); - map_.AddRange(150, 10, "baz"); + map_.AddRange(VMAddr(100), kUnknownSize, "foo"); + map_.AddRange(VMAddr(200), 50, "bar"); + map_.AddRange(VMAddr(150), 10, "baz"); AssertMainMapEquals({ - {100, 150, kNoTranslation, "foo"}, - {150, 160, kNoTranslation, "baz"}, - {200, 250, kNoTranslation, "bar"}, + {VMAddr(100), VMAddr(150), kNoTranslation, "foo"}, + {VMAddr(150), VMAddr(160), kNoTranslation, "baz"}, + {VMAddr(200), VMAddr(250), kNoTranslation, "bar"}, }); std::string label; - ASSERT_TRUE(map_.TryGetLabel(100, &label)); + ASSERT_TRUE(map_.TryGetLabel(VMAddr(100), &label)); ASSERT_EQ(label, "foo"); - ASSERT_TRUE(map_.TryGetLabel(155, &label)); + ASSERT_TRUE(map_.TryGetLabel(VMAddr(155), &label)); ASSERT_EQ(label, "baz"); - ASSERT_TRUE(map_.TryGetLabel(249, &label)); + ASSERT_TRUE(map_.TryGetLabel(VMAddr(249), &label)); ASSERT_EQ(label, "bar"); - ASSERT_FALSE(map_.TryGetLabel(250, &label)); + ASSERT_FALSE(map_.TryGetLabel(VMAddr(250), &label)); - ASSERT_TRUE(map_.TryGetLabelForRange(100, 10, &label)); + ASSERT_TRUE(map_.TryGetLabelForRange(VMAddr(100), 10, &label)); ASSERT_EQ(label, "foo"); - ASSERT_TRUE(map_.TryGetLabelForRange(155, 3, &label)); + ASSERT_TRUE(map_.TryGetLabelForRange(VMAddr(155), 3, &label)); ASSERT_EQ(label, "baz"); - ASSERT_TRUE(map_.TryGetLabelForRange(200, 50, &label)); + ASSERT_TRUE(map_.TryGetLabelForRange(VMAddr(200), 50, &label)); ASSERT_EQ(label, "bar"); - ASSERT_FALSE(map_.TryGetLabelForRange(200, 51, &label)); + ASSERT_FALSE(map_.TryGetLabelForRange(VMAddr(200), 51, &label)); } TEST_F(RangeMapTest, Translation) { - map_.AddDualRange(20, 5, 120, "foo"); + map_.AddDualRange(VMAddr(20), 5, VMAddr(120), "foo"); CheckConsistency(); AssertMainMapEquals({ - {20, 25, 120, "foo"} + {VMAddr(20), VMAddr(25), VMAddr(120), "foo"} }); - ASSERT_TRUE(map2_.AddRangeWithTranslation(20, 5, "translate me", map_, false, + ASSERT_TRUE(map2_.AddRangeWithTranslation(VMAddr(20), 5, "translate me", map_, false, &map3_)); CheckConsistency(); AssertMapEquals(map2_, { - {20, 25, kNoTranslation, "translate me"} + {VMAddr(20), VMAddr(25), kNoTranslation, "translate me"} }); AssertMapEquals(map3_, { - {120, 125, kNoTranslation, "translate me"} + {VMAddr(120), VMAddr(125), kNoTranslation, "translate me"} }); - map_.AddDualRange(1000, 30, 1100, "bar"); - ASSERT_TRUE(map2_.AddRangeWithTranslation(1000, 5, "translate me2", map_, + map_.AddDualRange(VMAddr(1000), 30, VMAddr(1100), "bar"); + ASSERT_TRUE(map2_.AddRangeWithTranslation(VMAddr(1000), 5, "translate me2", map_, false, &map3_)); AssertMapEquals(map2_, { - {20, 25, kNoTranslation, "translate me"}, - {1000, 1005, kNoTranslation, "translate me2"} + {VMAddr(20), VMAddr(25), kNoTranslation, "translate me"}, + {VMAddr(1000), VMAddr(1005), kNoTranslation, "translate me2"} }); AssertMapEquals(map3_, { - {120, 125, kNoTranslation, "translate me"}, - {1100, 1105, kNoTranslation, "translate me2"} + {VMAddr(120), VMAddr(125), kNoTranslation, "translate me"}, + {VMAddr(1100), VMAddr(1105), kNoTranslation, "translate me2"} }); // Starts before base map. - ASSERT_FALSE(map2_.AddRangeWithTranslation(15, 8, "translate me", map_, false, + ASSERT_FALSE(map2_.AddRangeWithTranslation(VMAddr(15), 8, "translate me", map_, false, &map3_)); // Extends past base map. - ASSERT_FALSE(map2_.AddRangeWithTranslation(22, 15, "translate me", map_, + ASSERT_FALSE(map2_.AddRangeWithTranslation(VMAddr(22), 15, "translate me", map_, false, &map3_)); // Starts and ends in base map, but skips range in the middle. - ASSERT_FALSE(map2_.AddRangeWithTranslation(20, 1000, "translate me", map_, + ASSERT_FALSE(map2_.AddRangeWithTranslation(VMAddr(20), 1000, "translate me", map_, false, &map3_)); } TEST_F(RangeMapTest, Translation2) { - map_.AddRange(5, 5, "foo"); - map_.AddDualRange(20, 5, 120, "bar"); - map_.AddRange(25, 5, "baz"); - map_.AddDualRange(30, 5, 130, "quux"); + map_.AddRange(VMAddr(5), 5, "foo"); + map_.AddDualRange(VMAddr(20), 5, VMAddr(120), "bar"); + map_.AddRange(VMAddr(25), 5, "baz"); + map_.AddDualRange(VMAddr(30), 5, VMAddr(130), "quux"); CheckConsistency(); AssertMainMapEquals({ - {5, 10, kNoTranslation, "foo"}, - {20, 25, 120, "bar"}, - {25, 30, kNoTranslation, "baz"}, - {30, 35, 130, "quux"} + {VMAddr(5), VMAddr(10), kNoTranslation, "foo"}, + {VMAddr(20), VMAddr(25), VMAddr(120), "bar"}, + {VMAddr(25), VMAddr(30), kNoTranslation, "baz"}, + {VMAddr(30), VMAddr(35), VMAddr(130), "quux"} }); - ASSERT_TRUE(map2_.AddRangeWithTranslation(20, 15, "translate me", map_, false, + ASSERT_TRUE(map2_.AddRangeWithTranslation(VMAddr(20), 15, "translate me", map_, false, &map3_)); CheckConsistency(); AssertMapEquals(map2_, { - {20, 25, kNoTranslation, "translate me"}, - {25, 30, kNoTranslation, "translate me"}, - {30, 35, kNoTranslation, "translate me"} + {VMAddr(20), VMAddr(25), kNoTranslation, "translate me"}, + {VMAddr(25), VMAddr(30), kNoTranslation, "translate me"}, + {VMAddr(30), VMAddr(35), kNoTranslation, "translate me"} }); AssertMapEquals(map3_, { - {120, 125, kNoTranslation, "translate me"}, - {130, 135, kNoTranslation, "translate me"} + {VMAddr(120), VMAddr(125), kNoTranslation, "translate me"}, + {VMAddr(130), VMAddr(135), kNoTranslation, "translate me"} }); } TEST_F(RangeMapTest, UnknownTranslation) { - map_.AddDualRange(20, 10, 120, "foo"); + map_.AddDualRange(VMAddr(20), 10, VMAddr(120), "foo"); CheckConsistency(); AssertMainMapEquals({ - {20, 30, 120, "foo"} + {VMAddr(20), VMAddr(30), VMAddr(120), "foo"} }); - map2_.AddRangeWithTranslation(25, kUnknownSize, "translate me", map_, false, + map2_.AddRangeWithTranslation(VMAddr(25), kUnknownSize, "translate me", map_, false, &map3_); CheckConsistency(); AssertMapEquals(map2_, { - {25, UINT64_MAX, kNoTranslation, "translate me"} + {VMAddr(25), VMAddr(UINT64_MAX), kNoTranslation, "translate me"} }); AssertMapEquals(map3_, { - {125, UINT64_MAX, kNoTranslation, "translate me"} + {VMAddr(125), VMAddr(UINT64_MAX), kNoTranslation, "translate me"} }); - map2_.AddRange(20, 10, "fallback"); + map2_.AddRange(VMAddr(20), 10, "fallback"); AssertRollupEquals({&map_, &map2_}, { - {{"foo", "fallback"}, 20, 25}, - {{"foo", "translate me"}, 25, 30}, + {{"foo", "fallback"}, VMAddr(20), VMAddr(25)}, + {{"foo", "translate me"}, VMAddr(25), VMAddr(30)}, }); }