YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/util/mem_tracker.cc
Line
Count
Source (jump to first uncovered line)
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
//
18
// The following only applies to changes made to this file as part of YugaByte development.
19
//
20
// Portions Copyright (c) YugaByte, Inc.
21
//
22
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
23
// in compliance with the License.  You may obtain a copy of the License at
24
//
25
// http://www.apache.org/licenses/LICENSE-2.0
26
//
27
// Unless required by applicable law or agreed to in writing, software distributed under the License
28
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
29
// or implied.  See the License for the specific language governing permissions and limitations
30
// under the License.
31
//
32
33
#include "yb/util/mem_tracker.h"
34
35
#include <algorithm>
36
#include <limits>
37
#include <list>
38
#include <memory>
39
#include <mutex>
40
41
#ifdef TCMALLOC_ENABLED
42
#include <gperftools/malloc_extension.h>
43
#endif
44
45
#include "yb/gutil/map-util.h"
46
#include "yb/gutil/once.h"
47
#include "yb/gutil/strings/human_readable.h"
48
#include "yb/gutil/strings/substitute.h"
49
50
#include "yb/util/debug-util.h"
51
#include "yb/util/debug/trace_event.h"
52
#include "yb/util/env.h"
53
#include "yb/util/flag_tags.h"
54
#include "yb/util/format.h"
55
#include "yb/util/memory/memory.h"
56
#include "yb/util/metrics.h"
57
#include "yb/util/mutex.h"
58
#include "yb/util/random_util.h"
59
#include "yb/util/size_literals.h"
60
#include "yb/util/status.h"
61
#include "yb/util/status_log.h"
62
#include "yb/util/logging.h"
63
64
using namespace std::literals;
65
66
DEFINE_int64(memory_limit_hard_bytes, 0,
67
             "Maximum amount of memory this daemon should use, in bytes. "
68
             "A value of 0 autosizes based on the total system memory. "
69
             "A value of -1 disables all memory limiting.");
70
TAG_FLAG(memory_limit_hard_bytes, stable);
71
DEFINE_double(default_memory_limit_to_ram_ratio, 0.85,
72
              "If memory_limit_hard_bytes is left unspecified, then it is "
73
              "set to default_memory_limit_to_ram_ratio * Available RAM.");
74
TAG_FLAG(default_memory_limit_to_ram_ratio, advanced);
75
TAG_FLAG(default_memory_limit_to_ram_ratio, hidden);
76
77
DEFINE_int32(memory_limit_soft_percentage, 85,
78
             "Percentage of the hard memory limit that this daemon may "
79
             "consume before memory throttling of writes begins. The greater "
80
             "the excess, the higher the chance of throttling. In general, a "
81
             "lower soft limit leads to smoother write latencies but "
82
             "decreased throughput, and vice versa for a higher soft limit.");
83
TAG_FLAG(memory_limit_soft_percentage, advanced);
84
85
DEFINE_int32(memory_limit_warn_threshold_percentage, 98,
86
             "Percentage of the hard memory limit that this daemon may "
87
             "consume before WARNING level messages are periodically logged.");
88
TAG_FLAG(memory_limit_warn_threshold_percentage, advanced);
89
90
91
DEFINE_int64(server_tcmalloc_max_total_thread_cache_bytes, -1, "Total number of bytes to "
92
             "use for the thread cache for tcmalloc across all threads in the tserver/master.");
93
DEFINE_int64(tserver_tcmalloc_max_total_thread_cache_bytes, -1, "Total number of bytes to "
94
             "use for the thread cache for tcmalloc across all threads in the tserver. "
95
             "This is being deprecated and is used to fallback/override the value set "
96
             "on the tserver by server_tcmalloc_max_total_thread_cache_bytes." );
97
98
#ifdef TCMALLOC_ENABLED
99
DEFINE_int32(tcmalloc_max_free_bytes_percentage, 10,
100
             "Maximum percentage of the RSS that tcmalloc is allowed to use for "
101
             "reserved but unallocated memory.");
102
TAG_FLAG(tcmalloc_max_free_bytes_percentage, advanced);
103
#endif
104
105
DEFINE_bool(mem_tracker_logging, false,
106
            "Enable logging of memory tracker consume/release operations");
107
108
DEFINE_bool(mem_tracker_log_stack_trace, false,
109
            "Enable logging of stack traces on memory tracker consume/release operations. "
110
            "Only takes effect if mem_tracker_logging is also enabled.");
111
112
DEFINE_int64(mem_tracker_update_consumption_interval_us, 2000000,
113
             "Interval that is used to update memory consumption from external source. "
114
             "For instance from tcmalloc statistics.");
115
116
DEFINE_int64(mem_tracker_tcmalloc_gc_release_bytes, -1,
117
             "When the total amount of memory from calls to Release() since the last GC exceeds "
118
             "this flag, a new tcmalloc GC will be triggered. This GC will clear the tcmalloc "
119
             "page heap freelist. A higher value implies less aggressive GC, i.e. higher memory "
120
             "overhead, but more efficient in terms of runtime.");
121
TAG_FLAG(mem_tracker_tcmalloc_gc_release_bytes, runtime);
122
123
namespace yb {
124
125
// NOTE: this class has been adapted from Impala, so the code style varies
126
// somewhat from yb.
127
128
using std::deque;
129
using std::list;
130
using std::string;
131
using std::stringstream;
132
using std::shared_ptr;
133
using std::vector;
134
135
using strings::Substitute;
136
137
namespace {
138
139
// The ancestor for all trackers. Every tracker is visible from the root down.
140
shared_ptr<MemTracker> root_tracker;
141
GoogleOnceType root_tracker_once = GOOGLE_ONCE_INIT;
142
143
// Total amount of memory from calls to Release() since the last GC. If this
144
// is greater than mem_tracker_tcmalloc_gc_release_bytes, this will trigger a tcmalloc gc.
145
Atomic64 released_memory_since_gc;
146
147
// Validate that various flags are percentages.
148
24.6k
bool ValidatePercentage(const char* flagname, int value) {
149
24.6k
  if (value >= 0 && value <= 100) {
150
24.6k
    return true;
151
24.6k
  }
152
0
  LOG(ERROR) << Substitute("$0 must be a percentage, value $1 is invalid",
153
0
      flagname, value);
154
0
  return false;
155
0
}
156
157
// Marked as unused because this is not referenced in release mode.
158
bool dummy[] __attribute__((unused)) = {
159
    google::RegisterFlagValidator(&FLAGS_memory_limit_soft_percentage, &ValidatePercentage),
160
    google::RegisterFlagValidator(&FLAGS_memory_limit_warn_threshold_percentage,
161
        &ValidatePercentage)
162
#ifdef TCMALLOC_ENABLED
163
    , google::RegisterFlagValidator(&FLAGS_tcmalloc_max_free_bytes_percentage, &ValidatePercentage)
164
#endif
165
};
166
167
template <class TrackerMetrics>
168
bool TryIncrementBy(int64_t delta, int64_t max, HighWaterMark* consumption,
169
20.0M
                    const std::unique_ptr<TrackerMetrics>& metrics) {
170
20.0M
  if (consumption->TryIncrementBy(delta, max)) {
171
20.0M
    if (metrics) {
172
10.7M
      metrics->metric_->IncrementBy(delta);
173
10.7M
    }
174
20.0M
    return true;
175
20.0M
  }
176
18.4E
  return false;
177
18.4E
}
178
179
template <class TrackerMetrics>
180
void IncrementBy(int64_t amount, HighWaterMark* consumption,
181
990M
                 const std::unique_ptr<TrackerMetrics>& metrics) {
182
990M
  consumption->IncrementBy(amount);
183
990M
  if (metrics) {
184
742M
    metrics->metric_->IncrementBy(amount);
185
742M
  }
186
990M
}
187
188
3.37M
std::string CreateMetricName(const MemTracker& mem_tracker) {
189
3.37M
  if (mem_tracker.metric_entity() &&
190
3.37M
        (!mem_tracker.parent() ||
191
3.37M
            mem_tracker.parent()->metric_entity().get() != mem_tracker.metric_entity().get())) {
192
1.64M
    return "mem_tracker";
193
1.64M
  }
194
1.72M
  std::string id = mem_tracker.id();
195
1.72M
  EscapeMetricNameForPrometheus(&id);
196
1.72M
  if (mem_tracker.parent()) {
197
1.72M
    return CreateMetricName(*mem_tracker.parent()) + "_" + id;
198
18.4E
  } else {
199
18.4E
    return id;
200
18.4E
  }
201
1.72M
}
202
203
3.28M
std::string CreateMetricLabel(const MemTracker& mem_tracker) {
204
3.28M
  return Format("Memory consumed by $0", mem_tracker.ToString());
205
3.28M
}
206
207
1.64M
std::string CreateMetricDescription(const MemTracker& mem_tracker) {
208
1.64M
  return CreateMetricLabel(mem_tracker);
209
1.64M
}
210
211
} // namespace
212
213
class MemTracker::TrackerMetrics {
214
 public:
215
  explicit TrackerMetrics(const MetricEntityPtr& metric_entity)
216
1.64M
      : metric_entity_(metric_entity) {
217
1.64M
  }
218
219
1.64M
  void Init(const MemTracker& mem_tracker, const std::string& name_suffix) {
220
1.64M
    std::string name = CreateMetricName(mem_tracker);
221
1.64M
    if (!name_suffix.empty()) {
222
417k
      name += "_";
223
417k
      name += name_suffix;
224
417k
    }
225
1.64M
    metric_ = metric_entity_->FindOrCreateGauge(
226
1.64M
        std::unique_ptr<GaugePrototype<int64_t>>(new OwningGaugePrototype<int64_t>(
227
1.64M
          metric_entity_->prototype().name(), std::move(name),
228
1.64M
          CreateMetricLabel(mem_tracker), MetricUnit::kBytes,
229
1.64M
          CreateMetricDescription(mem_tracker), yb::MetricLevel::kInfo)),
230
1.64M
        mem_tracker.consumption());
231
1.64M
  }
232
233
  TrackerMetrics(TrackerMetrics&) = delete;
234
  void operator=(const TrackerMetrics&) = delete;
235
236
1.17M
  ~TrackerMetrics() {
237
1.17M
    metric_entity_->Remove(metric_->prototype());
238
1.17M
  }
239
240
  MetricEntityPtr metric_entity_;
241
  scoped_refptr<AtomicGauge<int64_t>> metric_;
242
};
243
244
10.3k
void MemTracker::SetTCMallocCacheMemory() {
245
#ifdef TCMALLOC_ENABLED
246
  constexpr const char* const kTcMallocMaxThreadCacheBytes =
247
      "tcmalloc.max_total_thread_cache_bytes";
248
249
  auto flag_value_to_use =
250
      (FLAGS_tserver_tcmalloc_max_total_thread_cache_bytes != -1
251
           ? FLAGS_tserver_tcmalloc_max_total_thread_cache_bytes
252
           : FLAGS_server_tcmalloc_max_total_thread_cache_bytes);
253
  if (flag_value_to_use < 0) {
254
    const auto mem_limit = MemTracker::GetRootTracker()->limit();
255
    FLAGS_server_tcmalloc_max_total_thread_cache_bytes =
256
        std::min(std::max(static_cast<size_t>(2.5 * mem_limit / 100), 32_MB), 2_GB);
257
    FLAGS_tserver_tcmalloc_max_total_thread_cache_bytes =
258
        FLAGS_server_tcmalloc_max_total_thread_cache_bytes;
259
  }
260
  LOG(INFO) << "Setting tcmalloc max thread cache bytes to: "
261
            << FLAGS_server_tcmalloc_max_total_thread_cache_bytes;
262
  if (!MallocExtension::instance()->SetNumericProperty(
263
          kTcMallocMaxThreadCacheBytes, FLAGS_server_tcmalloc_max_total_thread_cache_bytes)) {
264
    LOG(FATAL) << "Failed to set Tcmalloc property: " << kTcMallocMaxThreadCacheBytes;
265
  }
266
#endif
267
10.3k
}
268
269
12.9k
void MemTracker::CreateRootTracker() {
270
12.9k
  DCHECK_ONLY_NOTNULL(dummy);
271
12.9k
  int64_t limit = FLAGS_memory_limit_hard_bytes;
272
12.9k
  if (limit == 0) {
273
    // If no limit is provided, we'll use
274
    // - 85% of the RAM for tservers.
275
    // - 10% of the RAM for masters.
276
40
    int64_t total_ram;
277
40
    CHECK_OK(Env::Default()->GetTotalRAMBytes(&total_ram));
278
40
    limit = total_ram * FLAGS_default_memory_limit_to_ram_ratio;
279
40
  }
280
281
12.9k
  ConsumptionFunctor consumption_functor;
282
283
  #ifdef TCMALLOC_ENABLED
284
  consumption_functor = &MemTracker::GetTCMallocActualHeapSizeBytes;
285
286
  if (FLAGS_mem_tracker_tcmalloc_gc_release_bytes < 0) {
287
    // Allocate 1% of memory to the tcmallc page heap freelist.
288
    // On a 4GB RAM machine, the master gets 10%, so 400MB, so 1% is 4MB.
289
    // On a 16GB RAM machine, the tserver gets 85%, so 13.6GB, so 1% is 136MB, so cap at 128MB.
290
    FLAGS_mem_tracker_tcmalloc_gc_release_bytes =
291
        std::min(static_cast<size_t>(1.0 * limit / 100), 128_MB);
292
  }
293
  #endif
294
295
12.9k
  root_tracker = std::make_shared<MemTracker>(
296
12.9k
      limit, "root", std::move(consumption_functor), nullptr /* parent */, AddToParent::kTrue,
297
12.9k
      CreateMetrics::kFalse);
298
299
12.9k
  LOG(INFO) << StringPrintf("MemTracker: hard memory limit is %.6f GB",
300
12.9k
                            (static_cast<float>(limit) / (1024.0 * 1024.0 * 1024.0)));
301
12.9k
  LOG(INFO) << StringPrintf("MemTracker: soft memory limit is %.6f GB",
302
12.9k
                            (static_cast<float>(root_tracker->soft_limit_) /
303
12.9k
                                (1024.0 * 1024.0 * 1024.0)));
304
12.9k
}
305
306
shared_ptr<MemTracker> MemTracker::CreateTracker(int64_t byte_limit,
307
                                                 const string& id,
308
                                                 ConsumptionFunctor consumption_functor,
309
                                                 const shared_ptr<MemTracker>& parent,
310
                                                 AddToParent add_to_parent,
311
294k
                                                 CreateMetrics create_metrics) {
312
280k
  shared_ptr<MemTracker> real_parent = parent ? parent : GetRootTracker();
313
294k
  return real_parent->CreateChild(
314
294k
      byte_limit, id, std::move(consumption_functor), MayExist::kFalse, add_to_parent,
315
294k
      create_metrics);
316
294k
}
317
318
shared_ptr<MemTracker> MemTracker::CreateChild(int64_t byte_limit,
319
                                               const string& id,
320
                                               ConsumptionFunctor consumption_functor,
321
                                               MayExist may_exist,
322
                                               AddToParent add_to_parent,
323
5.58M
                                               CreateMetrics create_metrics) {
324
5.58M
  std::lock_guard<std::mutex> lock(child_trackers_mutex_);
325
5.58M
  if (may_exist) {
326
5.31M
    auto result = FindChildUnlocked(id);
327
5.31M
    if (result) {
328
3.87M
      return result;
329
3.87M
    }
330
1.71M
  }
331
1.71M
  auto result = std::make_shared<MemTracker>(
332
1.71M
      byte_limit, id, std::move(consumption_functor), shared_from_this(), add_to_parent,
333
1.71M
      create_metrics);
334
1.71M
  auto p = child_trackers_.emplace(id, result);
335
1.71M
  if (!p.second) {
336
2
    auto existing = p.first->second.lock();
337
2
    if (existing) {
338
0
      LOG(DFATAL) << Format("Duplicate memory tracker (id $0) on parent $1", id, ToString());
339
0
      return existing;
340
0
    }
341
2
    p.first->second = result;
342
2
  }
343
344
1.71M
  return result;
345
1.71M
}
346
347
MemTracker::MemTracker(int64_t byte_limit, const string& id,
348
                       ConsumptionFunctor consumption_functor, std::shared_ptr<MemTracker> parent,
349
                       AddToParent add_to_parent, CreateMetrics create_metrics)
350
    : limit_(byte_limit),
351
      soft_limit_(limit_ == -1 ? -1 : (limit_ * FLAGS_memory_limit_soft_percentage) / 100),
352
      id_(id),
353
      consumption_functor_(std::move(consumption_functor)),
354
      descr_(Substitute("memory consumption for $0", id)),
355
      parent_(std::move(parent)),
356
      rand_(GetRandomSeed32()),
357
      enable_logging_(FLAGS_mem_tracker_logging),
358
      log_stack_(FLAGS_mem_tracker_log_stack_trace),
359
1.75M
      add_to_parent_(add_to_parent) {
360
9
  VLOG(1) << "Creating tracker " << ToString();
361
1.75M
  UpdateConsumption();
362
363
1.75M
  all_trackers_.push_back(this);
364
1.75M
  if (has_limit()) {
365
248k
    limit_trackers_.push_back(this);
366
248k
  }
367
1.75M
  if (parent_ && add_to_parent) {
368
1.66M
    all_trackers_.insert(
369
1.66M
        all_trackers_.end(), parent_->all_trackers_.begin(), parent_->all_trackers_.end());
370
1.66M
    limit_trackers_.insert(
371
1.66M
        limit_trackers_.end(), parent_->limit_trackers_.begin(), parent_->limit_trackers_.end());
372
1.66M
  }
373
374
1.75M
  if (create_metrics) {
375
2.76M
    for (MemTracker* tracker = this; tracker; tracker = tracker->parent().get()) {
376
2.65M
      if (tracker->metric_entity()) {
377
1.11M
        metrics_ = std::make_unique<TrackerMetrics>(tracker->metric_entity());
378
1.11M
        metrics_->Init(*this, std::string());
379
1.11M
        break;
380
1.11M
      }
381
2.65M
    }
382
1.23M
  }
383
1.75M
}
384
385
1.27M
MemTracker::~MemTracker() {
386
139
  VLOG(1) << "Destroying tracker " << ToString();
387
1.27M
  if (!consumption_functor_) {
388
0
    DCHECK_EQ(consumption(), 0) << "Memory tracker " << ToString();
389
1.27M
  }
390
1.27M
  if (parent_) {
391
1.27M
    if (add_to_parent_) {
392
1.22M
      parent_->Release(consumption());
393
1.22M
    }
394
1.27M
  }
395
1.27M
}
396
397
190k
void MemTracker::UnregisterFromParent() {
398
190k
  DCHECK(parent_);
399
190k
  parent_->UnregisterChild(id_);
400
190k
}
401
402
190k
void MemTracker::UnregisterChild(const std::string& id) {
403
190k
  std::lock_guard<std::mutex> lock(child_trackers_mutex_);
404
190k
  child_trackers_.erase(id);
405
190k
}
406
407
3.28M
string MemTracker::ToString() const {
408
3.28M
  string s;
409
3.28M
  const MemTracker* tracker = this;
410
18.6M
  while (tracker) {
411
15.3M
    if (s != "") {
412
12.1M
      s += "->";
413
12.1M
    }
414
15.3M
    s += tracker->id();
415
15.3M
    tracker = tracker->parent_.get();
416
15.3M
  }
417
3.28M
  return s;
418
3.28M
}
419
420
MemTrackerPtr MemTracker::FindTracker(const std::string& id,
421
4
                                      const MemTrackerPtr& parent) {
422
3
  shared_ptr<MemTracker> real_parent = parent ? parent : GetRootTracker();
423
4
  return real_parent->FindChild(id);
424
4
}
425
426
5
MemTrackerPtr MemTracker::FindChild(const std::string& id) {
427
5
  std::lock_guard<std::mutex> lock(child_trackers_mutex_);
428
5
  return FindChildUnlocked(id);
429
5
}
430
431
5.31M
MemTrackerPtr MemTracker::FindChildUnlocked(const std::string& id) {
432
5.31M
  auto it = child_trackers_.find(id);
433
5.31M
  if (it != child_trackers_.end()) {
434
4.45M
    auto result = it->second.lock();
435
4.45M
    if (!result) {
436
588k
      child_trackers_.erase(it);
437
588k
    }
438
4.45M
    return result;
439
4.45M
  }
440
859k
  return MemTrackerPtr();
441
859k
}
442
443
shared_ptr<MemTracker> MemTracker::FindOrCreateTracker(int64_t byte_limit,
444
                                                       const string& id,
445
                                                       const shared_ptr<MemTracker>& parent,
446
                                                       AddToParent add_to_parent,
447
5.29M
                                                       CreateMetrics create_metrics) {
448
5.27M
  shared_ptr<MemTracker> real_parent = parent ? parent : GetRootTracker();
449
5.29M
  return real_parent->CreateChild(
450
5.29M
      byte_limit, id, ConsumptionFunctor(), MayExist::kTrue, add_to_parent, create_metrics);
451
5.29M
}
452
453
1.33k
std::vector<MemTrackerPtr> MemTracker::ListChildren() {
454
1.33k
  std::vector<MemTrackerPtr> result;
455
1.33k
  ListDescendantTrackers(&result, OnlyChildren::kTrue);
456
1.33k
  return result;
457
1.33k
}
458
459
void MemTracker::ListDescendantTrackers(
460
1.34k
    std::vector<MemTrackerPtr>* out, OnlyChildren only_children) {
461
1.34k
  size_t begin = out->size();
462
1.34k
  {
463
1.34k
    std::lock_guard<std::mutex> lock(child_trackers_mutex_);
464
2.63k
    for (auto it = child_trackers_.begin(); it != child_trackers_.end();) {
465
1.28k
      auto child = it->second.lock();
466
1.28k
      if (child) {
467
1.28k
        out->push_back(std::move(child));
468
1.28k
        ++it;
469
1
      } else {
470
1
        it = child_trackers_.erase(it);
471
1
      }
472
1.28k
    }
473
1.34k
  }
474
1.34k
  if (!only_children) {
475
7
    size_t end = out->size();
476
11
    for (size_t i = begin; i != end; ++i) {
477
4
      (*out)[i]->ListDescendantTrackers(out);
478
4
    }
479
7
  }
480
1.34k
}
481
482
3
std::vector<MemTrackerPtr> MemTracker::ListTrackers() {
483
3
  std::vector<MemTrackerPtr> result;
484
3
  auto root = GetRootTracker();
485
3
  result.push_back(root);
486
3
  root->ListDescendantTrackers(&result);
487
3
  return result;
488
3
}
489
490
1.19G
bool MemTracker::UpdateConsumption(bool force) {
491
1.19G
  if (poll_children_consumption_functors_) {
492
0
    poll_children_consumption_functors_();
493
0
  }
494
495
1.19G
  if (consumption_functor_) {
496
0
    auto now = CoarseMonoClock::now();
497
0
    auto interval = std::chrono::microseconds(
498
0
        GetAtomicFlag(&FLAGS_mem_tracker_update_consumption_interval_us));
499
0
    if (force || now > last_consumption_update_ + interval) {
500
0
      last_consumption_update_ = now;
501
0
      auto value = consumption_functor_();
502
0
      consumption_.set_value(value);
503
0
      if (metrics_) {
504
0
        metrics_->metric_->set_value(value);
505
0
      }
506
0
    }
507
0
    return true;
508
0
  }
509
510
1.19G
  return false;
511
1.19G
}
512
513
112M
void MemTracker::Consume(int64_t bytes) {
514
112M
  if (bytes < 0) {
515
9
    Release(-bytes);
516
9
    return;
517
9
  }
518
519
112M
  if (UpdateConsumption()) {
520
0
    return;
521
0
  }
522
112M
  if (bytes == 0) {
523
105k
    return;
524
105k
  }
525
112M
  if (PREDICT_FALSE(enable_logging_)) {
526
0
    LogUpdate(true, bytes);
527
0
  }
528
506M
  for (auto& tracker : all_trackers_) {
529
506M
    if (!tracker->UpdateConsumption()) {
530
506M
      IncrementBy(bytes, &tracker->consumption_, tracker->metrics_);
531
506M
      DCHECK_GE(tracker->consumption_.current_value(), 0);
532
506M
    }
533
506M
  }
534
112M
}
535
536
14.9M
bool MemTracker::TryConsume(int64_t bytes, MemTracker** blocking_mem_tracker) {
537
14.9M
  UpdateConsumption();
538
14.9M
  if (bytes <= 0) {
539
5.70M
    return true;
540
5.70M
  }
541
9.28M
  if (PREDICT_FALSE(enable_logging_)) {
542
0
    LogUpdate(true, bytes);
543
0
  }
544
545
9.28M
  ssize_t i = 0;
546
  // Walk the tracker tree top-down, to avoid expanding a limit on a child whose parent
547
  // won't accommodate the change.
548
53.1M
  for (i = all_trackers_.size() - 1; i >= 0; --i) {
549
43.8M
    MemTracker *tracker = all_trackers_[i];
550
43.8M
    if (tracker->limit_ < 0) {
551
23.8M
      IncrementBy(bytes, &tracker->consumption_, tracker->metrics_);
552
20.0M
    } else {
553
20.0M
      if (!TryIncrementBy(bytes, tracker->limit_, &tracker->consumption_, tracker->metrics_)) {
554
        // One of the trackers failed, attempt to GC memory or expand our limit. If that
555
        // succeeds, TryUpdate() again. Bail if either fails.
556
4.25k
        if (!tracker->GcMemory(tracker->limit_ - bytes) ||
557
4.17k
            tracker->ExpandLimit(bytes)) {
558
78
          if (!TryIncrementBy(bytes, tracker->limit_, &tracker->consumption_, tracker->metrics_)) {
559
0
            break;
560
0
          }
561
4.17k
        } else {
562
4.17k
          break;
563
4.17k
        }
564
4.25k
      }
565
20.0M
    }
566
43.8M
  }
567
  // Everyone succeeded, return.
568
9.28M
  if (i == -1) {
569
9.28M
    return true;
570
9.28M
  }
571
572
  // Someone failed, roll back the ones that succeeded.
573
  // TODO: this doesn't roll it back completely since the max values for
574
  // the updated trackers aren't decremented. The max values are only used
575
  // for error reporting so this is probably okay. Rolling those back is
576
  // pretty hard; we'd need something like 2PC.
577
  //
578
  // TODO: This might leave us with an allocated resource that we can't use. Do we need
579
  // to adjust the consumption of the query tracker to stop the resource from never
580
  // getting used by a subsequent TryConsume()?
581
10.8k
  for (ssize_t j = all_trackers_.size(); --j > i;) {
582
8.47k
    IncrementBy(-bytes, &all_trackers_[j]->consumption_, all_trackers_[j]->metrics_);
583
8.47k
  }
584
2.40k
  if (blocking_mem_tracker) {
585
1.77k
    *blocking_mem_tracker = all_trackers_[i];
586
1.77k
  }
587
588
2.40k
  return false;
589
2.40k
}
590
591
101M
void MemTracker::Release(int64_t bytes) {
592
101M
  if (bytes < 0) {
593
0
    Consume(-bytes);
594
0
    return;
595
0
  }
596
597
101M
  if (PREDICT_FALSE(base::subtle::Barrier_AtomicIncrement(&released_memory_since_gc, bytes) >
598
101M
                    GetAtomicFlag(&FLAGS_mem_tracker_tcmalloc_gc_release_bytes))) {
599
101M
    GcTcmalloc();
600
101M
  }
601
602
101M
  if (UpdateConsumption()) {
603
0
    return;
604
0
  }
605
606
101M
  if (bytes == 0) {
607
1.27M
    return;
608
1.27M
  }
609
99.9M
  if (PREDICT_FALSE(enable_logging_)) {
610
0
    LogUpdate(false, bytes);
611
0
  }
612
613
462M
  for (auto& tracker : all_trackers_) {
614
463M
    if (!tracker->UpdateConsumption()) {
615
463M
      IncrementBy(-bytes, &tracker->consumption_, tracker->metrics_);
616
      // If a UDF calls FunctionContext::TrackAllocation() but allocates less than the
617
      // reported amount, the subsequent call to FunctionContext::Free() may cause the
618
      // process mem tracker to go negative until it is synced back to the tcmalloc
619
      // metric. Don't blow up in this case. (Note that this doesn't affect non-process
620
      // trackers since we can enforce that the reported memory usage is internally
621
      // consistent.)
622
0
      DCHECK_GE(tracker->consumption_.current_value(), 0) << "Tracker: " << tracker->ToString();
623
463M
    }
624
462M
  }
625
99.9M
}
626
627
7
bool MemTracker::AnyLimitExceeded() {
628
16
  for (const auto& tracker : limit_trackers_) {
629
16
    if (tracker->LimitExceeded()) {
630
3
      return true;
631
3
    }
632
16
  }
633
4
  return false;
634
7
}
635
636
7.55M
bool MemTracker::LimitExceeded() {
637
7.55M
  if (PREDICT_FALSE(CheckLimitExceeded())) {
638
100k
    return GcMemory(limit_);
639
100k
  }
640
7.45M
  return false;
641
7.45M
}
642
643
7.55M
SoftLimitExceededResult MemTracker::SoftLimitExceeded(double* score) {
644
  // Did we exceed the actual limit?
645
7.55M
  if (LimitExceeded()) {
646
100k
    return {true, consumption() * 100.0 / limit()};
647
100k
  }
648
649
  // No soft limit defined.
650
7.45M
  if (!has_limit() || limit_ == soft_limit_) {
651
0
    return {false, 0.0};
652
0
  }
653
654
  // Are we under the soft limit threshold?
655
7.45M
  int64_t usage = consumption();
656
7.45M
  if (usage < soft_limit_) {
657
7.23M
    return {false, 0.0};
658
7.23M
  }
659
660
  // We're over the threshold; were we randomly chosen to be over the soft limit?
661
212k
  if (*score == 0.0) {
662
201k
    *score = RandomUniformReal<double>();
663
201k
  }
664
212k
  if (usage + (limit_ - soft_limit_) * *score > limit_ && GcMemory(soft_limit_)) {
665
49.8k
    return {true, usage * 100.0 / limit()};
666
49.8k
  }
667
162k
  return {false, 0.0};
668
162k
}
669
670
7.25M
SoftLimitExceededResult MemTracker::AnySoftLimitExceeded(double* score) {
671
7.24M
  for (MemTracker* t : limit_trackers_) {
672
7.24M
    auto result = t->SoftLimitExceeded(score);
673
7.24M
    if (result.exceeded) {
674
72
      return result;
675
72
    }
676
7.24M
  }
677
7.25M
  return {false, 0.0};
678
7.25M
}
679
680
2.39k
int64_t MemTracker::SpareCapacity() const {
681
2.39k
  int64_t result = std::numeric_limits<int64_t>::max();
682
7.18k
  for (const auto& tracker : limit_trackers_) {
683
7.18k
    int64_t mem_left = tracker->limit() - tracker->consumption();
684
7.18k
    result = std::min(result, mem_left);
685
7.18k
  }
686
2.39k
  return result;
687
2.39k
}
688
689
158k
bool MemTracker::GcMemory(int64_t max_consumption) {
690
158k
  if (max_consumption < 0) {
691
    // Impossible to GC enough memory to reach the goal.
692
3.96k
    return true;
693
3.96k
  }
694
695
154k
  {
696
154k
    int64_t current_consumption = GetUpdatedConsumption();
697
    // Check if someone gc'd before us
698
154k
    if (current_consumption <= max_consumption) {
699
0
      return false;
700
0
    }
701
702
    // Create vector of alive garbage collectors. Also remove stale garbage collectors.
703
154k
    std::vector<std::shared_ptr<GarbageCollector>> collectors;
704
154k
    {
705
154k
      std::lock_guard<simple_spinlock> l(gc_mutex_);
706
154k
      collectors.reserve(gcs_.size());
707
154k
      auto w = gcs_.begin();
708
154k
      for (auto i = gcs_.begin(); i != gcs_.end(); ++i) {
709
82
        auto gc = i->lock();
710
82
        if (!gc) {
711
0
          continue;
712
0
        }
713
82
        collectors.push_back(gc);
714
82
        if (w != i) {
715
0
          *w = *i;
716
0
        }
717
82
        ++w;
718
82
      }
719
154k
      gcs_.erase(w, gcs_.end());
720
154k
    }
721
722
    // Try to free up some memory
723
80
    for (const auto& gc : collectors) {
724
80
      gc->CollectGarbage(current_consumption - max_consumption);
725
80
      current_consumption = GetUpdatedConsumption();
726
80
      if (current_consumption <= max_consumption) {
727
79
        break;
728
79
      }
729
80
    }
730
154k
  }
731
732
154k
  int64_t current_consumption = GetUpdatedConsumption();
733
154k
  if (current_consumption > max_consumption) {
734
154k
    std::vector<MemTrackerPtr> children;
735
154k
    {
736
154k
      std::lock_guard<std::mutex> lock(child_trackers_mutex_);
737
158k
      for (auto it = child_trackers_.begin(); it != child_trackers_.end();) {
738
4.06k
        auto child = it->second.lock();
739
4.06k
        if (child) {
740
4.06k
          children.push_back(std::move(child));
741
4.06k
          ++it;
742
1
        } else {
743
1
          it = child_trackers_.erase(it);
744
1
        }
745
4.06k
      }
746
154k
    }
747
748
4.05k
    for (const auto& child : children) {
749
4.05k
      bool did_gc = child->GcMemory(max_consumption - current_consumption + child->consumption());
750
4.05k
      if (did_gc) {
751
4.05k
        current_consumption = GetUpdatedConsumption();
752
4.05k
        if (current_consumption <= max_consumption) {
753
3
          return true;
754
3
        }
755
4.05k
      }
756
4.05k
    }
757
154k
  }
758
759
154k
  return consumption() > max_consumption;
760
154k
}
761
762
101M
void MemTracker::GcTcmalloc() {
763
#ifdef TCMALLOC_ENABLED
764
  released_memory_since_gc = 0;
765
  TRACE_EVENT0("process", "MemTracker::GcTcmalloc");
766
767
  // Number of bytes in the 'NORMAL' free list (i.e reserved by tcmalloc but
768
  // not in use).
769
  int64_t bytes_overhead = GetTCMallocProperty("tcmalloc.pageheap_free_bytes");
770
  // Bytes allocated by the application.
771
  int64_t bytes_used = GetTCMallocCurrentAllocatedBytes();
772
773
  int64_t max_overhead = bytes_used * FLAGS_tcmalloc_max_free_bytes_percentage / 100.0;
774
  if (bytes_overhead > max_overhead) {
775
    int64_t extra = bytes_overhead - max_overhead;
776
    while (extra > 0) {
777
      // Release 1MB at a time, so that tcmalloc releases its page heap lock
778
      // allowing other threads to make progress. This still disrupts the current
779
      // thread, but is better than disrupting all.
780
      MallocExtension::instance()->ReleaseToSystem(1024 * 1024);
781
      extra -= 1024 * 1024;
782
    }
783
  }
784
785
#else
786
  // Nothing to do if not using tcmalloc.
787
101M
#endif
788
101M
}
789
790
0
string MemTracker::LogUsage(const string& prefix, int64_t usage_threshold, int indent) const {
791
0
  stringstream ss;
792
0
  ss << prefix << std::string(indent, ' ') << id_ << ":";
793
0
  if (CheckLimitExceeded()) {
794
0
    ss << " memory limit exceeded.";
795
0
  }
796
0
  if (limit_ > 0) {
797
0
    ss << " Limit=" << HumanReadableNumBytes::ToString(limit_);
798
0
  }
799
0
  ss << " Consumption=" << HumanReadableNumBytes::ToString(consumption());
800
801
0
  stringstream prefix_ss;
802
0
  prefix_ss << prefix << "  ";
803
0
  string new_prefix = prefix_ss.str();
804
0
  std::lock_guard<std::mutex> lock(child_trackers_mutex_);
805
0
  for (const auto& p : child_trackers_) {
806
0
    auto child = p.second.lock();
807
0
    if (child && child->consumption() >= usage_threshold) {
808
0
      ss << std::endl;
809
0
      ss << child->LogUsage(prefix, usage_threshold, indent + 2);
810
0
    }
811
0
  }
812
0
  return ss.str();
813
0
}
814
815
0
void MemTracker::LogUpdate(bool is_consume, int64_t bytes) const {
816
0
  stringstream ss;
817
0
  ss << this << " " << (is_consume ? "Consume: " : "Release: ") << bytes
818
0
     << " Consumption: " << consumption() << " Limit: " << limit_;
819
0
  if (log_stack_) {
820
0
    ss << std::endl << GetStackTrace();
821
0
  }
822
0
  LOG(ERROR) << ss.str();
823
0
}
824
825
261k
shared_ptr<MemTracker> MemTracker::GetRootTracker() {
826
261k
  GoogleOnceInit(&root_tracker_once, &MemTracker::CreateRootTracker);
827
261k
  return root_tracker;
828
261k
}
829
830
void MemTracker::SetMetricEntity(
831
523k
    const MetricEntityPtr& metric_entity, const std::string& name_suffix) {
832
523k
  if (metrics_) {
833
0
    LOG_IF(DFATAL, metric_entity->id() != metrics_->metric_entity_->id())
834
0
        << "SetMetricEntity (" << metric_entity->id() << ") while "
835
0
        << ToString() << " already has a different metric entity "
836
0
        << metrics_->metric_entity_->id();
837
21
    return;
838
21
  }
839
523k
  metrics_ = std::make_unique<TrackerMetrics>(metric_entity);
840
523k
  metrics_->Init(*this, name_suffix);
841
523k
}
842
843
13.8M
scoped_refptr<MetricEntity> MemTracker::metric_entity() const {
844
12.0M
  return metrics_ ? metrics_->metric_entity_ : nullptr;
845
13.8M
}
846
847
const MemTrackerData& CollectMemTrackerData(const MemTrackerPtr& tracker, int depth,
848
1.33k
                                            std::vector<MemTrackerData>* output) {
849
1.33k
  size_t idx = output->size();
850
1.33k
  output->push_back({tracker, depth, 0});
851
852
1.33k
  auto children = tracker->ListChildren();
853
1.10k
  std::sort(children.begin(), children.end(), [](const auto& lhs, const auto& rhs) {
854
1.10k
    return lhs->id() < rhs->id();
855
1.10k
  });
856
857
1.28k
  for (const auto& child : children) {
858
1.28k
    const auto& child_data = CollectMemTrackerData(child, depth + 1, output);
859
1.28k
    (*output)[idx].consumption_excluded_from_ancestors +=
860
1.28k
        child_data.consumption_excluded_from_ancestors;
861
1.28k
    if (!child_data.tracker->add_to_parent()) {
862
159
      (*output)[idx].consumption_excluded_from_ancestors += child_data.tracker->consumption();
863
159
    }
864
1.28k
  }
865
866
1.33k
  return (*output)[idx];
867
1.33k
}
868
869
53
std::string DumpMemTrackers() {
870
53
  std::ostringstream out;
871
53
  std::vector<MemTrackerData> trackers;
872
53
  CollectMemTrackerData(MemTracker::GetRootTracker(), 0, &trackers);
873
1.24k
  for (const auto& data : trackers) {
874
1.24k
    const auto& tracker = data.tracker;
875
1.24k
    const std::string current_consumption_str =
876
1.24k
        HumanReadableNumBytes::ToString(tracker->consumption());
877
1.24k
    out << std::string(data.depth, ' ') << tracker->id() << ": ";
878
1.24k
    if (!data.consumption_excluded_from_ancestors || data.tracker->UpdateConsumption()) {
879
913
      out << current_consumption_str;
880
330
    } else {
881
330
      auto full_consumption_str = HumanReadableNumBytes::ToString(
882
330
          tracker->consumption() + data.consumption_excluded_from_ancestors);
883
330
      out << current_consumption_str << " (" << full_consumption_str << ")";
884
330
    }
885
1.24k
    out << std::endl;
886
1.24k
  }
887
53
  return out.str();
888
53
}
889
890
53
std::string DumpMemoryUsage() {
891
53
  std::ostringstream out;
892
53
  auto tcmalloc_stats = TcMallocStats();
893
53
  if (!tcmalloc_stats.empty()) {
894
0
    out << "TCMalloc stats: \n" << tcmalloc_stats << "\n";
895
0
  }
896
53
  out << "Memory usage: \n" << DumpMemTrackers();
897
53
  return out.str();
898
53
}
899
900
bool CheckMemoryPressureWithLogging(
901
4.56M
    const MemTrackerPtr& mem_tracker, double score, const char* error_prefix) {
902
4.56M
  const auto soft_limit_exceeded_result = mem_tracker->AnySoftLimitExceeded(&score);
903
4.56M
  if (!soft_limit_exceeded_result.exceeded) {
904
4.55M
    return true;
905
4.55M
  }
906
907
6.06k
  const std::string msg = StringPrintf(
908
6.06k
      "Soft memory limit exceeded (at %.2f%% of capacity), score: %.2f",
909
6.06k
      soft_limit_exceeded_result.current_capacity_pct, score);
910
6.06k
  if (soft_limit_exceeded_result.current_capacity_pct >=
911
0
      FLAGS_memory_limit_warn_threshold_percentage) {
912
0
    YB_LOG_EVERY_N_SECS(WARNING, 1) << error_prefix << msg << THROTTLE_MSG;
913
6.06k
  } else {
914
6.06k
    YB_LOG_EVERY_N_SECS(INFO, 1) << error_prefix << msg << THROTTLE_MSG;
915
6.06k
  }
916
917
6.06k
  return false;
918
6.06k
}
919
920
} // namespace yb