Home | History | Annotate | Download | only in trace_event
      1 // Copyright 2015 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/trace_event/process_memory_dump.h"
      6 
      7 #include <stddef.h>
      8 
      9 #include "base/memory/aligned_memory.h"
     10 #include "base/process/process_metrics.h"
     11 #include "base/trace_event/memory_allocator_dump_guid.h"
     12 #include "base/trace_event/trace_event_argument.h"
     13 #include "testing/gtest/include/gtest/gtest.h"
     14 
     15 namespace base {
     16 namespace trace_event {
     17 
     18 TEST(ProcessMemoryDumpTest, Clear) {
     19   scoped_ptr<ProcessMemoryDump> pmd1(new ProcessMemoryDump(nullptr));
     20   pmd1->CreateAllocatorDump("mad1");
     21   pmd1->CreateAllocatorDump("mad2");
     22   ASSERT_FALSE(pmd1->allocator_dumps().empty());
     23 
     24   pmd1->process_totals()->set_resident_set_bytes(42);
     25   pmd1->set_has_process_totals();
     26 
     27   pmd1->process_mmaps()->AddVMRegion(ProcessMemoryMaps::VMRegion());
     28   pmd1->set_has_process_mmaps();
     29 
     30   pmd1->AddOwnershipEdge(MemoryAllocatorDumpGuid(42),
     31                          MemoryAllocatorDumpGuid(4242));
     32 
     33   MemoryAllocatorDumpGuid shared_mad_guid(1);
     34   pmd1->CreateSharedGlobalAllocatorDump(shared_mad_guid);
     35 
     36   pmd1->Clear();
     37   ASSERT_TRUE(pmd1->allocator_dumps().empty());
     38   ASSERT_TRUE(pmd1->allocator_dumps_edges().empty());
     39   ASSERT_EQ(nullptr, pmd1->GetAllocatorDump("mad1"));
     40   ASSERT_EQ(nullptr, pmd1->GetAllocatorDump("mad2"));
     41   ASSERT_FALSE(pmd1->has_process_totals());
     42   ASSERT_FALSE(pmd1->has_process_mmaps());
     43   ASSERT_TRUE(pmd1->process_mmaps()->vm_regions().empty());
     44   ASSERT_EQ(nullptr, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid));
     45 
     46   // Check that calling AsValueInto() doesn't cause a crash.
     47   scoped_refptr<TracedValue> traced_value(new TracedValue());
     48   pmd1->AsValueInto(traced_value.get());
     49 
     50   // Check that the pmd can be reused and behaves as expected.
     51   auto mad1 = pmd1->CreateAllocatorDump("mad1");
     52   auto mad3 = pmd1->CreateAllocatorDump("mad3");
     53   auto shared_mad = pmd1->CreateSharedGlobalAllocatorDump(shared_mad_guid);
     54   ASSERT_EQ(3u, pmd1->allocator_dumps().size());
     55   ASSERT_EQ(mad1, pmd1->GetAllocatorDump("mad1"));
     56   ASSERT_EQ(nullptr, pmd1->GetAllocatorDump("mad2"));
     57   ASSERT_EQ(mad3, pmd1->GetAllocatorDump("mad3"));
     58   ASSERT_EQ(shared_mad, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid));
     59 
     60   traced_value = new TracedValue();
     61   pmd1->AsValueInto(traced_value.get());
     62 
     63   pmd1.reset();
     64 }
     65 
     66 TEST(ProcessMemoryDumpTest, TakeAllDumpsFrom) {
     67   scoped_refptr<TracedValue> traced_value(new TracedValue());
     68 
     69   scoped_ptr<ProcessMemoryDump> pmd1(new ProcessMemoryDump(nullptr));
     70   auto mad1_1 = pmd1->CreateAllocatorDump("pmd1/mad1");
     71   auto mad1_2 = pmd1->CreateAllocatorDump("pmd1/mad2");
     72   pmd1->AddOwnershipEdge(mad1_1->guid(), mad1_2->guid());
     73 
     74   scoped_ptr<ProcessMemoryDump> pmd2(new ProcessMemoryDump(nullptr));
     75   auto mad2_1 = pmd2->CreateAllocatorDump("pmd2/mad1");
     76   auto mad2_2 = pmd2->CreateAllocatorDump("pmd2/mad2");
     77   pmd1->AddOwnershipEdge(mad2_1->guid(), mad2_2->guid());
     78 
     79   MemoryAllocatorDumpGuid shared_mad_guid(1);
     80   auto shared_mad = pmd2->CreateSharedGlobalAllocatorDump(shared_mad_guid);
     81 
     82   pmd1->TakeAllDumpsFrom(pmd2.get());
     83 
     84   // Make sure that pmd2 is empty but still usable after it has been emptied.
     85   ASSERT_TRUE(pmd2->allocator_dumps().empty());
     86   ASSERT_TRUE(pmd2->allocator_dumps_edges().empty());
     87   pmd2->CreateAllocatorDump("pmd2/this_mad_stays_with_pmd2");
     88   ASSERT_EQ(1u, pmd2->allocator_dumps().size());
     89   ASSERT_EQ(1u, pmd2->allocator_dumps().count("pmd2/this_mad_stays_with_pmd2"));
     90   pmd2->AddOwnershipEdge(MemoryAllocatorDumpGuid(42),
     91                          MemoryAllocatorDumpGuid(4242));
     92 
     93   // Check that calling AsValueInto() doesn't cause a crash.
     94   pmd2->AsValueInto(traced_value.get());
     95 
     96   // Free the |pmd2| to check that the memory ownership of the two MAD(s)
     97   // has been transferred to |pmd1|.
     98   pmd2.reset();
     99 
    100   // Now check that |pmd1| has been effectively merged.
    101   ASSERT_EQ(5u, pmd1->allocator_dumps().size());
    102   ASSERT_EQ(1u, pmd1->allocator_dumps().count("pmd1/mad1"));
    103   ASSERT_EQ(1u, pmd1->allocator_dumps().count("pmd1/mad2"));
    104   ASSERT_EQ(1u, pmd1->allocator_dumps().count("pmd2/mad1"));
    105   ASSERT_EQ(1u, pmd1->allocator_dumps().count("pmd1/mad2"));
    106   ASSERT_EQ(2u, pmd1->allocator_dumps_edges().size());
    107   ASSERT_EQ(shared_mad, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid));
    108 
    109   // Check that calling AsValueInto() doesn't cause a crash.
    110   traced_value = new TracedValue();
    111   pmd1->AsValueInto(traced_value.get());
    112 
    113   pmd1.reset();
    114 }
    115 
    116 TEST(ProcessMemoryDumpTest, Suballocations) {
    117   scoped_ptr<ProcessMemoryDump> pmd(new ProcessMemoryDump(nullptr));
    118   const std::string allocator_dump_name = "fakealloc/allocated_objects";
    119   pmd->CreateAllocatorDump(allocator_dump_name);
    120 
    121   // Create one allocation with an auto-assigned guid and mark it as a
    122   // suballocation of "fakealloc/allocated_objects".
    123   auto pic1_dump = pmd->CreateAllocatorDump("picturemanager/picture1");
    124   pmd->AddSuballocation(pic1_dump->guid(), allocator_dump_name);
    125 
    126   // Same here, but this time create an allocation with an explicit guid.
    127   auto pic2_dump = pmd->CreateAllocatorDump("picturemanager/picture2",
    128                                             MemoryAllocatorDumpGuid(0x42));
    129   pmd->AddSuballocation(pic2_dump->guid(), allocator_dump_name);
    130 
    131   // Now check that AddSuballocation() has created anonymous child dumps under
    132   // "fakealloc/allocated_objects".
    133   auto anon_node_1_it = pmd->allocator_dumps().find(
    134       allocator_dump_name + "/__" + pic1_dump->guid().ToString());
    135   ASSERT_NE(pmd->allocator_dumps().end(), anon_node_1_it);
    136 
    137   auto anon_node_2_it =
    138       pmd->allocator_dumps().find(allocator_dump_name + "/__42");
    139   ASSERT_NE(pmd->allocator_dumps().end(), anon_node_2_it);
    140 
    141   // Finally check that AddSuballocation() has created also the
    142   // edges between the pictures and the anonymous allocator child dumps.
    143   bool found_edge[2]{false, false};
    144   for (const auto& e : pmd->allocator_dumps_edges()) {
    145     found_edge[0] |= (e.source == pic1_dump->guid() &&
    146                       e.target == anon_node_1_it->second->guid());
    147     found_edge[1] |= (e.source == pic2_dump->guid() &&
    148                       e.target == anon_node_2_it->second->guid());
    149   }
    150   ASSERT_TRUE(found_edge[0]);
    151   ASSERT_TRUE(found_edge[1]);
    152 
    153   // Check that calling AsValueInto() doesn't cause a crash.
    154   scoped_refptr<TracedValue> traced_value(new TracedValue());
    155   pmd->AsValueInto(traced_value.get());
    156 
    157   pmd.reset();
    158 }
    159 
    160 #if defined(COUNT_RESIDENT_BYTES_SUPPORTED)
    161 TEST(ProcessMemoryDumpTest, CountResidentBytes) {
    162   const size_t page_size = base::GetPageSize();
    163 
    164   // Allocate few page of dirty memory and check if it is resident.
    165   const size_t size1 = 5 * page_size;
    166   scoped_ptr<char, base::AlignedFreeDeleter> memory1(
    167       static_cast<char*>(base::AlignedAlloc(size1, page_size)));
    168   memset(memory1.get(), 0, size1);
    169   size_t res1 = ProcessMemoryDump::CountResidentBytes(memory1.get(), size1);
    170   ASSERT_EQ(res1, size1);
    171 
    172   // Allocate a large memory segment (>32Mib).
    173   const size_t kVeryLargeMemorySize = 34 * 1024 * 1024;
    174   scoped_ptr<char, base::AlignedFreeDeleter> memory2(
    175       static_cast<char*>(base::AlignedAlloc(kVeryLargeMemorySize, page_size)));
    176   memset(memory2.get(), 0, kVeryLargeMemorySize);
    177   size_t res2 = ProcessMemoryDump::CountResidentBytes(memory2.get(),
    178                                                       kVeryLargeMemorySize);
    179   ASSERT_EQ(res2, kVeryLargeMemorySize);
    180 }
    181 #endif  // defined(COUNT_RESIDENT_BYTES_SUPPORTED)
    182 
    183 }  // namespace trace_event
    184 }  // namespace base
    185