Vuo  2.0.0
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 
119 bool VuoHeap_isPointerReadable(const void *pointer)
120 {
121  // Round down to the beginning of `pointer`'s heap page.
122  // Assume getpagesize() returns a power-of-two; subtracting 1 turns it into a bitmask.
123  int pageSize = getpagesize();
124  long heapPageMask = ~((long)pageSize-1);
125  long heapPage = (long)pointer & heapPageMask;
126 
127  // Try to load the page into core.
128  mlock((void *)heapPage, pageSize);
129  munlock((void *)heapPage, pageSize);
130 
131  // Check whether the page was successfully loaded into core.
132  char pageStatus[1];
133  if (mincore((void *)heapPage, pageSize, pageStatus) == 0)
134  return pageStatus[0] & MINCORE_INCORE;
135 
136  else
137  return false;
138 }
139 
143 static void VuoHeap_makeSafePointerSummary(char *summary, const void *pointer)
144 {
145  if (VuoHeap_isPointerReadable(pointer)
146  && VuoHeap_isPointerReadable((char *)pointer + 15))
147  {
148  // Page(s) are valid, so output the pointer's first 16 printable characters.
149  char *pointerAsChar = (char *)pointer;
150  for (int i = 0; i < 16; ++i)
151  if (isprint(pointerAsChar[i]))
152  summary[i] = pointerAsChar[i];
153  else
154  summary[i] = '_';
155  summary[16] = 0;
156  }
157  else
158  strlcpy(summary, "(not allocated)", 17);
159 }
160 
169 {
170  const char *format = "%s:%d :: %s() :: %s";
171  int size = snprintf(NULL, 0, format, e.file, e.line, e.function, e.variable);
172  char *description = (char *)malloc(size+1);
173  snprintf(description, size+1, format, e.file, e.line, e.function, e.variable);
174  return description;
175 }
176 
182 static void __attribute__((constructor(101))) VuoHeap_init()
183 {
184  referenceCounts = new map<const void *, VuoHeapEntry>;
185  singletons = new set<const void *>;
186  VuoHeap_trace = new set<const void *>;
187  referenceCountsSemaphore = (unsigned int *)malloc(sizeof(unsigned int));
189 
190 #ifdef VUOHEAP_TRACE
191  VUserLog("table=%p", referenceCounts);
192 #endif
193 
194 #if 0
195  // Periodically dump the referenceCounts table, to help find leaks.
196  const double dumpInterval = 5.0; // seconds
197  dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, DISPATCH_TIMER_STRICT, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
198  dispatch_source_set_timer(timer, dispatch_walltime(NULL,0), NSEC_PER_SEC*dumpInterval, NSEC_PER_SEC*dumpInterval);
199  dispatch_source_set_event_handler(timer, ^{
200  fprintf(stderr, "\n\n\n\n\nreferenceCounts:\n");
201  VuoHeap_lock();
202  for (map<const void *, VuoHeapEntry>::iterator i = referenceCounts->begin(); i != referenceCounts->end(); ++i)
203  {
204  const void *heapPointer = i->first;
205  char pointerSummary[17];
206  VuoHeap_makeSafePointerSummary(pointerSummary, heapPointer);
207  char *description = VuoHeap_makeDescription(i->second);
208  fprintf(stderr, "\t% 3d refs to %p \"%s\", registered at %s\n", i->second.referenceCount, heapPointer, pointerSummary, description);
209  free(description);
210  }
211  VuoHeap_unlock();
212  });
213  dispatch_resume(timer);
214 #endif
215 }
216 
220 void VuoHeap_report(void)
221 {
223  VuoHeap_lock();
225  {
226 
227  if (! referenceCounts->empty())
228  {
229  ostringstream errorMessage;
230  errorMessage << "On reference table " << referenceCounts
231  << ", VuoRelease was not called enough times for:" << endl;
232  for (map<const void *, VuoHeapEntry>::iterator i = referenceCounts->begin(); i != referenceCounts->end(); ++i)
233  {
234  const void *heapPointer = i->first;
235  char pointerSummary[17];
236  VuoHeap_makeSafePointerSummary(pointerSummary, heapPointer);
237  char *description = VuoHeap_makeDescription(i->second);
238  errorMessage << "\t" << setw(3) << i->second.referenceCount << " refs to " << heapPointer << " \"" << pointerSummary << "\", registered at " << description << endl;
239  free(description);
240  }
241  sendErrorWrapper(errorMessage.str().c_str());
242  }
243 
244  }
245  VuoHeap_unlock();
246 }
247 
263 int VuoRegisterF(const void *heapPointer, DeallocateFunctionType deallocate, const char *file, unsigned int linenumber, const char *func, const char *pointerName)
264 {
265  if (! heapPointer)
266  return -1;
267 
268  bool isAlreadyReferenceCounted;
269  int updatedCount;
270 
272  VuoHeap_lock();
274  {
275 
276 #ifdef VUOHEAP_TRACE
277 #ifdef VUOHEAP_TRACEALL
278  if (VuoHeap_isComposition())
279 #else
280  if (VuoHeap_trace->find(heapPointer) != VuoHeap_trace->end())
281 #endif
282  {
283  fprintf(stderr, "table=%p VuoRegister(%p) %s\n", referenceCounts, heapPointer, pointerName);
285  }
286 #endif
287 
288  map<const void *, VuoHeapEntry>::iterator i = referenceCounts->find(heapPointer);
289  isAlreadyReferenceCounted = (i != referenceCounts->end());
290  if (! isAlreadyReferenceCounted)
291  {
292  updatedCount = 0;
293  (*referenceCounts)[heapPointer] = (VuoHeapEntry){updatedCount, deallocate, file, linenumber, func, pointerName};
294  }
295  else
296  updatedCount = i->second.referenceCount;
297 
298  }
299  VuoHeap_unlock();
300 
301  if (isAlreadyReferenceCounted)
302  {
303  ostringstream errorMessage;
304  char *description = VuoHeap_makeDescription((VuoHeapEntry){0, NULL, file, linenumber, func, pointerName});
305  errorMessage << "On reference table " << referenceCounts
306  << ", VuoRegister was called more than once for " << heapPointer
307  << " " << description;
308  free(description);
309  sendErrorWrapper(errorMessage.str().c_str());
310  }
311 
312  return updatedCount;
313 }
314 
319 int VuoRegisterSingletonF(const void *heapPointer, const char *file, unsigned int linenumber, const char *func, const char *pointerName)
320 {
321  if (! heapPointer)
322  return -1;
323 
324  bool isAlreadyReferenceCounted;
325 
327  VuoHeap_lock();
329  {
330 
331 #ifdef VUOHEAP_TRACE
332 #ifdef VUOHEAP_TRACEALL
333  if (VuoHeap_isComposition())
334 #else
335  if (VuoHeap_trace->find(heapPointer) != VuoHeap_trace->end())
336 #endif
337  {
338  fprintf(stderr, "table=%p VuoRegisterSingleton(%p) %s\n", referenceCounts, heapPointer, pointerName);
340  }
341 #endif
342 
343  // Remove the singleton from the main reference-counting table, if it exists there.
344  // Enables reclassifying a pointer that was already VuoRegister()ed.
345  referenceCounts->erase(heapPointer);
346 
347  // Add the singleton to the singleton table.
348  isAlreadyReferenceCounted = (singletons->find(heapPointer) != singletons->end());
349  if (! isAlreadyReferenceCounted)
350  singletons->insert(heapPointer);
351  }
352  VuoHeap_unlock();
353 
354  if (isAlreadyReferenceCounted)
355  {
356  ostringstream errorMessage;
357  char *description = VuoHeap_makeDescription((VuoHeapEntry){0, NULL, file, linenumber, func, pointerName});
358  errorMessage << "On reference table " << referenceCounts
359  << ", VuoRegisterSingleton was called more than once for " << heapPointer
360  << " " << description;
361  free(description);
362  sendErrorWrapper(errorMessage.str().c_str());
363  }
364 
365  return isAlreadyReferenceCounted ? 1 : 0;
366 }
367 
376 extern "C" int VuoRetainF(const void *heapPointer, const char *file, unsigned int linenumber, const char *func)
377 {
378  return VuoRetain(heapPointer);
379 }
380 
388 int VuoRetain(const void *heapPointer)
389 {
390  if (! heapPointer)
391  return -1;
392 
393  int updatedCount = -1;
394  bool foundSingleton = false;
395  VuoHeapEntry entry;
396 
398  VuoHeap_lock();
400  {
401  map<const void *, VuoHeapEntry>::iterator i = referenceCounts->find(heapPointer);
402 
403 #ifdef VUOHEAP_TRACE
404 #ifdef VUOHEAP_TRACEALL
405  if (VuoHeap_isComposition())
406 #else
407  if (VuoHeap_trace->find(heapPointer) != VuoHeap_trace->end())
408 #endif
409  {
410  fprintf(stderr, "table=%p VuoRetain(%p) %s\n", referenceCounts, heapPointer, (i != referenceCounts->end()) ? i->second.variable : "");
412  }
413 #endif
414 
415  if (i != referenceCounts->end())
416  updatedCount = ++(i->second.referenceCount);
417  else
418  foundSingleton = singletons->find(heapPointer) != singletons->end();
419  entry = i->second;
420 
421  }
422  VuoHeap_unlock();
423 
424  if (updatedCount == -1 && !foundSingleton)
425  {
426  char pointerSummary[17];
427  VuoHeap_makeSafePointerSummary(pointerSummary, heapPointer);
428  ostringstream errorMessage;
429  errorMessage << "On reference table " << referenceCounts
430  << ", VuoRetain was called for unregistered pointer " << heapPointer
431  << " \"" << pointerSummary << "\"";
432  sendErrorWrapper(errorMessage.str().c_str());
433  }
434 
435  return updatedCount;
436 }
437 
446 extern "C" int VuoReleaseF(const void *heapPointer, const char *file, unsigned int linenumber, const char *func)
447 {
448  return VuoRelease(heapPointer);
449 }
450 
459 int VuoRelease(const void *heapPointer)
460 {
461  if (! heapPointer)
462  return -1;
463 
464  int updatedCount = -1;
465  bool foundSingleton = false;
466  bool isRegisteredWithoutRetain = false;
467  DeallocateFunctionType deallocate = NULL;
468 
470  VuoHeap_lock();
472  {
473  map<const void *, VuoHeapEntry>::iterator i = referenceCounts->find(heapPointer);
474 
475 #ifdef VUOHEAP_TRACE
476 #ifdef VUOHEAP_TRACEALL
477  if (VuoHeap_isComposition())
478 #else
479  if (VuoHeap_trace->find(heapPointer) != VuoHeap_trace->end())
480 #endif
481  {
482  fprintf(stderr, "table=%p VuoRelease(%p) %s\n", referenceCounts, heapPointer, (i != referenceCounts->end()) ? i->second.variable : "");
484  }
485 #endif
486 
487  if (i != referenceCounts->end())
488  {
489  if (i->second.referenceCount == 0)
490  {
491  isRegisteredWithoutRetain = true;
492  }
493  else
494  {
495  updatedCount = --(i->second.referenceCount);
496 
497  if (updatedCount == 0)
498  {
499  deallocate = i->second.deallocateFunction;
500  referenceCounts->erase(heapPointer);
501  }
502  }
503  }
504  else
505  foundSingleton = singletons->find(heapPointer) != singletons->end();
506 
507  }
508  VuoHeap_unlock();
509 
510  if (updatedCount == 0)
511  {
512 #ifdef VUOHEAP_TRACE
513 #ifdef VUOHEAP_TRACEALL
514  if (VuoHeap_isComposition())
515  {
516 #else
517  if (VuoHeap_trace->find(heapPointer) != VuoHeap_trace->end())
518  {
520  VuoHeap_lock();
522  {
523  VuoHeap_trace->erase(heapPointer);
524  }
525  VuoHeap_unlock();
526 #endif
527  fprintf(stderr, "table=%p VuoDeallocate(%p)\n", referenceCounts, heapPointer);
528 // VuoLog_backtrace();
529  }
530 #endif
531 
532  deallocate((void *)heapPointer);
533  }
534  else if (updatedCount == -1 && !foundSingleton)
535  {
536  char pointerSummary[17];
537  VuoHeap_makeSafePointerSummary(pointerSummary, heapPointer);
538  ostringstream errorMessage;
539  errorMessage << "On reference table " << referenceCounts
540  << ", VuoRelease was called for "
541  << (isRegisteredWithoutRetain ? "unretained" : "unregistered")
542  << " pointer " << heapPointer
543  << " \"" << pointerSummary << "\"";
544  sendErrorWrapper(errorMessage.str().c_str());
545  }
546 
547  return updatedCount;
548 }
549 
557 const char * VuoHeap_getDescription(const void *heapPointer)
558 {
559  map<const void *, VuoHeapEntry>::iterator i = referenceCounts->find(heapPointer);
560  if (i != referenceCounts->end())
561  return VuoHeap_makeDescription(i->second);
562 
563  return strdup("(pointer was not VuoRegister()ed)");
564 }
565 
571 void VuoHeap_addTrace(const void *heapPointer)
572 {
574  VuoHeap_lock();
576  {
577  VuoHeap_trace->insert(heapPointer);
578  }
579  VuoHeap_unlock();
580 }