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, &params_.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, &section->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