Merge branch 'master' of https://github.com/google/bloaty
diff --git a/src/bloaty.cc b/src/bloaty.cc
index 017a25c..068de28 100644
--- a/src/bloaty.cc
+++ b/src/bloaty.cc
@@ -1493,6 +1493,10 @@
   }
 
   void ComputeRollup(Rollup* rollup) {
+    for (auto& map : maps_) {
+      map->vm_map.Compress();
+      map->file_map.Compress();
+    }
     RangeMap::ComputeRollup(VmMaps(), [=](const std::vector<std::string>& keys,
                                           uint64_t addr, uint64_t end) {
       return rollup->AddSizes(keys, end - addr, true);
@@ -1506,14 +1510,17 @@
 
   void PrintMaps(const std::vector<const RangeMap*> maps) {
     uint64_t last = 0;
+    uint64_t max = maps[0]->GetMaxAddress();
+    int hex_digits = std::ceil(std::log2(max) / 4);
     RangeMap::ComputeRollup(maps, [&](const std::vector<std::string>& keys,
                                       uint64_t addr, uint64_t end) {
       if (addr > last) {
-        PrintMapRow("NO ENTRY", last, addr);
+        PrintMapRow("[-- Nothing mapped --]", last, addr, hex_digits);
       }
-      PrintMapRow(KeysToString(keys), addr, end);
+      PrintMapRow(KeysToString(keys), addr, end, hex_digits);
       last = end;
     });
+    printf("\n");
   }
 
   void PrintFileMaps() { PrintMaps(FileMaps()); }
@@ -1522,9 +1529,10 @@
   std::string KeysToString(const std::vector<std::string>& keys) {
     std::string ret;
 
-    for (size_t i = 0; i < keys.size(); i++) {
-      if (i > 0) {
-        ret += ", ";
+    // Start at offset 1 to skip the base map.
+    for (size_t i = 1; i < keys.size(); i++) {
+      if (i > 1) {
+        ret += "\t";
       }
       ret += keys[i];
     }
@@ -1532,9 +1540,10 @@
     return ret;
   }
 
-  void PrintMapRow(string_view str, uint64_t start, uint64_t end) {
-    printf("[%" PRIx64 ", %" PRIx64 "] %.*s\n", start, end, (int)str.size(),
-           str.data());
+  void PrintMapRow(string_view str, uint64_t start, uint64_t end, int hex_digits) {
+    printf("%.*" PRIx64 "-%.*" PRIx64 "\t %s\t\t%.*s\n", hex_digits, start,
+           hex_digits, end, LeftPad(std::to_string(end - start), 10).c_str(),
+           (int)str.size(), str.data());
   }
 
   DualMap* base_map() { return maps_[0].get(); }
diff --git a/src/range_map.cc b/src/range_map.cc
index 29b0af7..2886bd7 100644
--- a/src/range_map.cc
+++ b/src/range_map.cc
@@ -288,6 +288,21 @@
   return total_size == size;
 }
 
+void RangeMap::Compress() {
+  auto prev = mappings_.begin();
+  auto it = prev;
+  while (it != mappings_.end()) {
+    if (prev->first + prev->second.size == it->first &&
+        prev->second.label == it->second.label) {
+      prev->second.size += it->second.size;
+      mappings_.erase(it++);
+    } else {
+      prev = it;
+      ++it;
+    }
+  }
+}
+
 bool RangeMap::CoversRange(uint64_t addr, uint64_t size) const {
   auto it = FindContaining(addr);
   uint64_t end = addr + size;
@@ -304,4 +319,13 @@
   }
 }
 
+uint64_t RangeMap::GetMaxAddress() const {
+  if (mappings_.empty()) {
+    return 0;
+  } else {
+    auto& entry = *mappings_.rbegin();
+    return entry.first + entry.second.size;
+  }
+}
+
 }  // namespace bloaty
diff --git a/src/range_map.h b/src/range_map.h
index 9faa9e8..05a8edd 100644
--- a/src/range_map.h
+++ b/src/range_map.h
@@ -74,9 +74,21 @@
                                const RangeMap& translator, bool verbose,
                                RangeMap* other);
 
+  // Collapses adjacent ranges with the same label. This reduces memory usage
+  // and removes redundant noise from the output when dumping a full memory map
+  // (in normal Bloaty output it makes no difference, because all labels with
+  // the same name are added together).
+  //
+  // TODO(haberman): see if we can do this at insertion time instead, so it
+  // doesn't require a second pass.
+  void Compress();
+
   // Returns whether this RangeMap fully covers the given range.
   bool CoversRange(uint64_t addr, uint64_t size) const;
 
+  // Returns the maximum address contained in this map.
+  uint64_t GetMaxAddress() const;
+
   // Translates |addr| into the other domain, returning |true| if this was
   // successful.
   bool Translate(uint64_t addr, uint64_t *translated) const;