All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Modules Pages
tracking_resource_adaptor.hpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020-2024, NVIDIA CORPORATION.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #pragma once
17 
18 #include <rmm/detail/error.hpp>
19 #include <rmm/detail/export.hpp>
20 #include <rmm/detail/stack_trace.hpp>
21 #include <rmm/logger.hpp>
24 #include <rmm/resource_ref.hpp>
25 
26 #include <cstddef>
27 #include <map>
28 #include <mutex>
29 #include <shared_mutex>
30 #include <sstream>
31 
32 namespace RMM_NAMESPACE {
33 namespace mr {
54 template <typename Upstream>
56  public:
57  using read_lock_t =
58  std::shared_lock<std::shared_mutex>;
59  using write_lock_t =
60  std::unique_lock<std::shared_mutex>;
67  struct allocation_info {
68  std::unique_ptr<rmm::detail::stack_trace> strace;
69  std::size_t allocation_size;
70 
71  allocation_info() = delete;
78  allocation_info(std::size_t size, bool capture_stack)
79  : strace{[&]() {
80  return capture_stack ? std::make_unique<rmm::detail::stack_trace>() : nullptr;
81  }()},
82  allocation_size{size} {};
83  };
84 
92  tracking_resource_adaptor(device_async_resource_ref upstream, bool capture_stacks = false)
93  : capture_stacks_{capture_stacks}, allocated_bytes_{0}, upstream_{upstream}
94  {
95  }
96 
106  tracking_resource_adaptor(Upstream* upstream, bool capture_stacks = false)
107  : capture_stacks_{capture_stacks},
108  allocated_bytes_{0},
109  upstream_{to_device_async_resource_ref_checked(upstream)}
110  {
111  }
112 
113  tracking_resource_adaptor() = delete;
114  ~tracking_resource_adaptor() override = default;
117  default;
118  tracking_resource_adaptor& operator=(tracking_resource_adaptor const&) = delete;
120  default;
121 
125  [[nodiscard]] rmm::device_async_resource_ref get_upstream_resource() const noexcept
126  {
127  return upstream_;
128  }
129 
137  std::map<void*, allocation_info> const& get_outstanding_allocations() const noexcept
138  {
139  return allocations_;
140  }
141 
151  std::size_t get_allocated_bytes() const noexcept { return allocated_bytes_; }
152 
164  {
165  read_lock_t lock(mtx_);
166 
167  std::ostringstream oss;
168 
169  if (!allocations_.empty()) {
170  for (auto const& alloc : allocations_) {
171  oss << alloc.first << ": " << alloc.second.allocation_size << " B";
172  if (alloc.second.strace != nullptr) {
173  oss << " : callstack:" << std::endl << *alloc.second.strace;
174  }
175  oss << std::endl;
176  }
177  }
178 
179  return oss.str();
180  }
181 
187  {
188 #if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_DEBUG
189  RMM_LOG_DEBUG("Outstanding Allocations: %s", get_outstanding_allocations_str());
190 #endif // SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_DEBUG
191  }
192 
193  private:
207  void* do_allocate(std::size_t bytes, cuda_stream_view stream) override
208  {
209  void* ptr = get_upstream_resource().allocate_async(bytes, stream);
210  // track it.
211  {
212  write_lock_t lock(mtx_);
213  allocations_.emplace(ptr, allocation_info{bytes, capture_stacks_});
214  }
215  allocated_bytes_ += bytes;
216 
217  return ptr;
218  }
219 
227  void do_deallocate(void* ptr, std::size_t bytes, cuda_stream_view stream) override
228  {
229  get_upstream_resource().deallocate_async(ptr, bytes, stream);
230  {
231  write_lock_t lock(mtx_);
232 
233  const auto found = allocations_.find(ptr);
234 
235  // Ensure the allocation is found and the number of bytes match
236  if (found == allocations_.end()) {
237  // Don't throw but log an error. Throwing in a destructor (or any noexcept) will call
238  // std::terminate
239  RMM_LOG_ERROR(
240  "Deallocating a pointer that was not tracked. Ptr: %p [%zuB], Current Num. Allocations: "
241  "%zu",
242  ptr,
243  bytes,
244  this->allocations_.size());
245  } else {
246  allocations_.erase(found);
247 
248  auto allocated_bytes = found->second.allocation_size;
249 
250  if (allocated_bytes != bytes) {
251  // Don't throw but log an error. Throwing in a destructor (or any noexcept) will call
252  // std::terminate
253  RMM_LOG_ERROR(
254  "Alloc bytes (%zu) and Dealloc bytes (%zu) do not match", allocated_bytes, bytes);
255 
256  bytes = allocated_bytes;
257  }
258  }
259  }
260  allocated_bytes_ -= bytes;
261  }
262 
270  bool do_is_equal(device_memory_resource const& other) const noexcept override
271  {
272  if (this == &other) { return true; }
273  auto cast = dynamic_cast<tracking_resource_adaptor<Upstream> const*>(&other);
274  if (cast == nullptr) { return false; }
275  return get_upstream_resource() == cast->get_upstream_resource();
276  }
277 
278  bool capture_stacks_; // whether or not to capture call stacks
279  std::map<void*, allocation_info> allocations_; // map of active allocations
280  std::atomic<std::size_t> allocated_bytes_; // number of bytes currently allocated
281  std::shared_mutex mutable mtx_; // mutex for thread safe access to allocations_
282  device_async_resource_ref upstream_; // the upstream resource used for satisfying
283  // allocation requests
284 };
285 
294 template <typename Upstream>
295 [[deprecated(
296  "make_tracking_adaptor is deprecated in RMM 24.10. Use the tracking_resource_adaptor constructor "
297  "instead.")]]
299 {
300  return tracking_resource_adaptor<Upstream>{upstream};
301 }
302  // end of group
304 } // namespace mr
305 } // namespace RMM_NAMESPACE
Strongly-typed non-owning wrapper for CUDA streams with default constructor.
Definition: cuda_stream_view.hpp:41
Base class for all libcudf device memory allocation.
Definition: device_memory_resource.hpp:94
Resource that uses Upstream to allocate memory and tracks allocations.
Definition: tracking_resource_adaptor.hpp:55
tracking_resource_adaptor(Upstream *upstream, bool capture_stacks=false)
Construct a new tracking resource adaptor using upstream to satisfy allocation requests.
Definition: tracking_resource_adaptor.hpp:106
tracking_resource_adaptor(device_async_resource_ref upstream, bool capture_stacks=false)
Construct a new tracking resource adaptor using upstream to satisfy allocation requests.
Definition: tracking_resource_adaptor.hpp:92
std::size_t get_allocated_bytes() const noexcept
Query the number of bytes that have been allocated. Note that this can not be used to know how large ...
Definition: tracking_resource_adaptor.hpp:151
std::unique_lock< std::shared_mutex > write_lock_t
Type of lock used to synchronize write access.
Definition: tracking_resource_adaptor.hpp:60
tracking_resource_adaptor(tracking_resource_adaptor &&) noexcept=default
Default move constructor.
std::string get_outstanding_allocations_str() const
Gets a string containing the outstanding allocation pointers, their size, and optionally the stack tr...
Definition: tracking_resource_adaptor.hpp:163
std::map< void *, allocation_info > const & get_outstanding_allocations() const noexcept
Get the outstanding allocations map.
Definition: tracking_resource_adaptor.hpp:137
void log_outstanding_allocations() const
Log any outstanding allocations via RMM_LOG_DEBUG.
Definition: tracking_resource_adaptor.hpp:186
std::shared_lock< std::shared_mutex > read_lock_t
Type of lock used to synchronize read access.
Definition: tracking_resource_adaptor.hpp:58
tracking_resource_adaptor< Upstream > make_tracking_adaptor(Upstream *upstream)
Convenience factory to return a tracking_resource_adaptor around the upstream resource upstream.
Definition: tracking_resource_adaptor.hpp:298
cuda::mr::async_resource_ref< cuda::mr::device_accessible > device_async_resource_ref
Alias for a cuda::mr::async_resource_ref with the property cuda::mr::device_accessible.
Definition: resource_ref.hpp:41
device_async_resource_ref to_device_async_resource_ref_checked(Resource *res)
Convert pointer to memory resource into device_async_resource_ref, checking for nullptr
Definition: resource_ref.hpp:79
Management of per-device device_memory_resources.
Information stored about an allocation. Includes the size and a stack trace if the tracking_resource_...
Definition: tracking_resource_adaptor.hpp:67
std::unique_ptr< rmm::detail::stack_trace > strace
Stack trace of the allocation.
Definition: tracking_resource_adaptor.hpp:68
std::size_t allocation_size
Size of the allocation.
Definition: tracking_resource_adaptor.hpp:69
allocation_info(std::size_t size, bool capture_stack)
Construct a new allocation info object.
Definition: tracking_resource_adaptor.hpp:78