Merge pull request #95 from haberman/fixseparatedebug

Fixed a few bugs with total file size, and added tests to verify.
diff --git a/src/bloaty.cc b/src/bloaty.cc
index 3d4fd37..fa3ba4f 100644
--- a/src/bloaty.cc
+++ b/src/bloaty.cc
@@ -372,6 +372,8 @@
     }
   }
 
+  int64_t file_total() const { return file_total_; }
+
  private:
   BLOATY_DISALLOW_COPY_AND_ASSIGN(Rollup);
 
@@ -1134,6 +1136,8 @@
       pair.first->file_map.AddRangeWithTranslation(fileoff, filesize, label,
                                                     translator_->file_map,
                                                     &pair.first->vm_map);
+    } else {
+      pair.first->file_map.AddRange(fileoff, filesize, label);
     }
   }
 }
@@ -1513,10 +1517,6 @@
     sink_ptrs.push_back(sinks.back().get());
   }
 
-  // Ensure all parts of the VM/file-space are covered.  If all data sources had
-  // 100% coverage, this wouldn't be necessary.
-  maps.base_map()->file_map.AddRange(0, file->file_data().data().size(),
-                                     "[None]");
   std::string build_id = file->GetBuildId();
   if (!build_id.empty()) {
     auto iter = debug_files_.find(build_id);
@@ -1525,9 +1525,16 @@
       *out_build_id = build_id;
     }
   }
-  file->ProcessFile(sink_ptrs);
 
+  int64_t filesize_before = rollup->file_total();
+  file->ProcessFile(sink_ptrs);
   maps.ComputeRollup(filename, filename_position_, rollup);
+
+  // The ObjectFile implementation must guarantee this.
+  int64_t filesize = rollup->file_total() - filesize_before;
+  (void)filesize;
+  assert(filesize == file->file_data().data().size());
+
   if (verbose_level > 0) {
     fprintf(stderr, "FILE MAP:\n");
     maps.PrintFileMaps(filename, filename_position_);
diff --git a/src/elf.cc b/src/elf.cc
index 4a575a5..08598d3 100644
--- a/src/elf.cc
+++ b/src/elf.cc
@@ -708,24 +708,6 @@
 }
 
 template <class Func>
-void OnElfFile(const ElfFile& elf, string_view filename,
-               unsigned long index_base, RangeSink* sink, Func func) {
-  func(elf, filename, index_base);
-
-  // Add these *after* running the user callback.  That way if there is
-  // overlap, the user's annotations will take precedence.
-  MaybeAddFileRange(sink, "[ELF Headers]", elf.header_region());
-  MaybeAddFileRange(sink, "[ELF Headers]", elf.section_headers());
-  MaybeAddFileRange(sink, "[ELF Headers]", elf.segment_headers());
-
-  // Any sections of the file not covered by any segments/sections/symbols/etc.
-  if (sink && (sink->data_source() == DataSource::kSegments ||
-               sink->data_source() == DataSource::kSections)) {
-    sink->AddFileRange("[Unmapped]", elf.entire_file());
-  }
-}
-
-template <class Func>
 bool ForEachElf(const InputFile& file, RangeSink* sink, Func func) {
   ArFile ar_file(file.data());
   unsigned long index_base = 0;
@@ -742,7 +724,7 @@
         case ArFile::MemberFile::kNormal: {
           ElfFile elf(member.contents);
           if (elf.IsOpen()) {
-            OnElfFile(elf, member.filename, index_base, sink, func);
+            func(elf, member.filename, index_base);
             index_base += elf.section_count();
           } else {
             MaybeAddFileRange(sink, "[AR Non-ELF Member File]",
@@ -766,20 +748,24 @@
       return false;
     }
 
-    OnElfFile(elf, file.filename(), index_base, sink, func);
+    func(elf, file.filename(), index_base);
   }
 
   return true;
 }
 
+void AddELFFallback(RangeSink* sink) {
+  ForEachElf(sink->input_file(), sink,
+             [sink](const ElfFile& elf, string_view /*filename*/,
+                    uint32_t /*index_base*/) {
+               sink->AddFileRange("[ELF Headers]", elf.header_region());
+               sink->AddFileRange("[ELF Headers]", elf.section_headers());
+               sink->AddFileRange("[ELF Headers]", elf.segment_headers());
 
-// There are several programs that offer useful information about
-// binaries:
-//
-// - objdump: display object file headers and contents (including disassembly)
-// - readelf: more ELF-specific objdump (no disassembly though)
-// - nm: display symbols
-// - size: display binary size
+             });
+  // The last-line fallback to make sure we cover the entire file.
+  sink->AddFileRange("[Unmapped]", sink->input_file().data());
+}
 
 // For object files, addresses are relative to the section they live in, which
 // is indicated by ndx.  We split this into:
@@ -1127,6 +1113,7 @@
         default:
           THROW("unknown data source");
       }
+      AddELFFallback(sink);
     }
   }
 
diff --git a/src/macho.cc b/src/macho.cc
index ac5dd1e..8c380c4 100644
--- a/src/macho.cc
+++ b/src/macho.cc
@@ -377,6 +377,10 @@
   }
 }
 
+static void AddMachOFallback(RangeSink* sink) {
+  sink->AddFileRange("[Unmapped]", sink->input_file().data());
+}
+
 class MachOObjectFile : public ObjectFile {
  public:
   MachOObjectFile(std::unique_ptr<InputFile> file_data)
@@ -404,6 +408,7 @@
         default:
           THROW("Mach-O doesn't support this data source");
       }
+      AddMachOFallback(sink);
     }
   }
 
diff --git a/tests/bloaty_test.cc b/tests/bloaty_test.cc
index e7f370a..006c03c 100644
--- a/tests/bloaty_test.cc
+++ b/tests/bloaty_test.cc
@@ -254,3 +254,8 @@
     std::make_tuple("foo_y", 4, 0)
   });
 }
+
+TEST_F(BloatyTest, SeparateDebug) {
+  RunBloaty({"bloaty", "--debug-file=05-binary.bin", "07-binary-stripped.bin",
+             "-d", "symbols"});
+}
diff --git a/tests/test.h b/tests/test.h
index dfc5231..0e68579 100644
--- a/tests/test.h
+++ b/tests/test.h
@@ -128,6 +128,17 @@
 
   void CheckConsistency(const bloaty::Options& options) {
     ASSERT_EQ(options.base_filename_size() > 0, output_->diff_mode());
+
+    if (!output_->diff_mode()) {
+      size_t total_input_size = 0;
+      for (const auto& filename : options.filename()) {
+        uint64_t size;
+        ASSERT_TRUE(GetFileSize(filename, &size));
+        total_input_size += size;
+      }
+      ASSERT_EQ(top_row_->filesize, total_input_size);
+    }
+
     int rows = 0;
     CheckConsistencyForRow(*top_row_, true, output_->diff_mode(), &rows);
     CheckCSVConsistency(rows);
diff --git a/tests/testdata/linux-x86/07-binary-stripped.bin b/tests/testdata/linux-x86/07-binary-stripped.bin
new file mode 100644
index 0000000..9bcbe46
--- /dev/null
+++ b/tests/testdata/linux-x86/07-binary-stripped.bin
Binary files differ
diff --git a/tests/testdata/linux-x86_64/07-binary-stripped.bin b/tests/testdata/linux-x86_64/07-binary-stripped.bin
new file mode 100644
index 0000000..0035670
--- /dev/null
+++ b/tests/testdata/linux-x86_64/07-binary-stripped.bin
Binary files differ
diff --git a/tests/testdata/make_test_files.sh b/tests/testdata/make_test_files.sh
index 85ea950..bb75402 100755
--- a/tests/testdata/make_test_files.sh
+++ b/tests/testdata/make_test_files.sh
@@ -114,3 +114,7 @@
 "
 
 make_ar "06-diff.a" "foo2.o" "bar.o" "a_filename_longer_than_sixteen_chars.o"
+
+cp "05-binary.bin" "07-binary-stripped.bin"
+strip "07-binary-stripped.bin"
+publish "07-binary-stripped.bin"