diff --git a/src/bloaty.cc b/src/bloaty.cc index 78147eaa..cf2698fa 100644 --- a/src/bloaty.cc +++ b/src/bloaty.cc @@ -338,8 +338,9 @@ class Rollup { CreateRows(row, base, options, true); } - void SetFilterRegex(const ReImpl* regex) { - filter_regex_ = regex; + void SetFilterRegex(const ReImpl* regex_include, const ReImpl* regex_exclude) { + filter_regex_include_ = regex_include; + filter_regex_exclude_ = regex_exclude; } // Subtract the values in "other" from this. @@ -379,7 +380,8 @@ class Rollup { int64_t filtered_vm_total_ = 0; int64_t filtered_file_total_ = 0; - const ReImpl* filter_regex_ = nullptr; + const ReImpl* filter_regex_include_ = nullptr; + const ReImpl* filter_regex_exclude_ = nullptr; // Putting Rollup by value seems to work on some compilers/libs but not // others. @@ -398,20 +400,33 @@ class Rollup { // If there are more entries names[i+1, i+2, etc] add them to sub-rollups. void AddInternal(const std::vector& names, size_t i, uint64_t size, bool is_vmsize) { - if (filter_regex_ != nullptr) { + if (filter_regex_include_ != nullptr || filter_regex_exclude_ != nullptr) { // filter_regex_ is only set in the root rollup, which checks the full // label hierarchy for a match to determine whether a region should be // considered. - bool any_matched = false; + bool exclude = false; + if (filter_regex_include_ != nullptr) { + bool any_matched = false; + + for (const auto& name : names) { + if (ReImpl::PartialMatch(name, *filter_regex_include_)) { + any_matched = true; + break; + } + } + exclude = !any_matched; + } - for (const auto& name : names) { - if (ReImpl::PartialMatch(name, *filter_regex_)) { - any_matched = true; - break; + if (!exclude && filter_regex_exclude_ != nullptr) { + for (const auto& name : names) { + if (ReImpl::PartialMatch(name, *filter_regex_exclude_)) { + exclude = true; + break; + } } } - if (!any_matched) { + if (exclude) { // Ignore this region in the rollup and don't visit sub-rollups. if (is_vmsize) { CheckedAdd(&filtered_vm_total_, size); @@ -1789,13 +1804,17 @@ void Bloaty::ScanAndRollupFiles( std::vector threads(num_threads); ThreadSafeIterIndex index(filenames.size()); - std::unique_ptr regex = nullptr; + std::unique_ptr regex_include = nullptr; + std::unique_ptr regex_exclude = nullptr; if (options_.has_source_filter()) { - regex = absl::make_unique(options_.source_filter()); + regex_include = absl::make_unique(options_.source_filter()); + } + if (options_.has_not_source_filter()) { + regex_exclude = absl::make_unique(options_.not_source_filter()); } for (int i = 0; i < num_threads; i++) { - thread_data[i].rollup.SetFilterRegex(regex.get()); + thread_data[i].rollup.SetFilterRegex(regex_include.get(), regex_exclude.get()); threads[i] = std::thread([this, &index, &filenames](PerThreadData* data) { try { @@ -1940,6 +1959,8 @@ USAGE: bloaty [OPTION]... FILE... [-- BASE_FILE...] --list-sources Show a list of available sources and exit. --source-filter=PATTERN Only show keys with names matching this pattern. + --not-source-filter=PATTERN + Exclude keys with names matching this pattern. Options for debugging Bloaty: @@ -2139,6 +2160,8 @@ bool DoParseOptions(bool skip_unknown, int* argc, char** argv[], } } else if (args.TryParseOption("--source-filter", &option)) { options->set_source_filter(std::string(option)); + } else if (args.TryParseOption("--not-source-filter", &option)) { + options->set_not_source_filter(std::string(option)); } else if (args.TryParseFlag("-v")) { options->set_verbose_level(1); } else if (args.TryParseFlag("-vv")) { @@ -2247,6 +2270,13 @@ void BloatyDoMain(const Options& options, const InputFileFactory& file_factory, } } + if (options.has_not_source_filter()) { + ReImpl re(options.not_source_filter()); + if (!re.ok()) { + THROW("invalid regex for not_source_filter"); + } + } + verbose_level = options.verbose_level(); if (options.data_source_size() > 0) { diff --git a/src/bloaty.proto b/src/bloaty.proto index 59db6416..a2f9e682 100644 --- a/src/bloaty.proto +++ b/src/bloaty.proto @@ -72,6 +72,9 @@ message Options { // Regex with which to filter names in the data sources. optional string source_filter = 13; + + // Regex with which to exclude names in the data sources. + optional string not_source_filter = 14; } // A custom data source allows users to create their own label space by diff --git a/tests/bloaty_test_pe.cc b/tests/bloaty_test_pe.cc index 14a247ff..3d64365b 100644 --- a/tests/bloaty_test_pe.cc +++ b/tests/bloaty_test_pe.cc @@ -110,15 +110,21 @@ TEST_P(BloatyOutputTest, CheckOutput) { } static BloatyTestEntry tests[] = { + // Normal tests on MSVC 15 (2017) { "MSVCR15DLL", {}, "msvc-15.0-foo-bar.dll", "msvc-15.0-foo-bar.dll.txt" }, { "MSVCR15DLLSEG", {"-d", "segments"}, "msvc-15.0-foo-bar.dll", "msvc-15.0-foo-bar.dll.seg.txt" }, { "MSVC15EXE", {}, "msvc-15.0-foo-bar-main-cv.bin", "msvc-15.0-foo-bar-main-cv.bin.txt" }, { "MSVC15EXESEG", {"-d", "segments"}, "msvc-15.0-foo-bar-main-cv.bin", "msvc-15.0-foo-bar-main-cv.bin.seg.txt" }, + // Normal tests on MSVC 16 (2019) { "MSVCR16DLL", {}, "msvc-16.0-foo-bar.dll", "msvc-16.0-foo-bar.dll.txt" }, { "MSVCR16DLLSEG", {"-d", "segments"}, "msvc-16.0-foo-bar.dll", "msvc-16.0-foo-bar.dll.seg.txt" }, { "MSVC16EXE", {}, "msvc-16.0-foo-bar-main-cv.bin", "msvc-16.0-foo-bar-main-cv.bin.txt" }, { "MSVC16EXESEG", {"-d", "segments"}, "msvc-16.0-foo-bar-main-cv.bin", "msvc-16.0-foo-bar-main-cv.bin.seg.txt" }, + + // Filter tests + { "FILTERINCLUDE", {"--source-filter", "text"}, "msvc-15.0-foo-bar.dll", "filter_include.txt" }, + { "FILTEREXCLUDE", {"--not-source-filter", "text"}, "msvc-15.0-foo-bar.dll", "filter_exclude.txt" }, }; INSTANTIATE_TEST_SUITE_P(BloatyTest, diff --git a/tests/test.h b/tests/test.h index dc864f0a..91ed5f8c 100644 --- a/tests/test.h +++ b/tests/test.h @@ -140,7 +140,7 @@ class BloatyTest : public ::testing::Test { void CheckConsistency(const bloaty::Options& options) { ASSERT_EQ(options.base_filename_size() > 0, output_->diff_mode()); - if (!output_->diff_mode()) { + if (!output_->diff_mode() && !options.has_source_filter() && !options.has_not_source_filter()) { size_t total_input_size = 0; for (const auto& filename : options.filename()) { uint64_t size; diff --git a/tests/testdata/PE/x64/filter_exclude.txt b/tests/testdata/PE/x64/filter_exclude.txt new file mode 100644 index 00000000..330df540 --- /dev/null +++ b/tests/testdata/PE/x64/filter_exclude.txt @@ -0,0 +1,7 @@ +sections vmsize filesize +.rdata 2520 2560 +.data 1592 512 +[PE Headers] 696 696 +.pdata 408 512 +.reloc 24 512 +[Unmapped] 0 328 diff --git a/tests/testdata/PE/x64/filter_include.txt b/tests/testdata/PE/x64/filter_include.txt new file mode 100644 index 00000000..8aa3b0fd --- /dev/null +++ b/tests/testdata/PE/x64/filter_include.txt @@ -0,0 +1,2 @@ +sections vmsize filesize +.text 3587 4096 diff --git a/tests/testdata/PE/x86/filter_exclude.txt b/tests/testdata/PE/x86/filter_exclude.txt new file mode 100644 index 00000000..e2427c43 --- /dev/null +++ b/tests/testdata/PE/x86/filter_exclude.txt @@ -0,0 +1,6 @@ +sections vmsize filesize +.rdata 1772 2048 +.data 908 512 +[PE Headers] 656 656 +.reloc 296 512 +[Unmapped] 0 368 diff --git a/tests/testdata/PE/x86/filter_include.txt b/tests/testdata/PE/x86/filter_include.txt new file mode 100644 index 00000000..8fbc3f26 --- /dev/null +++ b/tests/testdata/PE/x86/filter_include.txt @@ -0,0 +1,2 @@ +sections vmsize filesize +.text 3172 3584