| // Copyright 2018 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "base/sampling_heap_profiler/sampling_heap_profiler.h" | 
 |  | 
 | #include <stdlib.h> | 
 | #include <cinttypes> | 
 |  | 
 | #include "base/allocator/allocator_shim.h" | 
 | #include "base/debug/alias.h" | 
 | #include "base/threading/simple_thread.h" | 
 | #include "build_config.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 |  | 
 | namespace base { | 
 | namespace { | 
 |  | 
 | class SamplingHeapProfilerTest : public ::testing::Test { | 
 | #if defined(OS_MACOSX) | 
 |   void SetUp() override { allocator::InitializeAllocatorShim(); } | 
 | #endif | 
 | }; | 
 |  | 
 | class SamplesCollector : public SamplingHeapProfiler::SamplesObserver { | 
 |  public: | 
 |   explicit SamplesCollector(size_t watch_size) : watch_size_(watch_size) {} | 
 |  | 
 |   void SampleAdded(uint32_t id, size_t size, size_t) override { | 
 |     if (sample_added || size != watch_size_) | 
 |       return; | 
 |     sample_id_ = id; | 
 |     sample_added = true; | 
 |   } | 
 |  | 
 |   void SampleRemoved(uint32_t id) override { | 
 |     if (id == sample_id_) | 
 |       sample_removed = true; | 
 |   } | 
 |  | 
 |   bool sample_added = false; | 
 |   bool sample_removed = false; | 
 |  | 
 |  private: | 
 |   size_t watch_size_; | 
 |   uint32_t sample_id_ = 0; | 
 | }; | 
 |  | 
 | TEST_F(SamplingHeapProfilerTest, CollectSamples) { | 
 |   SamplingHeapProfiler::InitTLSSlot(); | 
 |   SamplesCollector collector(10000); | 
 |   SamplingHeapProfiler* profiler = SamplingHeapProfiler::GetInstance(); | 
 |   profiler->SuppressRandomnessForTest(true); | 
 |   profiler->SetSamplingInterval(1024); | 
 |   profiler->Start(); | 
 |   profiler->AddSamplesObserver(&collector); | 
 |   void* volatile p = malloc(10000); | 
 |   free(p); | 
 |   profiler->Stop(); | 
 |   profiler->RemoveSamplesObserver(&collector); | 
 |   CHECK(collector.sample_added); | 
 |   CHECK(collector.sample_removed); | 
 | } | 
 |  | 
 | const int kNumberOfAllocations = 10000; | 
 |  | 
 | NOINLINE void Allocate1() { | 
 |   void* p = malloc(400); | 
 |   base::debug::Alias(&p); | 
 | } | 
 |  | 
 | NOINLINE void Allocate2() { | 
 |   void* p = malloc(700); | 
 |   base::debug::Alias(&p); | 
 | } | 
 |  | 
 | NOINLINE void Allocate3() { | 
 |   void* p = malloc(20480); | 
 |   base::debug::Alias(&p); | 
 | } | 
 |  | 
 | class MyThread1 : public SimpleThread { | 
 |  public: | 
 |   MyThread1() : SimpleThread("MyThread1") {} | 
 |   void Run() override { | 
 |     for (int i = 0; i < kNumberOfAllocations; ++i) | 
 |       Allocate1(); | 
 |   } | 
 | }; | 
 |  | 
 | class MyThread2 : public SimpleThread { | 
 |  public: | 
 |   MyThread2() : SimpleThread("MyThread2") {} | 
 |   void Run() override { | 
 |     for (int i = 0; i < kNumberOfAllocations; ++i) | 
 |       Allocate2(); | 
 |   } | 
 | }; | 
 |  | 
 | void CheckAllocationPattern(void (*allocate_callback)()) { | 
 |   SamplingHeapProfiler::InitTLSSlot(); | 
 |   SamplingHeapProfiler* profiler = SamplingHeapProfiler::GetInstance(); | 
 |   profiler->SuppressRandomnessForTest(false); | 
 |   profiler->SetSamplingInterval(10240); | 
 |   base::TimeTicks t0 = base::TimeTicks::Now(); | 
 |   std::map<size_t, size_t> sums; | 
 |   const int iterations = 40; | 
 |   for (int i = 0; i < iterations; ++i) { | 
 |     uint32_t id = profiler->Start(); | 
 |     allocate_callback(); | 
 |     std::vector<SamplingHeapProfiler::Sample> samples = | 
 |         profiler->GetSamples(id); | 
 |     profiler->Stop(); | 
 |     std::map<size_t, size_t> buckets; | 
 |     for (auto& sample : samples) { | 
 |       buckets[sample.size] += sample.total; | 
 |     } | 
 |     for (auto& it : buckets) { | 
 |       if (it.first != 400 && it.first != 700 && it.first != 20480) | 
 |         continue; | 
 |       sums[it.first] += it.second; | 
 |       printf("%zu,", it.second); | 
 |     } | 
 |     printf("\n"); | 
 |   } | 
 |  | 
 |   printf("Time taken %" PRIu64 "ms\n", | 
 |          (base::TimeTicks::Now() - t0).InMilliseconds()); | 
 |  | 
 |   for (auto sum : sums) { | 
 |     intptr_t expected = sum.first * kNumberOfAllocations; | 
 |     intptr_t actual = sum.second / iterations; | 
 |     printf("%zu:\tmean: %zu\trelative error: %.2f%%\n", sum.first, actual, | 
 |            100. * (actual - expected) / expected); | 
 |   } | 
 | } | 
 |  | 
 | // Manual tests to check precision of the sampling profiler. | 
 | // Yes, they do leak lots of memory. | 
 |  | 
 | TEST_F(SamplingHeapProfilerTest, DISABLED_ParallelLargeSmallStats) { | 
 |   CheckAllocationPattern([]() { | 
 |     SimpleThread* t1 = new MyThread1(); | 
 |     SimpleThread* t2 = new MyThread2(); | 
 |     t1->Start(); | 
 |     t2->Start(); | 
 |     for (int i = 0; i < kNumberOfAllocations; ++i) | 
 |       Allocate3(); | 
 |     t1->Join(); | 
 |     t2->Join(); | 
 |   }); | 
 | } | 
 |  | 
 | TEST_F(SamplingHeapProfilerTest, DISABLED_SequentialLargeSmallStats) { | 
 |   CheckAllocationPattern([]() { | 
 |     for (int i = 0; i < kNumberOfAllocations; ++i) { | 
 |       Allocate1(); | 
 |       Allocate2(); | 
 |       Allocate3(); | 
 |     } | 
 |   }); | 
 | } | 
 |  | 
 | }  // namespace | 
 | }  // namespace base |