#include "huge_page_deducer.h"  

#include "base/logging.h"
#include "compat/string.h"
#include "compat/test.h"


namespace quipper {
namespace {
using PerfEvent = PerfDataProto::PerfEvent;
using MMapEvent = PerfDataProto::MMapEvent;
using ::testing::EqualsProto;
using ::testing::Pointwise;
using ::testing::proto::Partially;

// AddMmap is a helper function to create simple MMapEvents, with which
// testcases can encode "maps" entries similar to /proc/self/maps in a tabular
// one-line-per-entry.
void AddMmap(uint32_t pid, uint64_t mmap_start, uint64_t length, uint64_t pgoff,
             const string& file, RepeatedPtrField<PerfEvent>* events) {
  MMapEvent* ev = events->Add()->mutable_mmap_event();
  ev->set_pid(pid);
  ev->set_start(mmap_start);
  ev->set_len(length);
  ev->set_pgoff(pgoff);
  ev->set_filename(file);
}

TEST(HugePageDeducer, HugePagesMappings) {
  RepeatedPtrField<PerfEvent> events;
  {
    MMapEvent* ev = events.Add()->mutable_mmap_event();
    ev->set_pid(1234);
    ev->set_start(0x40000000);
    ev->set_len(0x18000);
    ev->set_pgoff(0);
    ev->set_filename("/usr/lib/libfoo.so");
  }
  {
    MMapEvent* ev = events.Add()->mutable_mmap_event();
    ev->set_pid(1234);
    ev->set_start(0x40018000);
    ev->set_len(0x1e8000);
    ev->set_pgoff(0);
    ev->set_filename("/opt/google/chrome/chrome");
  }
  {
    MMapEvent* ev = events.Add()->mutable_mmap_event();
    ev->set_pid(1234);
    ev->set_start(0x40200000);
    ev->set_len(0x1c00000);
    ev->set_pgoff(0);
    ev->set_filename("//anon");
  }
  {
    MMapEvent* ev = events.Add()->mutable_mmap_event();
    ev->set_pid(1234);
    ev->set_start(0x41e00000);
    ev->set_len(0x4000000);
    ev->set_pgoff(0x1de8000);
    ev->set_filename("/opt/google/chrome/chrome");
  }
  {
    MMapEvent* ev = events.Add()->mutable_mmap_event();
    ev->set_pid(2345);
    ev->set_start(0x45e00000);
    ev->set_len(0x1e00000);
    ev->set_pgoff(0);
    ev->set_filename("//anon");
  }
  {
    MMapEvent* ev = events.Add()->mutable_mmap_event();
    ev->set_pid(2345);
    ev->set_start(0x47c00000);
    ev->set_len(0x4000000);
    ev->set_pgoff(0x1e00000);
    ev->set_filename("/opt/google/chrome/chrome");
  }

  DeduceHugePages(&events);
  CombineMappings(&events);

  ASSERT_GE(events.size(), 3);
  EXPECT_EQ(events.size(), 3);

  EXPECT_THAT(events,
              Pointwise(Partially(EqualsProto()),
                        {
                            "mmap_event: { start: 0x40000000 len:0x18000 "
                            "pgoff: 0 filename: '/usr/lib/libfoo.so'}",
                            "mmap_event: { start: 0x40018000 len:0x5de8000 "
                            "pgoff: 0 filename: '/opt/google/chrome/chrome'}",
                            "mmap_event: { start: 0x45e00000 len:0x5e00000 "
                            "pgoff: 0 filename: '/opt/google/chrome/chrome'}",
                        }));

  EXPECT_EQ("/usr/lib/libfoo.so", events[0].mmap_event().filename());
  EXPECT_EQ(0x40000000, events[0].mmap_event().start());
  EXPECT_EQ(0x18000, events[0].mmap_event().len());
  EXPECT_EQ(0x0, events[0].mmap_event().pgoff());

  // The split Chrome mappings should have been combined.
  EXPECT_EQ("/opt/google/chrome/chrome", events[2].mmap_event().filename());
  EXPECT_EQ(0x40018000, events[1].mmap_event().start());
  EXPECT_EQ(0x5de8000, events[1].mmap_event().len());
  EXPECT_EQ(0x0, events[1].mmap_event().pgoff());

  EXPECT_EQ("/opt/google/chrome/chrome", events[2].mmap_event().filename());
  EXPECT_EQ(0x45e00000, events[2].mmap_event().start());
  EXPECT_EQ(0x5e00000, events[2].mmap_event().len());
  EXPECT_EQ(0x0, events[2].mmap_event().pgoff());
}

enum HugepageTextStyle {
  kAnonHugepageText,
  kNoHugepageText,
};

class HugepageTextStyleDependent
    : public ::testing::TestWithParam<HugepageTextStyle> {
 protected:
  void AddHugepageTextMmap(uint32_t pid, uint64_t mmap_start, uint64_t length,
                           uint64_t pgoff, string file,
                           RepeatedPtrField<PerfEvent>* events) {
    // Various hugepage implementations and perf versions result in various
    // quirks in how hugepages are reported.

    switch (GetParam()) {
      case kNoHugepageText:
        // Do nothing; the maps are complete and file-backed
        break;
      case kAnonHugepageText:
        // exec is remapped into anonymous memory, which perf reports as
        // '//anon'. Anonymous sections have no pgoff.
        file = "//anon";
        pgoff = 0;
        break;
      default:
        CHECK(false) << "Unimplemented";
    }
    AddMmap(pid, mmap_start, length, pgoff, file, events);
  }
};

TEST_P(HugepageTextStyleDependent, OnlyOneMappingThatIsHuge) {
  RepeatedPtrField<PerfEvent> events;
  AddHugepageTextMmap(1, 0x100200000, 0x200000, 0, "file", &events);

  DeduceHugePages(&events);
  CombineMappings(&events);

  // Don't check filename='file'; if it's backed by anonymous memory, it isn't
  // possible for quipper to deduce the filename without other mmaps immediately
  // adjacent.
  EXPECT_THAT(
      events,
      Pointwise(Partially(EqualsProto()),
                {"mmap_event: { start: 0x100200000 len: 0x200000 pgoff: 0}"}));
}

TEST_P(HugepageTextStyleDependent, OnlyOneMappingUnaligned) {
  RepeatedPtrField<PerfEvent> events;
  AddMmap(2, 0x200201000, 0x200000, 0, "file", &events);

  DeduceHugePages(&events);
  CombineMappings(&events);

  EXPECT_THAT(events, Pointwise(Partially(EqualsProto()),
                                {"mmap_event: { start: 0x200201000 "
                                 "len:0x200000 pgoff: 0 filename: 'file'}"}));
}

TEST_P(HugepageTextStyleDependent, FirstPageIsHugeWithSmallTail) {
  RepeatedPtrField<PerfEvent> events;
  AddHugepageTextMmap(3, 0x300400000, 0x400000, 0, "file", &events);
  AddMmap(3, 0x300800000, 0x001000, 0x400000, "file", &events);

  DeduceHugePages(&events);
  CombineMappings(&events);

  EXPECT_THAT(events, Pointwise(Partially(EqualsProto()),
                                {"mmap_event: { start: 0x300400000 "
                                 "len:0x401000 pgoff: 0 filename: 'file'}"}));
}

TEST_P(HugepageTextStyleDependent, DISABLED_FirstPageIsSmallWithHugeTail) {
  // This test is disabled because DeduceHugePage requires a non-zero pgoff
  // *after* a hugepage_text section in order to correctly deduce it, so it
  // is unable to deduce these cases.
  RepeatedPtrField<PerfEvent> events;
  AddMmap(4, 0x4003ff000, 0x001000, 0, "file", &events);
  AddHugepageTextMmap(4, 0x400400000, 0x200000, 0x001000, "file", &events);

  DeduceHugePages(&events);
  CombineMappings(&events);

  EXPECT_THAT(events, Pointwise(Partially(EqualsProto()),
                                {"mmap_event: { start: 0x4003ff000 "
                                 "len:0x201000 pgoff: 0 filename: 'file'}"}));
}

TEST_P(HugepageTextStyleDependent, HugePageBetweenTwoSmallSections) {
  RepeatedPtrField<PerfEvent> events;
  AddMmap(5, 0x5003ff000, 0x001000, 0, "file", &events);
  AddHugepageTextMmap(5, 0x500400000, 0x200000, 0x001000, "file", &events);
  AddMmap(5, 0x500600000, 0x001000, 0x201000, "file", &events);

  DeduceHugePages(&events);
  CombineMappings(&events);

  EXPECT_THAT(events, Pointwise(Partially(EqualsProto()),
                                {"mmap_event: { start: 0x5003ff000 "
                                 "len:0x202000 pgoff: 0 filename: 'file'}"}));
}

TEST_P(HugepageTextStyleDependent, HugePageSplitByEarlyMlockBetweenTwoSmall) {
  RepeatedPtrField<PerfEvent> events;
  AddMmap(6, 0x6003ff000, 0x001000, 0, "file", &events);
  AddHugepageTextMmap(6, 0x600400000, 0x3f8000, 0x001000, "file", &events);
  AddHugepageTextMmap(6, 0x6007f8000, 0x008000, 0x3f9000, "file", &events);
  AddMmap(6, 0x600800000, 0x001000, 0x401000, "file", &events);

  DeduceHugePages(&events);
  CombineMappings(&events);

  EXPECT_THAT(events, Pointwise(Partially(EqualsProto()),
                                {"mmap_event: { start: 0x6003ff000 "
                                 "len:0x402000 pgoff: 0 filename: 'file'}"}));
}

TEST_P(HugepageTextStyleDependent, HugePageSplitByLateMlockBetweenTwoSmall) {
  RepeatedPtrField<PerfEvent> events;
  AddMmap(7, 0x7003ff000, 0x001000, 0, "file", &events);
  AddHugepageTextMmap(7, 0x700400000, 0x008000, 0x001000, "file", &events);
  AddHugepageTextMmap(7, 0x700408000, 0x3f8000, 0x009000, "file", &events);
  AddMmap(7, 0x700800000, 0x001000, 0x401000, "file", &events);

  DeduceHugePages(&events);
  CombineMappings(&events);

  EXPECT_THAT(events, Pointwise(Partially(EqualsProto()),
                                {"mmap_event: { start: 0x7003ff000 "
                                 "len:0x402000 pgoff: 0 filename: 'file'}"}));
}

TEST_P(HugepageTextStyleDependent, HugePageSplitEvenlyByMlockBetweenTwoSmall) {
  RepeatedPtrField<PerfEvent> events;
  AddMmap(8, 0x8003ff000, 0x001000, 0, "file", &events);
  AddHugepageTextMmap(8, 0x800400000, 0x0f8000, 0x001000, "file", &events);
  AddHugepageTextMmap(8, 0x8004f8000, 0x008000, 0x0f9000, "file", &events);
  AddHugepageTextMmap(8, 0x800500000, 0x100000, 0x101000, "file", &events);
  AddMmap(8, 0x800600000, 0x001000, 0x201000, "file", &events);

  DeduceHugePages(&events);
  CombineMappings(&events);

  EXPECT_THAT(events, Pointwise(Partially(EqualsProto()),
                                {"mmap_event: { start: 0x8003ff000 "
                                 "len:0x202000 pgoff: 0 filename: 'file'}"}));
}

TEST_P(HugepageTextStyleDependent, MultipleContiguousHugepages) {
  RepeatedPtrField<PerfEvent> events;
  AddMmap(9, 0x9003ff000, 0x001000, 0, "file", &events);
  AddHugepageTextMmap(9, 0x900400000, 0x200000, 0x001000, "file", &events);
  AddHugepageTextMmap(9, 0x900600000, 0x200000, 0x201000, "file", &events);
  AddMmap(9, 0x900800000, 0x001000, 0x401000, "file", &events);

  DeduceHugePages(&events);
  CombineMappings(&events);

  EXPECT_THAT(events, Pointwise(Partially(EqualsProto()),
                                {"mmap_event: { start: 0x9003ff000 "
                                 "len:0x402000 pgoff: 0 filename: 'file'}"}));
}

TEST_P(HugepageTextStyleDependent, MultipleContiguousMlockSplitHugepages) {
  // Think:
  // - hugepage_text 4MiB range
  // - mlock alternating 512-KiB chunks
  RepeatedPtrField<PerfEvent> events;
  AddMmap(10, 0xa003ff000, 0x001000, 0, "file", &events);
  AddHugepageTextMmap(10, 0xa00400000, 0x080000, 0x001000, "file", &events);
  AddHugepageTextMmap(10, 0xa00480000, 0x080000, 0x081000, "file", &events);
  AddHugepageTextMmap(10, 0xa00500000, 0x080000, 0x101000, "file", &events);
  AddHugepageTextMmap(10, 0xa00580000, 0x080000, 0x181000, "file", &events);
  AddHugepageTextMmap(10, 0xa00600000, 0x080000, 0x201000, "file", &events);
  AddHugepageTextMmap(10, 0xa00680000, 0x080000, 0x281000, "file", &events);
  AddHugepageTextMmap(10, 0xa00700000, 0x080000, 0x301000, "file", &events);
  AddHugepageTextMmap(10, 0xa00780000, 0x080000, 0x381000, "file", &events);
  AddMmap(10, 0xa00800000, 0x001000, 0x401000, "file", &events);

  DeduceHugePages(&events);
  CombineMappings(&events);

  EXPECT_THAT(events, Pointwise(Partially(EqualsProto()),
                                {"mmap_event: { start: 0xa003ff000 "
                                 "len:0x402000 pgoff: 0 filename: 'file'}"}));
}

TEST_P(HugepageTextStyleDependent, MultipleWithUnalignedInitialHugePage) {
  // Base on real program
  RepeatedPtrField<PerfEvent> events;

  AddHugepageTextMmap(11, 0x85d32e000, 0x6d2000, 0x0, "file", &events);
  AddHugepageTextMmap(11, 0x85da00000, 0x6a00000, 0x6d2000, "file", &events);
  AddMmap(11, 0x864400000, 0x200000, 0x70d2000, "file", &events);
  AddHugepageTextMmap(11, 0x864600000, 0x200000, 0x72d2000, "file", &events);
  AddMmap(11, 0x864800000, 0x600000, 0x74d2000, "file", &events);
  AddHugepageTextMmap(11, 0x864e00000, 0x200000, 0x7ad2000, "file", &events);
  AddMmap(11, 0x865000000, 0x4a000, 0x7cd2000, "file", &events);
  AddMmap(11, 0x86504a000, 0x1000, 0x7d1c000, "file", &events);
  AddMmap(11, 0xa3d368000, 0x3a96000, 0x0, "file2", &events);
  AddMmap(11, 0xa467cc000, 0x2000, 0x0, "file3", &events);

  DeduceHugePages(&events);
  CombineMappings(&events);

  EXPECT_THAT(events, Pointwise(Partially(EqualsProto()),
                                {
                                    "mmap_event: { start: 0x85d32e000 "
                                    "len:0x7d1d000 pgoff: 0 filename: 'file'}",
                                    "mmap_event: { start: 0xa3d368000 "
                                    "len:0x3a96000 pgoff: 0 filename: 'file2'}",
                                    "mmap_event: { start: 0xa467cc000 "
                                    "len:0x2000,  pgoff: 0 filename: 'file3'}",
                                }));
}

TEST_P(HugepageTextStyleDependent, MultipleWithUnalignedInitialHugePage2) {
  // Base on real program
  RepeatedPtrField<PerfEvent> events;
  AddHugepageTextMmap(12, 0xbcff6000, 0x200000, 0x00000000, "file", &events);
  AddMmap(12, 0xbd1f6000, 0x300a000, 0x200000, "file", &events);
  AddHugepageTextMmap(12, 0xc0200000, 0x2b374000, 0x320a000, "file", &events);
  AddHugepageTextMmap(12, 0xeb574000, 0x514000, 0x2e57e000, "file", &events);
  AddHugepageTextMmap(12, 0xeba88000, 0x1d78000, 0x2ea92000, "file", &events);
  AddMmap(12, 0xed800000, 0x1200000, 0x3080a000, "file", &events);
  AddHugepageTextMmap(12, 0xeea00000, 0x200000, 0x31a0a000, "file", &events);
  AddMmap(12, 0xeec00000, 0x2800000, 0x31c0a000, "file", &events);
  AddHugepageTextMmap(12, 0xf1400000, 0x200000, 0x3440a000, "file", &events);
  AddMmap(12, 0xf1600000, 0x89f000, 0x3460a000, "file", &events);
  AddMmap(12, 0xf1e9f000, 0x1000, 0x34ea9000, "file", &events);

  DeduceHugePages(&events);
  CombineMappings(&events);

  EXPECT_THAT(events, Pointwise(Partially(EqualsProto()),
                                {"mmap_event: { start: 0xbcff6000 "
                                 "len:0x34eaa000 pgoff: 0 filename: 'file'}"}));
}

TEST_P(HugepageTextStyleDependent, NoMmaps) {
  RepeatedPtrField<PerfEvent> events;
  events.Add();

  DeduceHugePages(&events);
  CombineMappings(&events);

  EXPECT_THAT(events, Pointwise(EqualsProto(), std::vector<PerfEvent>(1)));
}
TEST_P(HugepageTextStyleDependent, MultipleNonMmaps) {
  RepeatedPtrField<PerfEvent> events;
  events.Add();
  events.Add();

  DeduceHugePages(&events);
  CombineMappings(&events);

  EXPECT_THAT(events, Pointwise(EqualsProto(), std::vector<PerfEvent>(2)));
}
TEST_P(HugepageTextStyleDependent, NonMmapFirstMmap) {
  RepeatedPtrField<PerfEvent> events;
  events.Add();
  AddHugepageTextMmap(12, 0, 0x200000, 0, "file", &events);

  DeduceHugePages(&events);
  CombineMappings(&events);

  EXPECT_THAT(events, Pointwise(Partially(EqualsProto()),
                                {"", "mmap_event: { pgoff: 0 }"}));
}
TEST_P(HugepageTextStyleDependent, NonMmapAfterLastMmap) {
  RepeatedPtrField<PerfEvent> events;
  AddHugepageTextMmap(12, 0, 0x200000, 0, "file", &events);
  events.Add();

  DeduceHugePages(&events);
  CombineMappings(&events);

  EXPECT_THAT(events, Pointwise(Partially(EqualsProto()),
                                {"mmap_event: { pgoff: 0 }", ""}));
}

INSTANTIATE_TEST_CASE_P(NoHugepageText, HugepageTextStyleDependent,
                        ::testing::Values(kNoHugepageText));
INSTANTIATE_TEST_CASE_P(AnonHugepageText, HugepageTextStyleDependent,
                        ::testing::Values(kAnonHugepageText));

TEST(HugePageDeducer, DoesNotChangeVirtuallyContiguousPgoffNonContiguous) {
  // We've seen programs with strange memory layouts having virtually contiguous
  // memory backed by non-contiguous bits of a file.
  RepeatedPtrField<PerfEvent> events;
  AddMmap(758463, 0x2f278000, 0x20000, 0, "lib0.so", &events);
  AddMmap(758463, 0x2f29d000, 0x2000, 0, "shm", &events);
  AddMmap(758463, 0x2f2a2000, 0xa000, 0, "lib1.so", &events);
  AddMmap(758463, 0x3d400000, 0x9ee000, 0, "lib2.so", &events);
  AddMmap(758463, 0x3e000000, 0x16000, 0, "lib3.so", &events);
  AddMmap(758463, 0x3e400000, 0x270000, 0x1a00000, "shm", &events);
  AddMmap(758463, 0x3e670000, 0x10000, 0x1aaac000, "shm", &events);
  AddMmap(758463, 0x3e680000, 0x10000, 0x1b410000, "shm", &events);

  DeduceHugePages(&events);
  CombineMappings(&events);

  EXPECT_THAT(events,
              Pointwise(Partially(EqualsProto()),
                        {
                            "mmap_event: { pgoff: 0 filename: 'lib0.so' }",
                            "mmap_event: { pgoff: 0 filename: 'shm' }",
                            "mmap_event: { pgoff: 0 filename: 'lib1.so' }",
                            "mmap_event: { pgoff: 0 filename: 'lib2.so' }",
                            "mmap_event: { pgoff: 0 filename: 'lib3.so' }",
                            "mmap_event: { pgoff: 0x1a00000 filename: 'shm' }",
                            "mmap_event: { pgoff: 0x1aaac000 filename: 'shm' }",
                            "mmap_event: { pgoff: 0x1b410000 filename: 'shm' }",
                        }));
}

TEST(HugePageDeducer, IgnoresDynamicMmaps) {
  // Now, let's watch a binary hugepage_text itself.
  RepeatedPtrField<PerfEvent> events;
  AddMmap(6531, 0x560d76b25000, 0x24ce000, 0, "main", &events);
  events.rbegin()->set_timestamp(700413232676401);
  AddMmap(6531, 0x7f686a1ec000, 0x24000, 0, "ld.so", &events);
  events.rbegin()->set_timestamp(700413232691935);
  AddMmap(6531, 0x7ffea5dc8000, 0x2000, 0, "[vdso]", &events);
  events.rbegin()->set_timestamp(700413232701418);
  AddMmap(6531, 0x7f686a1e3000, 0x5000, 0, "lib1.so", &events);
  events.rbegin()->set_timestamp(700413232824216);
  AddMmap(6531, 0x7f686a1a8000, 0x3a000, 0, "lib2.so", &events);
  events.rbegin()->set_timestamp(700413232854520);
  AddMmap(6531, 0x7f6869ea7000, 0x5000, 0, "lib3.so", &events);
  events.rbegin()->set_timestamp(700413248827794);
  AddMmap(6531, 0x7f6867e00000, 0x200000, 0, "/anon_hugepage (deleted)",
          &events);
  events.rbegin()->set_timestamp(700413295816043);
  AddMmap(6531, 0x7f6867c00000, 0x200000, 0, "/anon_hugepage (deleted)",
          &events);
  events.rbegin()->set_timestamp(700413305947499);
  AddMmap(6531, 0x7f68663f8000, 0x1e00000, 0x7f68663f8000, "//anon", &events);
  events.rbegin()->set_timestamp(700413306012797);
  AddMmap(6531, 0x7f6866525000, 0x1a00000, 0x7f6866525000, "//anon", &events);
  events.rbegin()->set_timestamp(700413312132909);

  DeduceHugePages(&events);
  CombineMappings(&events);

  EXPECT_THAT(
      events,
      Pointwise(
          Partially(EqualsProto()),
          {
              "mmap_event: { pgoff: 0 filename: 'main' }",
              "mmap_event: { pgoff: 0 filename: 'ld.so' }",
              "mmap_event: { pgoff: 0 filename: '[vdso]' }",
              "mmap_event: { pgoff: 0 filename: 'lib1.so' }",
              "mmap_event: { pgoff: 0 filename: 'lib2.so' }",
              "mmap_event: { pgoff: 0 filename: 'lib3.so' }",
              "mmap_event: { pgoff: 0 filename: '/anon_hugepage (deleted)' }",
              "mmap_event: { pgoff: 0 filename: '/anon_hugepage (deleted)' }",
              "mmap_event: { pgoff: 0x7f68663f8000 filename: '//anon' }",
              "mmap_event: { pgoff: 0x7f6866525000 filename: '//anon' }",
          }));
}

TEST(HugePageDeducer, Regression62446346) {
  RepeatedPtrField<PerfEvent> events;

  // Perf infers the filename is "file", but at offset 0 for
  // hugepage-backed, anonymous mappings.
  //
  // vaddr start   - vaddr end     vaddr-size    elf-offset
  // [0x55a685bfb000-55a685dfb000) (0x200000)   @ 0]:          file
  // [0x55a685dfb000-55a687c00000) (0x1e05000)  @ 0x200000]:   file
  // [0x55a687c00000-55a6a5200000) (0x1d600000) @ 0]:          file
  // [0x55a6a5200000-55a6a6400000) (0x1200000)  @ 0x1f605000]: file
  // [0x55a6a6400000-55a6a6600000) (0x200000)   @ 0]:          file
  // [0x55a6a6600000-55a6a8800000) (0x2200000)  @ 0x20a05000]: file
  // [0x55a6a8800000-55a6a8a00000) (0x200000)   @ 0]:          file
  // [0x55a6a8a00000-55a6a90ca000) (0x6ca000)   @ 0x22e05000]: file
  // [0x55a6a90ca000-55a6a90cb000) (0x1000)     @ 0x234cf000]: file
  {
    MMapEvent* ev = events.Add()->mutable_mmap_event();
    ev->set_pid(1234);
    ev->set_start(0x55a685bfb000);
    ev->set_len(0x200000);
    ev->set_pgoff(0);
    ev->set_filename("file");
  }
  {
    MMapEvent* ev = events.Add()->mutable_mmap_event();
    ev->set_pid(1234);
    ev->set_start(0x55a685dfb000);
    ev->set_len(0x1e05000);
    ev->set_pgoff(0x200000);
    ev->set_filename("file");
  }
  {
    MMapEvent* ev = events.Add()->mutable_mmap_event();
    ev->set_pid(1234);
    ev->set_start(0x55a687c00000);
    ev->set_len(0x1d600000);
    ev->set_pgoff(0);
    ev->set_filename("file");
  }
  {
    MMapEvent* ev = events.Add()->mutable_mmap_event();
    ev->set_pid(1234);
    ev->set_start(0x55a6a5200000);
    ev->set_len(0x1200000);
    ev->set_pgoff(0x1f605000);
    ev->set_filename("file");
  }
  {
    MMapEvent* ev = events.Add()->mutable_mmap_event();
    ev->set_pid(1234);
    ev->set_start(0x55a6a6400000);
    ev->set_len(0x200000);
    ev->set_pgoff(0);
    ev->set_filename("file");
  }
  {
    MMapEvent* ev = events.Add()->mutable_mmap_event();
    ev->set_pid(1234);
    ev->set_start(0x55a6a6600000);
    ev->set_len(0x2200000);
    ev->set_pgoff(0x20a05000);
    ev->set_filename("file");
  }
  {
    MMapEvent* ev = events.Add()->mutable_mmap_event();
    ev->set_pid(1234);
    ev->set_start(0x55a6a8800000);
    ev->set_len(0x200000);
    ev->set_pgoff(0);
    ev->set_filename("file");
  }
  {
    MMapEvent* ev = events.Add()->mutable_mmap_event();
    ev->set_pid(1234);
    ev->set_start(0x55a6a8a00000);
    ev->set_len(0x6ca000);
    ev->set_pgoff(0x22e05000);
    ev->set_filename("file");
  }
  {
    MMapEvent* ev = events.Add()->mutable_mmap_event();
    ev->set_pid(1234);
    ev->set_start(0x55a6a90ca000);
    ev->set_len(0x1000);
    ev->set_pgoff(0x234cf000);
    ev->set_filename("file");
  }

  DeduceHugePages(&events);
  CombineMappings(&events);

  ASSERT_EQ(1, events.size());

  EXPECT_EQ("file", events[0].mmap_event().filename());
  EXPECT_EQ(0x55a685bfb000, events[0].mmap_event().start());
  EXPECT_EQ(0x55a6a90cb000 - 0x55a685bfb000, events[0].mmap_event().len());
  EXPECT_EQ(0, events[0].mmap_event().pgoff());
}

}  // namespace
}  // namespace quipper