Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 100 additions & 52 deletions src/bloaty.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1148,7 +1148,7 @@ 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, vmsize, "", translator_->GetVMMap(segment_id_),
false, &file_map);
file_map.ForEachRange(
[this, &contains](uint64_t fileoff, uint64_t filesize) {
Expand Down Expand Up @@ -1181,7 +1181,7 @@ bool RangeSink::IsVerboseForFileRange(uint64_t fileoff, uint64_t filesize) {
RangeMap file_map;
bool contains = false;
file_map.AddRangeWithTranslation(fileoff, filesize, "",
translator_->file_map, false, &vm_map);
translator_->GetFileMap(), false, &vm_map);
vm_map.ForEachRange([this, &contains](uint64_t vmaddr, uint64_t vmsize) {
if (ContainsVerboseVMAddr(vmaddr, vmsize)) {
contains = true;
Expand All @@ -1208,15 +1208,15 @@ void RangeSink::AddFileRange(const char* analyzer, string_view name,
for (auto& pair : outputs_) {
const std::string label = pair.second->Munge(name);
if (translator_) {
bool ok = pair.first->file_map.AddRangeWithTranslation(
fileoff, filesize, label, translator_->file_map, verbose,
&pair.first->vm_map);
bool ok = pair.first->GetFileMap().AddRangeWithTranslation(
fileoff, filesize, label, translator_->GetFileMap(), verbose,
&pair.first->GetVMMap(segment_id_));
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->GetFileMap().AddRange(fileoff, filesize, label);
}
}
}
Expand All @@ -1234,10 +1234,10 @@ 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)) {
bool ok = pair.first->file_map.AddRangeWithTranslation(
file_offset, file_range.size(), label, translator_->file_map, verbose,
&pair.first->vm_map);
if (pair.first->GetVMMap(segment_id_).TryGetLabel(label_from_vmaddr, &label)) {
bool ok = pair.first->GetFileMap().AddRangeWithTranslation(
file_offset, file_range.size(), label, translator_->GetFileMap(), verbose,
&pair.first->GetVMMap(segment_id_));
if (!ok) {
WARN("File range ($0, $1) for label $2 extends beyond base map",
file_offset, file_range.size(), label);
Expand All @@ -1263,11 +1263,11 @@ void RangeSink::AddFileRangeForFileRange(const char* analyzer,
assert(translator_);
for (auto& pair : outputs_) {
std::string label;
if (pair.first->file_map.TryGetLabelForRange(
if (pair.first->GetFileMap().TryGetLabelForRange(
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,
&pair.first->vm_map);
bool ok = pair.first->GetFileMap().AddRangeWithTranslation(
file_offset, file_range.size(), label, translator_->GetFileMap(), verbose,
&pair.first->GetVMMap(segment_id_));
if (!ok) {
WARN("File range ($0, $1) for label $2 extends beyond base map",
file_offset, file_range.size(), label);
Expand All @@ -1292,10 +1292,10 @@ void RangeSink::AddVMRangeForVMAddr(const char* analyzer,
assert(translator_);
for (auto& pair : outputs_) {
std::string label;
if (pair.first->vm_map.TryGetLabel(label_from_vmaddr, &label)) {
bool ok = pair.first->vm_map.AddRangeWithTranslation(
addr, size, label, translator_->vm_map, verbose,
&pair.first->file_map);
if (pair.first->GetVMMap(segment_id_).TryGetLabel(label_from_vmaddr, &label)) {
bool ok = pair.first->GetVMMap(segment_id_).AddRangeWithTranslation(
addr, size, label, translator_->GetVMMap(segment_id_), verbose,
&pair.first->GetFileMap());
if (!ok && verbose_level > 1) {
WARN("VM range ($0, $1) for label $2 extends beyond base map", addr,
size, label);
Expand All @@ -1317,9 +1317,9 @@ void RangeSink::AddVMRange(const char* analyzer, uint64_t vmaddr,
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,
&pair.first->file_map);
bool ok = pair.first->GetVMMap(segment_id_).AddRangeWithTranslation(
vmaddr, vmsize, label, translator_->GetVMMap(segment_id_), verbose,
&pair.first->GetFileMap());
if (!ok) {
WARN("VM range ($0, $1) for label $2 extends beyond base map", vmaddr,
vmsize, name);
Expand Down Expand Up @@ -1360,8 +1360,8 @@ void RangeSink::AddRange(const char* analyzer, string_view name,
}

if (translator_) {
if (!translator_->vm_map.CoversRange(vmaddr, vmsize) ||
!translator_->file_map.CoversRange(fileoff, filesize)) {
if (!translator_->GetVMMap(segment_id_).CoversRange(vmaddr, vmsize) ||
!translator_->GetFileMap().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);
Expand All @@ -1373,11 +1373,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->GetVMMap(segment_id_).AddDualRange(vmaddr, common, fileoff, label);
pair.first->GetFileMap().AddDualRange(fileoff, common, vmaddr, label);

pair.first->vm_map.AddRange(vmaddr + common, vmsize - common, label);
pair.first->file_map.AddRange(fileoff + common, filesize - common, label);
pair.first->GetVMMap(segment_id_).AddRange(vmaddr + common, vmsize - common, label);
pair.first->GetFileMap().AddRange(fileoff + common, filesize - common, label);
}
}

Expand All @@ -1386,18 +1386,18 @@ 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_->GetFileMap().Translate(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());
translator_->GetFileMap().DebugString().c_str());
}
return translated;
}

std::string_view RangeSink::TranslateVMToFile(uint64_t address) {
assert(translator_);
uint64_t translated;
if (!translator_->vm_map.Translate(address, &translated) ||
if (!translator_->GetVMMap(segment_id_).Translate(address, &translated) ||
translated > file_->data().size()) {
THROWF("Can't translate VM pointer ($0) to file", address);

Expand Down Expand Up @@ -1863,14 +1863,33 @@ struct DualMaps {
}

void ComputeRollup(Rollup* rollup) {
// Collect all segment IDs across all maps.
std::set<int> segment_ids;
for (auto& map : maps_) {
map->vm_map.Compress();
map->file_map.Compress();
for (int id : map->VMSegmentIds()) {
segment_ids.insert(id);
}
}
// Compress and roll up each segment's VM maps separately.
for (int seg_id : segment_ids) {
std::vector<const RangeMap*> vm_maps;
for (auto& map : maps_) {
if (map->HasVMMap(seg_id)) {
map->GetVMMap(seg_id).Compress();
vm_maps.push_back(&map->GetVMMap(seg_id));
}
}
if (!vm_maps.empty()) {
RangeMap::ComputeRollup(vm_maps,
[=](const std::vector<std::string>& keys, uint64_t addr, uint64_t end) {
return rollup->AddSizes(keys, end - addr, true);
});
}
}
// File maps are shared across all segments.
for (auto& map : maps_) {
map->GetFileMap().Compress();
}
RangeMap::ComputeRollup(VmMaps(), [=](const std::vector<std::string>& keys,
uint64_t addr, uint64_t end) {
return rollup->AddSizes(keys, end - addr, true);
});
RangeMap::ComputeRollup(
FileMaps(),
[=](const std::vector<std::string>& keys, uint64_t addr, uint64_t end) {
Expand All @@ -1894,7 +1913,38 @@ struct DualMaps {
}

void PrintFileMaps() { PrintMaps(FileMaps()); }
void PrintVMMaps() { PrintMaps(VmMaps()); }

void PrintVMMaps() {
std::set<int> segment_ids;
for (auto& map : maps_) {
for (int id : map->VMSegmentIds()) {
segment_ids.insert(id);
}
}
for (int seg_id : segment_ids) {
if (segment_ids.size() > 1) {
auto it = segment_names_.find(seg_id);
if (it != segment_names_.end()) {
printf("%s:\n", it->second.c_str());
} else {
printf("Segment %d:\n", seg_id);
}
}
std::vector<const RangeMap*> vm_maps;
for (auto& map : maps_) {
if (map->HasVMMap(seg_id)) {
vm_maps.push_back(&map->GetVMMap(seg_id));
}
}
if (!vm_maps.empty()) {
PrintMaps(vm_maps);
}
}
}

void SetSegmentNames(std::map<int, std::string> names) {
segment_names_ = std::move(names);
}

std::string KeysToString(const std::vector<std::string>& keys) {
std::string ret;
Expand All @@ -1920,23 +1970,16 @@ struct DualMaps {
DualMap* base_map() { return maps_[0].get(); }

private:
std::vector<const RangeMap*> VmMaps() const {
std::vector<const RangeMap*> ret;
for (const auto& map : maps_) {
ret.push_back(&map->vm_map);
}
return ret;
}

std::vector<const RangeMap*> FileMaps() const {
std::vector<const RangeMap*> ret;
for (const auto& map : maps_) {
ret.push_back(&map->file_map);
ret.push_back(&map->GetFileMap());
}
return ret;
}

std::vector<std::unique_ptr<DualMap>> maps_;
std::map<int, std::string> segment_names_;
};

void Bloaty::ScanAndRollupFile(const std::string& filename, Rollup* rollup,
Expand Down Expand Up @@ -2001,14 +2044,19 @@ void Bloaty::ScanAndRollupFile(const std::string& filename, Rollup* rollup,
rollup->file_total() + rollup->filtered_file_total();
file->ProcessFile(sink_ptrs);

maps.SetSegmentNames(file->GetSegmentNames());

// 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->input_file().filename());
});
maps.base_map()->file_map.ForEachRange(
for (int seg_id : maps.base_map()->VMSegmentIds()) {
sink->set_segment_id(seg_id);
maps.base_map()->GetVMMap(seg_id).ForEachRange(
[sink](uint64_t start, uint64_t length) {
sink->AddVMRange("inputfile_vmcopier", start, length,
sink->input_file().filename());
});
}
maps.base_map()->GetFileMap().ForEachRange(
[sink](uint64_t start, uint64_t length) {
sink->AddFileRange("inputfile_filecopier",
sink->input_file().filename(), start, length);
Expand Down
50 changes: 48 additions & 2 deletions src/bloaty.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@
#define BLOATY_H_

#include <stdlib.h>
#include <assert.h>
#define __STDC_LIMIT_MACROS
#define __STDC_FORMAT_MACROS
#include <stdint.h>
#include <inttypes.h>

#include <list>
#include <map>
#include <memory>
#include <set>
#include <string>
Expand Down Expand Up @@ -126,6 +128,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
Expand Down Expand Up @@ -238,6 +242,7 @@ class RangeSink {
const DualMap* translator_;
std::vector<std::pair<DualMap*, const NameMunger*>> outputs_;
google::protobuf::Arena *arena_;
int segment_id_ = 0;
};

// NameMunger //////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -328,6 +333,11 @@ class ObjectFile {
// given here, otherwise it is |this|.
virtual void ProcessFile(const std::vector<RangeSink*>& sinks) const = 0;

// Optionally return human-readable names for segment e.g. "x86_64", or "arm64".
// Only meaningful for formats with multiple VM address spaces such
// as Mach-O universal binaries.
virtual std::map<int, std::string> GetSegmentNames() const { return {}; }

virtual bool GetDisassemblyInfo(std::string_view symbol,
DataSource symbol_source,
DisassemblyInfo* info) const = 0;
Expand Down Expand Up @@ -375,11 +385,47 @@ std::string ItaniumDemangle(std::string_view symbol, DataSource source);

// DualMap /////////////////////////////////////////////////////////////////////

// Contains a RangeMap for VM space and file space for a given file.
// Contains RangeMaps for VM space and file space for a given file.
// For universal binaries, each architecture slice has its own VM address space
// (keyed by segment_id). File offsets are shared across all segments and use a
// single RangeMap. For single-arch binaries, only segment_id 0 is used.

struct DualMap {
RangeMap vm_map;
RangeMap& GetVMMap(int segment_id) { return vm_maps_[segment_id]; }
const RangeMap& GetVMMap(int segment_id) const {
auto it = vm_maps_.find(segment_id);
if (it == vm_maps_.end()) {
static const RangeMap empty;
return empty;
}
return it->second;
}

RangeMap& GetFileMap() { return file_map; }
const RangeMap& GetFileMap() const { return file_map; }

bool HasVMMap(int segment_id) const {
return vm_maps_.find(segment_id) != vm_maps_.end();
}

std::vector<int> VMSegmentIds() const {
std::vector<int> ids;
for (const auto& kv : vm_maps_) {
ids.push_back(kv.first);
}
return ids;
}

RangeMap& vm_map() {
assert(HasVMMap(0));
return GetVMMap(0);
}
const RangeMap& vm_map() const { return GetVMMap(0); }

RangeMap file_map;

private:
std::map<int, RangeMap> vm_maps_;
};

struct DisassemblyInfo {
Expand Down
2 changes: 1 addition & 1 deletion src/disassemble.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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(target, &label)) {
op_str = label;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/dwarf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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.GetVMMap(sink->segment_id()).TryGetSize(addr, &size)) {
sink->AddVMRangeIgnoreDuplicate("dwarf_location", addr, size,
cu.unit_name());
} else {
Expand Down
Loading