Vuo 2.4.4
Loading...
Searching...
No Matches
VuoHeap.cc
Go to the documentation of this file.
1
10#include "VuoHeap.h"
11#include <dispatch/dispatch.h>
12#include <dlfcn.h>
13#include <sys/errno.h>
14#include <sys/mman.h>
15#include <map>
16#include <set>
17#include <sstream>
18#include <iomanip>
19using namespace std;
20#include "VuoLog.h"
21#include "VuoRuntime.h"
22
23static set<const void *> *VuoHeap_trace;
24static pthread_mutex_t VuoHeap_mutex = PTHREAD_MUTEX_INITIALIZER;
25
29void sendErrorWrapper(const char *message)
30{
31#ifdef VUOHEAP_TRACE
32 fprintf(stderr, "%s\n", message);
34#endif
35
36 VuoSendErrorType *vuoSendError = (VuoSendErrorType *) dlsym(RTLD_SELF, "vuoSendError"); // for running composition in separate process as executable or in current process
37 if (! vuoSendError)
38 vuoSendError = (VuoSendErrorType *) dlsym(RTLD_DEFAULT, "vuoSendError"); // for running composition in separate process as dynamic libraries
39
40 pthread_key_t *vuoCompositionStateKey = (pthread_key_t *) dlsym(RTLD_SELF, "vuoCompositionStateKey");
42 vuoCompositionStateKey = (pthread_key_t *) dlsym(RTLD_DEFAULT, "vuoCompositionStateKey");
43
44 void *compositionState = NULL;
46 compositionState = pthread_getspecific(*vuoCompositionStateKey);
47
48 if (vuoSendError && compositionState)
49 vuoSendError((VuoCompositionState *)compositionState, message);
50 else
51 VUserLog("%s", message);
52}
53
54#ifdef VUOHEAP_TRACEALL
58static bool VuoHeap_isComposition(void)
59{
60 static bool isComposition = false;
61 static dispatch_once_t once = 0;
62 dispatch_once(&once, ^{
63 isComposition = dlsym(RTLD_DEFAULT, "vuoCompositionStateKey");
64 });
65 return isComposition;
66}
67#endif
68
72typedef struct
73{
74 int referenceCount;
75 DeallocateFunctionType deallocateFunction;
76
77 const char *file;
78 unsigned int line;
79 const char *function;
80 const char *variable;
81#ifdef VUOHEAP_TRACE
82 vector<string> backtrace;
83#endif
85
86static map<const void *, VuoHeapEntry> *referenceCounts;
87static set<const void *> *singletons;
88
94static inline bool VuoHeap_isPointerValid(const void *pointer)
95{
96 // On macOS, memory allocated by `malloc` is 16-byte aligned,
97 // so any non-16-byte-aligned pointers are suspicious.
98 // https://opensource.apple.com/source/Libc/Libc-825.26/gen/malloc.3.auto.html says
99 // "The allocated memory is aligned such that it can be used for any data type,
100 // including AltiVec- and SSE-related types."
101 // And https://software.intel.com/en-us/cpp-compiler-developer-guide-and-reference-alignment-support says
102 // "When using the IntelĀ® Streaming SIMD Extensions (IntelĀ® SSE) intrinsics,
103 // you should align data to 16 bytes in memory operations."
104 if ((unsigned long)pointer & 0xf)
105 return false;
106
107 // On macOS, `malloc` shouldn't give us any pointers below 4 GB.
108 // https://opensource.apple.com/source/ld64/ld64-242/doc/man/man1/ld.1.auto.html says
109 // "By default the linker creates an unreadable segment starting at address zero named __PAGEZERO.
110 // On 64-bit architectures, the default size is 4GB."
111 if ((unsigned long)pointer < 0x100000000)
112 return false;
113
114 // x86_64 is currently limited to 48 bits (256 TB) of virtual address space.
115 // http://support.amd.com/TechDocs/24593.pdf, page 131, says
116 // "Bits 63:48 are a sign extension of bit 47".
117 // This will no longer be the case if/when chips with 5-level paging are produced.
118 if ((unsigned long)pointer > 0xffffffffffff)
119 return false;
120
121 return true;
122}
123
130bool VuoHeap_isPointerReadable(const void *pointer)
131{
132 // Round down to the beginning of `pointer`'s heap page.
133 // Assume getpagesize() returns a power-of-two; subtracting 1 turns it into a bitmask.
134 int pageSize = getpagesize();
135 long heapPageMask = ~((long)pageSize-1);
136 long heapPage = (long)pointer & heapPageMask;
137
138 // Try to load the page into core.
139 mlock((void *)heapPage, pageSize);
140 munlock((void *)heapPage, pageSize);
141
142 // Check whether the page was successfully loaded into core.
143 char pageStatus[1];
144 if (mincore((void *)heapPage, pageSize, pageStatus) == 0)
145 return pageStatus[0] & MINCORE_INCORE;
146
147 else
148 return false;
149}
150
154static void VuoHeap_makeSafePointerSummary(char *summary, const void *pointer)
155{
156 if (VuoHeap_isPointerReadable(pointer)
157 && VuoHeap_isPointerReadable((char *)pointer + 15))
158 {
159 // Page(s) are valid, so output the pointer's first 16 printable characters.
160 char *pointerAsChar = (char *)pointer;
161 for (int i = 0; i < 16; ++i)
162 if (isprint(pointerAsChar[i]))
163 summary[i] = pointerAsChar[i];
164 else
165 summary[i] = '_';
166 summary[16] = 0;
167 }
168 else
169 strlcpy(summary, "(not allocated)", 17);
170}
171
180{
181#ifdef VUOHEAP_TRACE
182 ostringstream oss;
183 int i = 1;
184 for (auto line : e.backtrace)
185 {
186 char *lineNumber;
187 asprintf(&lineNumber, "%3d ", i++);
188 oss << lineNumber << line << endl;
189 free(lineNumber);
190 }
191
192 const char *format = "%s:%d :: %s() :: %s\n%s";
193 int size = snprintf(NULL, 0, format, e.file, e.line, e.function, e.variable, oss.str().c_str());
194 char *description = (char *)malloc(size+1);
195 snprintf(description, size+1, format, e.file, e.line, e.function, e.variable, oss.str().c_str());
196#else
197 const char *format = "%s:%d :: %s() :: %s";
198 int size = snprintf(NULL, 0, format, e.file, e.line, e.function, e.variable);
199 char *description = (char *)malloc(size+1);
200 snprintf(description, size+1, format, e.file, e.line, e.function, e.variable);
201#endif
202 return description;
203}
204
210static void __attribute__((constructor(101))) VuoHeap_init()
211{
212 referenceCounts = new map<const void *, VuoHeapEntry>;
213 singletons = new set<const void *>;
214 VuoHeap_trace = new set<const void *>;
215
216#if 0
217 // Periodically dump the referenceCounts table, to help find leaks.
218 const double dumpInterval = 5.0; // seconds
219 dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, DISPATCH_TIMER_STRICT, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
220 dispatch_source_set_timer(timer, dispatch_walltime(NULL,0), NSEC_PER_SEC*dumpInterval, NSEC_PER_SEC*dumpInterval);
221 dispatch_source_set_event_handler(timer, ^{
222 fprintf(stderr, "\n\n\n\n\nreferenceCounts:\n");
223 pthread_mutex_lock(&VuoHeap_mutex);
224 for (map<const void *, VuoHeapEntry>::iterator i = referenceCounts->begin(); i != referenceCounts->end(); ++i)
225 {
226 const void *heapPointer = i->first;
227 char pointerSummary[17];
228 VuoHeap_makeSafePointerSummary(pointerSummary, heapPointer);
229 char *description = VuoHeap_makeDescription(i->second);
230 fprintf(stderr, "\t% 3d refs to %p \"%s\", registered at %s\n", i->second.referenceCount, heapPointer, pointerSummary, description);
231 free(description);
232 }
233 pthread_mutex_unlock(&VuoHeap_mutex);
234 });
235 dispatch_resume(timer);
236#endif
237}
238
243{
244 pthread_mutex_lock(&VuoHeap_mutex);
245
246 if (! referenceCounts->empty())
247 {
248 ostringstream errorMessage;
249 errorMessage << "On reference table " << referenceCounts
250 << ", VuoRelease was not called enough times for:" << endl;
251 for (map<const void *, VuoHeapEntry>::iterator i = referenceCounts->begin(); i != referenceCounts->end(); ++i)
252 {
253 const void *heapPointer = i->first;
254 char pointerSummary[17];
255 VuoHeap_makeSafePointerSummary(pointerSummary, heapPointer);
256 char *description = VuoHeap_makeDescription(i->second);
257 errorMessage << "\t" << setw(3) << i->second.referenceCount << " refs to " << heapPointer << " \"" << pointerSummary << "\", registered at " << description << endl;
258 free(description);
259 }
260 sendErrorWrapper(errorMessage.str().c_str());
261 }
262
263 pthread_mutex_unlock(&VuoHeap_mutex);
264}
265
281int VuoRegisterF(const void *heapPointer, DeallocateFunctionType deallocate, const char *file, unsigned int linenumber, const char *func, const char *pointerName)
282{
283 if (! heapPointer)
284 return -1;
285
286 if (!VuoHeap_isPointerValid(heapPointer))
287 {
288 ostringstream errorMessage;
289 char *description = VuoHeap_makeDescription((VuoHeapEntry){0, NULL, file, linenumber, func, pointerName,
290#ifdef VUOHEAP_TRACE
292#endif
293 });
294 errorMessage << "On reference table " << referenceCounts
295 << ", VuoRegister was called for bogus pointer " << heapPointer
296 << " " << description;
297 free(description);
298 sendErrorWrapper(errorMessage.str().c_str());
299 }
300
301 bool isAlreadyReferenceCounted;
302 int updatedCount;
303
304 pthread_mutex_lock(&VuoHeap_mutex);
305 {
306#ifdef VUOHEAP_TRACE
307#ifdef VUOHEAP_TRACEALL
308 if (VuoHeap_isComposition())
309#else
310 if (VuoHeap_trace->find(heapPointer) != VuoHeap_trace->end())
311#endif
312 {
313 fprintf(stderr, "table=%p VuoRegister(%p) %s\n", referenceCounts, heapPointer, pointerName);
315 }
316#endif
317
318 isAlreadyReferenceCounted = referenceCounts->count(heapPointer);
319 if (! isAlreadyReferenceCounted)
320 {
321 updatedCount = 0;
322 (*referenceCounts)[heapPointer] = (VuoHeapEntry){updatedCount, deallocate, file, linenumber, func, pointerName,
323#ifdef VUOHEAP_TRACE
325#endif
326 };
327 }
328 else
329 updatedCount = (*referenceCounts)[heapPointer].referenceCount;
330 }
331 pthread_mutex_unlock(&VuoHeap_mutex);
332
333 if (isAlreadyReferenceCounted)
334 {
335 ostringstream errorMessage;
336 char *description = VuoHeap_makeDescription((VuoHeapEntry){0, NULL, file, linenumber, func, pointerName,
337#ifdef VUOHEAP_TRACE
339#endif
340 });
341 errorMessage << "On reference table " << referenceCounts
342 << ", VuoRegister was called more than once for " << heapPointer
343 << " " << description;
344 free(description);
345 sendErrorWrapper(errorMessage.str().c_str());
346 }
347
348 return updatedCount;
349}
350
355int VuoRegisterSingletonF(const void *heapPointer, const char *file, unsigned int linenumber, const char *func, const char *pointerName)
356{
357 if (! heapPointer)
358 return -1;
359
360 if (!VuoHeap_isPointerValid(heapPointer))
361 {
362 ostringstream errorMessage;
363 char *description = VuoHeap_makeDescription((VuoHeapEntry){0, NULL, file, linenumber, func, pointerName,
364#ifdef VUOHEAP_TRACE
366#endif
367 });
368 errorMessage << "On reference table " << referenceCounts
369 << ", VuoRegisterSingleton was called for bogus pointer " << heapPointer
370 << " " << description;
371 free(description);
372 sendErrorWrapper(errorMessage.str().c_str());
373 }
374
375 bool isAlreadyReferenceCounted;
376
377 pthread_mutex_lock(&VuoHeap_mutex);
378 {
379#ifdef VUOHEAP_TRACE
380#ifdef VUOHEAP_TRACEALL
381 if (VuoHeap_isComposition())
382#else
383 if (VuoHeap_trace->find(heapPointer) != VuoHeap_trace->end())
384#endif
385 {
386 fprintf(stderr, "table=%p VuoRegisterSingleton(%p) %s\n", referenceCounts, heapPointer, pointerName);
388 }
389#endif
390
391 // Remove the singleton from the main reference-counting table, if it exists there.
392 // Enables reclassifying a pointer that was already VuoRegister()ed.
393 referenceCounts->erase(heapPointer);
394
395 // Add the singleton to the singleton table.
396 isAlreadyReferenceCounted = (singletons->find(heapPointer) != singletons->end());
397 if (! isAlreadyReferenceCounted)
398 singletons->insert(heapPointer);
399 }
400 pthread_mutex_unlock(&VuoHeap_mutex);
401
402 if (isAlreadyReferenceCounted)
403 {
404 ostringstream errorMessage;
405 char *description = VuoHeap_makeDescription((VuoHeapEntry){0, NULL, file, linenumber, func, pointerName,
406#ifdef VUOHEAP_TRACE
408#endif
409 });
410 errorMessage << "On reference table " << referenceCounts
411 << ", VuoRegisterSingleton was called more than once for " << heapPointer
412 << " " << description;
413 free(description);
414 sendErrorWrapper(errorMessage.str().c_str());
415 }
416
417 return isAlreadyReferenceCounted ? 1 : 0;
418}
419
428extern "C" int VuoRetainF(const void *heapPointer, const char *file, unsigned int linenumber, const char *func)
429{
430 return VuoRetain(heapPointer);
431}
432
440int VuoRetain(const void *heapPointer)
441{
442 if (! heapPointer)
443 return -1;
444
445 if (!VuoHeap_isPointerValid(heapPointer))
446 {
447 char pointerSummary[17];
448 VuoHeap_makeSafePointerSummary(pointerSummary, heapPointer);
449 ostringstream errorMessage;
450 errorMessage << "On reference table " << referenceCounts
451 << ", VuoRetain was called for bogus pointer " << heapPointer
452 << " \"" << pointerSummary << "\"";
453 sendErrorWrapper(errorMessage.str().c_str());
454 }
455
456 int updatedCount = -1;
457 bool foundSingleton = false;
458
459 pthread_mutex_lock(&VuoHeap_mutex);
460 {
461 map<const void *, VuoHeapEntry>::iterator i = referenceCounts->find(heapPointer);
462
463#ifdef VUOHEAP_TRACE
464#ifdef VUOHEAP_TRACEALL
465 if (VuoHeap_isComposition())
466#else
467 if (VuoHeap_trace->find(heapPointer) != VuoHeap_trace->end())
468#endif
469 {
470 fprintf(stderr, "table=%p VuoRetain(%p) %s\n", referenceCounts, heapPointer, (i != referenceCounts->end()) ? i->second.variable : "");
472 }
473#endif
474
475 if (i != referenceCounts->end())
476 updatedCount = ++(i->second.referenceCount);
477 else
478 foundSingleton = singletons->find(heapPointer) != singletons->end();
479 }
480 pthread_mutex_unlock(&VuoHeap_mutex);
481
482 if (updatedCount == -1 && !foundSingleton)
483 {
484 char pointerSummary[17];
485 VuoHeap_makeSafePointerSummary(pointerSummary, heapPointer);
486 ostringstream errorMessage;
487 errorMessage << "On reference table " << referenceCounts
488 << ", VuoRetain was called for unregistered pointer " << heapPointer
489 << " \"" << pointerSummary << "\"";
490 sendErrorWrapper(errorMessage.str().c_str());
491 }
492
493 return updatedCount;
494}
495
504extern "C" int VuoReleaseF(const void *heapPointer, const char *file, unsigned int linenumber, const char *func)
505{
506 return VuoRelease(heapPointer);
507}
508
517int VuoRelease(const void *heapPointer)
518{
519 if (! heapPointer)
520 return -1;
521
522 if (!VuoHeap_isPointerValid(heapPointer))
523 {
524 char pointerSummary[17];
525 VuoHeap_makeSafePointerSummary(pointerSummary, heapPointer);
526 ostringstream errorMessage;
527 errorMessage << "On reference table " << referenceCounts
528 << ", VuoRelease was called for bogus pointer " << heapPointer
529 << " \"" << pointerSummary << "\"";
530 sendErrorWrapper(errorMessage.str().c_str());
531 }
532
533 int updatedCount = -1;
534 bool foundSingleton = false;
535 bool isRegisteredWithoutRetain = false;
536 DeallocateFunctionType deallocate = NULL;
537
538 pthread_mutex_lock(&VuoHeap_mutex);
539 {
540 map<const void *, VuoHeapEntry>::iterator i = referenceCounts->find(heapPointer);
541
542#ifdef VUOHEAP_TRACE
543#ifdef VUOHEAP_TRACEALL
544 if (VuoHeap_isComposition())
545#else
546 if (VuoHeap_trace->find(heapPointer) != VuoHeap_trace->end())
547#endif
548 {
549 fprintf(stderr, "table=%p VuoRelease(%p) %s\n", referenceCounts, heapPointer, (i != referenceCounts->end()) ? i->second.variable : "");
551 }
552#endif
553
554 if (i != referenceCounts->end())
555 {
556 if (i->second.referenceCount == 0)
557 {
558 isRegisteredWithoutRetain = true;
559 }
560 else
561 {
562 updatedCount = --(i->second.referenceCount);
563
564 if (updatedCount == 0)
565 {
566 deallocate = i->second.deallocateFunction;
567 referenceCounts->erase(heapPointer);
568 }
569 }
570 }
571 else
572 foundSingleton = singletons->find(heapPointer) != singletons->end();
573
574#ifdef VUOHEAP_TRACE
575 if (updatedCount == 0)
576 {
577#ifdef VUOHEAP_TRACEALL
578 if (VuoHeap_isComposition())
579 {
580#else
581 if (VuoHeap_trace->find(heapPointer) != VuoHeap_trace->end())
582 {
583 VuoHeap_trace->erase(heapPointer);
584#endif
585 fprintf(stderr, "table=%p VuoDeallocate(%p)\n", referenceCounts, heapPointer);
586// VuoLog_backtrace();
587 }
588 }
589#endif
590 }
591 pthread_mutex_unlock(&VuoHeap_mutex);
592
593 if (updatedCount == 0)
594 deallocate((void *)heapPointer);
595 else if (updatedCount == -1 && !foundSingleton)
596 {
597 char pointerSummary[17];
598 VuoHeap_makeSafePointerSummary(pointerSummary, heapPointer);
599 ostringstream errorMessage;
600 errorMessage << "On reference table " << referenceCounts
601 << ", VuoRelease was called for "
602 << (isRegisteredWithoutRetain ? "unretained" : "unregistered")
603 << " pointer " << heapPointer
604 << " \"" << pointerSummary << "\"";
605 sendErrorWrapper(errorMessage.str().c_str());
606 }
607
608 return updatedCount;
609}
610
618char *VuoHeap_getDescription(const void *heapPointer)
619{
620 char *description = nullptr;
621
622 pthread_mutex_lock(&VuoHeap_mutex);
623
624 map<const void *, VuoHeapEntry>::iterator i = referenceCounts->find(heapPointer);
625 if (i != referenceCounts->end())
626 description = VuoHeap_makeDescription(i->second);
627
628 pthread_mutex_unlock(&VuoHeap_mutex);
629
630 if (description)
631 return description;
632 else
633 return strdup("(pointer was not VuoRegister()ed)");
634}
635
641void VuoHeap_addTrace(const void *heapPointer)
642{
643 pthread_mutex_lock(&VuoHeap_mutex);
644
645 VuoHeap_trace->insert(heapPointer);
646
647 pthread_mutex_unlock(&VuoHeap_mutex);
648}