Merge branch 'abbrev' of github.com:google/bloaty into abbrev
diff --git a/.gitmodules b/.gitmodules
index bad512e..c0d6fa8 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -4,6 +4,6 @@
[submodule "third_party/googletest"]
path = third_party/googletest
url = https://github.com/google/googletest.git
-[submodule "third_party/libFuzzer"]
- path = third_party/libFuzzer
- url = https://chromium.googlesource.com/chromium/llvm-project/llvm/lib/Fuzzer
+[submodule "third_party/abseil-cpp"]
+ path = third_party/abseil-cpp
+ url = https://github.com/abseil/abseil-cpp.git
diff --git a/.travis.yml b/.travis.yml
index a540b91..70af010 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,5 @@
language: cpp
-sudo: required # only required for dist: trusty
dist: trusty
compiler:
@@ -11,4 +10,4 @@
- linux
- osx
-script: make test
+script: mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Debug .. && make -j4 && make test
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..e720865
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,75 @@
+cmake_minimum_required (VERSION 2.6)
+project (Bloaty)
+
+set(RE2_BUILD_TESTING OFF CACHE BOOL "enable testing for RE2" FORCE)
+add_subdirectory(third_party/re2)
+include_directories(third_party/re2)
+
+include_directories(.)
+include_directories(src)
+include_directories(third_party/abseil-cpp)
+set(CMAKE_CXX_FLAGS "-std=c++11 -W -Wall -Wno-sign-compare")
+set(CMAKE_CXX_FLAGS_DEBUG "-g")
+set(CMAKE_CXX_FLAGS_RELEASE "-O2")
+
+add_library(libbloaty
+ src/bloaty.cc
+ src/dwarf.cc
+ src/elf.cc
+ src/macho.cc
+ # Until Abseil has a proper CMake build system
+ third_party/abseil-cpp/absl/base/internal/throw_delegate.cc
+ third_party/abseil-cpp/absl/base/internal/raw_logging.cc # Grrrr...
+ third_party/abseil-cpp/absl/strings/ascii.cc
+ third_party/abseil-cpp/absl/strings/internal/memutil.cc
+ third_party/abseil-cpp/absl/strings/numbers.cc
+ third_party/abseil-cpp/absl/strings/str_cat.cc
+ third_party/abseil-cpp/absl/strings/string_view.cc
+ )
+
+add_executable(bloaty src/main.cc)
+target_link_libraries(bloaty libbloaty re2)
+
+# All of this is to add -pthread, which is required by re2 (not us).
+find_package(Threads REQUIRED)
+if(THREADS_HAVE_PTHREAD_ARG)
+ set_property(TARGET bloaty PROPERTY COMPILE_OPTIONS "-pthread")
+ set_property(TARGET bloaty PROPERTY INTERFACE_COMPILE_OPTIONS "-pthread")
+endif()
+if(CMAKE_THREAD_LIBS_INIT)
+ target_link_libraries(bloaty "${CMAKE_THREAD_LIBS_INIT}")
+endif()
+
+enable_testing()
+
+if(BUILD_TESTING)
+ add_subdirectory(third_party/googletest)
+ include_directories(third_party/googletest/googletest/include)
+ include_directories(third_party/googletest/googlemock/include)
+
+ set(TEST_TARGETS
+ bloaty_test
+ bloaty_misc_test
+ range_map_test
+ )
+
+ foreach(target ${TEST_TARGETS})
+ add_executable(${target} tests/${target}.cc)
+ target_link_libraries(${target} libbloaty re2 gtest_main gmock "${CMAKE_THREAD_LIBS_INIT}")
+ endforeach(target)
+
+ if($ENV{LIB_FUZZING_ENGINE})
+ add_executable(fuzz_test tests/fuzz_target.cc)
+ else()
+ add_executable(fuzz_test tests/fuzz_target.cc tests/fuzz_driver.cc)
+ endif()
+ target_link_libraries(fuzz_test libbloaty re2 "${CMAKE_THREAD_LIBS_INIT}")
+
+ file(GLOB fuzz_corpus tests/testdata/fuzz_corpus/*)
+
+ add_test(NAME range_map_test COMMAND range_map_test)
+ add_test(NAME bloaty_test_x86-64 COMMAND bloaty_test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests/testdata/linux-x86_64)
+ add_test(NAME bloaty_test_x86 COMMAND bloaty_test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests/testdata/linux-x86)
+ add_test(NAME bloaty_misc_test COMMAND bloaty_misc_test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests/testdata/misc)
+ add_test(NAME fuzz_test COMMAND fuzz_test ${fuzz_corpus} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests/testdata/fuzz_corpus)
+endif()
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 94f6cc6..0000000
--- a/Makefile
+++ /dev/null
@@ -1,103 +0,0 @@
-
-.PHONY: clean deepclean
-
-# Disable -Wsign-compare because StringPiece currently unhelpfully defines
-# size() as ssize_t instad of size_t.
-CXXFLAGS=-std=c++11 -W -Wall -Wno-sign-compare -g -I third_party/re2 -I. -Isrc
-RE2_H=third_party/re2/re2/re2.h
-RE2_A=third_party/re2/obj/libre2.a
-
-bloaty: src/main.cc src/libbloaty.a $(RE2_A)
- $(CXX) $(GC_SECTIONS) $(CXXFLAGS) -O2 -o $@ $^ -lpthread
-
-OBJS=src/bloaty.o src/dwarf.o src/elf.o src/macho.o
-
-$(OBJS): %.o : %.cc src/bloaty.h src/dwarf_constants.h $(RE2_H)
- $(CXX) $(CXXFLAGS) -O2 -c -o $@ $<
-
-src/libbloaty.a: $(OBJS)
- ar rcs $@ $^
-
-third_party/re2/obj/libre2.a: third_party/re2/Makefile
- $(MAKE) -C third_party/re2 CPPFLAGS="-ffunction-sections -fdata-sections -g"
-
-# These targets share a pattern match to coerce make into only executing once
-# See this discussion: http://stackoverflow.com/a/3077254/1780018
-third%party/re2/Makefile third%party/re2/re2/re2.h third%party/googletest/CMakeLists.txt third%party/libFuzzer/build.sh: .gitmodules
- git submodule init && git submodule update
- @# Ensure .gitmodules cannot be newer
- touch -r .gitmodules $@
-
-clean:
- rm -f bloaty src/*.o src/*.a
- rm -f tests/range_map_test tests/bloaty_test tests/bloaty_misc_test
- rm -rf *.dSYM
-
-deepclean: clean
- rm -rf third_party/re2 third_party/googletest third_party/libFuzzer
-
-## Tests #######################################################################
-
-TESTFLAGS=-Ithird_party/googletest/googletest/include -Ithird_party/googletest/googlemock/include
-TESTLIBS=src/libbloaty-test.a $(RE2_A) third_party/googletest/googlemock/gtest/libgtest_main.a third_party/googletest/googlemock/gtest/libgtest.a
-
-ifeq ($(CXX), clang++)
- TESTFLAGS += -fsanitize=address
-endif
-
-TESTOBJS=$(OBJS:src/%.o=src/%.test.o)
-
-$(TESTOBJS): %.test.o : %.cc src/bloaty.h src/dwarf_constants.h $(RE2_H)
- $(CXX) $(CXXFLAGS) $(TESTFLAGS) -c -o $@ $<
-
-src/libbloaty-test.a: $(TESTOBJS)
- ar rcs $@ $^
-
-test: tests/range_map_test tests/bloaty_test tests/bloaty_misc_test
- TOP=`pwd`; \
- tests/range_map_test && \
- (cd tests/testdata/linux-x86_64 && $$TOP/tests/bloaty_test) && \
- (cd tests/testdata/linux-x86 && $$TOP/tests/bloaty_test) && \
- (cd tests/testdata/misc && $$TOP/tests/bloaty_misc_test)
-
-tests/range_map_test: tests/range_map_test.cc $(TESTLIBS)
- $(CXX) $(CXXFLAGS) $(TESTFLAGS) -o $@ $^ -lpthread
-
-tests/bloaty_test: tests/bloaty_test.cc $(TESTLIBS)
- $(CXX) $(CXXFLAGS) $(TESTFLAGS) -o $@ $^ -lpthread
-
-tests/bloaty_misc_test: tests/bloaty_misc_test.cc $(TESTLIBS)
- $(CXX) $(CXXFLAGS) $(TESTFLAGS) -o $@ $^ -lpthread
-
-third_party/googletest/googlemock/gtest/libgtest_main.a: third_party/googletest/CMakeLists.txt
- cd third_party/googletest && cmake . && $(MAKE)
-
-## Fuzzing #####################################################################
-
-FUZZFLAGS=-fsanitize=address -fsanitize-coverage=trace-pc-guard
-TESTCMD=-fsanitize-coverage=trace-pc-guard -c -x c++ /dev/null -o /dev/null 2> /dev/null
-TESTRESULT=$(shell $(CXX) $(TESTCMD) && echo ok)
-ifeq ($(TESTRESULT), ok)
-fuzz: tests/fuzz_target
-else
-fuzz:
- echo "Fuzzing requires that CXX is a very recent Clang (ie from svn"
- false
-endif
-
-LIBFUZZER=third_party/libFuzzer/libFuzzer.a
-FUZZLIBS=$(BLOATYLIBS) $(LIBFUZZER)
-
-FUZZOBJS=$(OBJS:src/%.o=src/%.fuzz.o)
-
-$(FUZZOBJS): %.fuzz.o : %.cc src/bloaty.h src/dwarf_constants.h $(RE2_H)
- $(CXX) $(CXXFLAGS) $(FUZZFLAGS) -c -o $@ $<
-
-src/libbloaty-fuzz.a: $(FUZZOBJS)
- ar rcs $@ $^
-
-$(LIBFUZZER): third_party/libFuzzer/build.sh
- cd third_party/libFuzzer && ./build.sh
-
-tests/fuzz_target: tests/fuzz_target.cc src/libbloaty-fuzz.a $(LIBFUZZER) $(RE2_A)
- $(CXX) $(CXXFLAGS) $(FUZZFLAGS) -o $@ $^ -lpthread
diff --git a/src/bloaty.cc b/src/bloaty.cc
index e3d86cb..dbea25e 100644
--- a/src/bloaty.cc
+++ b/src/bloaty.cc
@@ -13,13 +13,14 @@
// limitations under the License.
#include <array>
-#include <cmath>
#include <cinttypes>
+#include <cmath>
#include <fstream>
#include <iostream>
#include <limits>
#include <map>
#include <memory>
+#include <queue>
#include <set>
#include <sstream>
#include <string>
@@ -38,11 +39,15 @@
#include <sys/wait.h>
#include <unistd.h>
+#include "absl/strings/str_join.h"
+#include "absl/strings/string_view.h"
#include "re2/re2.h"
#include <assert.h>
#include "bloaty.h"
+using absl::string_view;
+
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#define CHECK_SYSCALL(call) \
@@ -104,6 +109,32 @@
}
}
+std::string CSVEscape(string_view str) {
+ bool need_escape = false;
+
+ for (char ch : str) {
+ if (ch == '"' || ch == ',') {
+ need_escape = true;
+ break;
+ }
+ }
+
+ if (need_escape) {
+ std::string ret = "\"";
+ for (char ch : str) {
+ if (ch == '"') {
+ ret += "\"\"";
+ } else {
+ ret += ch;
+ }
+ }
+ ret += "\"";
+ return ret;
+ } else {
+ return std::string(str);
+ }
+}
+
template <class Func>
class RankComparator {
public:
@@ -117,7 +148,7 @@
};
template <class Func>
-RankComparator<Func> MakeRankComparator(Func func) {
+constexpr RankComparator<Func> MakeRankComparator(Func func) {
return RankComparator<Func>(func);
}
@@ -196,14 +227,14 @@
class NameStripper {
public:
bool StripName(const std::string& name) {
- if (name[name.size() - 1] != ')') {
- // (anonymous namespace)::ctype_w
- stripped_ = &name;
- return false;
- }
-
int nesting = 0;
for (size_t n = name.size() - 1; n < name.size(); --n) {
+ if (name[n] == ':' && nesting == 0) {
+ // (anonymous namespace)::ctype_w
+ stripped_ = &name;
+ return false;
+ }
+
if (name[n] == '(') {
if (--nesting == 0) {
storage_ = name.substr(0, n);
@@ -345,7 +376,7 @@
// applied in sequence.
void AddRegex(const std::string& regex, const std::string& replacement);
- std::string Munge(StringPiece name) const;
+ std::string Munge(string_view name) const;
private:
BLOATY_DISALLOW_COPY_AND_ASSIGN(NameMunger);
@@ -353,12 +384,12 @@
};
void NameMunger::AddRegex(const std::string& regex, const std::string& replacement) {
- std::unique_ptr<RE2> re2(new RE2(StringPiece(regex)));
+ std::unique_ptr<RE2> re2(new RE2(regex));
regexes_.push_back(std::make_pair(std::move(re2), replacement));
}
-std::string NameMunger::Munge(StringPiece name) const {
- std::string ret(name.as_string());
+std::string NameMunger::Munge(string_view name) const {
+ std::string ret(name);
for (const auto& pair : regexes_) {
if (RE2::Replace(&ret, *pair.first, pair.second)) {
@@ -673,6 +704,124 @@
}
+// SuffixArray /////////////////////////////////////////////////////////////////
+
+// A generalized enhanced suffix array
+// https://en.wikipedia.org/wiki/Suffix_array
+//
+// Generalized means it holds suffixes from multiple source strings. Enhanced
+// means that in addition to the suffix array proper, it also can contain other
+// arrays such as the "lcp" array (longest common prefix) and others.
+
+class SuffixArray {
+ public:
+ size_t size() const { return array_.size(); }
+
+ // You may only call this when the object is first constructed. Once any
+ // other methods are called it may no longer be called.
+ void AddString(string_view str) {
+ assert(array_.empty());
+ strings_.push_back(str);
+ }
+
+ std::pair<string_view, size_t> GetSuffix(size_t i) {
+ Entry e = entry(i);
+ return std::make_pair(GetStringFromEntry(e), e.str);
+ }
+
+ uint32_t GetLcp(size_t i) {
+ return lcp_[i];
+ }
+
+ void ComputeArray();
+ void ComputeLcp();
+
+ private:
+ struct Entry {
+ // This suffix is strings_[str].substr(ofs).
+ Entry(uint32_t str_, uint32_t ofs_) : str(str_), ofs(ofs_) {}
+ uint32_t str;
+ uint32_t ofs;
+ };
+
+ Entry entry(size_t i) { return array_[i]; }
+
+ string_view GetStringFromEntry(Entry entry) {
+ return strings_[entry.str].substr(entry.ofs);
+ }
+
+ std::vector<string_view> strings_;
+
+ std::vector<Entry> array_;
+ std::vector<uint32_t> lcp_;
+};
+
+void SuffixArray::ComputeArray() {
+ assert(array_.empty());
+
+ // This is a naive, O(n^2 lg n) algorithm.
+ // Several O(n) algorithms exist, but they are much, much more complicated.
+ // The fastest known algorithm is described by Nong, Zhang & Chan (2009):
+ // Linear Suffix Array Construction by Almost Pure Induced-Sorting
+ // We can replace this with a faster algorithm if/when it is an issue.
+ for (size_t i = 0; i < strings_.size(); i++) {
+ string_view str = strings_[i];
+ for (size_t j = 0; j < str.size(); j++) {
+ char last_ch = 0;
+ if (j > 0) last_ch = str[j - 1];
+ if (j == 0 || last_ch == '/' || last_ch == '<') {
+ array_.push_back(Entry(i, j));
+ }
+ }
+ }
+
+ std::sort(array_.begin(), array_.end(), [this](Entry a, Entry b) {
+ return GetStringFromEntry(a) < GetStringFromEntry(b);
+ });
+}
+
+void SuffixArray::ComputeLcp() {
+ assert(!array_.empty());
+ assert(lcp_.empty());
+
+ // This is a naive, O(n^2) algorithm.
+ // Several O(n) algorithms exist, but they are much, much more complicated.
+ // The fastest known O(n) algorithm is described by Fischer (2011):
+ // Inducing the LCP-Array
+ // We can replace this with a faster algorithm if/when it is an issue.
+ lcp_.resize(array_.size());
+ string_view last = GetStringFromEntry(entry(0));
+ for (size_t i = 1; i < array_.size(); i++) {
+ string_view curr = GetStringFromEntry(entry(i));
+ size_t limit = std::min(curr.size(), last.size());
+ size_t j;
+ int nesting = 0;
+ size_t nesting_start = 0;
+ for (j = 0; j < limit; j++) {
+ if (curr[j] != last[j]) {
+ break;
+ }
+ if (curr[j] == ',' && nesting == 0) {
+ break;
+ }
+ if (curr[j] == '<' || curr[j] == '(') {
+ if (nesting++ == 0) {
+ nesting_start = j;
+ }
+ }
+ if ((curr[j] == '>' || curr[j] == ')') && --nesting < 0) {
+ break;
+ }
+ }
+ if (nesting > 0) {
+ j = nesting_start;
+ }
+ lcp_[i] = j;
+ last = curr;
+ }
+}
+
+
// RollupOutput ////////////////////////////////////////////////////////////////
// RollupOutput represents rollup data after we have applied output massaging
@@ -775,8 +924,8 @@
}
}
-void RollupOutput::PrintRow(const RollupRow& row, size_t indent,
- std::ostream* out) const {
+void RollupOutput::PrettyPrintRow(const RollupRow& row, size_t indent,
+ std::ostream* out) const {
*out << FixedWidthString("", indent) << " "
<< PercentString(row.vmpercent, row.diff_mode) << " "
<< SiPrint(row.vmsize, row.diff_mode) << " "
@@ -785,10 +934,10 @@
<< PercentString(row.filepercent, row.diff_mode) << "\n";
}
-void RollupOutput::PrintTree(const RollupRow& row, size_t indent,
- std::ostream* out) const {
+void RollupOutput::PrettyPrintTree(const RollupRow& row, size_t indent,
+ std::ostream* out) const {
// Rows are printed before their sub-rows.
- PrintRow(row, indent, out);
+ PrettyPrintRow(row, indent, out);
// For now we don't print "confounding" sub-entries. For example, if we're
// doing a two-level analysis "-d section,symbol", and a section is growing
@@ -799,19 +948,19 @@
if (row.vmsize > 0 || row.filesize > 0) {
for (const auto& child : row.sorted_children) {
- PrintTree(child, indent + 4, out);
+ PrettyPrintTree(child, indent + 4, out);
}
}
if (row.vmsize < 0 || row.filesize < 0) {
for (const auto& child : row.shrinking) {
- PrintTree(child, indent + 4, out);
+ PrettyPrintTree(child, indent + 4, out);
}
}
if ((row.vmsize < 0) != (row.filesize < 0)) {
for (const auto& child : row.mixed) {
- PrintTree(child, indent + 4, out);
+ PrettyPrintTree(child, indent + 4, out);
}
}
}
@@ -827,64 +976,69 @@
void RollupOutput::AbbreviateToFit(size_t width) {
std::vector<std::string*> names;
std::string best_prefix;
- int best_prefix_weight = 0;
CollectNames(&toplevel_row_, &names);
+ SuffixArray suffixes;
- while (1) {
- printf("Yo!\n");
- sleep(1);
- size_t saw_overwide = false;
- for (auto name : names) {
- if (name->size() > width) {
- saw_overwide = true;
- break;
- }
- }
- if (!saw_overwide) {
- return;
- }
+ for (size_t i = 0; i < names.size(); i++) {
+ suffixes.AddString(*names[i]);
+ }
- size_t n = 0;
- std::unordered_set<const std::string*> have_prefix;
- std::copy(names.begin(), names.end(), std::inserter(have_prefix ,have_prefix.end()));
- while (have_prefix.size() > 1) {
- const std::string* str0 = *have_prefix.begin();
- char ch = str0->at(n);
- if (true /*ch == '/'*/) {
- int weight = n * have_prefix.size();
- if (weight > best_prefix_weight) {
- best_prefix = str0->substr(0, n);
- best_prefix_weight = weight;
- printf("Try prefix: %s\n", best_prefix.c_str());
- printf("Try prefix weight: %d\n", best_prefix_weight);
- }
- }
- for (auto it = have_prefix.begin(); it != have_prefix.end();) {
- printf("Str: %s\n", (*it)->c_str());
- if ((*it)->at(n) != ch) {
- it = have_prefix.erase(it); // previously this was something like m_map.erase(it++);
- } else {
- ++it;
- }
- }
- n++;
- }
- printf("Best prefix: %s\n", best_prefix.c_str());
- printf("Best prefix weight: %d\n", best_prefix_weight);
+ suffixes.ComputeArray();
+ suffixes.ComputeLcp();
- // Remove the best prefix from all strings that have it.
- std::string replace_with =
- std::string("[") + std::to_string(abbrevs_.size()) + "]";
- abbrevs_.push_back(best_prefix);
- for (auto name : names) {
- if (name->compare(0, best_prefix.length(), best_prefix) == 0) {
- name->replace(0, best_prefix.length(), replace_with);
+ struct Abbrev {
+ Abbrev(size_t weight_, string_view str_)
+ : weight(weight_),
+ str(str_.data(),
+ str_.size()) {}
+ bool operator <(const Abbrev& b) const { return weight < b.weight; }
+ size_t weight;
+ std::string str;
+ };
+
+ std::priority_queue<Abbrev> abbrevs;
+ std::vector<size_t> start_indexes;
+
+ for (size_t i = 0; i < suffixes.size(); i++) {
+ size_t lcp = suffixes.GetLcp(i);
+ std::cout << "Suffix: " << suffixes.GetSuffix(i).first << ", lcp: " << lcp << "\n";
+ if (start_indexes.empty() || lcp > suffixes.GetLcp(start_indexes.back())) {
+ start_indexes.push_back(i);
+ }
+ while (lcp < suffixes.GetLcp(start_indexes.back())) {
+ size_t index = start_indexes.back();
+ size_t len = suffixes.GetLcp(index);
+ start_indexes.pop_back();
+ size_t weight = (i - index + 1) * len * len;
+ std::cout << "Weight: " << weight << "\n";
+ if (weight > 0) {
+ string_view substr = suffixes.GetSuffix(index).first.substr(0, len);
+ abbrevs.emplace(weight, substr);
}
}
}
+
+ char ch = 'A';
+ while (!abbrevs.empty()) {
+ const std::string abbrev_text = abbrevs.top().str;
+ abbrevs.pop();
+ if (abbrev_text.size() < 30) {
+ continue;
+ }
+ std::string replacement(1, ch);
+ abbrevs_.emplace_back(replacement, abbrev_text);
+ ch++;
+ for (const auto& name : names) {
+ size_t pos;
+ while ((pos = name->find(abbrev_text)) != std::string::npos) {
+ name->replace(pos, abbrev_text.size(), replacement);
+ }
+ }
+ std::cout << "Abbrev: " << abbrevs.top().str << ", weight:" << abbrevs.top().weight << "\n";
+ }
}
-void RollupOutput::Print(std::ostream* out) const {
+void RollupOutput::PrettyPrint(std::ostream* out) const {
*out << " VM SIZE ";
PrintSpaces(longest_label_, out);
*out << " FILE SIZE";
@@ -903,7 +1057,7 @@
}
for (const auto& child : toplevel_row_.sorted_children) {
- PrintTree(child, 0, out);
+ PrettyPrintTree(child, 0, out);
}
if (toplevel_row_.diff_mode) {
@@ -914,7 +1068,7 @@
*out << " --------------";
*out << "\n";
for (const auto& child : toplevel_row_.shrinking) {
- PrintTree(child, 0, out);
+ PrettyPrintTree(child, 0, out);
}
}
@@ -925,7 +1079,7 @@
*out << " +-+-+-+-+-+-+-";
*out << "\n";
for (const auto& child : toplevel_row_.mixed) {
- PrintTree(child, 0, out);
+ PrettyPrintTree(child, 0, out);
}
}
@@ -934,18 +1088,70 @@
}
// The "TOTAL" row comes after all other rows.
- PrintRow(toplevel_row_, 0, out);
+ PrettyPrintRow(toplevel_row_, 0, out);
if (abbrevs_.size() > 0) {
- *out << "\n";
- *out << " Abbreviations:\n";
- int i = 0;
for (const auto& abbrev : abbrevs_) {
- *out << " [" << i++ << "]: " << abbrev << "\n";
+ *out << " " << abbrev.first << ": " << abbrev.second << "\n";
}
}
}
+void RollupOutput::PrintRowToCSV(const RollupRow& row,
+ string_view parent_labels,
+ std::ostream* out) const {
+ if (parent_labels.size() > 0) {
+ *out << parent_labels << ",";
+ }
+
+ *out << absl::StrJoin(std::make_tuple(CSVEscape(row.name),
+ row.vmsize,
+ row.filesize),
+ ",") << "\n";
+}
+
+void RollupOutput::PrintTreeToCSV(const RollupRow& row,
+ string_view parent_labels,
+ std::ostream* out) const {
+ if (row.sorted_children.size() > 0 ||
+ row.shrinking.size() > 0 ||
+ row.mixed.size() > 0) {
+ std::string labels;
+ if (parent_labels.size() > 0) {
+ labels = absl::StrJoin(
+ std::make_tuple(parent_labels, CSVEscape(row.name)), ",");
+ } else {
+ labels = CSVEscape(row.name);
+ }
+ for (const auto& child_row : row.sorted_children) {
+ PrintTreeToCSV(child_row, labels, out);
+ }
+ for (const auto& child_row : row.shrinking) {
+ PrintTreeToCSV(child_row, labels, out);
+ }
+ for (const auto& child_row : row.mixed) {
+ PrintTreeToCSV(child_row, labels, out);
+ }
+ } else {
+ PrintRowToCSV(row, parent_labels, out);
+ }
+}
+
+void RollupOutput::PrintToCSV(std::ostream* out) const {
+ std::vector<std::string> names(source_names_);
+ names.push_back("vmsize");
+ names.push_back("filesize");
+ *out << absl::StrJoin(names, ",") << "\n";
+ for (const auto& child_row : toplevel_row_.sorted_children) {
+ PrintTreeToCSV(child_row, "", out);
+ }
+ for (const auto& child_row : toplevel_row_.shrinking) {
+ PrintTreeToCSV(child_row, "", out);
+ }
+ for (const auto& child_row : toplevel_row_.mixed) {
+ PrintTreeToCSV(child_row, "", out);
+ }
+}
// RangeMap ////////////////////////////////////////////////////////////////////
@@ -1210,7 +1416,7 @@
RangeMap* vm_map() { return &vm_map_; }
protected:
- std::string ApplyNameRegexes(StringPiece name);
+ std::string ApplyNameRegexes(string_view name);
private:
BLOATY_DISALLOW_COPY_AND_ASSIGN(MemoryMap);
@@ -1221,8 +1427,8 @@
std::unique_ptr<NameMunger> munger_;
};
-std::string MemoryMap::ApplyNameRegexes(StringPiece name) {
- return munger_ ? munger_->Munge(name) : std::string(name.as_string());
+std::string MemoryMap::ApplyNameRegexes(string_view name) {
+ return munger_ ? munger_->Munge(name) : std::string(name);
}
@@ -1265,7 +1471,7 @@
return false;
}
- data_.set(map, buf.st_size);
+ data_ = string_view(map, buf.st_size);
return true;
}
@@ -1300,7 +1506,7 @@
RangeSink::~RangeSink() {}
-void RangeSink::AddFileRange(StringPiece name, uint64_t fileoff,
+void RangeSink::AddFileRange(string_view name, uint64_t fileoff,
uint64_t filesize) {
if (verbose_level > 2) {
fprintf(stderr, "[%s] AddFileRange(%.*s, %" PRIx64 ", %" PRIx64 ")\n",
@@ -1340,7 +1546,7 @@
AddVMRange(vmaddr, vmsize, name);
}
-void RangeSink::AddRange(StringPiece name, uint64_t vmaddr, uint64_t vmsize,
+void RangeSink::AddRange(string_view name, uint64_t vmaddr, uint64_t vmsize,
uint64_t fileoff, uint64_t filesize) {
if (verbose_level > 2) {
fprintf(stderr, "[%s] AddRange(%.*s, %" PRIx64 ", %" PRIx64 ", %" PRIx64
@@ -1536,7 +1742,7 @@
return ret;
}
- void PrintMapRow(StringPiece str, uint64_t start, uint64_t end) {
+ void PrintMapRow(string_view str, uint64_t start, uint64_t end) {
printf("[%" PRIx64 ", %" PRIx64 "] %.*s\n", start, end, (int)str.size(),
str.data());
}
@@ -1613,6 +1819,7 @@
Options:
+ --csv Output in CSV format instead of human-readable.
-d <sources> Comma-separated list of sources to scan.
-n <num> How many rows to show per level before collapsing
other keys into '[Other]'. Set to '0' for unlimited.
@@ -1663,6 +1870,8 @@
return false;
}
base_files = true;
+ } else if (strcmp(argv[i], "--csv") == 0) {
+ output->SetCSV(true);
} else if (strcmp(argv[i], "-d") == 0) {
if (!CheckNextArg(i, argc, "-d")) {
return false;
@@ -1671,6 +1880,7 @@
Split(argv[++i], ',', &names);
for (const auto& name : names) {
CHECK_RETURN(bloaty.AddDataSource(name));
+ output->AddDataSourceName(name);
}
} else if (strcmp(argv[i], "-r") == 0) {
std::string source_name, regex, substitution;
diff --git a/src/bloaty.h b/src/bloaty.h
index 2611b8d..1a327aa 100644
--- a/src/bloaty.h
+++ b/src/bloaty.h
@@ -29,6 +29,7 @@
#include <unordered_map>
#include <vector>
+#include "absl/strings/string_view.h"
#include "re2/re2.h"
#define BLOATY_DISALLOW_COPY_AND_ASSIGN(class_name) \
@@ -37,8 +38,6 @@
namespace bloaty {
-typedef re2::StringPiece StringPiece;
-
class MemoryMap;
enum class DataSource {
@@ -56,18 +55,20 @@
virtual ~InputFile() {}
const std::string& filename() const { return filename_; }
- StringPiece data() const { return data_; }
+ absl::string_view data() const { return data_; }
private:
BLOATY_DISALLOW_COPY_AND_ASSIGN(InputFile);
const std::string filename_;
protected:
- StringPiece data_;
+ absl::string_view data_;
};
class InputFileFactory {
public:
+ virtual ~InputFileFactory() {}
+
// Returns nullptr if the file could not be opened.
virtual std::unique_ptr<InputFile> TryOpenFile(
const std::string& filename) const = 0;
@@ -99,18 +100,19 @@
// 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
// .debug_* mappings don't exist in memory.
- void AddRange(StringPiece name, uint64_t vmaddr, uint64_t vmsize,
+ void AddRange(absl::string_view name, uint64_t vmaddr, uint64_t vmsize,
uint64_t fileoff, uint64_t filesize);
- void AddRange(StringPiece name, uint64_t vmaddr, uint64_t vmsize,
- StringPiece file_range) {
+ void AddRange(absl::string_view name, uint64_t vmaddr, uint64_t vmsize,
+ absl::string_view file_range) {
AddRange(name, vmaddr, vmsize, file_range.data() - file_->data().data(),
file_range.size());
}
- void AddFileRange(StringPiece name, uint64_t fileoff, uint64_t filesize);
+ void AddFileRange(absl::string_view name,
+ uint64_t fileoff, uint64_t filesize);
- void AddFileRange(StringPiece name, StringPiece file_range) {
+ void AddFileRange(absl::string_view name, absl::string_view file_range) {
AddFileRange(name, file_range.data() - file_->data().data(),
file_range.size());
}
@@ -168,17 +170,17 @@
namespace dwarf {
struct File {
- StringPiece debug_info;
- StringPiece debug_types;
- StringPiece debug_str;
- StringPiece debug_abbrev;
- StringPiece debug_aranges;
- StringPiece debug_line;
+ absl::string_view debug_info;
+ absl::string_view debug_types;
+ absl::string_view debug_str;
+ absl::string_view debug_abbrev;
+ absl::string_view debug_aranges;
+ absl::string_view debug_line;
};
} // namespace dwarf
-typedef std::map<StringPiece, std::pair<uint64_t, uint64_t>> SymbolTable;
+typedef std::map<absl::string_view, std::pair<uint64_t, uint64_t>> SymbolTable;
// Provided by dwarf.cc. To use these, a module should fill in a dwarf::File
// and then call these functions.
@@ -370,19 +372,46 @@
public:
RollupOutput() : toplevel_row_("TOTAL") {}
const RollupRow& toplevel_row() { return toplevel_row_; }
- void Print(std::ostream* out) const;
+ void PrettyPrint(std::ostream* out) const;
+ void PrintToCSV(std::ostream* out) const;
void AbbreviateToFit(size_t width);
+ void Print(std::ostream* out) const {
+ if (csv_) {
+ PrintToCSV(out);
+ } else {
+ PrettyPrint(out);
+ }
+ }
+
+ void SetCSV(bool csv) { csv_ = csv; }
+
+ void AddDataSourceName(absl::string_view name) {
+ source_names_.emplace_back(std::string(name));
+ }
+
private:
BLOATY_DISALLOW_COPY_AND_ASSIGN(RollupOutput);
friend class Rollup;
+ bool csv_ = false;
size_t longest_label_;
+ std::vector<std::string> source_names_;
RollupRow toplevel_row_;
- std::vector<std::string> abbrevs_;
+ std::vector<std::pair<std::string, std::string>> abbrevs_;
void PrintRow(const RollupRow& row, size_t indent, std::ostream* out) const;
void PrintTree(const RollupRow& row, size_t indent, std::ostream* out) const;
+ void PrettyPrintRow(const RollupRow& row, size_t indent,
+ std::ostream* out) const;
+ void PrettyPrintTree(const RollupRow& row, size_t indent,
+ std::ostream* out) const;
+ void PrintRowToCSV(const RollupRow& row,
+ absl::string_view parent_labels,
+ std::ostream* out) const;
+ void PrintTreeToCSV(const RollupRow& row,
+ absl::string_view parent_labels,
+ std::ostream* out) const;
static void CollectNames(RollupRow* row, std::vector<std::string*>* names);
};
diff --git a/src/dwarf.cc b/src/dwarf.cc
index d1ebd7f..620234e 100644
--- a/src/dwarf.cc
+++ b/src/dwarf.cc
@@ -24,11 +24,13 @@
#include <unordered_set>
#include <vector>
+#include "absl/strings/string_view.h"
#include "bloaty.h"
#include "dwarf_constants.h"
#include "re2/re2.h"
using namespace dwarf2reader;
+using absl::string_view;
static size_t AlignUpTo(size_t offset, size_t granularity) {
// Granularity must be a power of two.
@@ -43,7 +45,7 @@
}
#define CHECK_RETURN(call) if (!(call)) { return false; }
-#define CHECK_RETURN_STRINGPIECE(call) if (!(call)) { return StringPiece(); }
+#define CHECK_RETURN_STRINGPIECE(call) if (!(call)) { return string_view(); }
// Low-level Parsing Routines //////////////////////////////////////////////////
@@ -53,27 +55,27 @@
// is layered on top of these.
template <class T>
-bool ReadMemcpy(StringPiece* data, T* val) {
+bool ReadMemcpy(string_view* data, T* val) {
CHECK_RETURN(data->size() >= sizeof(T));
memcpy(val, data->data(), sizeof(T));
data->remove_prefix(sizeof(T));
return true;
}
-bool ReadPiece(size_t bytes, StringPiece* data, StringPiece* val) {
+bool ReadPiece(size_t bytes, string_view* data, string_view* val) {
CHECK_RETURN(data->size() >= bytes);
*val = data->substr(0, bytes);
data->remove_prefix(bytes);
return true;
}
-bool SkipBytes(size_t bytes, StringPiece* data) {
+bool SkipBytes(size_t bytes, string_view* data) {
CHECK_RETURN(data->size() >= bytes);
data->remove_prefix(bytes);
return true;
}
-bool ReadNullTerminated(StringPiece* data, StringPiece* val) {
+bool ReadNullTerminated(string_view* data, string_view* val) {
const char* nullz =
static_cast<const char*>(memchr(data->data(), '\0', data->size()));
@@ -91,7 +93,7 @@
template <class T>
typename std::enable_if<std::is_unsigned<T>::value, bool>::type ReadLEB128(
- StringPiece* data, T* out) {
+ string_view* data, T* out) {
uint64_t ret = 0;
int shift = 0;
int maxshift = 70;
@@ -119,7 +121,7 @@
template <class T>
typename std::enable_if<std::is_signed<T>::value, bool>::type ReadLEB128(
- StringPiece* data, T* out) {
+ string_view* data, T* out) {
int64_t ret = 0;
int shift = 0;
int maxshift = 70;
@@ -150,7 +152,7 @@
return false;
}
-bool SkipLEB128(StringPiece* data) {
+bool SkipLEB128(string_view* data) {
size_t limit =
std::min(static_cast<size_t>(data->size()), static_cast<size_t>(10));
for (size_t i = 0; i < limit; i++) {
@@ -182,7 +184,7 @@
// Reads a DWARF offset based on whether we are reading dwarf32 or dwarf64
// format.
- bool ReadDWARFOffset(StringPiece* data, uint64_t* ofs) const {
+ bool ReadDWARFOffset(string_view* data, uint64_t* ofs) const {
if (dwarf64) {
return ReadMemcpy(data, ofs);
} else {
@@ -194,7 +196,7 @@
}
// Reads an address according to the expected address_size.
- bool ReadAddress(StringPiece* data, uint64_t* addr) const {
+ bool ReadAddress(string_view* data, uint64_t* addr) const {
if (address_size == 8) {
return ReadMemcpy(data, addr);
} else if (address_size == 4) {
@@ -215,7 +217,7 @@
//
// Stores the range for this section in |data| and all of the remaining data
// in |next|.
- bool ReadInitialLength(StringPiece* data, StringPiece* next) {
+ bool ReadInitialLength(string_view* data, string_view* next) {
uint64_t len;
uint32_t len32;
CHECK_RETURN(ReadMemcpy(data, &len32));
@@ -253,7 +255,7 @@
public:
// Reads abbreviations until a terminating abbreviation is seen. Returns
// false if there is a parse error or a premature EOF.
- bool ReadAbbrevs(StringPiece data);
+ bool ReadAbbrevs(string_view data);
// In a DWARF abbreviation, each attribute has a name and a form.
struct Attribute {
@@ -290,7 +292,7 @@
std::unordered_map<uint32_t, Abbrev> abbrev_;
};
-bool AbbrevTable::ReadAbbrevs(StringPiece data) {
+bool AbbrevTable::ReadAbbrevs(string_view data) {
while (true) {
uint32_t code;
CHECK_RETURN(ReadLEB128(&data, &code));
@@ -347,18 +349,18 @@
class StringTable {
public:
// Construct with the debug_str data from a DWARF file.
- StringTable(StringPiece debug_str) : debug_str_(debug_str) {}
+ StringTable(string_view debug_str) : debug_str_(debug_str) {}
// Read a string from the table.
- bool ReadEntry(size_t ofs, StringPiece* val) const;
+ bool ReadEntry(size_t ofs, string_view* val) const;
private:
- StringPiece debug_str_;
+ string_view debug_str_;
};
-bool StringTable::ReadEntry(size_t ofs, StringPiece* val) const {
+bool StringTable::ReadEntry(size_t ofs, string_view* val) const {
CHECK_RETURN(ofs < debug_str_.size());
- StringPiece str = debug_str_.substr(ofs);
+ string_view str = debug_str_.substr(ofs);
CHECK_RETURN(ReadNullTerminated(&str, val));
return true;
}
@@ -370,7 +372,7 @@
class AddressRanges {
public:
- AddressRanges(StringPiece data) : section_(data), next_unit_(data) {}
+ AddressRanges(string_view data) : section_(data), next_unit_(data) {}
// Offset into .debug_info for the current compilation unit.
uint64_t debug_info_offset() { return debug_info_offset_; }
@@ -390,9 +392,9 @@
private:
CompilationUnitSizes sizes_;
- StringPiece section_;
- StringPiece unit_remaining_;
- StringPiece next_unit_;
+ string_view section_;
+ string_view unit_remaining_;
+ string_view next_unit_;
uint64_t debug_info_offset_;
uint64_t address_;
uint64_t length_;
@@ -507,13 +509,13 @@
// APIs for our friends to use to update our state.
// Call to get the current read head where attributes should be parsed.
- StringPiece ReadAttributesBegin() {
+ string_view ReadAttributesBegin() {
assert(state_ == State::kReadyToReadAttributes);
return remaining_;
}
// When some data has been parsed, this updates our read head.
- bool ReadAttributesEnd(StringPiece remaining, uint64_t sibling) {
+ bool ReadAttributesEnd(string_view remaining, uint64_t sibling) {
assert(state_ == State::kReadyToReadAttributes);
if (remaining.data() == nullptr) {
state_ = State::kError;
@@ -528,7 +530,7 @@
// Internal APIs.
- bool ReadCompilationUnitHeader(StringPiece data);
+ bool ReadCompilationUnitHeader(string_view data);
bool ReadCode();
enum class State {
@@ -546,14 +548,14 @@
const AbbrevTable::Abbrev* current_abbrev_;
// Our current read position.
- StringPiece remaining_;
+ string_view remaining_;
uint64_t sibling_offset_;
// The read position of the next entry at each level, or size()==0 for levels
// where we don't know (because we're not at the top-level and the previous
// DIE didn't include DW_AT_sibling). Length of this array indicates the
// current depth.
- StringPiece next_unit_;
+ string_view next_unit_;
// All of the AbbrevTables we've read from .debug_abbrev, indexed by their
// offset within .debug_abbrev.
@@ -563,7 +565,7 @@
Section section_;
// Information about the current compilation unit.
- StringPiece unit_data_;
+ string_view unit_data_;
CompilationUnitSizes unit_sizes_;
AbbrevTable* unit_abbrev_;
@@ -584,7 +586,7 @@
bool DIEReader::ReadCode() {
uint32_t code;
state_ = State::kError;
- StringPiece data = remaining_;
+ string_view data = remaining_;
CHECK_RETURN(ReadLEB128(&data, &code));
@@ -625,7 +627,7 @@
}
bool DIEReader::SeekToCompilationUnit(Section section, uint64_t offset) {
- StringPiece data;
+ string_view data;
section_ = section;
if (section == Section::kDebugInfo) {
@@ -642,14 +644,14 @@
return true;
}
-bool DIEReader::ReadCompilationUnitHeader(StringPiece data) {
+bool DIEReader::ReadCompilationUnitHeader(string_view data) {
if (data.size() == 0) {
state_ = State::kEof;
return false;
}
- StringPiece unit_data = data;
- StringPiece next_unit;
+ string_view unit_data = data;
+ string_view next_unit;
unit_sizes_.ReadInitialLength(&data, &next_unit);
uint16_t version;
@@ -667,7 +669,8 @@
// If we haven't already read abbreviations for this debug_abbrev_offset, we
// need to do so now.
if (unit_abbrev_->IsEmpty()) {
- StringPiece abbrev_data = dwarf_.debug_abbrev;
+ string_view abbrev_data = dwarf_.debug_abbrev;
+ CHECK_RETURN(abbrev_data.size() >= debug_abbrev_offset);
abbrev_data.remove_prefix(debug_abbrev_offset);
CHECK_RETURN(unit_abbrev_->ReadAbbrevs(abbrev_data));
}
@@ -706,13 +709,13 @@
// is not concerned with any possible *semantic* differences between the forms.
// For example, DW_FORM_block and DW_FORM_exprloc both represent delimited
// sections of the input, so this code treats them identically (both map to
-// StringPiece) even though DW_FORM_exprloc carries extra semantic meaning about
+// string_view) even though DW_FORM_exprloc carries extra semantic meaning about
// the *interpretation* of those bytes.
// The type of the decoding function yielded from all GetFunctionForForm()
// functions. The return value indicates the data that remains after we parsed
// our value out. If return_value.data() == nullptr, there was an error.
-typedef StringPiece FormDecodeFunc(const DIEReader& reader, StringPiece data,
+typedef string_view FormDecodeFunc(const DIEReader& reader, string_view data,
void* val);
// Helper to get decoding function as a function pointer.
@@ -729,31 +732,31 @@
template <class Derived>
class FormReaderBase {
public:
- FormReaderBase(const DIEReader& reader, StringPiece data)
+ FormReaderBase(const DIEReader& reader, string_view data)
: reader_(reader), data_(data) {}
- StringPiece data() const { return data_; }
+ string_view data() const { return data_; }
protected:
const DIEReader& reader_;
- StringPiece data_;
+ string_view data_;
// Function for parsing a specific, known form. This function compiles into
// extremely tight/optimized code for parsing this specific form into one
// specific C++ type.
template <bool (Derived::*mf)()>
- static StringPiece ReadAttr(const DIEReader& reader, StringPiece data,
+ static string_view ReadAttr(const DIEReader& reader, string_view data,
void* val) {
Derived form_reader(reader, data,
static_cast<typename Derived::type*>(val));
- if ((form_reader.*mf)() == false) { return StringPiece(); }
+ if ((form_reader.*mf)() == false) { return string_view(); }
return form_reader.data();
}
// Function for parsing the "indirect" form, which only gives you the concrete
// form when you see the data. This compiles into a switch() statement based
// on the form we parse.
- static StringPiece ReadIndirect(const DIEReader& reader, StringPiece data,
+ static string_view ReadIndirect(const DIEReader& reader, string_view data,
void* value) {
uint16_t form;
CHECK_RETURN_STRINGPIECE(ReadLEB128(&data, &form));
@@ -768,20 +771,20 @@
}
};
-// FormReader for StringPiece. We accept the true string forms (DW_FORM_string
+// FormReader for string_view. We accept the true string forms (DW_FORM_string
// and DW_FORM_strp) as well as a number of other forms that contain delimited
// string data. We also accept the generic/opaque DW_FORM_data* types; the
-// StringPiece can store the uninterpreted data which can then be interpreted by
+// string_view can store the uninterpreted data which can then be interpreted by
// a higher layer.
template <>
-class FormReader<StringPiece> : public FormReaderBase<FormReader<StringPiece>> {
+class FormReader<string_view> : public FormReaderBase<FormReader<string_view>> {
public:
typedef FormReader ME;
typedef FormReaderBase<ME> Base;
- typedef StringPiece type;
+ typedef string_view type;
using Base::data_;
- FormReader(const DIEReader& reader, StringPiece data, StringPiece* val)
+ FormReader(const DIEReader& reader, string_view data, string_view* val)
: Base(reader, data), val_(val) {}
template <class Func>
@@ -821,7 +824,7 @@
}
private:
- StringPiece* val_;
+ string_view* val_;
template <size_t N>
bool ReadFixed() {
@@ -869,7 +872,7 @@
typedef T type;
using Base::data_;
- FormReader(const DIEReader& reader, StringPiece data, T* val)
+ FormReader(const DIEReader& reader, string_view data, T* val)
: Base(reader, data), val_(val) {}
template <class Func>
@@ -961,7 +964,7 @@
typedef bool type;
using Base::data_;
- FormReader(const DIEReader& reader, StringPiece data, bool* val)
+ FormReader(const DIEReader& reader, string_view data, bool* val)
: Base(reader, data), val_(val) {}
template <class Func>
@@ -1004,7 +1007,7 @@
typedef void type;
using Base::data_;
- FormReader(const DIEReader& reader, StringPiece data, void* /*val*/)
+ FormReader(const DIEReader& reader, string_view data, void* /*val*/)
: Base(reader, data) {}
template <class Func>
@@ -1143,7 +1146,7 @@
CompilationUnitSizes sizes, void* data,
bool* has);
- StringPiece ReadAttributes(const DIEReader& reader, StringPiece data) const;
+ string_view ReadAttributes(const DIEReader& reader, string_view data) const;
private:
std::vector<AttrAction> action_list_;
@@ -1206,8 +1209,8 @@
// The fast path function that reads all attributes by simply calling a list of
// function pointers to super-specialized functions.
-StringPiece ActionBuf::ReadAttributes(const DIEReader& reader,
- StringPiece data) const {
+string_view ActionBuf::ReadAttributes(const DIEReader& reader,
+ string_view data) const {
for (const auto& action : action_list_) {
assert(action.func);
data = action.func(reader, data, action.data);
@@ -1263,7 +1266,7 @@
// If we wanted to allow some parameters to be optional, we could support
// having params have an optional<> type.
bool ReadAttributes(DIEReader* reader) {
- StringPiece data = reader->ReadAttributesBegin();
+ string_view data = reader->ReadAttributesBegin();
// Clear all existing attributes.
values_ = std::tuple<Args...>();
@@ -1393,7 +1396,7 @@
};
struct FileName {
- StringPiece name;
+ string_view name;
uint32_t directory_index;
uint64_t modified_time;
uint64_t file_size;
@@ -1402,11 +1405,30 @@
bool SeekToOffset(uint64_t offset, uint8_t address_size);
bool ReadLineInfo();
const LineInfo& lineinfo() const { return info_; }
- const FileName& filename(size_t i) const { return file_names_[i]; }
- StringPiece include_directory(size_t i) const {
+ const FileName& filename(size_t i) const { return filenames_[i]; }
+ string_view include_directory(size_t i) const {
return include_directories_[i];
}
+ const std::string& GetExpandedFilename(size_t index) {
+ // Generate these lazily.
+ if (expanded_filenames_.empty()) {
+ expanded_filenames_.resize(filenames_.size());
+ }
+
+ auto& ret = expanded_filenames_[index];
+ if (ret.empty()) {
+ const FileName& filename = filenames_[index];
+ string_view directory = include_directories_[filename.directory_index];
+ ret = std::string(directory);
+ if (!ret.empty()) {
+ ret += "/";
+ }
+ ret += std::string(filename.name);
+ }
+ return ret;
+ }
+
private:
struct Params {
uint8_t minimum_instruction_length;
@@ -1420,12 +1442,13 @@
const File& file_;
CompilationUnitSizes sizes_;
- std::vector<StringPiece> include_directories_;
- std::vector<FileName> file_names_;
+ std::vector<string_view> include_directories_;
+ std::vector<FileName> filenames_;
std::vector<uint8_t> standard_opcode_lengths_;
+ std::vector<std::string> expanded_filenames_;
- StringPiece program_;
- StringPiece remaining_;
+ string_view program_;
+ string_view remaining_;
// Whether we are in a "shadow" part of the bytecode program. Sometimes parts
// of the line info program make it into the final binary even though the
@@ -1465,7 +1488,7 @@
bool LineInfoReader::SeekToOffset(uint64_t offset, uint8_t address_size) {
CHECK_RETURN(file_.debug_line.size() > offset);
- StringPiece data = file_.debug_line.substr(offset);
+ string_view data = file_.debug_line.substr(offset);
program_ = data;
uint16_t version;
@@ -1475,7 +1498,7 @@
CHECK_RETURN(ReadMemcpy(&data, &version));
CHECK_RETURN(sizes_.ReadDWARFOffset(&data, &header_length));
- StringPiece program = data.substr(header_length);
+ string_view program = data.substr(header_length);
CHECK_RETURN(ReadMemcpy(&data, ¶ms_.minimum_instruction_length));
if (version == 4) {
@@ -1498,10 +1521,10 @@
include_directories_.clear();
// Implicit current directory entry.
- include_directories_.push_back(StringPiece());
+ include_directories_.push_back(string_view());
while (true) {
- StringPiece dir;
+ string_view dir;
CHECK_RETURN(ReadNullTerminated(&data, &dir));
if (dir.size() == 0) {
break;
@@ -1510,10 +1533,11 @@
}
// Read file_names.
- file_names_.clear();
+ filenames_.clear();
+ expanded_filenames_.clear();
// Filename 0 is unused.
- file_names_.push_back(FileName());
+ filenames_.push_back(FileName());
while (true) {
FileName file_name;
CHECK_RETURN(ReadNullTerminated(&data, &file_name.name));
@@ -1523,7 +1547,7 @@
CHECK_RETURN(ReadLEB128(&data, &file_name.directory_index));
CHECK_RETURN(ReadLEB128(&data, &file_name.modified_time));
CHECK_RETURN(ReadLEB128(&data, &file_name.file_size));
- file_names_.push_back(file_name);
+ filenames_.push_back(file_name);
}
info_ = LineInfo(params_.default_is_stmt);
@@ -1542,7 +1566,7 @@
// Final step of DW_LNE_end_sequence.
info_.end_sequence = false;
- StringPiece data = remaining_;
+ string_view data = remaining_;
while (true) {
if (data.size() == 0) {
@@ -1593,7 +1617,7 @@
CHECK_RETURN(ReadLEB128(&data, &file_name.directory_index));
CHECK_RETURN(ReadLEB128(&data, &file_name.modified_time));
CHECK_RETURN(ReadLEB128(&data, &file_name.file_size));
- file_names_.push_back(file_name);
+ filenames_.push_back(file_name);
break;
}
case DW_LNE_set_discriminator:
@@ -1631,6 +1655,7 @@
case DW_LNS_set_file: {
uint32_t operand;
CHECK_RETURN(ReadLEB128(&data, &operand));
+ CHECK_RETURN(operand < filenames_.size());
info_.file = operand;
break;
}
@@ -1711,14 +1736,14 @@
die_reader_.GetTag() == DW_TAG_compile_unit &&
attr_reader_.ReadAttributes(&die_reader_) &&
attr_reader_.HasAttribute<0>()) {
- return attr_reader_.GetAttribute<0>().as_string();
+ return std::string(attr_reader_.GetAttribute<0>());
} else {
return missing_;
}
}
dwarf::DIEReader die_reader_;
- dwarf::FixedAttrReader<StringPiece> attr_reader_;
+ dwarf::FixedAttrReader<string_view> attr_reader_;
std::unordered_map<uint64_t, std::string> map_;
std::string missing_;
} map(file);
@@ -1743,7 +1768,7 @@
static bool ReadDWARFDebugInfo(const dwarf::File& file,
const SymbolTable& symtab, RangeSink* sink) {
dwarf::DIEReader die_reader(file);
- dwarf::FixedAttrReader<StringPiece, StringPiece, uint64_t, uint64_t>
+ dwarf::FixedAttrReader<string_view, string_view, uint64_t, uint64_t>
attr_reader(&die_reader, {DW_AT_name, DW_AT_linkage_name, DW_AT_low_pc,
DW_AT_high_pc});
@@ -1751,7 +1776,7 @@
do {
CHECK_RETURN(attr_reader.ReadAttributes(&die_reader));
- std::string name = attr_reader.GetAttribute<0>().as_string();
+ std::string name = std::string(attr_reader.GetAttribute<0>());
if (name.empty()) {
continue;
}
@@ -1802,6 +1827,36 @@
}
}
+static void ReadDWARFStmtList(bool include_line,
+ dwarf::LineInfoReader* line_info_reader,
+ RangeSink* sink) {
+ uint64_t span_startaddr = 0;
+ std::string last_source;
+
+ while (line_info_reader->ReadLineInfo()) {
+ const auto& line_info = line_info_reader->lineinfo();
+ auto addr = line_info.address;
+ auto number = line_info.line;
+ auto name =
+ line_info.end_sequence
+ ? last_source
+ : LineInfoKey(line_info_reader->GetExpandedFilename(line_info.file),
+ number, include_line);
+ if (!span_startaddr) {
+ span_startaddr = addr;
+ } else if (line_info.end_sequence ||
+ (!last_source.empty() && name != last_source)) {
+ sink->AddVMRange(span_startaddr, addr - span_startaddr, last_source);
+ if (line_info.end_sequence) {
+ span_startaddr = 0;
+ } else {
+ span_startaddr = addr;
+ }
+ }
+ last_source = name;
+ }
+}
+
bool ReadDWARFInlines(const dwarf::File& file, RangeSink* sink,
bool include_line) {
if (!file.debug_info.size() || !file.debug_line.size()) {
@@ -1812,68 +1867,17 @@
dwarf::DIEReader die_reader(file);
dwarf::LineInfoReader line_info_reader(file);
dwarf::FixedAttrReader<uint64_t> attr_reader(&die_reader, {DW_AT_stmt_list});
- std::unordered_map<uint64_t, std::string> map_;
- std::string missing_;
-
- class FilenameMap {
- public:
- FilenameMap(const dwarf::LineInfoReader& reader) : reader_(reader) {}
- const std::string& GetSourceFilename(size_t index) {
- auto& ret = filenames_[index];
- if (ret.empty()) {
- const dwarf::LineInfoReader::FileName& filename =
- reader_.filename(index);
- StringPiece directory =
- reader_.include_directory(filename.directory_index);
- ret = directory.as_string();
- if (!ret.empty()) {
- ret += "/";
- }
- ret += filename.name.as_string();
- }
- return ret;
- }
-
- private:
- const dwarf::LineInfoReader& reader_;
- std::unordered_map<uint32_t, std::string> filenames_;
- };
die_reader.SeekToStart(dwarf::DIEReader::Section::kDebugInfo);
while (true) {
CHECK_RETURN(attr_reader.ReadAttributes(&die_reader));
- FilenameMap map(line_info_reader);
- if (!attr_reader.HasAttribute<0>()) {
- continue;
- }
-
- CHECK_RETURN(line_info_reader.SeekToOffset(attr_reader.GetAttribute<0>(),
- die_reader.unit_sizes().address_size));
- uint64_t span_startaddr = 0;
- std::string last_source;
-
- while (line_info_reader.ReadLineInfo()) {
- const auto& line_info = line_info_reader.lineinfo();
- auto addr = line_info.address;
- auto number = line_info.line;
- auto name = line_info.end_sequence
- ? last_source
- : LineInfoKey(map.GetSourceFilename(line_info.file),
- number, include_line);
- if (!span_startaddr) {
- span_startaddr = addr;
- } else if (line_info.end_sequence ||
- (!last_source.empty() && name != last_source)) {
- sink->AddVMRange(span_startaddr, addr - span_startaddr, last_source);
- if (line_info.end_sequence) {
- span_startaddr = 0;
- } else {
- span_startaddr = addr;
- }
- }
- last_source = name;
+ if (attr_reader.HasAttribute<0>()) {
+ uint64_t offset = attr_reader.GetAttribute<0>();
+ CHECK_RETURN(line_info_reader.SeekToOffset(
+ offset, die_reader.unit_sizes().address_size));
+ ReadDWARFStmtList(include_line, &line_info_reader, sink);
}
if (!die_reader.NextCompilationUnit()) {
diff --git a/src/elf.cc b/src/elf.cc
index b8fcb3f..ec2c683 100644
--- a/src/elf.cc
+++ b/src/elf.cc
@@ -12,8 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <algorithm>
#include <string>
#include <iostream>
+#include "absl/strings/string_view.h"
#include "re2/re2.h"
#include "third_party/freebsd_elf/elf.h"
#include "bloaty.h"
@@ -22,6 +24,7 @@
#include <limits.h>
#include <stdlib.h>
+using absl::string_view;
#define CHECK_RETURN(call) if (!(call)) { return false; }
@@ -41,30 +44,29 @@
T operator()(T val) { return val; }
};
-bool StringPieceToSize(StringPiece str, size_t* out) {
+bool StringViewToSize(string_view str, size_t* out) {
char *end = nullptr;
*out = strtoul(str.data(), &end, 10);
return end != str.data() && *out != ULONG_MAX;
}
-
// ElfFile /////////////////////////////////////////////////////////////////////
// For parsing the pieces we need out of an ELF file (.o, .so, and binaries).
class ElfFile {
public:
- ElfFile(StringPiece data) : data_(data) {
+ ElfFile(string_view data) : data_(data) {
ok_ = Initialize();
}
bool IsOpen() { return ok_; }
// Regions of the file where different headers live.
- StringPiece entire_file() const { return data_; }
- StringPiece header_region() const { return header_region_; }
- StringPiece section_headers() const { return section_headers_; }
- StringPiece segment_headers() const { return segment_headers_; }
+ string_view entire_file() const { return data_; }
+ string_view header_region() const { return header_region_; }
+ string_view section_headers() const { return section_headers_; }
+ string_view segment_headers() const { return segment_headers_; }
const Elf64_Ehdr& header() const { return header_; }
Elf64_Xword section_count() const { return section_count_; }
@@ -74,32 +76,32 @@
class Segment {
public:
const Elf64_Phdr& header() const { return header_; }
- StringPiece contents() const { return contents_; }
+ string_view contents() const { return contents_; }
private:
friend class ElfFile;
Elf64_Phdr header_;
- StringPiece contents_;
+ string_view contents_;
};
// Represents an ELF section (.text, .data, .bss, etc.)
class Section {
public:
const Elf64_Shdr& header() const { return header_; }
- StringPiece contents() const { return contents_; }
+ string_view contents() const { return contents_; }
// If header().sh_type == SHT_STRTAB.
- bool ReadName(Elf64_Word index, StringPiece* name) const;
+ bool ReadName(Elf64_Word index, string_view* name) const;
// If header().sh_type == SHT_SYMTAB
- Elf64_Word GetSymbolCount() const;
+ bool GetSymbolCount(Elf64_Word* count) const;
bool ReadSymbol(Elf64_Word index, Elf64_Sym* sym) const;
private:
friend class ElfFile;
const ElfFile* elf_;
Elf64_Shdr header_;
- StringPiece contents_;
+ string_view contents_;
};
bool ReadSegment(Elf64_Word index, Segment* segment) const;
@@ -113,7 +115,8 @@
bool Initialize();
- bool SetRegion(size_t start, size_t n, StringPiece* out) const {
+ bool SetRegion(size_t start, size_t n, string_view* out) const {
+ CHECK_RETURN(SIZE_MAX - n > start);
CHECK_RETURN(start + n <= data_.size());
*out = data_.substr(start, n);
return true;
@@ -123,7 +126,7 @@
// conversion and 32->64 bit conversion, when necessary.
class StructReader {
public:
- StructReader(const ElfFile& elf, StringPiece data)
+ StructReader(const ElfFile& elf, string_view data)
: elf_(elf), data_(data) {}
template <class T32, class T64, class Munger>
@@ -137,7 +140,7 @@
private:
const ElfFile& elf_;
- StringPiece data_;
+ string_view data_;
template <class T32, class T64, class Munger>
bool ReadFallback(size_t offset, T64* out) const;
@@ -158,13 +161,13 @@
bool ok_;
bool is_64bit_;
bool is_native_endian_;
- StringPiece data_;
+ string_view data_;
Elf64_Ehdr header_;
Elf64_Xword section_count_;
Elf64_Xword section_string_index_;
- StringPiece header_region_;
- StringPiece section_headers_;
- StringPiece segment_headers_;
+ string_view header_region_;
+ string_view section_headers_;
+ string_view segment_headers_;
};
// ELF uses different structure definitions for 32/64 bit files. The sizes of
@@ -258,14 +261,14 @@
return true;
}
-bool ElfFile::Section::ReadName(Elf64_Word index, StringPiece* name) const {
+bool ElfFile::Section::ReadName(Elf64_Word index, string_view* name) const {
assert(header().sh_type == SHT_STRTAB);
if (index == SHN_UNDEF || index >= contents_.size()) {
return false;
}
- name->set(contents_.data(), contents_.size());
+ *name = string_view(contents_.data(), contents_.size());
name->remove_prefix(index);
const char* null_pos =
@@ -279,9 +282,11 @@
return true;
}
-Elf64_Word ElfFile::Section::GetSymbolCount() const {
+bool ElfFile::Section::GetSymbolCount(Elf64_Word* count) const {
assert(header().sh_type == SHT_SYMTAB);
- return contents_.size() / header_.sh_entsize;
+ CHECK_RETURN(header_.sh_entsize > 0);
+ *count = contents_.size() / header_.sh_entsize;
+ return true;
}
bool ElfFile::Section::ReadSymbol(Elf64_Word index, Elf64_Sym* sym) const {
@@ -361,7 +366,7 @@
}
bool ElfFile::ReadSegment(Elf64_Word index, Segment* segment) const {
- assert(index < header_.e_phnum);
+ CHECK_RETURN(index < header_.e_phnum);
Elf64_Phdr* header = &segment->header_;
CHECK_RETURN(ReadStruct<Elf32_Phdr>(
@@ -372,14 +377,14 @@
}
bool ElfFile::ReadSection(Elf64_Word index, Section* section) const {
- assert(index < section_count_);
+ CHECK_RETURN(index < section_count_);
Elf64_Shdr* header = §ion->header_;
CHECK_RETURN(ReadStruct<Elf32_Shdr>(
header_.e_shoff + header_.e_shentsize * index, ShdrMunger(), header));
if (header->sh_type == SHT_NOBITS) {
- section->contents_ = StringPiece();
+ section->contents_ = string_view();
} else {
CHECK_RETURN(
SetRegion(header->sh_offset, header->sh_size, §ion->contents_));
@@ -401,14 +406,14 @@
class ArFile {
public:
- ArFile(StringPiece data)
+ ArFile(string_view data)
: magic_(data.substr(0, kMagicSize)),
- contents_(data.substr(kMagicSize)) {}
+ contents_(data.substr(std::min<size_t>(data.size(), kMagicSize))) {}
- bool IsOpen() const { return magic() == StringPiece(kMagic); }
+ bool IsOpen() const { return magic() == string_view(kMagic); }
- StringPiece magic() const { return magic_; }
- StringPiece contents() const { return contents_; }
+ string_view magic() const { return magic_; }
+ string_view contents() const { return contents_; }
struct MemberFile {
enum {
@@ -416,10 +421,10 @@
kLongFilenameTable, // Stores long filenames, users should ignore.
kNormal, // Regular data file.
} file_type;
- StringPiece filename; // Only when file_type == kNormal
+ string_view filename; // Only when file_type == kNormal
size_t size;
- StringPiece header;
- StringPiece contents;
+ string_view header;
+ string_view contents;
};
class MemberReader {
@@ -429,20 +434,20 @@
bool IsEof() const { return remaining_.size() == 0; }
private:
- bool Consume(size_t n, StringPiece* field) {
+ bool Consume(size_t n, string_view* field) {
CHECK_RETURN(remaining_.size() >= n);
*field = remaining_.substr(0, n);
remaining_.remove_prefix(n);
return true;
}
- StringPiece long_filenames_;
- StringPiece remaining_;
+ string_view long_filenames_;
+ string_view remaining_;
};
private:
- const StringPiece magic_;
- const StringPiece contents_;
+ const string_view magic_;
+ const string_view contents_;
static constexpr const char* kMagic = "!<arch>\n";
static constexpr int kMagicSize = 8;
@@ -466,9 +471,9 @@
const Header* header = reinterpret_cast<const Header*>(remaining_.data());
CHECK_RETURN(Consume(sizeof(Header), &file->header));
- StringPiece file_id(&header->file_id[0], sizeof(header->file_id));
- StringPiece size_str(&header->size[0], sizeof(header->size));
- CHECK_RETURN(StringPieceToSize(size_str, &file->size));
+ string_view file_id(&header->file_id[0], sizeof(header->file_id));
+ string_view size_str(&header->size[0], sizeof(header->size));
+ CHECK_RETURN(StringViewToSize(size_str, &file->size));
CHECK_RETURN(Consume(file->size, &file->contents));
file->file_type = MemberFile::kNormal;
@@ -481,7 +486,7 @@
long_filenames_ = file->contents;
} else if (isdigit(file_id[1])) {
size_t offset;
- CHECK_RETURN(StringPieceToSize(file_id.substr(1), &offset));
+ CHECK_RETURN(StringViewToSize(file_id.substr(1), &offset));
size_t end = long_filenames_.find('/', offset);
if (end == std::string::npos) {
@@ -507,14 +512,14 @@
return true;
}
-void MaybeAddFileRange(RangeSink* sink, StringPiece label, StringPiece range) {
+void MaybeAddFileRange(RangeSink* sink, string_view label, string_view range) {
if (sink) {
sink->AddFileRange(label, range);
}
}
template <class Func>
-bool OnElfFile(const ElfFile& elf, StringPiece filename,
+bool OnElfFile(const ElfFile& elf, string_view filename,
unsigned long index_base, RangeSink* sink, Func func) {
CHECK_RETURN(func(elf, filename, index_base));
@@ -599,12 +604,12 @@
}
}
-static bool IsArchiveFile(StringPiece data) {
+static bool IsArchiveFile(string_view data) {
ArFile ar(data);
return ar.IsOpen();
}
-static bool IsObjectFile(StringPiece data) {
+static bool IsObjectFile(string_view data) {
ElfFile elf(data);
return IsArchiveFile(data) || (elf.IsOpen() && elf.header().e_type == ET_REL);
}
@@ -623,7 +628,7 @@
static bool ReadELFSymbols(const InputFile& file, RangeSink* sink,
SymbolTable* table) {
bool is_object = IsObjectFile(file.data());
- return ForEachElf(file, sink, [=](const ElfFile& elf, StringPiece /*filename*/,
+ return ForEachElf(file, sink, [=](const ElfFile& elf, string_view /*filename*/,
uint32_t index_base) {
for (Elf64_Xword i = 1; i < elf.section_count(); i++) {
ElfFile::Section section;
@@ -633,7 +638,8 @@
continue;
}
- Elf64_Word symbol_count = section.GetSymbolCount();
+ Elf64_Word symbol_count;
+ CHECK_RETURN(section.GetSymbolCount(&symbol_count));
// Find the corresponding section where the strings for the symbol table
// can be found.
@@ -643,7 +649,7 @@
for (Elf64_Word i = 1; i < symbol_count; i++) {
Elf64_Sym sym;
- StringPiece name;
+ string_view name;
CHECK_RETURN(section.ReadSymbol(i, &sym));
int type = ELF64_ST_TYPE(sym.st_info);
@@ -661,7 +667,7 @@
uint64_t full_addr =
ToVMAddr(sym.st_value, index_base + sym.st_shndx, is_object);
if (sink) {
- sink->AddVMRangeAllowAlias(full_addr, sym.st_size, name.as_string());
+ sink->AddVMRangeAllowAlias(full_addr, sym.st_size, std::string(name));
}
if (table) {
table->insert(
@@ -684,7 +690,7 @@
bool is_object = IsObjectFile(sink->input_file().data());
return ForEachElf(
sink->input_file(), sink,
- [=](const ElfFile& elf, StringPiece filename, uint32_t index_base) {
+ [=](const ElfFile& elf, string_view filename, uint32_t index_base) {
if (elf.section_count() == 0) {
return true;
}
@@ -704,7 +710,7 @@
return true;
}
- StringPiece name;
+ string_view name;
CHECK_RETURN(section_names.ReadName(header.sh_name, &name));
auto addr = header.sh_addr;
@@ -712,12 +718,12 @@
auto filesize = (header.sh_type == SHT_NOBITS) ? 0 : size;
auto vmsize = (header.sh_flags & SHF_ALLOC) ? size : 0;
- StringPiece contents = section.contents().substr(0, filesize);
+ string_view contents = section.contents().substr(0, filesize);
uint64_t full_addr = ToVMAddr(addr, index_base + i, is_object);
if (report_by == kReportByFlags) {
- name_from_flags = name.as_string();
+ name_from_flags = std::string(name);
name_from_flags = "Section [";
@@ -764,7 +770,7 @@
}
return ForEachElf(sink->input_file(), sink, [=](const ElfFile& elf,
- StringPiece /*filename*/,
+ string_view /*filename*/,
uint32_t /*index_base*/) {
for (Elf64_Xword i = 0; i < elf.header().e_phnum; i++) {
ElfFile::Segment segment;
@@ -816,7 +822,7 @@
return true;
}
- StringPiece name;
+ string_view name;
CHECK_RETURN(section_names.ReadName(header.sh_name, &name));
if (name == ".debug_aranges") {
diff --git a/tests/fuzz_driver.cc b/tests/fuzz_driver.cc
new file mode 100644
index 0000000..e25dc01
--- /dev/null
+++ b/tests/fuzz_driver.cc
@@ -0,0 +1,23 @@
+#include <cassert>
+#include <iostream>
+#include <fstream>
+#include <vector>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+int main(int argc, char **argv) {
+ for (int i = 1; i < argc; i++) {
+ std::ifstream in(argv[i]);
+ in.seekg(0, in.end);
+ size_t length = in.tellg();
+ in.seekg (0, in.beg);
+ std::cout << "Reading " << length << " bytes from " << argv[i] << std::endl;
+ // Allocate exactly length bytes so that we reliably catch buffer overflows.
+ std::vector<char> bytes(length);
+ in.read(bytes.data(), bytes.size());
+ assert(in);
+ LLVMFuzzerTestOneInput(reinterpret_cast<const uint8_t *>(bytes.data()),
+ bytes.size());
+ std::cout << "Execution successful" << std::endl;
+ }
+}
diff --git a/tests/fuzz_target.cc b/tests/fuzz_target.cc
index ea18b7a..4b89eae 100644
--- a/tests/fuzz_target.cc
+++ b/tests/fuzz_target.cc
@@ -15,11 +15,15 @@
#include "bloaty.h"
#include "strarr.h"
+#include "absl/strings/string_view.h"
+
+using absl::string_view;
+
namespace bloaty {
class StringPieceInputFile : public InputFile {
public:
- StringPieceInputFile(StringPiece data)
+ StringPieceInputFile(string_view data)
: InputFile("fake_StringPieceInputFile_file") {
data_ = data;
}
@@ -27,11 +31,11 @@
class StringPieceInputFileFactory : public InputFileFactory {
public:
- StringPieceInputFileFactory(StringPiece data) : data_(data) {}
+ StringPieceInputFileFactory(string_view data) : data_(data) {}
private:
- StringPiece data_;
+ string_view data_;
std::unique_ptr<InputFile> TryOpenFile(
- const std::string& filename) const override {
+ const std::string& /* filename */) const override {
return std::unique_ptr<InputFile>(new StringPieceInputFile(data_));
}
};
@@ -47,7 +51,7 @@
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
const char *data2 = reinterpret_cast<const char*>(data);
- bloaty::StringPieceInputFileFactory factory(bloaty::StringPiece(data2, size));
+ bloaty::StringPieceInputFileFactory factory(string_view(data2, size));
// Try all of the data sources.
RunBloaty(factory, "segments");
diff --git a/tests/strarr.h b/tests/strarr.h
index d6f49ec..b8b2214 100644
--- a/tests/strarr.h
+++ b/tests/strarr.h
@@ -16,6 +16,7 @@
#define BLOATY_TESTS_STRARR_H_
#include <memory>
+#include <vector>
// For constructing arrays of strings in the slightly peculiar format
// required by execve().
diff --git a/tests/test.h b/tests/test.h
index 5fbd5cc..bb6129a 100644
--- a/tests/test.h
+++ b/tests/test.h
@@ -106,7 +106,7 @@
if (bloaty::BloatyMain(strings.size(), StrArr(strings).get(), factory,
output_.get())) {
CheckConsistency();
- output_->Print(&std::cerr);
+ output_->PrettyPrint(&std::cerr);
return true;
} else {
std::cerr << "Bloaty returned error." << "\n";
diff --git a/tests/testdata/fuzz_corpus/0034ecacd5427aafc6b97413da2053b36de5059f b/tests/testdata/fuzz_corpus/0034ecacd5427aafc6b97413da2053b36de5059f
new file mode 100644
index 0000000..133d6fa
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/0034ecacd5427aafc6b97413da2053b36de5059f
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/0153168d08d78a4eb486cdd421b3efd6a4e12844 b/tests/testdata/fuzz_corpus/0153168d08d78a4eb486cdd421b3efd6a4e12844
new file mode 100644
index 0000000..deef34e
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/0153168d08d78a4eb486cdd421b3efd6a4e12844
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/04deff284542b1271c7ed6da11b4389342793f4d b/tests/testdata/fuzz_corpus/04deff284542b1271c7ed6da11b4389342793f4d
new file mode 100644
index 0000000..72d3413
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/04deff284542b1271c7ed6da11b4389342793f4d
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/0512fc56ee361da71476c098b91d1081e5dbc4ad b/tests/testdata/fuzz_corpus/0512fc56ee361da71476c098b91d1081e5dbc4ad
new file mode 100644
index 0000000..b2fe4f2
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/0512fc56ee361da71476c098b91d1081e5dbc4ad
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/0c7d074fcd0d6863b497f6137c6cacffc59c2ae8 b/tests/testdata/fuzz_corpus/0c7d074fcd0d6863b497f6137c6cacffc59c2ae8
new file mode 100644
index 0000000..69d27e8
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/0c7d074fcd0d6863b497f6137c6cacffc59c2ae8
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/0efb04f81a05b500031405eccae9d7e8ea0721c5 b/tests/testdata/fuzz_corpus/0efb04f81a05b500031405eccae9d7e8ea0721c5
new file mode 100644
index 0000000..4a2a97c
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/0efb04f81a05b500031405eccae9d7e8ea0721c5
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/0f6736109fcd5db53450385486c4586f884feb23 b/tests/testdata/fuzz_corpus/0f6736109fcd5db53450385486c4586f884feb23
new file mode 100644
index 0000000..089d14b
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/0f6736109fcd5db53450385486c4586f884feb23
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/110a37d69bfc8f6da2f8180e907d7d2f12da1eb2 b/tests/testdata/fuzz_corpus/110a37d69bfc8f6da2f8180e907d7d2f12da1eb2
new file mode 100644
index 0000000..8b3a6b4
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/110a37d69bfc8f6da2f8180e907d7d2f12da1eb2
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/15c502b13029920e528a2982fc1559689764aaf8 b/tests/testdata/fuzz_corpus/15c502b13029920e528a2982fc1559689764aaf8
new file mode 100644
index 0000000..f4d0e23
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/15c502b13029920e528a2982fc1559689764aaf8
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/1846aea81a4e97327d5e82c8ab9e6d4c43bffee3 b/tests/testdata/fuzz_corpus/1846aea81a4e97327d5e82c8ab9e6d4c43bffee3
new file mode 100644
index 0000000..cf85b99
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/1846aea81a4e97327d5e82c8ab9e6d4c43bffee3
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/1930321f0302e111b64e38b8456ef8473f3e71d8 b/tests/testdata/fuzz_corpus/1930321f0302e111b64e38b8456ef8473f3e71d8
new file mode 100644
index 0000000..1bcd874
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/1930321f0302e111b64e38b8456ef8473f3e71d8
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/1bfe776624349462cb1d818443af106215021470 b/tests/testdata/fuzz_corpus/1bfe776624349462cb1d818443af106215021470
new file mode 100644
index 0000000..381a6fe
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/1bfe776624349462cb1d818443af106215021470
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/270dcbc8975aaff7d869faa520be996460e6f7be b/tests/testdata/fuzz_corpus/270dcbc8975aaff7d869faa520be996460e6f7be
new file mode 100644
index 0000000..d8918ce
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/270dcbc8975aaff7d869faa520be996460e6f7be
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/2877069c49bf5773d158de6911842a58768b74c3 b/tests/testdata/fuzz_corpus/2877069c49bf5773d158de6911842a58768b74c3
new file mode 100644
index 0000000..691bb03
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/2877069c49bf5773d158de6911842a58768b74c3
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/28d7fbe0ff87b53a011656f9e3c9c3aeb2ce2018 b/tests/testdata/fuzz_corpus/28d7fbe0ff87b53a011656f9e3c9c3aeb2ce2018
new file mode 100644
index 0000000..285ace1
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/28d7fbe0ff87b53a011656f9e3c9c3aeb2ce2018
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/2f6f7647f2e81f50a3f787dda064cffe03354aa8 b/tests/testdata/fuzz_corpus/2f6f7647f2e81f50a3f787dda064cffe03354aa8
new file mode 100644
index 0000000..ab4d79c
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/2f6f7647f2e81f50a3f787dda064cffe03354aa8
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/2fd5be6e7a99d71434a756a4d59a8d44db4942bb b/tests/testdata/fuzz_corpus/2fd5be6e7a99d71434a756a4d59a8d44db4942bb
new file mode 100644
index 0000000..51969e6
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/2fd5be6e7a99d71434a756a4d59a8d44db4942bb
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/3115b1163086c5904008b9a5d17a761863910214 b/tests/testdata/fuzz_corpus/3115b1163086c5904008b9a5d17a761863910214
new file mode 100644
index 0000000..39d6185
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/3115b1163086c5904008b9a5d17a761863910214
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/327c150b2d13636bb3ea5129cb58af30675e5599 b/tests/testdata/fuzz_corpus/327c150b2d13636bb3ea5129cb58af30675e5599
new file mode 100644
index 0000000..2bf5486
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/327c150b2d13636bb3ea5129cb58af30675e5599
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/3e96523b6d0025b39ddd0a771fc9f99dd1590877 b/tests/testdata/fuzz_corpus/3e96523b6d0025b39ddd0a771fc9f99dd1590877
new file mode 100644
index 0000000..c7fb188
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/3e96523b6d0025b39ddd0a771fc9f99dd1590877
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/3f3c4745b7053aca15608204a7592bac44d690cb b/tests/testdata/fuzz_corpus/3f3c4745b7053aca15608204a7592bac44d690cb
new file mode 100644
index 0000000..b98a94b
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/3f3c4745b7053aca15608204a7592bac44d690cb
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/412f1573ff1a9675377481456d8809a850d03f1b b/tests/testdata/fuzz_corpus/412f1573ff1a9675377481456d8809a850d03f1b
new file mode 100644
index 0000000..43c14c5
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/412f1573ff1a9675377481456d8809a850d03f1b
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/42f2cd88cae45b6add339ed2c2a9074ff55d9db0 b/tests/testdata/fuzz_corpus/42f2cd88cae45b6add339ed2c2a9074ff55d9db0
new file mode 100644
index 0000000..ce0b3b8
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/42f2cd88cae45b6add339ed2c2a9074ff55d9db0
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/459ef92fc33d1d9fc6048f293bab5ddb584f94a4 b/tests/testdata/fuzz_corpus/459ef92fc33d1d9fc6048f293bab5ddb584f94a4
new file mode 100644
index 0000000..d0cb244
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/459ef92fc33d1d9fc6048f293bab5ddb584f94a4
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/53a2d35a2dfe33981111fce5c8fb6514dd9570cb b/tests/testdata/fuzz_corpus/53a2d35a2dfe33981111fce5c8fb6514dd9570cb
new file mode 100644
index 0000000..336f4cb
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/53a2d35a2dfe33981111fce5c8fb6514dd9570cb
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/57354041fcdfcc3613a0762adfd5189ca60abc80 b/tests/testdata/fuzz_corpus/57354041fcdfcc3613a0762adfd5189ca60abc80
new file mode 100644
index 0000000..a1214a7
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/57354041fcdfcc3613a0762adfd5189ca60abc80
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/5a90c59187664f79cdf1ded1a6eef6854ddd9a07 b/tests/testdata/fuzz_corpus/5a90c59187664f79cdf1ded1a6eef6854ddd9a07
new file mode 100644
index 0000000..4ed21ca
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/5a90c59187664f79cdf1ded1a6eef6854ddd9a07
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/5e8ec9cbd600dcc8f6dc5eafaf34226706378b60 b/tests/testdata/fuzz_corpus/5e8ec9cbd600dcc8f6dc5eafaf34226706378b60
new file mode 100644
index 0000000..1916a86
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/5e8ec9cbd600dcc8f6dc5eafaf34226706378b60
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/64779227c42248607f46879f9e4007e66ee68269 b/tests/testdata/fuzz_corpus/64779227c42248607f46879f9e4007e66ee68269
new file mode 100644
index 0000000..bb35594
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/64779227c42248607f46879f9e4007e66ee68269
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/6511ded4f638705a5cdd071d7e21cb4febb7234c b/tests/testdata/fuzz_corpus/6511ded4f638705a5cdd071d7e21cb4febb7234c
new file mode 100644
index 0000000..1605989
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/6511ded4f638705a5cdd071d7e21cb4febb7234c
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/66845a4bce637e02379f5dbf1b860ceb7725a96d b/tests/testdata/fuzz_corpus/66845a4bce637e02379f5dbf1b860ceb7725a96d
new file mode 100644
index 0000000..d36e4a7
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/66845a4bce637e02379f5dbf1b860ceb7725a96d
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/6d385d65872fa08e194a8b806ccfd87e49f5a554 b/tests/testdata/fuzz_corpus/6d385d65872fa08e194a8b806ccfd87e49f5a554
new file mode 100644
index 0000000..2c88bfd
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/6d385d65872fa08e194a8b806ccfd87e49f5a554
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/6d7db4d97103830cd33688f18b7c6944218b58f8 b/tests/testdata/fuzz_corpus/6d7db4d97103830cd33688f18b7c6944218b58f8
new file mode 100644
index 0000000..3a1479a
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/6d7db4d97103830cd33688f18b7c6944218b58f8
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/6f0a1ec2ebc980c9296486ad1a5b8a564549aa9a b/tests/testdata/fuzz_corpus/6f0a1ec2ebc980c9296486ad1a5b8a564549aa9a
new file mode 100644
index 0000000..dc1ce42
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/6f0a1ec2ebc980c9296486ad1a5b8a564549aa9a
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/6fa62db4fbfc54538513558d0886ff8ae74e58ed b/tests/testdata/fuzz_corpus/6fa62db4fbfc54538513558d0886ff8ae74e58ed
new file mode 100644
index 0000000..bcbdc28
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/6fa62db4fbfc54538513558d0886ff8ae74e58ed
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/7b06150aa15f8aed1abd7a93f1772b893efc150e b/tests/testdata/fuzz_corpus/7b06150aa15f8aed1abd7a93f1772b893efc150e
new file mode 100644
index 0000000..fb01003
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/7b06150aa15f8aed1abd7a93f1772b893efc150e
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/7d09e7259aa0fb3da736b98b94211f71a5e513e6 b/tests/testdata/fuzz_corpus/7d09e7259aa0fb3da736b98b94211f71a5e513e6
new file mode 100644
index 0000000..6728660
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/7d09e7259aa0fb3da736b98b94211f71a5e513e6
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/7e290e80959e9f3b045387f7ec257182cb23721d b/tests/testdata/fuzz_corpus/7e290e80959e9f3b045387f7ec257182cb23721d
new file mode 100644
index 0000000..c4a8fac
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/7e290e80959e9f3b045387f7ec257182cb23721d
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/827e96b748c33f032574b9f2b7f084920feb76ab b/tests/testdata/fuzz_corpus/827e96b748c33f032574b9f2b7f084920feb76ab
new file mode 100644
index 0000000..43ae0c8
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/827e96b748c33f032574b9f2b7f084920feb76ab
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/84675e905d3771b59fd51f606bc2a14f549aba43 b/tests/testdata/fuzz_corpus/84675e905d3771b59fd51f606bc2a14f549aba43
new file mode 100644
index 0000000..b5b75bc
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/84675e905d3771b59fd51f606bc2a14f549aba43
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/8631458a27f52b7e3cdfb06a6bde899901bfd3ac b/tests/testdata/fuzz_corpus/8631458a27f52b7e3cdfb06a6bde899901bfd3ac
new file mode 100644
index 0000000..ba36762
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/8631458a27f52b7e3cdfb06a6bde899901bfd3ac
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/86a3d4b71ee172cd476d035fb9445bcbb835d92a b/tests/testdata/fuzz_corpus/86a3d4b71ee172cd476d035fb9445bcbb835d92a
new file mode 100644
index 0000000..31c7a5a
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/86a3d4b71ee172cd476d035fb9445bcbb835d92a
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/8be73e77c819315082ac4f40acc964ddfa7be6fa b/tests/testdata/fuzz_corpus/8be73e77c819315082ac4f40acc964ddfa7be6fa
new file mode 100644
index 0000000..7bf02ff
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/8be73e77c819315082ac4f40acc964ddfa7be6fa
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/8fc314d43f2d412e20a822b5f595bf61005342a9 b/tests/testdata/fuzz_corpus/8fc314d43f2d412e20a822b5f595bf61005342a9
new file mode 100644
index 0000000..e35caef
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/8fc314d43f2d412e20a822b5f595bf61005342a9
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/91acbe9b1ef167d88e8a57f16db2aa740865accd b/tests/testdata/fuzz_corpus/91acbe9b1ef167d88e8a57f16db2aa740865accd
new file mode 100644
index 0000000..9cc9dfa
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/91acbe9b1ef167d88e8a57f16db2aa740865accd
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/9b5a5fa4a46bcca17df149785daf9cd14f1c0443 b/tests/testdata/fuzz_corpus/9b5a5fa4a46bcca17df149785daf9cd14f1c0443
new file mode 100644
index 0000000..7415c4c
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/9b5a5fa4a46bcca17df149785daf9cd14f1c0443
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/9e079b888e5d223ef0bebf13ce1e26ebdd82752a b/tests/testdata/fuzz_corpus/9e079b888e5d223ef0bebf13ce1e26ebdd82752a
new file mode 100644
index 0000000..03c73f7
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/9e079b888e5d223ef0bebf13ce1e26ebdd82752a
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/a22fdce1317617bf89f3283cbd44ef490a57b5e2 b/tests/testdata/fuzz_corpus/a22fdce1317617bf89f3283cbd44ef490a57b5e2
new file mode 100644
index 0000000..54973d6
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/a22fdce1317617bf89f3283cbd44ef490a57b5e2
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/a4d1a2b246e0a1f133774daa28328c0d7ce5c3e5 b/tests/testdata/fuzz_corpus/a4d1a2b246e0a1f133774daa28328c0d7ce5c3e5
new file mode 100644
index 0000000..b70f241
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/a4d1a2b246e0a1f133774daa28328c0d7ce5c3e5
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/af0df3abd6ff306ca9161b6f6ebc96f21c6dfa98 b/tests/testdata/fuzz_corpus/af0df3abd6ff306ca9161b6f6ebc96f21c6dfa98
new file mode 100644
index 0000000..c1198fe
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/af0df3abd6ff306ca9161b6f6ebc96f21c6dfa98
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/b3a904cebb1d3070ca96cf70ec0b9ef5d1612a45 b/tests/testdata/fuzz_corpus/b3a904cebb1d3070ca96cf70ec0b9ef5d1612a45
new file mode 100644
index 0000000..b511d55
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/b3a904cebb1d3070ca96cf70ec0b9ef5d1612a45
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/bf2cb5de1de6ca492f159dc3cce67cf88a6244aa b/tests/testdata/fuzz_corpus/bf2cb5de1de6ca492f159dc3cce67cf88a6244aa
new file mode 100644
index 0000000..8c8338b
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/bf2cb5de1de6ca492f159dc3cce67cf88a6244aa
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/c121e995dd4575473e468801b301da0f219f5de7 b/tests/testdata/fuzz_corpus/c121e995dd4575473e468801b301da0f219f5de7
new file mode 100644
index 0000000..1241e8c
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/c121e995dd4575473e468801b301da0f219f5de7
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/c561ab1d99f16a04898518914dd1cea4afa7e358 b/tests/testdata/fuzz_corpus/c561ab1d99f16a04898518914dd1cea4afa7e358
new file mode 100644
index 0000000..e15376e
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/c561ab1d99f16a04898518914dd1cea4afa7e358
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/c98c037db24035a40d40f91084a56f470bb6fbc5 b/tests/testdata/fuzz_corpus/c98c037db24035a40d40f91084a56f470bb6fbc5
new file mode 100644
index 0000000..82dfe42
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/c98c037db24035a40d40f91084a56f470bb6fbc5
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/cd838035892825e361fe0f936f93fa62aaf2fab1 b/tests/testdata/fuzz_corpus/cd838035892825e361fe0f936f93fa62aaf2fab1
new file mode 100644
index 0000000..67a809c
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/cd838035892825e361fe0f936f93fa62aaf2fab1
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/d3cc4e4dddf87cb0d41135b7a22d03fc4ec11bbc b/tests/testdata/fuzz_corpus/d3cc4e4dddf87cb0d41135b7a22d03fc4ec11bbc
new file mode 100644
index 0000000..2b9411a
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/d3cc4e4dddf87cb0d41135b7a22d03fc4ec11bbc
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/daebef8f49a59b71cf57d4771b09f9f8545b83d8 b/tests/testdata/fuzz_corpus/daebef8f49a59b71cf57d4771b09f9f8545b83d8
new file mode 100644
index 0000000..5669340
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/daebef8f49a59b71cf57d4771b09f9f8545b83d8
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/dda6875f2313476f402e9a283ecaf2b50cfae316 b/tests/testdata/fuzz_corpus/dda6875f2313476f402e9a283ecaf2b50cfae316
new file mode 100644
index 0000000..bd7cfad
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/dda6875f2313476f402e9a283ecaf2b50cfae316
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/df7a639969efbe5943b6a7fa5eff4f732a50a4f6 b/tests/testdata/fuzz_corpus/df7a639969efbe5943b6a7fa5eff4f732a50a4f6
new file mode 100644
index 0000000..85ae79d
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/df7a639969efbe5943b6a7fa5eff4f732a50a4f6
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/e08b7c26f946f4761f2cecdc81f4e5e7651db9a7 b/tests/testdata/fuzz_corpus/e08b7c26f946f4761f2cecdc81f4e5e7651db9a7
new file mode 100644
index 0000000..a7799b8
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/e08b7c26f946f4761f2cecdc81f4e5e7651db9a7
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/e33d3e649729bea900f870b0cd5335f312d9ed42 b/tests/testdata/fuzz_corpus/e33d3e649729bea900f870b0cd5335f312d9ed42
new file mode 100644
index 0000000..47deab1
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/e33d3e649729bea900f870b0cd5335f312d9ed42
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/e4a3653bac41c8f39cc625286daa813e0ce603b0 b/tests/testdata/fuzz_corpus/e4a3653bac41c8f39cc625286daa813e0ce603b0
new file mode 100644
index 0000000..1844628
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/e4a3653bac41c8f39cc625286daa813e0ce603b0
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/f0ac70f708130bb9cc4aba5ebe1a3c500c4ea11a b/tests/testdata/fuzz_corpus/f0ac70f708130bb9cc4aba5ebe1a3c500c4ea11a
new file mode 100644
index 0000000..ffb7c42
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/f0ac70f708130bb9cc4aba5ebe1a3c500c4ea11a
Binary files differ
diff --git a/tests/testdata/fuzz_corpus/fc88742708813d5dd57936fad4c6e9bd6ed125ac b/tests/testdata/fuzz_corpus/fc88742708813d5dd57936fad4c6e9bd6ed125ac
new file mode 100644
index 0000000..2ba4a8e
--- /dev/null
+++ b/tests/testdata/fuzz_corpus/fc88742708813d5dd57936fad4c6e9bd6ed125ac
Binary files differ
diff --git a/third_party/abseil-cpp b/third_party/abseil-cpp
new file mode 160000
index 0000000..cc4bed2
--- /dev/null
+++ b/third_party/abseil-cpp
@@ -0,0 +1 @@
+Subproject commit cc4bed2d74f7c8717e31f9579214ab52a9c9c610
diff --git a/third_party/googletest b/third_party/googletest
index a2b8a8e..7b6561c 160000
--- a/third_party/googletest
+++ b/third_party/googletest
@@ -1 +1 @@
-Subproject commit a2b8a8e07628e5fd60644b6dd99c1b5e7d7f1f47
+Subproject commit 7b6561c56e353100aca8458d7bc49c4e0119bae8
diff --git a/third_party/libFuzzer b/third_party/libFuzzer
deleted file mode 160000
index 1b543d6..0000000
--- a/third_party/libFuzzer
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 1b543d6e5073b56be214394890c9193979a3d7e1
diff --git a/third_party/re2 b/third_party/re2
index c964d9b..16dd885 160000
--- a/third_party/re2
+++ b/third_party/re2
@@ -1 +1 @@
-Subproject commit c964d9b07816f9b52ca692e23b8b3dba33beec54
+Subproject commit 16dd8856b79b3c6163a5b6da40aa45267031a79d