aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/tcmalloc/tcmalloc/heap_profiling_test.cc
blob: 5c2473ffedca4dcf052627ae6f562d90395f287a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// Copyright 2019 The TCMalloc Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <stdint.h>
#include <stdlib.h>

#include <memory>
#include <new>

#include "gtest/gtest.h"
#include "benchmark/benchmark.h"
#include "tcmalloc/internal/logging.h"
#include "tcmalloc/internal/parameter_accessors.h"
#include "tcmalloc/malloc_extension.h"
#include "tcmalloc/static_vars.h"

namespace tcmalloc {
namespace {

int64_t ProfileSize(ProfileType type) {
  int64_t total = 0;

  MallocExtension::SnapshotCurrent(type).Iterate(
      [&](const Profile::Sample &e) { total += e.sum; });
  return total;
}

class ScopedPeakGrowthFraction {
 public:
  explicit ScopedPeakGrowthFraction(double temporary_value)
      : previous_(TCMalloc_Internal_GetPeakSamplingHeapGrowthFraction()) {
    TCMalloc_Internal_SetPeakSamplingHeapGrowthFraction(temporary_value);
  }

  ~ScopedPeakGrowthFraction() {
    TCMalloc_Internal_SetPeakSamplingHeapGrowthFraction(previous_);
  }

 private:
  double previous_;
};

TEST(HeapProfilingTest, PeakHeapTracking) {
  // Adjust high watermark threshold for our scenario, to be independent of
  // changes to the default.  As we use a random value for choosing our next
  // sampling point, we may overweight some allocations above their true size.
  ScopedPeakGrowthFraction s(1.25);

  int64_t start_peak_sz = ProfileSize(ProfileType::kPeakHeap);

  // make a large allocation to force a new peak heap sample
  // (total live: 50MiB)
  void *first = ::operator new(50 << 20);
  // TODO(b/183453911): Remove workaround for GCC 10.x deleting operator new,
  // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94295.
  benchmark::DoNotOptimize(first);
  int64_t peak_after_first = ProfileSize(ProfileType::kPeakHeap);
  EXPECT_NEAR(peak_after_first, start_peak_sz + (50 << 20), 10 << 20);

  // a small allocation shouldn't increase the peak
  // (total live: 54MiB)
  void *second = ::operator new(4 << 20);
  benchmark::DoNotOptimize(second);
  int64_t peak_after_second = ProfileSize(ProfileType::kPeakHeap);
  EXPECT_EQ(peak_after_second, peak_after_first);

  // but a large one should
  // (total live: 254MiB)
  void *third = ::operator new(200 << 20);
  benchmark::DoNotOptimize(third);
  int64_t peak_after_third = ProfileSize(ProfileType::kPeakHeap);
  EXPECT_NEAR(peak_after_third, peak_after_second + (200 << 20), 10 << 20);

  // freeing everything shouldn't affect the peak
  // (total live: 0MiB)
  ::operator delete(first);
  EXPECT_EQ(ProfileSize(ProfileType::kPeakHeap), peak_after_third);

  ::operator delete(second);
  EXPECT_EQ(ProfileSize(ProfileType::kPeakHeap), peak_after_third);

  ::operator delete(third);
  EXPECT_EQ(ProfileSize(ProfileType::kPeakHeap), peak_after_third);

  // going back up less than previous peak shouldn't affect the peak
  // (total live: 200MiB)
  void *fourth = ::operator new(100 << 20);
  benchmark::DoNotOptimize(fourth);
  void *fifth = ::operator new(100 << 20);
  benchmark::DoNotOptimize(fifth);
  EXPECT_EQ(ProfileSize(ProfileType::kPeakHeap), peak_after_third);

  // passing the old peak significantly, even with many small allocations,
  // should generate a new one
  // (total live: 200MiB + 256MiB = 456MiB, 80% over the 254MiB peak)
  void *bitsy[1 << 10];
  for (int i = 0; i < 1 << 10; i++) {
    bitsy[i] = ::operator new(1 << 18);
    benchmark::DoNotOptimize(bitsy[i]);
  }
  EXPECT_GT(ProfileSize(ProfileType::kPeakHeap), peak_after_third);

  ::operator delete(fourth);
  ::operator delete(fifth);
  for (int i = 0; i < 1 << 10; i++) {
    ::operator delete(bitsy[i]);
  }
}

}  // namespace
}  // namespace tcmalloc