diff --git a/src/bloaty.cc b/src/bloaty.cc index 3dbc924b..200c6b4f 100644 --- a/src/bloaty.cc +++ b/src/bloaty.cc @@ -18,6 +18,12 @@ // It's very hard to figure out why. For the moment this seems to fix it, // but ideally we'd have a better solution here. typedef size_t z_size_t; +#include +#include +#include +#include +#include +#include #include #include @@ -33,13 +39,6 @@ typedef size_t z_size_t; #include #include #include - -#include -#include -#include -#include -#include -#include #if !defined(_WIN32) #include #include @@ -53,14 +52,13 @@ typedef size_t z_size_t; #include "absl/debugging/internal/demangle.h" #include "absl/memory/memory.h" #include "absl/strings/numbers.h" -#include "absl/strings/string_view.h" #include "absl/strings/str_join.h" +#include "absl/strings/string_view.h" #include "absl/strings/substitute.h" -#include "google/protobuf/io/zero_copy_stream_impl.h" -#include "google/protobuf/text_format.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" @@ -187,8 +185,7 @@ std::string ItaniumDemangle(string_view symbol, DataSource source) { return std::string(symbol); } } else if (source == DataSource::kFullSymbols) { - char* demangled = - __cxa_demangle(demangle_from.data(), NULL, NULL, NULL); + char* demangled = __cxa_demangle(demangle_from.data(), NULL, NULL, NULL); if (demangled) { std::string ret(demangled); free(demangled); @@ -202,10 +199,10 @@ std::string ItaniumDemangle(string_view symbol, DataSource source) { } } - // NameMunger ////////////////////////////////////////////////////////////////// -void NameMunger::AddRegex(const std::string& regex, const std::string& replacement) { +void NameMunger::AddRegex(const std::string& regex, + const std::string& replacement) { auto reg = absl::make_unique(regex); regexes_.push_back(std::make_pair(std::move(reg), replacement)); } @@ -223,7 +220,6 @@ std::string NameMunger::Munge(string_view name) const { return name_str; } - // Rollup ////////////////////////////////////////////////////////////////////// // A Rollup is a hierarchical tally of sizes. Its graphical representation is @@ -259,8 +255,8 @@ class Rollup { Rollup(Rollup&& other) = default; Rollup& operator=(Rollup&& other) = default; - void AddSizes(const std::vector& names, - uint64_t size, bool is_vmsize) { + void AddSizes(const std::vector& names, uint64_t size, + bool is_vmsize) { // We start at 1 to exclude the base map (see base_map_). AddInternal(names, 1, size, is_vmsize); } @@ -274,45 +270,40 @@ class Rollup { void CreateDiffModeRollupOutput(Rollup* base, const Options& options, RollupOutput* output) const { RollupRow* row = &output->toplevel_row_; - row->vmsize = vm_total_; - row->filesize = file_total_; - row->filtered_vmsize = filtered_vm_total_; - row->filtered_filesize = filtered_file_total_; + row->size.vm = vm_total_; + row->size.file = file_total_; + row->filtered_size.vm = filtered_vm_total_; + row->filtered_size.file = filtered_file_total_; row->vmpercent = 100; row->filepercent = 100; output->diff_mode_ = true; CreateRows(row, base, options, true); } - void SetFilterRegex(const ReImpl* regex) { - filter_regex_ = regex; - } + void SetFilterRegex(const ReImpl* regex) { filter_regex_ = regex; } - // Subtract the values in "other" from this. - void Subtract(const Rollup& other) { - vm_total_ -= other.vm_total_; - file_total_ -= other.file_total_; + // Add the values in "other" from this. + void Add(const Rollup& other) { + vm_total_ += other.vm_total_; + file_total_ += other.file_total_; for (const auto& other_child : other.children_) { auto& child = children_[other_child.first]; if (child.get() == NULL) { child.reset(new Rollup()); } - child->Subtract(*other_child.second); + child->Add(*other_child.second); } } - // Add the values in "other" from this. - void Add(const Rollup& other) { - vm_total_ += other.vm_total_; - file_total_ += other.file_total_; - + // Create entries for all children which exist in "other" but not in this. + void AddEntriesFrom(const Rollup& other) { for (const auto& other_child : other.children_) { auto& child = children_[other_child.first]; if (child.get() == NULL) { child.reset(new Rollup()); } - child->Add(*other_child.second); + child->AddEntriesFrom(*other_child.second); } } @@ -413,11 +404,33 @@ void Rollup::CreateRows(RollupRow* row, const Rollup* base, } for (const auto& value : children_) { - if (value.second->vm_total_ != 0 || value.second->file_total_ != 0) { + int64_t vm_total = value.second->vm_total_; + int64_t file_total = value.second->file_total_; + Rollup* base_child = nullptr; + + if (base) { + // Reassign sizes to base during a diff to compare to target sizes. + auto it = base->children_.find(value.first); + if (it != base->children_.end()) { + base_child = it->second.get(); + vm_total -= base_child->vm_total_; + file_total -= base_child->file_total_; + } + } + + if (vm_total != 0 || file_total != 0) { row->sorted_children.emplace_back(value.first); RollupRow& child_row = row->sorted_children.back(); - child_row.vmsize = value.second->vm_total_; - child_row.filesize = value.second->file_total_; + child_row.size.vm = vm_total; + child_row.size.file = file_total; + + // Preserve the old size for this label in the RollupRow output. + // If there is a diff base, the old sizes come from the size of the label + // in that base. Otherwise, the old size stays 0. + if (base_child) { + child_row.old_size.vm = base_child->vm_total_; + child_row.old_size.file = base_child->file_total_; + } } } @@ -452,14 +465,14 @@ void Rollup::SortAndAggregateRows(RollupRow* row, const Rollup* base, for (auto& child : child_rows) { switch (options.sort_by()) { case Options::SORTBY_VMSIZE: - child.sortkey = std::abs(child.vmsize); + child.sortkey = std::abs(child.size.vm); break; case Options::SORTBY_FILESIZE: - child.sortkey = std::abs(child.filesize); + child.sortkey = std::abs(child.size.file); break; case Options::SORTBY_BOTH: child.sortkey = - std::max(std::abs(child.vmsize), std::abs(child.filesize)); + std::max(std::abs(child.size.vm), std::abs(child.size.file)); break; default: BLOATY_UNREACHABLE(); @@ -478,8 +491,8 @@ void Rollup::SortAndAggregateRows(RollupRow* row, const Rollup* base, // out to "others_row". size_t i = child_rows.size() - 1; while (i >= options.max_rows_per_level()) { - CheckedAdd(&others_row.vmsize, child_rows[i].vmsize); - CheckedAdd(&others_row.filesize, child_rows[i].filesize); + CheckedAdd(&others_row.size.vm, child_rows[i].size.vm); + CheckedAdd(&others_row.size.file, child_rows[i].size.file); if (base) { auto it = base->children_.find(child_rows[i].name); if (it != base->children_.end()) { @@ -492,26 +505,26 @@ void Rollup::SortAndAggregateRows(RollupRow* row, const Rollup* base, i--; } - if (std::abs(others_row.vmsize) > 0 || std::abs(others_row.filesize) > 0) { + if (std::abs(others_row.size.vm) > 0 || std::abs(others_row.size.file) > 0) { child_rows.push_back(others_row); - CheckedAdd(&others_rollup.vm_total_, others_row.vmsize); - CheckedAdd(&others_rollup.file_total_, others_row.filesize); + CheckedAdd(&others_rollup.vm_total_, others_row.size.vm); + CheckedAdd(&others_rollup.file_total_, others_row.size.file); } // Now sort by actual value (positive or negative). for (auto& child : child_rows) { switch (options.sort_by()) { case Options::SORTBY_VMSIZE: - child.sortkey = child.vmsize; + child.sortkey = child.size.vm; break; case Options::SORTBY_FILESIZE: - child.sortkey = child.filesize; + child.sortkey = child.size.file; break; case Options::SORTBY_BOTH: - if (std::abs(child.vmsize) > std::abs(child.filesize)) { - child.sortkey = child.vmsize; + if (std::abs(child.size.vm) > std::abs(child.size.file)) { + child.sortkey = child.size.vm; } else { - child.sortkey = child.filesize; + child.sortkey = child.size.file; } break; default: @@ -524,8 +537,8 @@ void Rollup::SortAndAggregateRows(RollupRow* row, const Rollup* base, // For a non-diff, the percentage is compared to the total size of the parent. if (!base) { for (auto& child_row : child_rows) { - child_row.vmpercent = Percent(child_row.vmsize, row->vmsize); - child_row.filepercent = Percent(child_row.filesize, row->filesize); + child_row.vmpercent = Percent(child_row.size.vm, row->size.vm); + child_row.filepercent = Percent(child_row.size.file, row->size.file); } } @@ -561,7 +574,6 @@ void Rollup::SortAndAggregateRows(RollupRow* row, const Rollup* base, } } - // RollupOutput //////////////////////////////////////////////////////////////// // RollupOutput represents rollup data after we have applied output massaging @@ -600,14 +612,14 @@ std::string LeftPad(const std::string& input, size_t size) { return ret; } -std::string DoubleStringPrintf(const char *fmt, double d) { +std::string DoubleStringPrintf(const char* fmt, double d) { char buf[1024]; snprintf(buf, sizeof(buf), fmt, d); return std::string(buf); } std::string SiPrint(int64_t size, bool force_sign) { - const char *prefixes[] = {"", "Ki", "Mi", "Gi", "Ti"}; + const char* prefixes[] = {"", "Ki", "Mi", "Gi", "Ti"}; size_t num_prefixes = 5; size_t n = 0; double size_d = size; @@ -677,10 +689,10 @@ void RollupOutput::Print(const OutputOptions& options, std::ostream* out) { PrettyPrint(options, out); break; case bloaty::OutputFormat::kCSV: - PrintToCSV(out, /*tabs=*/false); + PrintToCSV(out, /*tabs=*/false, options.showAllSizesCSV); break; case bloaty::OutputFormat::kTSV: - PrintToCSV(out, /*tabs=*/true); + PrintToCSV(out, /*tabs=*/true, options.showAllSizesCSV); break; default: BLOATY_UNREACHABLE(); @@ -698,8 +710,8 @@ void RollupOutput::PrettyPrintRow(const RollupRow& row, size_t indent, if (&row != &toplevel_row_) { // Avoid printing this row if it is only zero. // This can happen when using --domain if the row is zero for this domain. - if ((!ShowFile(options) && row.vmsize == 0) || - (!ShowVM(options) && row.filesize == 0)) { + if ((!ShowFile(options) && row.size.vm == 0) || + (!ShowVM(options) && row.size.file == 0)) { return; } } @@ -708,12 +720,12 @@ void RollupOutput::PrettyPrintRow(const RollupRow& row, size_t indent, if (ShowFile(options)) { *out << PercentString(row.filepercent, diff_mode_) << " " - << SiPrint(row.filesize, diff_mode_) << " "; + << SiPrint(row.size.file, diff_mode_) << " "; } if (ShowVM(options)) { *out << PercentString(row.vmpercent, diff_mode_) << " " - << SiPrint(row.vmsize, diff_mode_) << " "; + << SiPrint(row.size.vm, diff_mode_) << " "; } *out << " " << row.name << "\n"; @@ -737,7 +749,7 @@ void RollupOutput::PrettyPrintTree(const RollupRow& row, size_t indent, // Rows are printed before their sub-rows. PrettyPrintRow(row, indent, options, out); - if (!row.vmsize && !row.filesize) { + if (!row.size.vm && !row.size.file) { return; } @@ -783,11 +795,12 @@ void RollupOutput::PrettyPrint(const OutputOptions& options, uint64_t file_filtered = 0; uint64_t vm_filtered = 0; + uint64_t filtered = 0; if (ShowFile(options)) { - file_filtered = toplevel_row_.filtered_filesize; + filtered += toplevel_row_.filtered_size.file; } if (ShowVM(options)) { - vm_filtered = toplevel_row_.filtered_vmsize; + filtered += toplevel_row_.filtered_size.vm; } if (vm_filtered == 0 && file_filtered == 0) { @@ -805,19 +818,28 @@ void RollupOutput::PrettyPrint(const OutputOptions& options, *out << SiPrint(vm_filtered, /*force_sign=*/false); } - *out << " of entries\n"; + *out << " of entries\n"; } void RollupOutput::PrintRowToCSV(const RollupRow& row, std::vector parent_labels, - std::ostream* out, bool tabs) const { + std::ostream* out, bool tabs, bool csvDiff) const { while (parent_labels.size() < source_names_.size()) { // If this label had no data at this level, append an empty string. parent_labels.push_back(""); } - parent_labels.push_back(std::to_string(row.vmsize)); - parent_labels.push_back(std::to_string(row.filesize)); + parent_labels.push_back(std::to_string(row.size.vm)); + parent_labels.push_back(std::to_string(row.size.file)); + + // If in diff where both old size are 0, get new size by adding diff size to + // old size. + if (csvDiff) { + parent_labels.push_back(std::to_string(row.old_size.vm)); + parent_labels.push_back(std::to_string(row.old_size.file)); + parent_labels.push_back(std::to_string(row.old_size.vm + (row.size.vm))); + parent_labels.push_back( + std::to_string(row.old_size.file + (row.size.file)));} std::string sep = tabs ? "\t" : ","; *out << absl::StrJoin(parent_labels, sep) << "\n"; @@ -825,7 +847,7 @@ void RollupOutput::PrintRowToCSV(const RollupRow& row, void RollupOutput::PrintTreeToCSV(const RollupRow& row, std::vector parent_labels, - std::ostream* out, bool tabs) const { + std::ostream* out, bool tabs, bool csvDiff) const { if (tabs) { parent_labels.push_back(row.name); } else { @@ -834,21 +856,28 @@ void RollupOutput::PrintTreeToCSV(const RollupRow& row, if (row.sorted_children.size() > 0) { for (const auto& child_row : row.sorted_children) { - PrintTreeToCSV(child_row, parent_labels, out, tabs); + PrintTreeToCSV(child_row, parent_labels, out, tabs, csvDiff); } } else { - PrintRowToCSV(row, parent_labels, out, tabs); + PrintRowToCSV(row, parent_labels, out, tabs, csvDiff); } } -void RollupOutput::PrintToCSV(std::ostream* out, bool tabs) const { +void RollupOutput::PrintToCSV(std::ostream* out, bool tabs, + bool csvDiff) const { std::vector names(source_names_); names.push_back("vmsize"); names.push_back("filesize"); + if (csvDiff) { + names.push_back("original_vmsize"); + names.push_back("original_filesize"); + names.push_back("current_vmsize"); + names.push_back("current_filesize"); + } std::string sep = tabs ? "\t" : ","; *out << absl::StrJoin(names, sep) << "\n"; for (const auto& child_row : toplevel_row_.sorted_children) { - PrintTreeToCSV(child_row, std::vector(), out, tabs); + PrintTreeToCSV(child_row, std::vector(), out, tabs, csvDiff); } } @@ -856,7 +885,6 @@ void RollupOutput::PrintToCSV(std::ostream* out, bool tabs) const { constexpr uint64_t RangeSink::kUnknownSize; - // MmapInputFile /////////////////////////////////////////////////////////////// #if !defined(_WIN32) @@ -875,7 +903,6 @@ class MmapInputFile : public InputFile { std::unique_ptr& file); }; - class FileDescriptor { public: FileDescriptor(int fd) : fd_(fd) {} @@ -897,7 +924,7 @@ bool MmapInputFile::DoTryOpen(absl::string_view filename, std::string str(filename); FileDescriptor fd(open(str.c_str(), O_RDONLY)); struct stat buf; - const char *map; + const char* map; if (fd.fd() < 0) { std::cerr << absl::Substitute("couldn't open file '$0': $1\n", filename, @@ -945,7 +972,7 @@ std::unique_ptr MmapInputFileFactory::OpenFile( return ret; } -#else // !_WIN32 +#else // !_WIN32 // MmapInputFile /////////////////////////////////////////////////////////////// @@ -989,9 +1016,9 @@ Win32MMapInputFile::Win32MMapInputFile(string_view filename, string_view data) bool Win32MMapInputFile::DoTryOpen(absl::string_view filename, std::unique_ptr& file) { std::string str(filename); - Win32Handle fd(::CreateFileA(str.c_str(), FILE_GENERIC_READ, - FILE_SHARE_READ, NULL, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, NULL)); + Win32Handle fd(::CreateFileA(str.c_str(), FILE_GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, + NULL)); LARGE_INTEGER li = {}; const char* map; @@ -1046,11 +1073,14 @@ std::unique_ptr MmapInputFileFactory::OpenFile( // RangeSink /////////////////////////////////////////////////////////////////// -RangeSink::RangeSink(const InputFile *file, const Options &options, - DataSource data_source, const DualMap *translator, - google::protobuf::Arena *arena) - : file_(file), options_(options), data_source_(data_source), - translator_(translator), arena_(arena) {} +RangeSink::RangeSink(const InputFile* file, const Options& options, + DataSource data_source, const DualMap* translator, + google::protobuf::Arena* arena) + : file_(file), + options_(options), + data_source_(data_source), + translator_(translator), + arena_(arena) {} RangeSink::~RangeSink() {} @@ -1346,21 +1376,23 @@ absl::string_view RangeSink::ZlibDecompress(absl::string_view data, } uint64_t mb = 1 << 20; // Limit for uncompressed size is 30x the compressed size + 128MB. - if (uncompressed_size > static_cast(data.size()) * 30 + (128 * mb)) { + if (uncompressed_size > + static_cast(data.size()) * 30 + (128 * mb)) { fprintf(stderr, "warning: ignoring compressed debug data, implausible uncompressed " "size (compressed: %zu, uncompressed: %" PRIu64 ")\n", data.size(), uncompressed_size); return absl::string_view(); } - unsigned char *dbuf = + unsigned char* dbuf = arena_->google::protobuf::Arena::CreateArray( arena_, uncompressed_size); uLongf zliblen = uncompressed_size; - if (uncompress(dbuf, &zliblen, (unsigned char*)(data.data()), data.size()) != Z_OK) { + if (uncompress(dbuf, &zliblen, (unsigned char*)(data.data()), data.size()) != + Z_OK) { THROW("Error decompressing debug info"); } - string_view sv(reinterpret_cast(dbuf), zliblen); + string_view sv(reinterpret_cast(dbuf), zliblen); return sv; } @@ -1403,7 +1435,6 @@ class ThreadSafeIterIndex { const int max_; }; - // Bloaty ////////////////////////////////////////////////////////////////////// // Represents a program execution and associated state. @@ -1501,8 +1532,9 @@ class Bloaty { std::unique_ptr arena_; }; -Bloaty::Bloaty(const InputFileFactory &factory, const Options &options) - : file_factory_(factory), options_(options), +Bloaty::Bloaty(const InputFileFactory& factory, const Options& options) + : file_factory_(factory), + options_(options), arena_(std::make_unique()) { AddBuiltInSources(data_sources, options); } @@ -1563,8 +1595,10 @@ void Bloaty::DefineCustomDataSource(const CustomDataSource& source) { auto iter = all_known_sources_.find(source.base_data_source()); if (iter == all_known_sources_.end()) { - THROWF("custom data source '$0': no such base source '$1'.\nTry --list-sources to see valid sources.", source.name(), - source.base_data_source()); + THROWF( + "custom data source '$0': no such base source '$1'.\nTry " + "--list-sources to see valid sources.", + source.name(), source.base_data_source()); } else if (!iter->second->munger->IsEmpty()) { THROWF("custom data source '$0' tries to depend on custom data source '$1'", source.name(), source.base_data_source()); @@ -1582,7 +1616,8 @@ void Bloaty::AddDataSource(const std::string& name) { source_names_.emplace_back(name); auto it = all_known_sources_.find(name); if (it == all_known_sources_.end()) { - THROWF("no such data source: $0.\nTry --list-sources to see valid sources.", name); + THROWF("no such data source: $0.\nTry --list-sources to see valid sources.", + name); } sources_.emplace_back(it->second.get()); @@ -1649,7 +1684,8 @@ struct DualMaps { return ret; } - void PrintMapRow(string_view str, uint64_t start, uint64_t end, int hex_digits) { + void PrintMapRow(string_view str, uint64_t start, uint64_t end, + int hex_digits) { printf("%.*" PRIx64 "-%.*" PRIx64 "\t %s\t\t%.*s\n", hex_digits, start, hex_digits, end, LeftPad(std::to_string(end - start), 10).c_str(), (int)str.size(), str.data()); @@ -1677,7 +1713,7 @@ struct DualMaps { std::vector> maps_; }; -void Bloaty::ScanAndRollupFile(const std::string &filename, Rollup* rollup, +void Bloaty::ScanAndRollupFile(const std::string& filename, Rollup* rollup, std::vector* out_build_ids) const { auto file = GetObjectFile(filename); @@ -1694,9 +1730,9 @@ void Bloaty::ScanAndRollupFile(const std::string &filename, Rollup* rollup, sink_ptrs.push_back(sinks.back().get()); for (auto source : sources_) { - sinks.push_back(absl::make_unique(&file->file_data(), options_, - source->effective_source, - maps.base_map(), arena_.get())); + sinks.push_back(absl::make_unique( + &file->file_data(), options_, source->effective_source, maps.base_map(), + arena_.get())); sinks.back()->AddOutput(maps.AppendMap(), source->munger.get()); // We handle the kInputFiles data source internally, without handing it off // to the file format implementation. This seems slightly simpler, since @@ -1719,8 +1755,8 @@ void Bloaty::ScanAndRollupFile(const std::string &filename, Rollup* rollup, } } - int64_t filesize_before = rollup->file_total() + - rollup->filtered_file_total(); + int64_t filesize_before = + rollup->file_total() + rollup->filtered_file_total(); file->ProcessFile(sink_ptrs); // kInputFile source: Copy the base map to the filename sink(s). @@ -1740,8 +1776,8 @@ void Bloaty::ScanAndRollupFile(const std::string &filename, Rollup* rollup, maps.ComputeRollup(rollup); // The ObjectFile implementation must guarantee this. - int64_t filesize = rollup->file_total() + - rollup->filtered_file_total() - filesize_before; + int64_t filesize = + rollup->file_total() + rollup->filtered_file_total() - filesize_before; (void)filesize; assert(filesize == file->file_data().data().size()); @@ -1758,10 +1794,9 @@ void Bloaty::ScanAndRollupFile(const std::string &filename, Rollup* rollup, } } -void Bloaty::ScanAndRollupFiles( - const std::vector& filenames, - std::vector* build_ids, - Rollup * rollup) const { +void Bloaty::ScanAndRollupFiles(const std::vector& filenames, + std::vector* build_ids, + Rollup* rollup) const { int num_cpus = std::thread::hardware_concurrency(); int num_threads = std::min(num_cpus, static_cast(filenames.size())); @@ -1782,16 +1817,18 @@ void Bloaty::ScanAndRollupFiles( for (int i = 0; i < num_threads; i++) { thread_data[i].rollup.SetFilterRegex(regex.get()); - threads[i] = std::thread([this, &index, &filenames](PerThreadData* data) { - try { - int j; - while (index.TryGetNext(&j)) { - ScanAndRollupFile(filenames[j], &data->rollup, &data->build_ids); - } - } catch (const bloaty::Error& e) { - index.Abort(e.what()); - } - }, &thread_data[i]); + threads[i] = std::thread( + [this, &index, &filenames](PerThreadData* data) { + try { + int j; + while (index.TryGetNext(&j)) { + ScanAndRollupFile(filenames[j], &data->rollup, &data->build_ids); + } + } catch (const bloaty::Error& e) { + index.Abort(e.what()); + } + }, + &thread_data[i]); } for (int i = 0; i < num_threads; i++) { @@ -1803,8 +1840,7 @@ void Bloaty::ScanAndRollupFiles( rollup->Add(data->rollup); } - build_ids->insert(build_ids->end(), - data->build_ids.begin(), + build_ids->insert(build_ids->end(), data->build_ids.begin(), data->build_ids.end()); } @@ -1838,7 +1874,7 @@ void Bloaty::ScanAndRollup(const Options& options, RollupOutput* output) { base_filenames.push_back(file_info.filename_); } ScanAndRollupFiles(base_filenames, &build_ids, &base); - rollup.Subtract(base); + rollup.AddEntriesFrom(base); rollup.CreateDiffModeRollupOutput(&base, options, output); } else { rollup.CreateRollupOutput(options, output); @@ -1854,8 +1890,7 @@ void Bloaty::ScanAndRollup(const Options& options, RollupOutput* output) { std::string unused_debug; for (const auto& pair : debug_files_) { unused_debug += absl::Substitute( - "$0 $1\n", - absl::BytesToHexString(pair.first).c_str(), + "$0 $1\n", absl::BytesToHexString(pair.first).c_str(), pair.second.c_str()); } @@ -1869,9 +1904,8 @@ void Bloaty::ScanAndRollup(const Options& options, RollupOutput* output) { "$0 $1\n", absl::BytesToHexString(file_info.build_id_).c_str(), file_info.filename_.c_str()); } - THROWF( - "Debug file(s) did not match any input file:\n$0\nInput Files:\n$1", - unused_debug.c_str(), input_files.c_str()); + THROWF("Debug file(s) did not match any input file:\n$0\nInput Files:\n$1", + unused_debug.c_str(), input_files.c_str()); } } @@ -1961,9 +1995,7 @@ class ArgParser { return ret; } - void ConsumeAndSaveArg() { - (*out_argv_)[(*out_argc_)++] = argv_[index_++]; - } + void ConsumeAndSaveArg() { (*out_argv_)[(*out_argc_)++] = argv_[index_++]; } // Singular flag like --csv or -v. bool TryParseFlag(string_view flag) { @@ -2154,6 +2186,7 @@ bool DoParseOptions(bool skip_unknown, int* argc, char** argv[], } } else { if (saw_separator) { + output_options->showAllSizesCSV = true; options->add_base_filename(std::string(args.ConsumeArg())); } else { options->add_filename(std::string(args.ConsumeArg())); diff --git a/src/bloaty.h b/src/bloaty.h index 82c0c55c..5f8d4700 100644 --- a/src/bloaty.h +++ b/src/bloaty.h @@ -337,18 +337,26 @@ void DisassembleFindReferences(const DisassemblyInfo& info, RangeSink* sink); class Rollup; +struct DomainSizes { + int64_t vm; + int64_t file; +}; + struct RollupRow { RollupRow(const std::string& name_) : name(name_) {} std::string name; - int64_t vmsize = 0; - int64_t filesize = 0; - int64_t filtered_vmsize = 0; - int64_t filtered_filesize = 0; + DomainSizes size = {0, 0}; + DomainSizes filtered_size = {0, 0}; + int64_t other_count = 0; int64_t sortkey; double vmpercent; double filepercent; + + // The size of the base in a diff mode. Otherwise stay 0. + DomainSizes old_size = {0, 0}; + std::vector sorted_children; static bool Compare(const RollupRow& a, const RollupRow& b) { @@ -377,6 +385,7 @@ struct OutputOptions { OutputFormat output_format = OutputFormat::kPrettyPrint; size_t max_label_len = 80; ShowDomain show = ShowDomain::kShowBoth; + bool showAllSizesCSV = false; }; struct RollupOutput { @@ -413,17 +422,17 @@ struct RollupOutput { static bool IsSame(const std::string& a, const std::string& b); void PrettyPrint(const OutputOptions& options, std::ostream* out) const; - void PrintToCSV(std::ostream* out, bool tabs) const; + void PrintToCSV(std::ostream* out, bool tabs, bool csvDiff) const; void PrettyPrintRow(const RollupRow& row, size_t indent, const OutputOptions& options, std::ostream* out) const; void PrettyPrintTree(const RollupRow& row, size_t indent, const OutputOptions& options, std::ostream* out) const; void PrintRowToCSV(const RollupRow& row, std::vector parent_labels, - std::ostream* out, bool tabs) const; + std::ostream* out, bool tabs, bool csvDiff) const; void PrintTreeToCSV(const RollupRow& row, std::vector parent_labels, - std::ostream* out, bool tabs) const; + std::ostream* out, bool tabs, bool csvDiff) const; }; bool ParseOptions(bool skip_unknown, int* argc, char** argv[], Options* options, diff --git a/tests/bloaty_misc_test.cc b/tests/bloaty_misc_test.cc index 3a4bf41c..a1d50843 100644 --- a/tests/bloaty_misc_test.cc +++ b/tests/bloaty_misc_test.cc @@ -27,7 +27,7 @@ TEST_F(BloatyTest, InlinesOnSmallFile) { {"bloaty", "-d", "compileunits", "03-small-binary-that-crashed-inlines.bin"}); RunBloaty( {"bloaty", "-d", "inlines", "03-small-binary-that-crashed-inlines.bin"}); - EXPECT_EQ(top_row_->vmsize, 2340); + EXPECT_EQ(top_row_->size.vm, 2340); } TEST_F(BloatyTest, GoBinary) { @@ -46,7 +46,7 @@ TEST_F(BloatyTest, ImplicitConstAndLineStrp) { TEST_F(BloatyTest, MultiThreaded) { RunBloaty({"bloaty", "02-section-count-overflow.o"}); - size_t file_size = top_row_->filesize; + size_t file_size = top_row_->size.file; // Bloaty doesn't know or care that you are passing the same file multiple // times. @@ -56,5 +56,5 @@ TEST_F(BloatyTest, MultiThreaded) { args.push_back("02-section-count-overflow.o"); } RunBloaty(args); // Heavily multithreaded test. - EXPECT_EQ(top_row_->filesize, file_size * 100); + EXPECT_EQ(top_row_->size.file, file_size * 100); } diff --git a/tests/bloaty_test.cc b/tests/bloaty_test.cc index 8187aca3..68eb9df4 100644 --- a/tests/bloaty_test.cc +++ b/tests/bloaty_test.cc @@ -21,20 +21,20 @@ TEST_F(BloatyTest, EmptyObjectFile) { // Empty .c file should result in a .o file with no vmsize. RunBloaty({"bloaty", file}); - EXPECT_EQ(top_row_->vmsize, 0); - EXPECT_EQ(top_row_->filesize, size); + EXPECT_EQ(top_row_->size.vm, 0); + EXPECT_EQ(top_row_->size.file, size); EXPECT_GT(top_row_->sorted_children.size(), 1); // Same with segments (we fake segments on .o files). RunBloaty({"bloaty", "-d", "segments", file}); - EXPECT_EQ(top_row_->vmsize, 0); - EXPECT_EQ(top_row_->filesize, size); + EXPECT_EQ(top_row_->size.vm, 0); + EXPECT_EQ(top_row_->size.file, size); EXPECT_GT(top_row_->sorted_children.size(), 1); // Same with symbols. RunBloaty({"bloaty", "-d", "symbols", file}); - EXPECT_EQ(top_row_->vmsize, 0); - EXPECT_EQ(top_row_->filesize, size); + EXPECT_EQ(top_row_->size.vm, 0); + EXPECT_EQ(top_row_->size.file, size); EXPECT_GT(top_row_->sorted_children.size(), 1); // We can't run any of these targets against object files. @@ -50,16 +50,16 @@ TEST_F(BloatyTest, SimpleObjectFile) { // Test "-n 0" which should return an unlimited number of rows. RunBloaty({"bloaty", "-n", "0", file}); - EXPECT_GT(top_row_->vmsize, 64); - EXPECT_LT(top_row_->vmsize, 300); - EXPECT_EQ(top_row_->filesize, size); + EXPECT_GT(top_row_->size.vm, 64); + EXPECT_LT(top_row_->size.vm, 300); + EXPECT_EQ(top_row_->size.file, size); EXPECT_GT(top_row_->sorted_children.size(), 1); // Same with segments (we fake segments on .o files). RunBloaty({"bloaty", "-d", "segments", file}); - EXPECT_GT(top_row_->vmsize, 64); - EXPECT_LT(top_row_->vmsize, 300); - EXPECT_EQ(top_row_->filesize, size); + EXPECT_GT(top_row_->size.vm, 64); + EXPECT_LT(top_row_->size.vm, 300); + EXPECT_EQ(top_row_->size.file, size); EXPECT_GT(top_row_->sorted_children.size(), 1); // For inputfiles we should get everything attributed to the input file. @@ -111,15 +111,15 @@ TEST_F(BloatyTest, SimpleArchiveFile) { ASSERT_TRUE(GetFileSize(file, &size)); RunBloaty({"bloaty", file}); - EXPECT_GT(top_row_->vmsize, 8000); - EXPECT_LT(top_row_->vmsize, 12000); - //EXPECT_EQ(top_row_->filesize, size); + EXPECT_GT(top_row_->size.vm, 8000); + EXPECT_LT(top_row_->size.vm, 12000); + //EXPECT_EQ(top_row_->size.file, size); EXPECT_GT(top_row_->sorted_children.size(), 3); RunBloaty({"bloaty", "-d", "segments", file}); - EXPECT_GT(top_row_->vmsize, 8000); - EXPECT_LT(top_row_->vmsize, 12000); - //EXPECT_EQ(top_row_->filesize, size); + EXPECT_GT(top_row_->size.vm, 8000); + EXPECT_LT(top_row_->size.vm, 12000); + //EXPECT_EQ(top_row_->size.file, size); RunBloaty({"bloaty", "-d", "symbols", "-n", "40", "-s", "vm", file}); AssertChildren(*top_row_, { @@ -174,15 +174,15 @@ TEST_F(BloatyTest, SimpleSharedObjectFile) { ASSERT_TRUE(GetFileSize(file, &size)); RunBloaty({"bloaty", file}); - EXPECT_GT(top_row_->vmsize, 8000); - EXPECT_LT(top_row_->vmsize, 12000); - EXPECT_EQ(top_row_->filesize, size); + EXPECT_GT(top_row_->size.vm, 8000); + EXPECT_LT(top_row_->size.vm, 12000); + EXPECT_EQ(top_row_->size.file, size); EXPECT_GT(top_row_->sorted_children.size(), 3); RunBloaty({"bloaty", "-d", "segments", file}); - EXPECT_GT(top_row_->vmsize, 8000); - EXPECT_LT(top_row_->vmsize, 12000); - EXPECT_EQ(top_row_->filesize, size); + EXPECT_GT(top_row_->size.vm, 8000); + EXPECT_LT(top_row_->size.vm, 12000); + EXPECT_EQ(top_row_->size.file, size); RunBloaty({"bloaty", "-d", "symbols", "-n", "50", file}); AssertChildren(*top_row_, { @@ -199,15 +199,15 @@ TEST_F(BloatyTest, SimpleBinary) { ASSERT_TRUE(GetFileSize(file, &size)); RunBloaty({"bloaty", file}); - EXPECT_GT(top_row_->vmsize, 8000); - EXPECT_LT(top_row_->vmsize, 12000); - EXPECT_EQ(top_row_->filesize, size); + EXPECT_GT(top_row_->size.vm, 8000); + EXPECT_LT(top_row_->size.vm, 12000); + EXPECT_EQ(top_row_->size.file, size); EXPECT_GT(top_row_->sorted_children.size(), 3); RunBloaty({"bloaty", "-d", "segments", file}); - EXPECT_GT(top_row_->vmsize, 8000); - EXPECT_LT(top_row_->vmsize, 12000); - EXPECT_EQ(top_row_->filesize, size); + EXPECT_GT(top_row_->size.vm, 8000); + EXPECT_LT(top_row_->size.vm, 12000); + EXPECT_EQ(top_row_->size.file, size); RunBloaty({"bloaty", "-d", "symbols", "-n", "50", "-s", "vm", file}); AssertChildren(*top_row_, { diff --git a/tests/test.h b/tests/test.h index dc864f0a..99517d73 100644 --- a/tests/test.h +++ b/tests/test.h @@ -81,16 +81,16 @@ class BloatyTest : public ::testing::Test { uint64_t vmtotal = 0; uint64_t filetotal = 0; for (const auto& child : row.sorted_children) { - vmtotal += child.vmsize; - filetotal += child.filesize; + vmtotal += child.size.vm; + filetotal += child.size.file; CheckConsistencyForRow(child, false, diff_mode, count); ASSERT_TRUE(names.insert(child.name).second); - ASSERT_FALSE(child.vmsize == 0 && child.filesize == 0); + ASSERT_FALSE(child.size.vm == 0 && child.size.file == 0); } if (!diff_mode) { - ASSERT_EQ(vmtotal, row.vmsize); - ASSERT_EQ(filetotal, row.filesize); + ASSERT_EQ(vmtotal, row.size.vm); + ASSERT_EQ(filetotal, row.size.file); } } else { // Count leaf rows. @@ -147,7 +147,7 @@ class BloatyTest : public ::testing::Test { ASSERT_TRUE(GetFileSize(filename, &size)); total_input_size += size; } - ASSERT_EQ(top_row_->filesize, total_input_size); + ASSERT_EQ(top_row_->size.file, total_input_size); } int rows = 0; @@ -248,21 +248,21 @@ class BloatyTest : public ::testing::Test { if (expected_vm == kUnknown) { // Always pass. } else if (expected_vm > 0) { - EXPECT_GE(child.vmsize, expected_vm); + EXPECT_GE(child.size.vm, expected_vm); // Allow some overhead. - EXPECT_LE(child.vmsize, (expected_vm * 1.1) + 100); + EXPECT_LE(child.size.vm, (expected_vm * 1.1) + 100); } else { ASSERT_TRUE(false); } if (expected_file == kSameAsVM) { - expected_file = child.vmsize; + expected_file = child.size.vm; } if (expected_file != kUnknown) { - EXPECT_GE(child.filesize, expected_file); + EXPECT_GE(child.size.file, expected_file); // Allow some overhead. - EXPECT_LE(child.filesize, (expected_file * 1.2) + 180); + EXPECT_LE(child.size.file, (expected_file * 1.2) + 180); } if (++i == children.size()) { diff --git a/tests/testdata/make_all_msvc_test_files.bat b/tests/testdata/make_all_msvc_test_files.bat old mode 100644 new mode 100755