Vuo  2.0.2
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>
19 using namespace std;
20 #include "VuoLog.h"
21 #include "VuoRuntime.h"
22 
23 static set<const void *> *VuoHeap_trace;
24 
28 void sendErrorWrapper(const char *message)
29 {
30 #ifdef VUOHEAP_TRACE
31  fprintf(stderr, "%s\n", message);
33 #endif
34 
35  VuoSendErrorType *vuoSendError = (VuoSendErrorType *) dlsym(RTLD_SELF, "vuoSendError"); // for running composition in separate process as executable or in current process
36  if (! vuoSendError)
37  vuoSendError = (VuoSendErrorType *) dlsym(RTLD_DEFAULT, "vuoSendError"); // for running composition in separate process as dynamic libraries
38 
39  pthread_key_t *vuoCompositionStateKey = (pthread_key_t *) dlsym(RTLD_SELF, "vuoCompositionStateKey");
41  vuoCompositionStateKey = (pthread_key_t *) dlsym(RTLD_DEFAULT, "vuoCompositionStateKey");
42 
43  void *compositionState = NULL;
45  compositionState = pthread_getspecific(*vuoCompositionStateKey);
46 
47  if (vuoSendError && compositionState)
48  vuoSendError((VuoCompositionState *)compositionState, message);
49  else
50  VUserLog("%s", message);
51 }
52 
53 #ifdef VUOHEAP_TRACEALL
54 
57 static bool VuoHeap_isComposition(void)
58 {
59  static bool isComposition = false;
60  static dispatch_once_t once = 0;
61  dispatch_once(&once, ^{
62  isComposition = dlsym(RTLD_DEFAULT, "vuoCompositionStateKey");
63  });
64  return isComposition;
65 }
66 #endif
67 
71 typedef struct
72 {
73  int referenceCount;
74  DeallocateFunctionType deallocateFunction;
75 
76  const char *file;
77  unsigned int line;
78  const char *function;
79  const char *variable;
80 } VuoHeapEntry;
81 
82 static map<const void *, VuoHeapEntry> *referenceCounts;
83 static set<const void *> *singletons;
84 static unsigned int *referenceCountsSemaphore = 0;
85 
92 static inline void VuoHeap_lock(void)
93 {
94  while (1)
95  {
96  for (int i = 0; i < 10000; ++i)
97  if (__sync_bool_compare_and_swap(referenceCountsSemaphore, 0, 1))
98  return;
99 
100  sched_yield();
101  }
102 }
103 
107 static inline void VuoHeap_unlock(void)
108 {
109  __sync_synchronize();
111 }
112 
118 static inline bool VuoHeap_isPointerValid(const void *pointer)
119 {
120  // On macOS, memory allocated by `malloc` is 16-byte aligned,
121  // so any non-16-byte-aligned pointers are suspicious.
122  // https://opensource.apple.com/source/Libc/Libc-825.26/gen/malloc.3.auto.html says
123  // "The allocated memory is aligned such that it can be used for any data type,
124  // including AltiVec- and SSE-related types."
125  // And https://software.intel.com/en-us/cpp-compiler-developer-guide-and-reference-alignment-support says
126  // "When using the IntelĀ® Streaming SIMD Extensions (IntelĀ® SSE) intrinsics,
127  // you should align data to 16 bytes in memory operations."
128  if ((unsigned long)pointer & 0xf)
129  return false;
130 
131  // On macOS, `malloc` shouldn't give us any pointers below 4 GB.
132  // https://opensource.apple.com/source/ld64/ld64-242/doc/man/man1/ld.1.auto.html says
133  // "By default the linker creates an unreadable segment starting at address zero named __PAGEZERO.
134  // On 64-bit architectures, the default size is 4GB."
135  if ((unsigned long)pointer < 0x100000000)
136  return false;
137 
138  // x86_64 is currently limited to 48 bits (256 TB) of virtual address space.
139  // http://support.amd.com/TechDocs/24593.pdf, page 131, says
140  // "Bits 63:48 are a sign extension of bit 47".
141  // This will no longer be the case if/when chips with 5-level paging are produced.
142  if ((unsigned long)pointer > 0xffffffffffff)
143  return false;
144 
145  return true;
146 }
147 
154 bool VuoHeap_isPointerReadable(const void *pointer)
155 {
156  // Round down to the beginning of `pointer`'s heap page.
157  // Assume getpagesize() returns a power-of-two; subtracting 1 turns it into a bitmask.
158  int pageSize = getpagesize();
159  long heapPageMask = ~((long)pageSize-1);
160  long heapPage = (long)pointer & heapPageMask;
161 
162  // Try to load the page into core.
163  mlock((void *)heapPage, pageSize);
164  munlock((void *)heapPage, pageSize);
165 
166  // Check whether the page was successfully loaded into core.
167  char pageStatus[1];
168  if (mincore((void *)heapPage, pageSize, pageStatus) == 0)
169  return pageStatus[0] & MINCORE_INCORE;
170 
171  else
172  return false;
173 }
174 
178 static void VuoHeap_makeSafePointerSummary(char *summary, const void *pointer)
179 {
180  if (VuoHeap_isPointerReadable(pointer)
181  && VuoHeap_isPointerReadable((char *)pointer + 15))
182  {
183  // Page(s) are valid, so output the pointer's first 16 printable characters.
184  char *pointerAsChar = (char *)pointer;
185  for (int i = 0; i < 16; ++i)
186  if (isprint(pointerAsChar[i]))
187  summary[i] = pointerAsChar[i];
188  else
189  summary[i] = '_';
190  summary[16] = 0;
191  }
192  else
193  strlcpy(summary, "(not allocated)", 17);
194 }
195 
204 {
205  const char *format = "%s:%d :: %s() :: %s";
206  int size = snprintf(NULL, 0, format, e.file, e.line, e.function, e.variable);
207  char *description = (char *)malloc(size+1);
208  snprintf(description, size+1, format, e.file, e.line, e.function, e.variable);
209  return description;
210 }
211 
217 static void __attribute__((constructor(101))) VuoHeap_init()
218 {
219  referenceCounts = new map<const void *, VuoHeapEntry>;
220  singletons = new set<const void *>;
221  VuoHeap_trace = new set<const void *>;
222  referenceCountsSemaphore = (unsigned int *)malloc(sizeof(unsigned int));
224 
225 #ifdef VUOHEAP_TRACE
226  VUserLog("table=%p", referenceCounts);
227 #endif
228 
229 #if 0
230  // Periodically dump the referenceCounts table, to help find leaks.
231  const double dumpInterval = 5.0; // seconds
232  dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, DISPATCH_TIMER_STRICT, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
233  dispatch_source_set_timer(timer, dispatch_walltime(NULL,0), NSEC_PER_SEC*dumpInterval, NSEC_PER_SEC*dumpInterval);
234  dispatch_source_set_event_handler(timer, ^{
235  fprintf(stderr, "\n\n\n\n\nreferenceCounts:\n");
236  VuoHeap_lock();
237  for (map<const void *, VuoHeapEntry>::iterator i = referenceCounts->begin(); i != referenceCounts->end(); ++i)
238  {
239  const void *heapPointer = i->first;
240  char pointerSummary[17];
241  VuoHeap_makeSafePointerSummary(pointerSummary, heapPointer);
242  char *description = VuoHeap_makeDescription(i->second);
243  fprintf(stderr, "\t% 3d refs to %p \"%s\", registered at %s\n", i->second.referenceCount, heapPointer, pointerSummary, description);
244  free(description);
245  }
246  VuoHeap_unlock();
247  });
248  dispatch_resume(timer);
249 #endif
250 }
251 
255 void VuoHeap_report(void)
256 {
258  VuoHeap_lock();
260  {
261 
262  if (! referenceCounts->empty())
263  {
264  ostringstream errorMessage;
265  errorMessage << "On reference table " << referenceCounts
266  << ", VuoRelease was not called enough times for:" << endl;
267  for (map<const void *, VuoHeapEntry>::iterator i = referenceCounts->begin(); i != referenceCounts->end(); ++i)
268  {
269  const void *heapPointer = i->first;
270  char pointerSummary[17];
271  VuoHeap_makeSafePointerSummary(pointerSummary, heapPointer);
272  char *description = VuoHeap_makeDescription(i->second);
273  errorMessage << "\t" << setw(3) << i->second.referenceCount << " refs to " << heapPointer << " \"" << pointerSummary << "\", registered at " << description << endl;
274  free(description);
275  }
276  sendErrorWrapper(errorMessage.str().c_str());
277  }
278 
279  }
280  VuoHeap_unlock();
281 }
282 
298 int VuoRegisterF(const void *heapPointer, DeallocateFunctionType deallocate, const char *file, unsigned int linenumber, const char *func, const char *pointerName)
299 {
300  if (! heapPointer)
301  return -1;
302 
303  if (!VuoHeap_isPointerValid(heapPointer))
304  {
305  ostringstream errorMessage;
306  char *description = VuoHeap_makeDescription((VuoHeapEntry){0, NULL, file, linenumber, func, pointerName});
307  errorMessage << "On reference table " << referenceCounts
308  << ", VuoRegister was called for bogus pointer " << heapPointer
309  << " " << description;
310  free(description);
311  sendErrorWrapper(errorMessage.str().c_str());
312  }
313 
314  bool isAlreadyReferenceCounted;
315  int updatedCount;
316 
318  VuoHeap_lock();
320  {
321 
322 #ifdef VUOHEAP_TRACE
323 #ifdef VUOHEAP_TRACEALL
324  if (VuoHeap_isComposition())
325 #else
326  if (VuoHeap_trace->find(heapPointer) != VuoHeap_trace->end())
327 #endif
328  {
329  fprintf(stderr, "table=%p VuoRegister(%p) %s\n", referenceCounts, heapPointer, pointerName);
331  }
332 #endif
333 
334  map<const void *, VuoHeapEntry>::iterator i = referenceCounts->find(heapPointer);
335  isAlreadyReferenceCounted = (i != referenceCounts->end());
336  if (! isAlreadyReferenceCounted)
337  {
338  updatedCount = 0;
339  (*referenceCounts)[heapPointer] = (VuoHeapEntry){updatedCount, deallocate, file, linenumber, func, pointerName};
340  }
341  else
342  updatedCount = i->second.referenceCount;
343 
344  }
345  VuoHeap_unlock();
346 
347  if (isAlreadyReferenceCounted)
348  {
349  ostringstream errorMessage;
350  char *description = VuoHeap_makeDescription((VuoHeapEntry){0, NULL, file, linenumber, func, pointerName});
351  errorMessage << "On reference table " << referenceCounts
352  << ", VuoRegister was called more than once for " << heapPointer
353  << " " << description;
354  free(description);
355  sendErrorWrapper(errorMessage.str().c_str());
356  }
357 
358  return updatedCount;
359 }
360 
365 int VuoRegisterSingletonF(const void *heapPointer, const char *file, unsigned int linenumber, const char *func, const char *pointerName)
366 {
367  if (! heapPointer)
368  return -1;
369 
370  if (!VuoHeap_isPointerValid(heapPointer))
371  {
372  ostringstream errorMessage;
373  char *description = VuoHeap_makeDescription((VuoHeapEntry){0, NULL, file, linenumber, func, pointerName});
374  errorMessage << "On reference table " << referenceCounts
375  << ", VuoRegisterSingleton was called for bogus pointer " << heapPointer
376  << " " << description;
377  free(description);
378  sendErrorWrapper(errorMessage.str().c_str());
379  }
380 
381  bool isAlreadyReferenceCounted;
382 
384  VuoHeap_lock();
386  {
387 
388 #ifdef VUOHEAP_TRACE
389 #ifdef VUOHEAP_TRACEALL
390  if (VuoHeap_isComposition())
391 #else
392  if (VuoHeap_trace->find(heapPointer) != VuoHeap_trace->end())
393 #endif
394  {
395  fprintf(stderr, "table=%p VuoRegisterSingleton(%p) %s\n", referenceCounts, heapPointer, pointerName);
397  }
398 #endif
399 
400  // Remove the singleton from the main reference-counting table, if it exists there.
401  // Enables reclassifying a pointer that was already VuoRegister()ed.
402  referenceCounts->erase(heapPointer);
403 
404  // Add the singleton to the singleton table.
405  isAlreadyReferenceCounted = (singletons->find(heapPointer) != singletons->end());
406  if (! isAlreadyReferenceCounted)
407  singletons->insert(heapPointer);
408  }
409  VuoHeap_unlock();
410 
411  if (isAlreadyReferenceCounted)
412  {
413  ostringstream errorMessage;
414  char *description = VuoHeap_makeDescription((VuoHeapEntry){0, NULL, file, linenumber, func, pointerName});
415  errorMessage << "On reference table " << referenceCounts
416  << ", VuoRegisterSingleton was called more than once for " << heapPointer
417  << " " << description;
418  free(description);
419  sendErrorWrapper(errorMessage.str().c_str());
420  }
421 
422  return isAlreadyReferenceCounted ? 1 : 0;
423 }
424 
433 extern "C" int VuoRetainF(const void *heapPointer, const char *file, unsigned int linenumber, const char *func)
434 {
435  return VuoRetain(heapPointer);
436 }
437 
445 int VuoRetain(const void *heapPointer)
446 {
447  if (! heapPointer)
448  return -1;
449 
450  if (!VuoHeap_isPointerValid(heapPointer))
451  {
452  char pointerSummary[17];
453  VuoHeap_makeSafePointerSummary(pointerSummary, heapPointer);
454  ostringstream errorMessage;
455  errorMessage << "On reference table " << referenceCounts
456  << ", VuoRetain was called for bogus pointer " << heapPointer
457  << " \"" << pointerSummary << "\"";
458  sendErrorWrapper(errorMessage.str().c_str());
459  }
460 
461  int updatedCount = -1;
462  bool foundSingleton = false;
463  VuoHeapEntry entry;
464 
466  VuoHeap_lock();
468  {
469  map<const void *, VuoHeapEntry>::iterator i = referenceCounts->find(heapPointer);
470 
471 #ifdef VUOHEAP_TRACE
472 #ifdef VUOHEAP_TRACEALL
473  if (VuoHeap_isComposition())
474 #else
475  if (VuoHeap_trace->find(heapPointer) != VuoHeap_trace->end())
476 #endif
477  {
478  fprintf(stderr, "table=%p VuoRetain(%p) %s\n", referenceCounts, heapPointer, (i != referenceCounts->end()) ? i->second.variable : "");
480  }
481 #endif
482 
483  if (i != referenceCounts->end())
484  updatedCount = ++(i->second.referenceCount);
485  else
486  foundSingleton = singletons->find(heapPointer) != singletons->end();
487  entry = i->second;
488 
489  }
490  VuoHeap_unlock();
491 
492  if (updatedCount == -1 && !foundSingleton)
493  {
494  char pointerSummary[17];
495  VuoHeap_makeSafePointerSummary(pointerSummary, heapPointer);
496  ostringstream errorMessage;
497  errorMessage << "On reference table " << referenceCounts
498  << ", VuoRetain was called for unregistered pointer " << heapPointer
499  << " \"" << pointerSummary << "\"";
500  sendErrorWrapper(errorMessage.str().c_str());
501  }
502 
503  return updatedCount;
504 }
505 
514 extern "C" int VuoReleaseF(const void *heapPointer, const char *file, unsigned int linenumber, const char *func)
515 {
516  return VuoRelease(heapPointer);
517 }
518 
527 int VuoRelease(const void *heapPointer)
528 {
529  if (! heapPointer)
530  return -1;
531 
532  if (!VuoHeap_isPointerValid(heapPointer))
533  {
534  char pointerSummary[17];
535  VuoHeap_makeSafePointerSummary(pointerSummary, heapPointer);
536  ostringstream errorMessage;
537  errorMessage << "On reference table " << referenceCounts
538  << ", VuoRelease was called for bogus pointer " << heapPointer
539  << " \"" << pointerSummary << "\"";
540  sendErrorWrapper(errorMessage.str().c_str());
541  }
542 
543  int updatedCount = -1;
544  bool foundSingleton = false;
545  bool isRegisteredWithoutRetain = false;
546  DeallocateFunctionType deallocate = NULL;
547 
549  VuoHeap_lock();
551  {
552  map<const void *, VuoHeapEntry>::iterator i = referenceCounts->find(heapPointer);
553 
554 #ifdef VUOHEAP_TRACE
555 #ifdef VUOHEAP_TRACEALL
556  if (VuoHeap_isComposition())
557 #else
558  if (VuoHeap_trace->find(heapPointer) != VuoHeap_trace->end())
559 #endif
560  {
561  fprintf(stderr, "table=%p VuoRelease(%p) %s\n", referenceCounts, heapPointer, (i != referenceCounts->end()) ? i->second.variable : "");
563  }
564 #endif
565 
566  if (i != referenceCounts->end())
567  {
568  if (i->second.referenceCount == 0)
569  {
570  isRegisteredWithoutRetain = true;
571  }
572  else
573  {
574  updatedCount = --(i->second.referenceCount);
575 
576  if (updatedCount == 0)
577  {
578  deallocate = i->second.deallocateFunction;
579  referenceCounts->erase(heapPointer);
580  }
581  }
582  }
583  else
584  foundSingleton = singletons->find(heapPointer) != singletons->end();
585 
586  }
587  VuoHeap_unlock();
588 
589  if (updatedCount == 0)
590  {
591 #ifdef VUOHEAP_TRACE
592 #ifdef VUOHEAP_TRACEALL
593  if (VuoHeap_isComposition())
594  {
595 #else
596  if (VuoHeap_trace->find(heapPointer) != VuoHeap_trace->end())
597  {
599  VuoHeap_lock();
601  {
602  VuoHeap_trace->erase(heapPointer);
603  }
604  VuoHeap_unlock();
605 #endif
606  fprintf(stderr, "table=%p VuoDeallocate(%p)\n", referenceCounts, heapPointer);
607 // VuoLog_backtrace();
608  }
609 #endif
610 
611  deallocate((void *)heapPointer);
612  }
613  else if (updatedCount == -1 && !foundSingleton)
614  {
615  char pointerSummary[17];
616  VuoHeap_makeSafePointerSummary(pointerSummary, heapPointer);
617  ostringstream errorMessage;
618  errorMessage << "On reference table " << referenceCounts
619  << ", VuoRelease was called for "
620  << (isRegisteredWithoutRetain ? "unretained" : "unregistered")
621  << " pointer " << heapPointer
622  << " \"" << pointerSummary << "\"";
623  sendErrorWrapper(errorMessage.str().c_str());
624  }
625 
626  return updatedCount;
627 }
628 
636 const char * VuoHeap_getDescription(const void *heapPointer)
637 {
638  map<const void *, VuoHeapEntry>::iterator i = referenceCounts->find(heapPointer);
639  if (i != referenceCounts->end())
640  return VuoHeap_makeDescription(i->second);
641 
642  return strdup("(pointer was not VuoRegister()ed)");
643 }
644 
650 void VuoHeap_addTrace(const void *heapPointer)
651 {
653  VuoHeap_lock();
655  {
656  VuoHeap_trace->insert(heapPointer);
657  }
658  VuoHeap_unlock();
659 }