Coverage Report

Created: 2026-09-28 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libjxl/tools/tracking_memory_manager.cc
Line
Count
Source
1
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
2
//
3
// Use of this source code is governed by a BSD-style
4
// license that can be found in the LICENSE file.
5
6
#include "tools/tracking_memory_manager.h"
7
8
#include <jxl/memory_manager.h>
9
10
#include <algorithm>
11
#include <cstddef>
12
#include <cstdint>
13
#include <mutex>
14
15
#include "lib/jxl/base/status.h"
16
#include "lib/jxl/memory_manager_internal.h"
17
18
namespace jpegxl {
19
namespace tools {
20
21
TrackingMemoryManager::TrackingMemoryManager(uint64_t cap, uint64_t total_cap)
22
49.6k
    : cap_(cap), total_cap_(total_cap) {
23
49.6k
  jxl::Status status = jxl::MemoryManagerInit(&default_, nullptr);
24
49.6k
  JXL_DASSERT(status);
25
49.6k
  (void)status;
26
49.6k
  inner_ = &default_;
27
28
49.6k
  outer_.opaque = reinterpret_cast<void*>(this);
29
49.6k
  outer_.alloc = &Alloc;
30
49.6k
  outer_.free = &Free;
31
49.6k
}
32
33
27.1M
void* TrackingMemoryManager::Alloc(void* opaque, size_t size) {
34
27.1M
  if (opaque == nullptr) {
35
0
    JXL_DEBUG_ABORT("Internal logic error");
36
0
    return nullptr;
37
0
  }
38
27.1M
  TrackingMemoryManager* self =
39
27.1M
      reinterpret_cast<TrackingMemoryManager*>(opaque);
40
27.1M
  {
41
27.1M
    std::lock_guard<std::mutex> guard(self->numbers_mutex_);
42
27.1M
    uint64_t new_total = self->total_bytes_allocated + size;
43
27.1M
    if (new_total < size ||
44
27.1M
        (self->total_cap_ && new_total > self->total_cap_)) {
45
      // Brittle "OOM" - even freeing memory won't undo it.
46
32
      self->total_bytes_allocated = self->total_cap_;
47
32
      self->seen_oom = true;
48
32
      return nullptr;
49
32
    }
50
27.1M
    uint64_t new_bytes_in_use = self->bytes_in_use_ + size;
51
27.1M
    if (new_bytes_in_use < size ||
52
27.1M
        (self->cap_ && new_bytes_in_use > self->cap_)) {
53
      // Soft "OOM"
54
77
      self->seen_oom = true;
55
77
      return nullptr;
56
77
    }
57
27.1M
    self->num_allocations_++;
58
27.1M
    self->total_allocations++;
59
27.1M
    self->bytes_in_use_ = new_bytes_in_use;
60
27.1M
    self->max_bytes_in_use = std::max(self->max_bytes_in_use, new_bytes_in_use);
61
27.1M
    self->total_bytes_allocated = new_total;
62
27.1M
  }
63
0
  void* result = self->inner_->alloc(self->inner_->opaque, size);
64
27.1M
  if (result != nullptr) {
65
27.1M
    std::lock_guard<std::mutex> guard(self->map_mutex_);
66
27.1M
    self->allocations_[result] = size;
67
18.4E
  } else {
68
    // Hard "OOM" - rollback accounting.
69
18.4E
    std::lock_guard<std::mutex> guard(self->numbers_mutex_);
70
18.4E
    self->seen_oom = true;
71
18.4E
    self->num_allocations_--;
72
18.4E
    self->bytes_in_use_ -= size;
73
18.4E
  }
74
27.1M
  return result;
75
27.1M
}
76
77
27.1M
void TrackingMemoryManager::Free(void* opaque, void* address) {
78
27.1M
  if (opaque == nullptr) {
79
0
    JXL_DEBUG_ABORT("Internal logic error");
80
0
    return;
81
0
  }
82
27.1M
  if (address == nullptr) return;
83
27.1M
  TrackingMemoryManager* self =
84
27.1M
      reinterpret_cast<TrackingMemoryManager*>(opaque);
85
27.1M
  bool found = false;
86
27.1M
  size_t size = 0;
87
27.1M
  {
88
27.1M
    std::lock_guard<std::mutex> guard(self->map_mutex_);
89
27.1M
    auto entry = self->allocations_.find(address);
90
27.1M
    if (entry != self->allocations_.end()) {
91
27.1M
      found = true;
92
27.1M
      size = entry->second;
93
27.1M
      self->allocations_.erase(entry);
94
18.4E
    } else {
95
18.4E
      JXL_DEBUG_ABORT("Internal logic error");
96
0
    }
97
27.1M
  }
98
99
27.1M
  if (found) {
100
27.1M
    std::lock_guard<std::mutex> guard(self->numbers_mutex_);
101
27.1M
    self->num_allocations_--;
102
27.1M
    self->bytes_in_use_ -= size;
103
27.1M
  }
104
27.1M
  self->inner_->free(self->inner_->opaque, address);
105
27.1M
}
106
107
49.6k
jxl::Status TrackingMemoryManager::Reset() {
108
49.6k
  if (num_allocations_ != 0) {
109
0
    return JXL_FAILURE("Memory leak");
110
0
  }
111
49.6k
  if (!allocations_.empty()) {
112
0
    return JXL_FAILURE("Internal logic error");
113
0
  }
114
49.6k
  if (bytes_in_use_ != 0) {
115
0
    return JXL_FAILURE("Internal logic error");
116
0
  }
117
49.6k
  seen_oom = false;
118
49.6k
  max_bytes_in_use = 0;
119
49.6k
  total_allocations = 0;
120
49.6k
  total_bytes_allocated = 0;
121
49.6k
  return true;
122
49.6k
}
123
124
}  // namespace tools
125
}  // namespace jpegxl