ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
MemoryManagerStatistic.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
9 
10 #include <Logging.h>
11 
12 #include <algorithm>
13 #include <cstdlib>
14 #include <numeric>
15 
16 namespace cloudViewer {
17 namespace core {
18 
20  // Ensure the static Logger instance is instantiated before the
21  // MemoryManagerStatistic instance.
22  // Since destruction of static instances happens in reverse order,
23  // this guarantees that the Logger can be used at any point in time.
25 
26  static MemoryManagerStatistic instance;
27  return instance;
28 }
29 
31  if (print_at_program_end_) {
32  // Always use the default print function (print to the console).
33  // Custom print functions like py::print may not work reliably
34  // at this point in time.
36  Print();
37 
38  // Indicate failure if possible leaks have been detected.
39  // This is useful to automatically let unit tests fail.
40  if (HasLeaks()) {
41  std::exit(EXIT_FAILURE);
42  }
43  }
44 }
45 
46 void MemoryManagerStatistic::SetPrintLevel(PrintLevel level) { level_ = level; }
47 
49  print_at_program_end_ = print;
50 }
51 
53  print_at_malloc_free_ = print;
54 }
55 
57  if (level_ == PrintLevel::None) {
58  return;
59  }
60 
61  if (level_ == PrintLevel::Unbalanced && !HasLeaks()) {
62  return;
63  }
64 
65  // Ensure all information gets printed.
66  auto old_level = utility::GetVerbosityLevel();
68 
69  utility::LogInfo("Memory Statistics: (Device) (#Malloc) (#Free)");
70  utility::LogInfo("---------------------------------------------");
71  for (const auto& value_pair : statistics_) {
72  // Simulate C++17 structured bindings for better readability.
73  const auto& device = value_pair.first;
74  const auto& statistics = value_pair.second;
75 
76  if (level_ == PrintLevel::Unbalanced && statistics.IsBalanced()) {
77  continue;
78  }
79 
80  if (!statistics.IsBalanced()) {
81  int64_t count_leaking =
82  statistics.count_malloc_ - statistics.count_free_;
83 
84  size_t leaking_byte_size = std::accumulate(
85  statistics.active_allocations_.begin(),
86  statistics.active_allocations_.end(), 0,
87  [](size_t count, auto ptr_byte_size) -> size_t {
88  return count + ptr_byte_size.second;
89  });
90 
91  utility::LogWarning("{}: {} {} --> {} with {} total bytes",
92  device.ToString(), statistics.count_malloc_,
93  statistics.count_free_, count_leaking,
94  leaking_byte_size);
95 
96  for (const auto& leak : statistics.active_allocations_) {
97  utility::LogWarning(" {} @ {} bytes", fmt::ptr(leak.first),
98  leak.second);
99  }
100  } else {
101  utility::LogInfo("{}: {} {}", device.ToString(),
102  statistics.count_malloc_, statistics.count_free_);
103  }
104  }
105  utility::LogInfo("---------------------------------------------");
106 
107  // Restore old verbosity level.
108  utility::SetVerbosityLevel(old_level);
109 }
110 
112  return std::any_of(statistics_.begin(), statistics_.end(),
113  [](const auto& value_pair) -> bool {
114  return !value_pair.second.IsBalanced();
115  });
116 }
117 
119  size_t byte_size,
120  const Device& device) {
121  std::lock_guard<std::mutex> lock(statistics_mutex_);
122 
123  // Filter nullptr. Empty allocations are not tracked.
124  if (ptr == nullptr && byte_size == 0) {
125  return;
126  }
127 
128  auto it = statistics_[device].active_allocations_.emplace(ptr, byte_size);
129  if (it.second) {
130  statistics_[device].count_malloc_++;
131  if (print_at_malloc_free_) {
132  utility::LogInfo("[Malloc] {}: {} @ {} bytes",
133  fmt::sprintf("%6s", device.ToString()),
134  fmt::ptr(ptr), byte_size);
135  }
136  } else {
138  "{} @ {} bytes on {} is still active and was not freed before",
139  fmt::ptr(ptr), byte_size, device.ToString());
140  }
141 }
142 
143 void MemoryManagerStatistic::CountFree(void* ptr, const Device& device) {
144  std::lock_guard<std::mutex> lock(statistics_mutex_);
145 
146  // Filter nullptr. Empty allocations are not tracked.
147  if (ptr == nullptr) {
148  return;
149  }
150 
151  auto num_to_erase = statistics_[device].active_allocations_.count(ptr);
152  if (num_to_erase == 1) {
153  if (print_at_malloc_free_) {
154  utility::LogInfo("[ Free ] {}: {} @ {} bytes",
155  fmt::sprintf("%6s", device.ToString()),
156  fmt::ptr(ptr),
157  statistics_[device].active_allocations_.at(ptr));
158  }
159  statistics_[device].active_allocations_.erase(ptr);
160  statistics_[device].count_free_++;
161  } else if (num_to_erase == 0) {
162  // Either the statistics were reset before or the given pointer is
163  // invalid. Do not increase any counts and ignore both cases.
164  } else {
165  // Should never reach here.
167  "Invalid number of erased allocations {} for {} on {}",
168  num_to_erase, fmt::ptr(ptr), device.ToString());
169  }
170 }
171 
173  std::lock_guard<std::mutex> lock(statistics_mutex_);
174  statistics_.clear();
175 }
176 
178  return count_malloc_ == count_free_;
179 }
180 
181 } // namespace core
182 } // namespace cloudViewer
int count
std::string ToString() const
Returns string representation of device, e.g. "CPU:0", "CUDA:0".
Definition: Device.cpp:89
void SetPrintAtMallocFree(bool print)
Enables or disables printing at each malloc and free.
void Print() const
Prints statistics for all recorded devices depending on the print level.
void SetPrintLevel(PrintLevel level)
Sets the level of provided information for printing.
void CountFree(void *ptr, const Device &device)
static MemoryManagerStatistic & GetInstance()
void CountMalloc(void *ptr, size_t byte_size, const Device &device)
Adds the given allocation to the statistics.
void ResetPrintFunction()
Reset the print function to the default one (print to console).
Definition: Logging.cpp:77
static Logger & GetInstance()
Get Logger global singleton instance.
Definition: Logging.cpp:25
#define LogWarning(...)
Definition: Logging.h:72
#define LogInfo(...)
Definition: Logging.h:81
#define LogError(...)
Definition: Logging.h:60
void SetVerbosityLevel(VerbosityLevel level)
Definition: Logging.cpp:89
VerbosityLevel GetVerbosityLevel()
Get global verbosity level of CloudViewer.
Definition: Logging.cpp:93
Generic file read and write utility for python interface.
#define IsBalanced
Definition: rename.h:110