Panda3D
memoryHook.cxx
Go to the documentation of this file.
1/**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file memoryHook.cxx
10 * @author drose
11 * @date 2007-06-28
12 */
13
14#include "memoryHook.h"
15#include "deletedBufferChain.h"
16#include <stdlib.h>
17#include "typeRegistry.h"
18
19#ifdef WIN32
20
21// Windows case.
22#ifndef WIN32_LEAN_AND_MEAN
23#define WIN32_LEAN_AND_MEAN 1
24#endif
25#include <windows.h>
26
27#else
28
29// Posix case.
30#include <unistd.h>
31#include <sys/types.h>
32#include <sys/mman.h>
33
34#ifndef MAP_ANON
35#define MAP_ANON 0x1000
36#endif
37
38#endif // WIN32
39
40using std::cerr;
41
42// Ensure we made the right decisions about the alignment size.
43static_assert(MEMORY_HOOK_ALIGNMENT >= sizeof(size_t),
44 "MEMORY_HOOK_ALIGNMENT should at least be sizeof(size_t)");
45static_assert(MEMORY_HOOK_ALIGNMENT >= sizeof(void *),
46 "MEMORY_HOOK_ALIGNMENT should at least be sizeof(void *)");
47static_assert(MEMORY_HOOK_ALIGNMENT * 8 >= NATIVE_WORDSIZE,
48 "MEMORY_HOOK_ALIGNMENT * 8 should at least be NATIVE_WORDSIZE");
49static_assert((MEMORY_HOOK_ALIGNMENT & (MEMORY_HOOK_ALIGNMENT - 1)) == 0,
50 "MEMORY_HOOK_ALIGNMENT should be a power of two");
51
52#if defined(CPPPARSER)
53
54#elif defined(USE_MEMORY_DLMALLOC)
55
56// Memory manager: DLMALLOC This is Doug Lea's memory manager. It is very
57// fast, but it is not thread-safe. However, we provide thread locking within
58// MemoryHook.
59
60#define DLMALLOC_EXPORT static
61#define USE_DL_PREFIX 1
62#define NO_MALLINFO 1
63#ifdef _DEBUG
64 #define DEBUG 1
65#endif
66// dlmalloc can do the alignment we ask for.
67#define MALLOC_ALIGNMENT MEMORY_HOOK_ALIGNMENT
68
69#include "dlmalloc_src.cxx"
70
71#define call_malloc dlmalloc
72#define call_realloc dlrealloc
73#define call_free dlfree
74#define MEMORY_HOOK_MALLOC_LOCK 1
75
76#elif defined(USE_MEMORY_PTMALLOC2)
77// This doesn't appear to work in Linux; perhaps it is clashing with the
78// system library. It also doesn't appear to be thread-safe on OSX.
79
80/*
81 * Memory manager: PTMALLOC2 Ptmalloc2 is a derivative of Doug Lea's memory
82 * manager that was made thread-safe by Wolfram Gloger, then was ported to
83 * windows by Niall Douglas. It is not quite as fast as dlmalloc (because the
84 * thread-safety constructs take a certain amount of CPU time), but it's still
85 * much faster than the windows allocator.
86 */
87
88#define USE_DL_PREFIX 1
89#define NO_MALLINFO 1
90#ifdef _DEBUG
91 #define MALLOC_DEBUG 2
92#endif
93#include "ptmalloc2_smp_src.cxx"
94
95#define call_malloc dlmalloc
96#define call_realloc dlrealloc
97#define call_free dlfree
98#undef MEMORY_HOOK_MALLOC_LOCK
99
100#else
101
102// Memory manager: MALLOC This option uses the built-in system allocator.
103// This is a good choice on linux, but it's a terrible choice on windows.
104
105#define call_malloc malloc
106#define call_realloc realloc
107#define call_free free
108#undef MEMORY_HOOK_MALLOC_LOCK
109
110#endif // USE_MEMORY_*
111
112/**
113 * Increments the amount of requested size as necessary to accommodate the
114 * extra data we might piggyback on each allocated block.
115 */
116INLINE static size_t
117inflate_size(size_t size) {
118#if defined(MEMORY_HOOK_DO_ALIGN)
119 // If we're aligning, we need to request the header size, plus extra bytes
120 // to give us wiggle room to adjust the pointer.
121 return size + sizeof(uintptr_t) * 2 + MEMORY_HOOK_ALIGNMENT - 1;
122#elif defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2)
123 // If we are can access the allocator's bookkeeping to figure out how many
124 // bytes were allocated, we don't need to add our own information.
125 return size;
126#elif defined(DO_MEMORY_USAGE)
127 // If we're not aligning, but we're tracking memory allocations, we just
128 // need the header size extra (this gives us a place to store the size of
129 // the allocated block). However, we do need to make sure that any
130 // alignment guarantee is kept.
131 return size + MEMORY_HOOK_ALIGNMENT;
132#else
133 // If we're not doing any of that, we can just allocate the precise
134 // requested amount.
135 return size;
136#endif // DO_MEMORY_USAGE
137}
138
139/**
140 * Converts an allocated pointer to a pointer returnable to the application.
141 * Stuffs size in the first n bytes of the allocated space.
142 */
143INLINE static void *
144alloc_to_ptr(void *alloc, size_t size) {
145#if defined(MEMORY_HOOK_DO_ALIGN)
146 // Add room for two uintptr_t values.
147 uintptr_t *root = (uintptr_t *)((char *)alloc + sizeof(uintptr_t) * 2);
148 // Align this to the requested boundary.
149 root = (uintptr_t *)(((uintptr_t)root + MEMORY_HOOK_ALIGNMENT - 1) & ~(MEMORY_HOOK_ALIGNMENT - 1));
150 root[-2] = size;
151 root[-1] = (uintptr_t)alloc; // Save the pointer we originally allocated.
152 return (void *)root;
153#elif defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2)
154 return alloc;
155#elif defined(DO_MEMORY_USAGE)
156 size_t *root = (size_t *)alloc;
157 root[0] = size;
158 return (void *)((char *)root + MEMORY_HOOK_ALIGNMENT);
159#else
160 return alloc;
161#endif // DO_MEMORY_USAGE
162}
163
164/**
165 * Converts an application pointer back to the original allocated pointer.
166 * Extracts size from the first n bytes of the allocated space, but only if
167 * DO_MEMORY_USAGE is defined.
168 */
169INLINE static void *
170ptr_to_alloc(void *ptr, size_t &size) {
171#if defined(MEMORY_HOOK_DO_ALIGN)
172 uintptr_t *root = (uintptr_t *)ptr;
173 size = root[-2];
174 return (void *)root[-1]; // Get the pointer we originally allocated.
175#elif defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2)
176#ifdef DO_MEMORY_USAGE
177 size = MemoryHook::get_ptr_size(ptr);
178#endif
179 return ptr;
180#elif defined(DO_MEMORY_USAGE)
181 size_t *root = (size_t *)((char *)ptr - MEMORY_HOOK_ALIGNMENT);
182 size = root[0];
183 return (void *)root;
184#else
185 return ptr;
186#endif // DO_MEMORY_USAGE
187}
188
189/**
190 *
191 */
192MemoryHook::
193MemoryHook() {
194#ifdef WIN32
195
196 // Windows case.
197 SYSTEM_INFO sysinfo;
198 GetSystemInfo(&sysinfo);
199
200 _page_size = (size_t)sysinfo.dwPageSize;
201
202#else
203
204 // Posix case.
205 _page_size = sysconf(_SC_PAGESIZE);
206
207#endif // WIN32
208
209 _total_heap_single_size = 0;
210 _total_heap_array_size = 0;
211 _requested_heap_size = 0;
212 _total_mmap_size = 0;
213 _max_heap_size = ~(size_t)0;
214}
215
216/**
217 *
218 */
219MemoryHook::
220MemoryHook(const MemoryHook &copy) :
221 _total_heap_single_size(copy._total_heap_single_size),
222 _total_heap_array_size(copy._total_heap_array_size),
223 _requested_heap_size(copy._requested_heap_size),
224 _total_mmap_size(copy._total_mmap_size),
225 _max_heap_size(copy._max_heap_size),
226 _page_size(copy._page_size) {
227
228 copy._lock.lock();
229 _deleted_chains = copy._deleted_chains;
230 copy._lock.unlock();
231}
232
233/**
234 *
235 */
236MemoryHook::
237~MemoryHook() {
238 // Really, we only have this destructor to shut up gcc about the virtual
239 // functions warning.
240}
241
242/**
243 * Allocates a block of memory from the heap, similar to malloc(). This will
244 * never return NULL; it will abort instead if memory is not available.
245 *
246 * This particular function should be used to allocate memory for a single
247 * object, as opposed to an array. The only difference is in the bookkeeping.
248 */
250heap_alloc_single(size_t size) {
251 size_t inflated_size = inflate_size(size);
252
253#ifdef MEMORY_HOOK_MALLOC_LOCK
254 _lock.lock();
255 void *alloc = call_malloc(inflated_size);
256 _lock.unlock();
257#else
258 void *alloc = call_malloc(inflated_size);
259#endif
260
261 while (alloc == nullptr) {
262 alloc_fail(inflated_size);
263#ifdef MEMORY_HOOK_MALLOC_LOCK
264 _lock.lock();
265 alloc = call_malloc(inflated_size);
266 _lock.unlock();
267#else
268 alloc = call_malloc(inflated_size);
269#endif
270 }
271
272#ifdef DO_MEMORY_USAGE
273 // In the DO_MEMORY_USAGE case, we want to track the total size of allocated
274 // bytes on the heap.
275#if defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2)
276 // dlmalloc may slightly overallocate, however.
277 size = get_ptr_size(alloc);
278 inflated_size = size;
279#endif
280 AtomicAdjust::add(_total_heap_single_size, (AtomicAdjust::Integer)size);
281 if ((size_t)AtomicAdjust::get(_total_heap_single_size) +
282 (size_t)AtomicAdjust::get(_total_heap_array_size) >
283 _max_heap_size) {
284 overflow_heap_size();
285 }
286#endif // DO_MEMORY_USAGE
287
288 void *ptr = alloc_to_ptr(alloc, size);
289#ifdef _DEBUG
290 assert(((uintptr_t)ptr % MEMORY_HOOK_ALIGNMENT) == 0);
291 assert(ptr >= alloc && (char *)ptr + size <= (char *)alloc + inflated_size);
292#endif
293 return ptr;
294}
295
296/**
297 * Releases a block of memory previously allocated via heap_alloc_single.
298 */
300heap_free_single(void *ptr) {
301 size_t size;
302 void *alloc = ptr_to_alloc(ptr, size);
303
304#ifdef DO_MEMORY_USAGE
305 assert((int)size <= _total_heap_single_size);
306 AtomicAdjust::add(_total_heap_single_size, -(AtomicAdjust::Integer)size);
307#endif // DO_MEMORY_USAGE
308
309#ifdef MEMORY_HOOK_MALLOC_LOCK
310 _lock.lock();
311 call_free(alloc);
312 _lock.unlock();
313#else
314 call_free(alloc);
315#endif
316}
317
318/**
319 * Allocates a block of memory from the heap, similar to malloc(). This will
320 * never return NULL; it will abort instead if memory is not available.
321 *
322 * This particular function should be used to allocate memory for an array of
323 * objects, as opposed to a single object. The only difference is in the
324 * bookkeeping.
325 */
327heap_alloc_array(size_t size) {
328 size_t inflated_size = inflate_size(size);
329
330#ifdef MEMORY_HOOK_MALLOC_LOCK
331 _lock.lock();
332 void *alloc = call_malloc(inflated_size);
333 _lock.unlock();
334#else
335 void *alloc = call_malloc(inflated_size);
336#endif
337
338 while (alloc == nullptr) {
339 alloc_fail(inflated_size);
340#ifdef MEMORY_HOOK_MALLOC_LOCK
341 _lock.lock();
342 alloc = call_malloc(inflated_size);
343 _lock.unlock();
344#else
345 alloc = call_malloc(inflated_size);
346#endif
347 }
348
349#ifdef DO_MEMORY_USAGE
350 // In the DO_MEMORY_USAGE case, we want to track the total size of allocated
351 // bytes on the heap.
352#if defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2)
353 // dlmalloc may slightly overallocate, however.
354 size = get_ptr_size(alloc);
355 inflated_size = size;
356#endif
357 AtomicAdjust::add(_total_heap_array_size, (AtomicAdjust::Integer)size);
358 if ((size_t)AtomicAdjust::get(_total_heap_single_size) +
359 (size_t)AtomicAdjust::get(_total_heap_array_size) >
360 _max_heap_size) {
361 overflow_heap_size();
362 }
363#endif // DO_MEMORY_USAGE
364
365 void *ptr = alloc_to_ptr(alloc, size);
366#ifdef _DEBUG
367 assert(((uintptr_t)ptr % MEMORY_HOOK_ALIGNMENT) == 0);
368 assert(ptr >= alloc && (char *)ptr + size <= (char *)alloc + inflated_size);
369#endif
370 return ptr;
371}
372
373/**
374 * Resizes a block of memory previously returned from heap_alloc_array.
375 */
377heap_realloc_array(void *ptr, size_t size) {
378 size_t orig_size;
379 void *alloc = ptr_to_alloc(ptr, orig_size);
380
381 size_t inflated_size = inflate_size(size);
382
383 void *alloc1 = alloc;
384#ifdef MEMORY_HOOK_MALLOC_LOCK
385 _lock.lock();
386 alloc1 = call_realloc(alloc1, inflated_size);
387 _lock.unlock();
388#else
389 alloc1 = call_realloc(alloc1, inflated_size);
390#endif
391
392 while (alloc1 == nullptr) {
393 alloc_fail(inflated_size);
394
395 // Recover the original pointer.
396 alloc1 = alloc;
397
398#ifdef MEMORY_HOOK_MALLOC_LOCK
399 _lock.lock();
400 alloc1 = call_realloc(alloc1, inflated_size);
401 _lock.unlock();
402#else
403 alloc1 = call_realloc(alloc1, inflated_size);
404#endif
405 }
406
407#ifdef DO_MEMORY_USAGE
408#if defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2)
409 // dlmalloc may slightly overallocate, however.
410 size = get_ptr_size(alloc1);
411 inflated_size = size;
412#endif
413 assert((AtomicAdjust::Integer)orig_size <= _total_heap_array_size);
414 AtomicAdjust::add(_total_heap_array_size, (AtomicAdjust::Integer)size-(AtomicAdjust::Integer)orig_size);
415#endif // DO_MEMORY_USAGE
416
417 // Align this to the requested boundary.
418#ifdef MEMORY_HOOK_DO_ALIGN
419 // This copies the code from alloc_to_ptr, since we can't write the size and
420 // pointer until after we have done the memmove.
421 uintptr_t *root = (uintptr_t *)((char *)alloc1 + sizeof(uintptr_t) * 2);
422 root = (uintptr_t *)(((uintptr_t)root + MEMORY_HOOK_ALIGNMENT - 1) & ~(MEMORY_HOOK_ALIGNMENT - 1));
423 void *ptr1 = (void *)root;
424
425 size_t orig_delta = (char *)ptr - (char *)alloc;
426 size_t new_delta = (char *)ptr1 - (char *)alloc1;
427 if (orig_delta != new_delta) {
428 memmove((char *)alloc1 + new_delta, (char *)alloc1 + orig_delta, std::min(size, orig_size));
429 }
430
431 root[-2] = size;
432 root[-1] = (uintptr_t)alloc1; // Save the pointer we originally allocated.
433#else
434 void *ptr1 = alloc_to_ptr(alloc1, size);
435#endif
436
437#ifdef _DEBUG
438 assert(ptr1 >= alloc1 && (char *)ptr1 + size <= (char *)alloc1 + inflated_size);
439 assert(((uintptr_t)ptr1 % MEMORY_HOOK_ALIGNMENT) == 0);
440#endif
441 return ptr1;
442}
443
444/**
445 * Releases a block of memory previously allocated via heap_alloc_array.
446 */
448heap_free_array(void *ptr) {
449 size_t size;
450 void *alloc = ptr_to_alloc(ptr, size);
451
452#ifdef DO_MEMORY_USAGE
453 assert((int)size <= _total_heap_array_size);
454 AtomicAdjust::add(_total_heap_array_size, -(AtomicAdjust::Integer)size);
455#endif // DO_MEMORY_USAGE
456
457#ifdef MEMORY_HOOK_MALLOC_LOCK
458 _lock.lock();
459 call_free(alloc);
460 _lock.unlock();
461#else
462 call_free(alloc);
463#endif
464}
465
466/**
467 * Attempts to release memory back to the system, if possible. The pad
468 * argument is the minimum amount of unused memory to keep in the heap
469 * (against future allocations). Any memory above that may be released to the
470 * system, reducing the memory size of this process. There is no guarantee
471 * that any memory may be released.
472 *
473 * Returns true if any memory was actually released, false otherwise.
474 */
476heap_trim(size_t pad) {
477 bool trimmed = false;
478
479#if defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2)
480 // Since malloc_trim() isn't standard C, we can't be sure it exists on a
481 // given platform. But if we're using dlmalloc, we know we have
482 // dlmalloc_trim.
483 _lock.lock();
484 if (dlmalloc_trim(pad)) {
485 trimmed = true;
486 }
487 _lock.unlock();
488#endif
489
490#ifdef WIN32
491 // Also, on Windows we have _heapmin().
492 if (_heapmin() == 0) {
493 trimmed = true;
494 }
495#endif
496
497 return trimmed;
498}
499
500/**
501 * Allocates a raw page or pages of memory directly from the OS. This will be
502 * in a different address space from the memory allocated by heap_alloc(), and
503 * so it won't contribute to fragmentation of that memory.
504 *
505 * The allocation size must be an integer multiple of the page size. Use
506 * round_to_page_size() if there is any doubt.
507 *
508 * If allow_exec is true, the memory will be flagged so that it is legal to
509 * execute code that has been written to this memory.
510 */
512mmap_alloc(size_t size, bool allow_exec) {
513 assert((size % _page_size) == 0);
514
515#ifdef DO_MEMORY_USAGE
516 _total_mmap_size += size;
517#endif
518
519#ifdef WIN32
520
521 // Windows case.
522 void *ptr = VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE,
523 allow_exec ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE);
524 if (ptr == nullptr) {
525 DWORD err = GetLastError();
526 cerr << "Couldn't allocate memory page of size " << size << ": ";
527
528 PVOID buffer;
529 DWORD length =
530 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
531 nullptr, err, 0, (LPTSTR)&buffer, 0, nullptr);
532 if (length != 0) {
533 cerr << (char *)buffer << "\n";
534 } else {
535 cerr << "Error code " << err << "\n";
536 }
537 LocalFree(buffer);
538 abort();
539 }
540
541 return ptr;
542
543#else
544
545 // Posix case.
546 int prot = PROT_READ | PROT_WRITE;
547 if (allow_exec) {
548 prot |= PROT_EXEC;
549 }
550 void *ptr = mmap(nullptr, size, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
551 if (ptr == (void *)-1) {
552 perror("mmap");
553 abort();
554 }
555
556 return ptr;
557
558#endif // WIN32
559}
560
561/**
562 * Frees a block of memory previously allocated via mmap_alloc(). You must
563 * know how large the block was.
564 */
566mmap_free(void *ptr, size_t size) {
567 assert((size % _page_size) == 0);
568
569#ifdef DO_MEMORY_USAGE
570 assert((int)size <= _total_mmap_size);
571 _total_mmap_size -= size;
572#endif
573
574#ifdef WIN32
575 VirtualFree(ptr, 0, MEM_RELEASE);
576#else
577 munmap(ptr, size);
578#endif
579}
580
581/**
582 * This special method exists only to provide a callback hook into
583 * MemoryUsage. It indicates that the indicated pointer, allocated from
584 * somewhere other than a call to heap_alloc(), now contains a pointer to the
585 * indicated ReferenceCount object. If orig_size is 0, it indicates that the
586 * ReferenceCount object has been destroyed.
587 */
589mark_pointer(void *, size_t, ReferenceCount *) {
590}
591
592/**
593 * Returns a pointer to a global DeletedBufferChain object suitable for
594 * allocating arrays of the indicated size. There is one unique
595 * DeletedBufferChain object for every different size.
596 */
598get_deleted_chain(size_t buffer_size) {
599 DeletedBufferChain *chain;
600
601 _lock.lock();
602 DeletedChains::iterator dci = _deleted_chains.find(buffer_size);
603 if (dci != _deleted_chains.end()) {
604 chain = (*dci).second;
605 } else {
606 // Once allocated, this DeletedBufferChain object is never deleted.
607 chain = new DeletedBufferChain(buffer_size);
608 _deleted_chains.insert(DeletedChains::value_type(buffer_size, chain));
609 }
610
611 _lock.unlock();
612 return chain;
613}
614
615/**
616 * This callback method is called whenever a low-level call to call_malloc()
617 * has returned NULL, indicating failure.
618 *
619 * Since this method is called very low-level, and may be in the middle of any
620 * number of critical sections, it will be difficult for this callback
621 * initiate any emergency high-level operation to make more memory available.
622 * However, this module is set up to assume that that's what this method does,
623 * and will make another alloc attempt after it returns. Probably the only
624 * sensible thing this method can do, however, is just to display a message
625 * and abort.
626 */
628alloc_fail(size_t attempted_size) {
629 cerr << "Out of memory allocating " << attempted_size << " bytes\n";
630 abort();
631}
632
633/**
634 * This callback method is called whenever the total allocated heap size
635 * exceeds _max_heap_size. It's mainly intended for reporting memory leaks,
636 * on the assumption that once we cross some specified threshold, we're just
637 * leaking memory.
638 *
639 * The implementation for this method is in MemoryUsage.
640 */
641void MemoryHook::
642overflow_heap_size() {
643#ifdef DO_MEMORY_USAGE
644 _max_heap_size = ~(size_t)0;
645#endif // DO_MEMORY_USAGE
646}
static Integer add(Integer &var, Integer delta)
Atomically computes var += delta.
static Integer get(const Integer &var)
Atomically retrieves the snapshot value of the indicated variable.
This template class can be used to provide faster allocation/deallocation for many Panda objects.
This class provides a wrapper around the various possible malloc schemes Panda might employ.
Definition: memoryHook.h:37
virtual void * mmap_alloc(size_t size, bool allow_exec)
Allocates a raw page or pages of memory directly from the OS.
Definition: memoryHook.cxx:512
virtual void mmap_free(void *ptr, size_t size)
Frees a block of memory previously allocated via mmap_alloc().
Definition: memoryHook.cxx:566
virtual void alloc_fail(size_t attempted_size)
This callback method is called whenever a low-level call to call_malloc() has returned NULL,...
Definition: memoryHook.cxx:628
virtual void * heap_alloc_array(size_t size)
Allocates a block of memory from the heap, similar to malloc().
Definition: memoryHook.cxx:327
DeletedBufferChain * get_deleted_chain(size_t buffer_size)
Returns a pointer to a global DeletedBufferChain object suitable for allocating arrays of the indicat...
Definition: memoryHook.cxx:598
virtual void heap_free_array(void *ptr)
Releases a block of memory previously allocated via heap_alloc_array.
Definition: memoryHook.cxx:448
virtual void * heap_alloc_single(size_t size)
Allocates a block of memory from the heap, similar to malloc().
Definition: memoryHook.cxx:250
virtual void mark_pointer(void *ptr, size_t orig_size, ReferenceCount *ref_ptr)
This special method exists only to provide a callback hook into MemoryUsage.
Definition: memoryHook.cxx:589
virtual void heap_free_single(void *ptr)
Releases a block of memory previously allocated via heap_alloc_single.
Definition: memoryHook.cxx:300
static size_t get_ptr_size(void *ptr)
Given a pointer that was returned by a MemoryHook allocation, returns the number of bytes that were a...
Definition: memoryHook.I:67
bool heap_trim(size_t pad)
Attempts to release memory back to the system, if possible.
Definition: memoryHook.cxx:476
virtual void * heap_realloc_array(void *ptr, size_t size)
Resizes a block of memory previously returned from heap_alloc_array.
Definition: memoryHook.cxx:377
A base class for all things that want to be reference-counted.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.