Merge branch 'master' of https://github.com/google/bloaty into pack
diff --git a/.gitmodules b/.gitmodules
index 875dfad..c0d6fa8 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -4,9 +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/leveldb"]
- path = third_party/leveldb
- url = https://github.com/google/leveldb.git
+[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..ebb7f15
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,65 @@
+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)
+ endforeach(target)
+
+ 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)
+endif()
diff --git a/src/bloaty.cc b/src/bloaty.cc
index b23326c..83fe980 100644
--- a/src/bloaty.cc
+++ b/src/bloaty.cc
@@ -38,11 +38,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 +108,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:
@@ -344,7 +374,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);
@@ -352,12 +382,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)) {
@@ -774,8 +804,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) << " "
@@ -784,10 +814,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
@@ -798,24 +828,24 @@
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);
}
}
}
-void RollupOutput::Print(std::ostream* out) const {
+void RollupOutput::PrettyPrint(std::ostream* out) const {
*out << " VM SIZE ";
PrintSpaces(longest_label_, out);
*out << " FILE SIZE";
@@ -834,7 +864,7 @@
}
for (const auto& child : toplevel_row_.sorted_children) {
- PrintTree(child, 0, out);
+ PrettyPrintTree(child, 0, out);
}
if (toplevel_row_.diff_mode) {
@@ -845,7 +875,7 @@
*out << " --------------";
*out << "\n";
for (const auto& child : toplevel_row_.shrinking) {
- PrintTree(child, 0, out);
+ PrettyPrintTree(child, 0, out);
}
}
@@ -856,7 +886,7 @@
*out << " +-+-+-+-+-+-+-";
*out << "\n";
for (const auto& child : toplevel_row_.mixed) {
- PrintTree(child, 0, out);
+ PrettyPrintTree(child, 0, out);
}
}
@@ -865,9 +895,64 @@
}
// The "TOTAL" row comes after all other rows.
- PrintRow(toplevel_row_, 0, out);
+ PrettyPrintRow(toplevel_row_, 0, out);
}
+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 ////////////////////////////////////////////////////////////////////
@@ -1120,8 +1205,8 @@
MemoryMap::~MemoryMap() {}
-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);
}
@@ -1164,7 +1249,7 @@
return false;
}
- data_.set(map, buf.st_size);
+ data_ = string_view(map, buf.st_size);
return true;
}
@@ -1199,7 +1284,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",
@@ -1239,7 +1324,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
@@ -1442,7 +1527,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());
}
@@ -1521,6 +1606,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.
@@ -1574,6 +1660,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;
@@ -1582,6 +1670,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 52c8567..e1147e0 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;
class NameMunger;
@@ -62,7 +61,7 @@
virtual ~InputFile() {}
const std::string& filename() const { return filename_; }
- StringPiece data() const { return data_; }
+ absl::string_view data() const { return data_; }
// Allows data sources to change the reported input file name.
// This is only intended to be used by pack files.
@@ -75,11 +74,13 @@
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;
@@ -127,18 +128,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());
}
@@ -203,17 +205,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.
@@ -437,17 +439,42 @@
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 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_;
- 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;
};
bool BloatyMain(int argc, char* argv[], const InputFileFactory& file_factory,
diff --git a/src/dwarf.cc b/src/dwarf.cc
index d1ebd7f..dac2304 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,7 @@
// 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;
abbrev_data.remove_prefix(debug_abbrev_offset);
CHECK_RETURN(unit_abbrev_->ReadAbbrevs(abbrev_data));
}
@@ -706,13 +708,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 +731,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 +770,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 +823,7 @@
}
private:
- StringPiece* val_;
+ string_view* val_;
template <size_t N>
bool ReadFixed() {
@@ -869,7 +871,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 +963,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 +1006,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 +1145,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 +1208,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 +1265,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 +1395,7 @@
};
struct FileName {
- StringPiece name;
+ string_view name;
uint32_t directory_index;
uint64_t modified_time;
uint64_t file_size;
@@ -1403,7 +1405,7 @@
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 {
+ string_view include_directory(size_t i) const {
return include_directories_[i];
}
@@ -1420,12 +1422,12 @@
const File& file_;
CompilationUnitSizes sizes_;
- std::vector<StringPiece> include_directories_;
+ std::vector<string_view> include_directories_;
std::vector<FileName> file_names_;
std::vector<uint8_t> standard_opcode_lengths_;
- 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 +1467,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 +1477,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 +1500,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;
@@ -1542,7 +1544,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) {
@@ -1711,14 +1713,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 +1745,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 +1753,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;
}
@@ -1823,13 +1825,13 @@
if (ret.empty()) {
const dwarf::LineInfoReader::FileName& filename =
reader_.filename(index);
- StringPiece directory =
+ string_view directory =
reader_.include_directory(filename.directory_index);
- ret = directory.as_string();
+ ret = std::string(directory);
if (!ret.empty()) {
ret += "/";
}
- ret += filename.name.as_string();
+ ret += std::string(filename.name);
}
return ret;
}
diff --git a/src/elf.cc b/src/elf.cc
index 5076ad4..5418e69 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,22 +76,22 @@
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;
@@ -99,7 +101,7 @@
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,7 @@
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(start + n <= data_.size());
*out = data_.substr(start, n);
return true;
@@ -123,7 +125,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 +139,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 +160,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 +260,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 =
@@ -379,7 +381,7 @@
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 +403,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 +418,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 +431,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 +468,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 +483,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 +509,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 +601,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 +625,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;
@@ -643,7 +645,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 +663,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 +686,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 +706,7 @@
return true;
}
- StringPiece name;
+ string_view name;
CHECK_RETURN(section_names.ReadName(header.sh_name, &name));
auto addr = header.sh_addr;
@@ -712,12 +714,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 [";
@@ -759,7 +761,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;
@@ -811,7 +813,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/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/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/libFuzzer b/third_party/libFuzzer
deleted file mode 160000
index 1b543d6..0000000
--- a/third_party/libFuzzer
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 1b543d6e5073b56be214394890c9193979a3d7e1