• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tudasc / TypeART / 25558306477

08 May 2026 01:29PM UTC coverage: 89.71% (-0.5%) from 90.246%
25558306477

Pull #188

github

web-flow
Merge d41880251 into 278119205
Pull Request #188: GPU memory allocation support

230 of 298 new or added lines in 20 files covered. (77.18%)

1 existing line in 1 file now uncovered.

5100 of 5685 relevant lines covered (89.71%)

42434.95 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.81
/lib/runtime/AllocationTracking.cpp
1
// TypeART library
2
//
3
// Copyright (c) 2017-2026 TypeART Authors
4
// Distributed under the BSD 3-Clause license.
5
// (See accompanying file LICENSE.txt or copy at
6
// https://opensource.org/licenses/BSD-3-Clause)
7
//
8
// Project home: https://github.com/tudasc/TypeART
9
//
10
// SPDX-License-Identifier: BSD-3-Clause
11
//
12

13
#include "AllocationTracking.h"
14

15
#include "AccessCounter.h"
16
#include "CallbackInterface.h"
17
#include "Runtime.h"
18
#include "RuntimeData.h"
19
#include "TypeDB.h"
20
#include "support/Logger.h"
21

22
#include "llvm/Support/raw_ostream.h"
23

24
#include <algorithm>
25
#include <cassert>
26
#include <iterator>
27
#include <type_traits>
28
#include <vector>
29

30
#ifdef TYPEART_BTREE
31
using namespace btree;
32
#endif
33

34
#define likely(x)   __builtin_expect(!!(x), 1)
35
#define unlikely(x) __builtin_expect(!!(x), 0)
36

37
#define CONCAT_(x, y) x##y
38
#define CONCAT(x, y)  CONCAT_(x, y)
39
#define GUARDNAME     CONCAT(typeart_guard_, __LINE__)
40

41
#define TYPEART_RUNTIME_GUARD     \
42
  typeart::RTGuard GUARDNAME;     \
43
  if (!GUARDNAME.shouldTrack()) { \
44
    return;                       \
45
  }
46

47
namespace typeart {
48

49
namespace detail {
50
template <class...>
51
constexpr std::false_type always_false{};
52
}  // namespace detail
53

54
template <typename Enum>
55
inline Enum operator|(Enum lhs, Enum rhs) {
458,782✔
56
  if constexpr (std::is_enum_v<Enum> && (std::is_same_v<Enum, AllocState> || std::is_same_v<Enum, FreeState>)) {
57
    using enum_t = typename std::underlying_type<Enum>::type;
58
    return static_cast<Enum>(static_cast<enum_t>(lhs) | static_cast<enum_t>(rhs));
458,782✔
59
  } else {
60
    static_assert(detail::always_false<Enum>);
61
  }
62
}
63
template <typename Enum>
64
inline void operator|=(Enum& lhs, Enum rhs) {
15,505✔
65
  if constexpr (std::is_enum_v<Enum> && (std::is_same_v<Enum, AllocState> || std::is_same_v<Enum, FreeState>)) {
66
    lhs = lhs | rhs;
15,505✔
67
  } else {
68
    static_assert(detail::always_false<Enum>);
69
  }
70
}
15,505✔
71

72
template <typename Enum>
73
inline Enum operator&(Enum lhs, Enum rhs) {
74
  if constexpr (std::is_enum_v<Enum> && std::is_same_v<Enum, AllocState>) {
408✔
75
    using enum_t = typename std::underlying_type<Enum>::type;
76
    return static_cast<Enum>(static_cast<enum_t>(lhs) & static_cast<enum_t>(rhs));
77
  } else {
78
    static_assert(detail::always_false<Enum>);
79
  }
80
}
81

82
template <typename Enum>
83
inline typename std::underlying_type<Enum>::type operator==(Enum lhs, Enum rhs) {
84
  if constexpr (std::is_enum_v<Enum> && std::is_same_v<Enum, AllocState>) {
85
    using enum_t = typename std::underlying_type<Enum>::type;
86
    return static_cast<enum_t>(lhs) & static_cast<enum_t>(rhs);
87
  } else {
88
    static_assert(detail::always_false<Enum>);
89
  }
90
}
91

92
using namespace debug;
93

94
namespace {
95
struct ThreadData final {
1,266✔
96
  RuntimeT::Stack stackVars;
97

98
  ThreadData() {
1,398✔
99
    stackVars.reserve(RuntimeT::StackReserve);
1,398✔
100
  }
1,398✔
101
};
102

103
thread_local ThreadData threadData;
1,398✔
104

105
}  // namespace
106

107
AllocationTracker::AllocationTracker(const TypeDB& db, Recorder& recorder) : typeDB{db}, runtime_recorder{recorder} {
1,559✔
108
}
1,559✔
109

110
void AllocationTracker::onAlloc(const void* addr, int typeId, size_t count, const void* retAddr) {
308,843✔
111
  const auto status = doAlloc(addr, typeId, count, retAddr);
308,843✔
112
  if (status != AllocState::ADDR_SKIPPED) {
308,843✔
113
    runtime_recorder.incHeapAlloc(typeId, count);
308,900✔
114
  }
308,900✔
115
  LOG_TRACE("Alloc " << toString(addr, typeId, count, retAddr, true) << " " << 'H');
308,957✔
116
}
308,975✔
117

118
void AllocationTracker::onAllocStack(const void* addr, int typeId, size_t count, const void* retAddr) {
130,053✔
119
  const auto status = doAlloc(addr, typeId, count, retAddr);
130,053✔
120
  if (status != AllocState::ADDR_SKIPPED) {
130,053✔
121
    threadData.stackVars.push_back(addr);
130,055✔
122
    runtime_recorder.incStackAlloc(typeId, count);
130,055✔
123
  }
130,055✔
124
  LOG_TRACE("Alloc " << toString(addr, typeId, count, retAddr) << " " << 'S');
130,057✔
125
}
130,057✔
126

127
void AllocationTracker::onAllocGlobal(const void* addr, int typeId, size_t count, const void* retAddr) {
4,209✔
128
  const auto status = doAlloc(addr, typeId, count, retAddr);
4,209✔
129
  if (status != AllocState::ADDR_SKIPPED) {
4,209✔
130
    runtime_recorder.incGlobalAlloc(typeId, count);
4,209✔
131
  }
4,209✔
132
  LOG_TRACE("Alloc " << toString(addr, typeId, count, retAddr) << " " << 'G');
4,209✔
133
}
4,209✔
134

135
AllocState AllocationTracker::doAlloc(const void* addr, int typeId, size_t count, const void* retAddr) {
443,151✔
136
  AllocState status = AllocState::NO_INIT;
443,151✔
137
  if (unlikely(!typeDB.isValid(typeId))) {
443,151✔
138
    status |= AllocState::UNKNOWN_ID;
52✔
139
    LOG_ERROR("Allocation of unknown type " << toString(addr, typeId, count, retAddr));
52✔
140
  }
52✔
141

142
  // Calling malloc with size 0 may return a nullptr or some address that can not be written to.
143
  // In the second case, the allocation is tracked anyway so that onFree() does not report an error.
144
  // On the other hand, an allocation on address 0x0 with size > 0 is an actual error.
145
  if (unlikely(count == 0)) {
443,151✔
146
    runtime_recorder.incZeroLengthAddr();
69✔
147
    status |= AllocState::ZERO_COUNT;
69✔
148
    LOG_WARNING("Zero-size allocation " << toString(addr, typeId, count, retAddr));
69✔
149
    if (addr == nullptr) {
69✔
150
      runtime_recorder.incZeroLengthAndNullAddr();
23✔
151
      LOG_ERROR("Zero-size and nullptr allocation " << toString(addr, typeId, count, retAddr));
23✔
152
      return status | AllocState::NULL_ZERO | AllocState::ADDR_SKIPPED;
23✔
153
    }
154
  } else if (unlikely(addr == nullptr)) {
443,128✔
155
    runtime_recorder.incNullAddr();
23✔
156
    LOG_ERROR("Nullptr allocation " << toString(addr, typeId, count, retAddr));
23✔
157
    return status | AllocState::NULL_PTR | AllocState::ADDR_SKIPPED;
23✔
158
  }
159

160
  const auto overridden = wrapper.put(addr, PointerInfo{typeId, count, retAddr});
443,105✔
161

162
  if (unlikely(overridden)) {
443,105✔
163
    runtime_recorder.incAddrReuse();
15,388✔
164
    status |= AllocState::ADDR_REUSE;
15,388✔
165
    LOG_WARNING("Pointer already in map " << toString(addr, typeId, count, retAddr));
15,388✔
166
    // LOG_WARNING("Overridden data in map " << toString(addr, def));
167
  }
15,382✔
168

169
  return status | AllocState::OK;
443,099✔
170
}
443,155✔
171

172
FreeState AllocationTracker::doFreeHeap(const void* addr, const void* retAddr) {
297,965✔
173
  if (unlikely(addr == nullptr)) {
297,965✔
174
    LOG_ERROR("Free on nullptr " << "(" << retAddr << ")");
23✔
175
    return FreeState::ADDR_SKIPPED | FreeState::NULL_PTR;
23✔
176
  }
177

178
  std::optional<PointerInfo> removed = wrapper.remove(addr);
297,942✔
179

180
  if (unlikely(!removed)) {
297,942✔
181
    LOG_ERROR("Free on unregistered address " << addr << " (" << retAddr << ")");
46✔
182
    return FreeState::ADDR_SKIPPED | FreeState::UNREG_ADDR;
46✔
183
  }
184

185
  LOG_TRACE("Free " << toString(addr, *removed));
297,896✔
186
  if constexpr (!std::is_same_v<Recorder, softcounter::NoneRecorder>) {
187
    runtime_recorder.incHeapFree(removed->typeId, removed->count);
297,866✔
188
  }
189
  return FreeState::OK;
297,866✔
190
}
297,967✔
191

192
void AllocationTracker::onFreeHeap(const void* addr, const void* retAddr) {
297,947✔
193
  const auto status = doFreeHeap(addr, retAddr);
297,947✔
194
  if (FreeState::OK == status) {
297,947✔
195
    runtime_recorder.decHeapAlloc();
297,881✔
196
  }
297,881✔
197
}
297,947✔
198

199
void AllocationTracker::onLeaveScope(int alloca_count, const void* retAddr) {
35,556✔
200
  if (unlikely(alloca_count > static_cast<int>(threadData.stackVars.size()))) {
35,556✔
201
    LOG_ERROR("Stack is smaller than requested de-allocation count. alloca_count: " << alloca_count << ". size: "
108✔
202
                                                                                    << threadData.stackVars.size());
16✔
203
    alloca_count = threadData.stackVars.size();
92✔
204
  }
92✔
205

206
  const auto cend      = threadData.stackVars.cend();
35,556✔
207
  const auto start_pos = (cend - alloca_count);
35,556✔
208
  LOG_TRACE("Freeing stack (" << alloca_count << ")  " << std::distance(start_pos, threadData.stackVars.cend()))
35,556✔
209

210
  wrapper.remove_range(start_pos, cend, [&](std::optional<PointerInfo>& removed, MemAddr addr) {
165,605✔
211
    if (unlikely(!removed)) {
130,055✔
212
      LOG_ERROR("Free on unregistered address " << addr << " (" << retAddr << ")");
9,384✔
213
    } else {
9,384✔
214
      LOG_TRACE("Free " << toString(addr, *removed));
120,671✔
215
      if constexpr (!std::is_same_v<Recorder, softcounter::NoneRecorder>) {
216
        runtime_recorder.incStackFree(removed->typeId, removed->count);
120,671✔
217
      }
218
    }
219
  });
130,055✔
220

221
  threadData.stackVars.erase(start_pos, cend);
35,550✔
222
  runtime_recorder.decStackAlloc(alloca_count);
35,550✔
223
  LOG_TRACE("Stack after free: " << threadData.stackVars.size());
35,550✔
224
}
35,574✔
225
// Base address
226
std::optional<RuntimeT::MapEntry> AllocationTracker::findBaseAlloc(const void* addr) {
16,999✔
227
  return wrapper.find(addr);
16,999✔
228
}
229

230
}  // namespace typeart
231

232
void __typeart_alloc(const void* addr, int typeId, size_t count) {
305,165✔
233
  TYPEART_RUNTIME_GUARD;
305,165✔
234
  const void* retAddr = __builtin_return_address(0);
305,165✔
235
  typeart::RuntimeSystem::get().allocation_tracker().onAlloc(addr, typeId, count, retAddr);
305,165✔
236
}
305,255✔
237

238
void __typeart_alloc_stack(const void* addr, int typeId, size_t count) {
89,049✔
239
  TYPEART_RUNTIME_GUARD;
89,049✔
240
  const void* retAddr = __builtin_return_address(0);
89,049✔
241
  typeart::RuntimeSystem::get().allocation_tracker().onAllocStack(addr, typeId, count, retAddr);
89,049✔
242
}
89,053✔
243

244
void __typeart_alloc_global(const void* addr, int typeId, size_t count) {
4,185✔
245
  TYPEART_RUNTIME_GUARD;
4,185✔
246
  const void* retAddr = __builtin_return_address(0);
4,185✔
247
  typeart::RuntimeSystem::get().allocation_tracker().onAllocGlobal(addr, typeId, count, retAddr);
4,185✔
248
}
4,185✔
249

250
void __typeart_free(const void* addr) {
294,760✔
251
  TYPEART_RUNTIME_GUARD;
294,760✔
252
  const void* retAddr = __builtin_return_address(0);
294,760✔
253
  typeart::RuntimeSystem::get().allocation_tracker().onFreeHeap(addr, retAddr);
294,760✔
254
}
294,788✔
255

256
void __typeart_leave_scope(int alloca_count) {
35,508✔
257
  TYPEART_RUNTIME_GUARD;
35,508✔
258
  const void* retAddr = __builtin_return_address(0);
35,508✔
259
  typeart::RuntimeSystem::get().allocation_tracker().onLeaveScope(alloca_count, retAddr);
35,508✔
260
}
35,508✔
261

262
void __typeart_alloc_omp(const void* addr, int typeId, size_t count) {
3,204✔
263
  TYPEART_RUNTIME_GUARD;
3,204✔
264
  const void* retAddr = __builtin_return_address(0);
3,204✔
265
  auto& rt            = typeart::RuntimeSystem::get();
3,204✔
266
  rt.allocation_tracker().onAlloc(addr, typeId, count, retAddr);
3,200✔
267
  rt.recorder.incOmpContextHeap();
3,200✔
268
}
3,208✔
269

270
void __typeart_alloc_stack_omp(const void* addr, int typeId, size_t count) {
312✔
271
  TYPEART_RUNTIME_GUARD;
312✔
272
  const void* retAddr = __builtin_return_address(0);
312✔
273
  auto& rt            = typeart::RuntimeSystem::get();
312✔
274
  rt.allocation_tracker().onAllocStack(addr, typeId, count, retAddr);
312✔
275
  rt.recorder.incOmpContextStack();
312✔
276
}
312✔
277

278
void __typeart_free_omp(const void* addr) {
3,200✔
279
  TYPEART_RUNTIME_GUARD;
3,200✔
280
  const void* retAddr = __builtin_return_address(0);
3,200✔
281
  auto& rt            = typeart::RuntimeSystem::get();
3,200✔
282
  rt.allocation_tracker().onFreeHeap(addr, retAddr);
3,198✔
283
  rt.recorder.incOmpContextFree();
3,200✔
284
}
3,204✔
285

286
void __typeart_leave_scope_omp(int alloca_count) {
48✔
287
  TYPEART_RUNTIME_GUARD;
48✔
288
  const void* retAddr = __builtin_return_address(0);
48✔
289
  typeart::RuntimeSystem::get().allocation_tracker().onLeaveScope(alloca_count, retAddr);
48✔
290
}
48✔
291

NEW
292
void __typeart_alloc_gpu(const void* addr, int typeId, size_t count) {
×
NEW
293
  TYPEART_RUNTIME_GUARD;
×
NEW
294
  const void* retAddr = __builtin_return_address(0);
×
NEW
295
  typeart::RuntimeSystem::get().allocation_tracker().onAlloc(addr, typeId, count, retAddr);
×
NEW
296
}
×
297

NEW
298
void __typeart_free_gpu(const void* addr) {
×
NEW
299
  TYPEART_RUNTIME_GUARD;
×
NEW
300
  const void* retAddr = __builtin_return_address(0);
×
NEW
301
  typeart::RuntimeSystem::get().allocation_tracker().onFreeHeap(addr, retAddr);
×
NEW
302
}
×
303

304
void __typeart_alloc_mty(const void* addr, const void* info, size_t count) {
573✔
305
  TYPEART_RUNTIME_GUARD;
573✔
306
  const void* retAddr = __builtin_return_address(0);
573✔
307
  const auto type_id  = reinterpret_cast<const typeart::global_types::GlobalTypeInfo*>(info)->type_id;
573✔
308
  auto& rt            = typeart::RuntimeSystem::get();
573✔
309
  assert(type_id == rt.type_translator().get_type_id_for(info) && "Type ID of global and lookup must match");
1,146✔
310
  rt.allocation_tracker().onAlloc(addr, type_id, count, retAddr);
573✔
311
}
573✔
312

NEW
313
void __typeart_alloc_mty_gpu(const void* addr, const void* info, size_t count) {
×
NEW
314
  TYPEART_RUNTIME_GUARD;
×
NEW
315
  const void* retAddr = __builtin_return_address(0);
×
NEW
316
  const auto type_id  = reinterpret_cast<const typeart::global_types::GlobalTypeInfo*>(info)->type_id;
×
NEW
317
  auto& rt            = typeart::RuntimeSystem::get();
×
NEW
318
  assert(type_id == rt.type_translator().get_type_id_for(info) && "Type ID of global and lookup must match");
×
NEW
319
  rt.allocation_tracker().onAlloc(addr, type_id, count, retAddr);
×
NEW
320
}
×
321

322
void __typeart_alloc_stack_mty(const void* addr, const void* info, size_t count) {
40,698✔
323
  TYPEART_RUNTIME_GUARD;
40,698✔
324
  const void* retAddr = __builtin_return_address(0);
40,698✔
325
  const auto type_id  = reinterpret_cast<const typeart::global_types::GlobalTypeInfo*>(info)->type_id;
40,698✔
326
  auto& rt            = typeart::RuntimeSystem::get();
40,698✔
327
  assert(type_id == rt.type_translator().get_type_id_for(info) && "Type ID of global and lookup must match");
81,394✔
328
  rt.allocation_tracker().onAllocStack(addr, type_id, count, retAddr);
40,697✔
329
}
40,698✔
330

331
void __typeart_alloc_global_mty(const void* addr, const void* info, size_t count) {
24✔
332
  TYPEART_RUNTIME_GUARD;
24✔
333
  const void* retAddr = __builtin_return_address(0);
24✔
334
  const auto type_id  = reinterpret_cast<const typeart::global_types::GlobalTypeInfo*>(info)->type_id;
24✔
335
  auto& rt            = typeart::RuntimeSystem::get();
24✔
336
  assert(type_id == rt.type_translator().get_type_id_for(info) && "Type ID of global and lookup must match");
48✔
337
  rt.allocation_tracker().onAllocGlobal(addr, type_id, count, retAddr);
24✔
338
}
24✔
339

340
void __typeart_alloc_global_mty_omp(const void* addr, const void* info, size_t count) {
×
341
  TYPEART_RUNTIME_GUARD;
×
342
  const void* retAddr = __builtin_return_address(0);
×
343
  const auto type_id  = reinterpret_cast<const typeart::global_types::GlobalTypeInfo*>(info)->type_id;
×
344
  auto& rt            = typeart::RuntimeSystem::get();
×
345
  assert(type_id == rt.type_translator().get_type_id_for(info) && "Type ID of global and lookup must match");
×
346
  rt.allocation_tracker().onAlloc(addr, type_id, count, retAddr);
×
347
}
×
348

349
void __typeart_alloc_stack_mty_omp(const void* addr, const void* info, size_t count) {
×
350
  TYPEART_RUNTIME_GUARD;
×
351
  const void* retAddr = __builtin_return_address(0);
×
352
  const auto type_id  = reinterpret_cast<const typeart::global_types::GlobalTypeInfo*>(info)->type_id;
×
353
  auto& rt            = typeart::RuntimeSystem::get();
×
354
  assert(type_id == rt.type_translator().get_type_id_for(info) && "Type ID of global and lookup must match");
×
355
  rt.allocation_tracker().onAllocStack(addr, type_id, count, retAddr);
×
356
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc