Coverage Report

Created: 2024-09-19 09:45

/proc/self/cwd/source/common/memory/stats.cc
Line
Count
Source (jump to first uncovered line)
1
#include "source/common/memory/stats.h"
2
3
#include <cstdint>
4
5
#include "source/common/common/assert.h"
6
#include "source/common/common/logger.h"
7
8
#if defined(TCMALLOC)
9
#include "tcmalloc/malloc_extension.h"
10
#elif defined(GPERFTOOLS_TCMALLOC)
11
#include "gperftools/malloc_extension.h"
12
#endif
13
14
namespace Envoy {
15
namespace Memory {
16
17
5.00k
uint64_t Stats::totalCurrentlyAllocated() {
18
#if defined(TCMALLOC)
19
  return tcmalloc::MallocExtension::GetNumericProperty("generic.current_allocated_bytes")
20
      .value_or(0);
21
#elif defined(GPERFTOOLS_TCMALLOC)
22
  size_t value = 0;
23
  MallocExtension::instance()->GetNumericProperty("generic.current_allocated_bytes", &value);
24
  return value;
25
#else
26
5.00k
  return 0;
27
5.00k
#endif
28
5.00k
}
29
30
5.00k
uint64_t Stats::totalCurrentlyReserved() {
31
#if defined(TCMALLOC)
32
  // In Google's tcmalloc the semantics of generic.heap_size has
33
  // changed: it doesn't include unmapped bytes.
34
  return tcmalloc::MallocExtension::GetNumericProperty("generic.heap_size").value_or(0) +
35
         tcmalloc::MallocExtension::GetNumericProperty("tcmalloc.pageheap_unmapped_bytes")
36
             .value_or(0);
37
#elif defined(GPERFTOOLS_TCMALLOC)
38
  size_t value = 0;
39
  MallocExtension::instance()->GetNumericProperty("generic.heap_size", &value);
40
  return value;
41
#else
42
5.00k
  return 0;
43
5.00k
#endif
44
5.00k
}
45
46
0
uint64_t Stats::totalThreadCacheBytes() {
47
#if defined(TCMALLOC)
48
  return tcmalloc::MallocExtension::GetNumericProperty("tcmalloc.current_total_thread_cache_bytes")
49
      .value_or(0);
50
#elif defined(GPERFTOOLS_TCMALLOC)
51
  size_t value = 0;
52
  MallocExtension::instance()->GetNumericProperty("tcmalloc.current_total_thread_cache_bytes",
53
                                                  &value);
54
  return value;
55
#else
56
0
  return 0;
57
0
#endif
58
0
}
59
60
0
uint64_t Stats::totalPageHeapFree() {
61
#if defined(TCMALLOC)
62
  return tcmalloc::MallocExtension::GetNumericProperty("tcmalloc.pageheap_free_bytes").value_or(0);
63
#elif defined(GPERFTOOLS_TCMALLOC)
64
  size_t value = 0;
65
  MallocExtension::instance()->GetNumericProperty("tcmalloc.pageheap_free_bytes", &value);
66
  return value;
67
#else
68
0
  return 0;
69
0
#endif
70
0
}
71
72
0
uint64_t Stats::totalPageHeapUnmapped() {
73
#if defined(TCMALLOC)
74
  return tcmalloc::MallocExtension::GetNumericProperty("tcmalloc.pageheap_unmapped_bytes")
75
      .value_or(0);
76
#elif defined(GPERFTOOLS_TCMALLOC)
77
  size_t value = 0;
78
  MallocExtension::instance()->GetNumericProperty("tcmalloc.pageheap_unmapped_bytes", &value);
79
  return value;
80
#else
81
0
  return 0;
82
0
#endif
83
0
}
84
85
5.00k
uint64_t Stats::totalPhysicalBytes() {
86
#if defined(TCMALLOC)
87
  return tcmalloc::MallocExtension::GetProperties()["generic.physical_memory_used"].value;
88
#elif defined(GPERFTOOLS_TCMALLOC)
89
  size_t value = 0;
90
  MallocExtension::instance()->GetNumericProperty("generic.total_physical_bytes", &value);
91
  return value;
92
#else
93
5.00k
  return 0;
94
5.00k
#endif
95
5.00k
}
96
97
0
void Stats::dumpStatsToLog() {
98
#if defined(TCMALLOC)
99
  ENVOY_LOG_MISC(debug, "TCMalloc stats:\n{}", tcmalloc::MallocExtension::GetStats());
100
#elif defined(GPERFTOOLS_TCMALLOC)
101
  constexpr int buffer_size = 100000;
102
  auto buffer = std::make_unique<char[]>(buffer_size);
103
  MallocExtension::instance()->GetStats(buffer.get(), buffer_size);
104
  ENVOY_LOG_MISC(debug, "TCMalloc stats:\n{}", buffer.get());
105
#else
106
0
  return;
107
0
#endif
108
0
}
109
110
AllocatorManager::AllocatorManager(
111
    Api::Api& api, Envoy::Stats::Scope& scope,
112
    const envoy::config::bootstrap::v3::MemoryAllocatorManager& config)
113
    : bytes_to_release_(config.bytes_to_release()),
114
      memory_release_interval_msec_(std::chrono::milliseconds(
115
          PROTOBUF_GET_MS_OR_DEFAULT(config, memory_release_interval, 1000))),
116
      allocator_manager_stats_(MemoryAllocatorManagerStats{
117
          MEMORY_ALLOCATOR_MANAGER_STATS(POOL_COUNTER_PREFIX(scope, "tcmalloc."))}),
118
4.47k
      api_(api) {
119
#if defined(GPERFTOOLS_TCMALLOC)
120
  if (bytes_to_release_ > 0) {
121
    ENVOY_LOG_MISC(error,
122
                   "Memory releasing is not supported for gperf tcmalloc, no memory releasing "
123
                   "will be configured.");
124
  }
125
#elif defined(TCMALLOC)
126
  configureBackgroundMemoryRelease();
127
#endif
128
4.47k
};
129
130
4.47k
AllocatorManager::~AllocatorManager() {
131
#if defined(TCMALLOC)
132
  if (tcmalloc_routine_dispatcher_) {
133
    tcmalloc_routine_dispatcher_->exit();
134
  }
135
  if (tcmalloc_thread_) {
136
    tcmalloc_thread_->join();
137
    tcmalloc_thread_.reset();
138
  }
139
#endif
140
4.47k
}
141
142
0
void AllocatorManager::tcmallocRelease() {
143
#if defined(TCMALLOC)
144
  tcmalloc::MallocExtension::ReleaseMemoryToSystem(bytes_to_release_);
145
#endif
146
0
}
147
148
/**
149
 * Configures tcmalloc release rate from the page heap. If `bytes_to_release_`
150
 * has been initialized to `0`, no heap memory will be released in background.
151
 */
152
0
void AllocatorManager::configureBackgroundMemoryRelease() {
153
0
  ENVOY_BUG(!tcmalloc_thread_, "Invalid state, tcmalloc has already been initialised");
154
0
  if (bytes_to_release_ > 0) {
155
0
    tcmalloc_routine_dispatcher_ = api_.allocateDispatcher(std::string(TCMALLOC_ROUTINE_THREAD_ID));
156
0
    memory_release_timer_ = tcmalloc_routine_dispatcher_->createTimer([this]() -> void {
157
0
      const uint64_t unmapped_bytes_before_release = Stats::totalPageHeapUnmapped();
158
0
      tcmallocRelease();
159
0
      const uint64_t unmapped_bytes_after_release = Stats::totalPageHeapUnmapped();
160
0
      if (unmapped_bytes_after_release > unmapped_bytes_before_release) {
161
        // Only increment stats if memory was actually released. As tcmalloc releases memory on a
162
        // span granularity, during some release rounds there may be no memory released, if during
163
        // past round too much memory was released.
164
        // https://github.com/google/tcmalloc/blob/master/tcmalloc/tcmalloc.cc#L298
165
0
        allocator_manager_stats_.released_by_timer_.inc();
166
0
      }
167
0
      memory_release_timer_->enableTimer(memory_release_interval_msec_);
168
0
    });
169
0
    tcmalloc_thread_ = api_.threadFactory().createThread(
170
0
        [this]() -> void {
171
0
          ENVOY_LOG_MISC(debug, "Started {}", TCMALLOC_ROUTINE_THREAD_ID);
172
0
          memory_release_timer_->enableTimer(memory_release_interval_msec_);
173
0
          tcmalloc_routine_dispatcher_->run(Event::Dispatcher::RunType::RunUntilExit);
174
0
        },
175
0
        Thread::Options{std::string(TCMALLOC_ROUTINE_THREAD_ID)});
176
0
    ENVOY_LOG_MISC(
177
0
        info, fmt::format(
178
0
                  "Configured tcmalloc with background release rate: {} bytes per {} milliseconds",
179
0
                  bytes_to_release_, memory_release_interval_msec_.count()));
180
0
  }
181
0
}
182
183
} // namespace Memory
184
} // namespace Envoy