15 #include <mach/mach_time.h>
17 #include <objc/objc-runtime.h>
22 #include <sys/sysctl.h>
24 #include <sys/types.h>
27 #ifndef __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES
28 #define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0
31 #include <CoreFoundation/CoreFoundation.h>
33 #include <AvailabilityMacros.h>
34 #if (MAC_OS_X_VERSION_MIN_REQUIRED == MAC_OS_X_VERSION_10_6) || (MAC_OS_X_VERSION_MIN_REQUIRED == MAC_OS_X_VERSION_10_7)
35 #include <ApplicationServices/ApplicationServices.h>
37 #include <CoreGraphics/CoreGraphics.h>
38 #include <CoreText/CoreText.h>
54 if (
auto ep = std::current_exception())
57 std::rethrow_exception(ep);
59 catch (std::exception
const &e)
62 const char *typeName =
typeid(e).name();
63 char *unmangled = abi::__cxa_demangle(typeName, 0, 0, &status);
67 VUserLog(
"Terminating due to uncaught %s: \"%s\"", typeName, e.what());
71 VUserLog(
"Terminating due to uncaught exception of unknown type");
76 VUserLog(
"Terminating because std::terminate was called (no exception data available)");
87 static mach_timebase_info_data_t info = {0,0};
90 kern_return_t ret = mach_timebase_info(&info);
91 if (ret != KERN_SUCCESS)
93 VUserLog(
"Failed to get mach timebase: %d", ret);
100 return (
double)mach_absolute_time() * info.numer / (info.denom * NSEC_PER_SEC);
119 } VuoLogProfileEntry;
120 typedef std::map<std::string, VuoLogProfileEntry> VuoLogProfileType;
121 static VuoLogProfileType *VuoLogProfile;
122 static dispatch_queue_t VuoLogProfileQueue;
130 static void __attribute__((constructor)) VuoLog_init(
void)
137 VuoLogProfileQueue = dispatch_queue_create(
"VuoLogProfile", NULL);
138 VuoLogProfile =
new VuoLogProfileType;
142 #if defined(VUO_PROFILE) || defined(DOXYGEN)
148 dispatch_sync(VuoLogProfileQueue, ^{
149 VuoLogProfileType::iterator i = VuoLogProfile->find(name);
150 if (i != VuoLogProfile->end())
152 i->second.total += time;
153 if (time < i->second.min)
154 i->second.min = time;
155 if (time > i->second.max)
156 i->second.max = time;
160 (*VuoLogProfile)[name] = (VuoLogProfileEntry){time, time, time, 1};
167 extern "C" void __attribute__((destructor)) VuoLog_dumpProfile(
void)
169 dispatch_sync(VuoLogProfileQueue, ^{
171 for (VuoLogProfileType::iterator i = VuoLogProfile->begin(); i != VuoLogProfile->end(); ++i)
172 fprintf(stderr,
"%30s %12.9f s (%7.4f%%) (min %12.9f, max %12.9f, avg %12.9f, count %lld)\n",
175 i->second.total * 100. / totalRuntime,
178 i->second.total / i->second.count,
193 #define VuoCrashReport_alignment __attribute__((aligned(8)))
210 VuoCrashReport_infoType VuoCrashReport __attribute__((section(
"__DATA,__crash_info"))) = { 4, NULL, NULL, NULL, NULL, NULL, NULL };
212 void VuoLog_statusF(
const char *file,
const unsigned int linenumber,
const char *
function,
const char *format, ...)
214 static dispatch_once_t statusInitialized = 0;
215 static dispatch_queue_t statusQueue;
216 dispatch_once(&statusInitialized, ^{
217 statusQueue = dispatch_queue_create(
"VuoLogStatus", NULL);
220 char *message =
nullptr;
224 va_start(args, format);
225 vasprintf(&message, format, args);
229 dispatch_sync(statusQueue, ^{
230 if (VuoCrashReport.message2)
231 free(VuoCrashReport.message2);
234 VuoCrashReport.message2 = message;
236 VuoCrashReport.message2 =
nullptr;
245 Class NSProcessInfoClass = objc_getClass(
"NSProcessInfo");
246 id processInfo = objc_msgSend((
id)NSProcessInfoClass, sel_getUid(
"processInfo"));
247 struct NSOperatingSystemVersion
253 typedef NSOperatingSystemVersion (*operatingSystemVersionType)(
id receiver,
SEL selector);
254 operatingSystemVersionType operatingSystemVersionFunc = (operatingSystemVersionType)objc_msgSend_stret;
255 NSOperatingSystemVersion operatingSystemVersion = operatingSystemVersionFunc(processInfo, sel_getUid(
"operatingSystemVersion"));
256 return operatingSystemVersion.minorVersion;
270 mib[2] = KERN_PROC_PID;
273 struct kinfo_proc info;
274 info.kp_proc.p_flag = 0;
275 size_t size =
sizeof(info);
276 if (sysctl(mib,
sizeof(mib) /
sizeof(*mib), &info, &size, NULL, 0))
278 VUserLog(
"Warning: Couldn't check debugger status: %s", strerror(errno));
282 return info.kp_proc.p_flag & P_TRACED;
285 void VuoLog(
const char *file,
const unsigned int linenumber,
const char *
function,
const char *format, ...)
289 va_start(args, format);
290 int size = vsnprintf(NULL, 0, format, args);
293 char *formattedString = (
char *)malloc(size+1);
294 va_start(args, format);
295 vsnprintf(formattedString, size+1, format, args);
298 char *formattedFunction = NULL;
302 if (
function &&
function[0] ==
'_' &&
function[1] ==
'_')
304 int actualFunctionLength = atoi(
function + 2);
305 if (actualFunctionLength)
306 formattedFunction = strndup(
function + 3 + (
int)log10(actualFunctionLength), actualFunctionLength);
308 formattedFunction = strndup(
function + 2, strrchr(
function + 2,
'_') - (
function + 2));
313 if (strncmp(formattedFunction,
"_ZN", 3) == 0)
316 int len, priorLen = 0;
317 while ((len = atoi(formattedFunction + pos)))
319 pos += 1 + (int)log10(len) + len;
322 char *f2 = strndup(formattedFunction + pos - priorLen, priorLen);
323 free(formattedFunction);
324 formattedFunction = f2;
331 const char *f = formattedFunction ? formattedFunction :
function;
332 size_t fLen = strlen(f);
333 if (f[fLen - 1] !=
']')
335 size_t mallocSize = fLen + 2 + 1;
336 char *f2 = (
char *)malloc(mallocSize);
337 strlcpy(f2, f, mallocSize);
338 strlcat(f2,
"()", mallocSize);
339 if (formattedFunction)
340 free(formattedFunction);
341 formattedFunction = f2;
345 const char *formattedFile = file;
348 formattedFile =
"(unknown)";
351 if (
const char *lastSlash = strrchr(formattedFile,
'/'))
352 formattedFile = lastSlash + 1;
357 static double priorTime = 0;
358 const char *separator =
"";
359 if (priorTime > 0 && time - priorTime > 0.5)
365 fprintf(stderr,
"%s\033[38;5;%dm# pid=%5d t=%8.4fs %27.27s:%-4u %41.41s %s\033[0m\n", separator, getpid()%144+88, getpid(), time, formattedFile, linenumber, formattedFunction ? formattedFunction :
function, formattedString);
374 extern struct mach_header __dso_handle;
375 typedef void *(*vuoMacOsLogCreateType)(
const char *subsystem,
const char *category);
376 typedef void (*vuoMacOsLogInternalType)(
void *dso,
void *log, uint8_t type,
const char *message, ...);
377 typedef void (*vuoMacOsLogImplType)(
void *dso,
void *log, uint8_t type,
const char *format, uint8_t *buf, uint32_t size);
378 static vuoMacOsLogCreateType vuoMacOsLogCreate = NULL;
379 static vuoMacOsLogInternalType vuoMacOsLogInternal = NULL;
380 static vuoMacOsLogImplType vuoMacOsLogImpl = NULL;
381 static dispatch_once_t once = 0;
382 static bool debuggerAttached =
false;
383 dispatch_once(&once, ^{
385 vuoMacOsLogCreate = (vuoMacOsLogCreateType)dlsym(RTLD_SELF,
"os_log_create");
387 vuoMacOsLogInternal = (vuoMacOsLogInternalType)dlsym(RTLD_SELF,
"_os_log_internal");
389 vuoMacOsLogImpl = (vuoMacOsLogImplType)dlsym(RTLD_SELF,
"_os_log_impl");
392 if (!debuggerAttached && vuoMacOsLogCreate)
394 void *log = vuoMacOsLogCreate(
"org.vuo", formattedFile);
396 if (vuoMacOsLogInternal)
397 vuoMacOsLogInternal(&__dso_handle, log, 0 ,
"%{public}41s:%-4u %{public}s", formattedFunction ? formattedFunction :
function, linenumber, formattedString);
398 else if (vuoMacOsLogImpl)
400 char *formattedForOsLog;
401 asprintf(&formattedForOsLog,
"%41s:%-4u %s", formattedFunction ? formattedFunction :
function, linenumber, formattedString);
404 uint8_t logFormatDescriptor[12] = {
411 memcpy(logFormatDescriptor + 4, &formattedForOsLog, 8);
413 vuoMacOsLogImpl(&__dso_handle, log, 0 ,
"%{public}s", logFormatDescriptor,
sizeof(logFormatDescriptor));
415 free(formattedForOsLog);
421 else if (!debuggerAttached)
423 aslmsg msg = asl_new(ASL_TYPE_MSG);
424 asl_set(msg, ASL_KEY_READ_UID,
"-1");
425 asl_log(NULL, msg, ASL_LEVEL_WARNING,
"%27s:%-4u %41s %s", formattedFile, linenumber, formattedFunction ? formattedFunction :
function, formattedString);
431 static dispatch_once_t historyInitialized = 0;
432 dispatch_once(&historyInitialized, ^{
441 char *formattedPrefixedString;
442 asprintf(&formattedPrefixedString,
"t=%8.4fs %27.27s:%-4u %41.41s %s", time, formattedFile, linenumber, formattedFunction ? formattedFunction :
function, formattedString);
443 free(formattedString);
444 if (formattedFunction)
445 free(formattedFunction);
467 if (VuoCrashReport.message)
468 free(VuoCrashReport.message);
476 char *message = (
char *)malloc(size);
481 strlcat(message,
"\n", size);
485 VuoCrashReport.message = message;
500 static dispatch_once_t checked = 0;
501 static bool debug =
false;
502 dispatch_once(&checked, ^{
503 debug = CFPreferencesGetAppBooleanValue(CFSTR(
"debug"), CFSTR(
"org.vuo.Editor"), NULL);
516 backtrace.erase(backtrace.begin());
519 for (
auto line : backtrace)
520 fprintf(stderr,
"%3d %s\n", i++, line.c_str());
530 size_t replacementLen = strlen(replacement);
532 regcomp(®ex, substringToRemove, REG_EXTENDED);
534 regmatch_t pmatch[nmatch];
535 while (!regexec(®ex, wholeString, nmatch, pmatch, 0))
537 strncpy(wholeString + pmatch[0].rm_so, replacement, replacementLen);
538 memmove(wholeString + pmatch[0].rm_so + replacementLen, wholeString + pmatch[0].rm_eo, strlen(wholeString + pmatch[0].rm_eo) + 1);
549 size_t size = backtrace(array, 100);
550 char **strings = backtrace_symbols(array, size);
551 vector<string> outputStrings;
554 for (
size_t i = 1; i < size; i++)
557 const int lineNumberLen = 4;
558 string trimmedLine = strings[i] + lineNumberLen;
560 const int libraryLen = 36;
561 const int addressLen = 19;
562 string symbol = strings[i] + lineNumberLen + libraryLen + addressLen;
563 string instructionOffset = symbol.substr(symbol.find(
' '));
564 symbol = symbol.substr(0, symbol.find(
' '));
567 char *unmangled = abi::__cxa_demangle(symbol.c_str(),
nullptr,
nullptr, &status);
572 VuoLog_replaceString(unmangled,
"basic_string<char, char_traits<char>, allocator<char> >",
"string");
577 outputStrings.push_back(trimmedLine.substr(0, libraryLen + addressLen) + unmangled + instructionOffset);
581 outputStrings.push_back(trimmedLine);
588 auto last = outputStrings.back();
589 if (last.substr(0, 4) ==
"??? "
590 && last.find(
" 0x0 + ") != string::npos)
591 outputStrings.pop_back();
593 last = outputStrings.back();
594 if (last.substr(0, 13) ==
"libdyld.dylib"
595 && last.find(
" start + ") != string::npos)
596 outputStrings.pop_back();
599 remove_if(outputStrings.begin(), outputStrings.end(),
600 [](
const string &s) {
601 return s.find(
"_dispatch_queue_override_invoke") != string::npos
602 || s.find(
"_dispatch_root_queue_drain") != string::npos
603 || s.find(
"_dispatch_worker_thread2") != string::npos
604 || s.find(
"_dispatch_lane_serial_drain") != string::npos
605 || s.find(
"_dispatch_lane_barrier_sync_invoke_and_complete") != string::npos
606 || s.find(
"_dispatch_lane_invoke") != string::npos
607 || s.find(
"_dispatch_workloop_worker_thread") != string::npos
608 || s.find(
"_dispatch_continuation_pop") != string::npos
609 || s.find(
"_dispatch_main_queue_callback_4CF") != string::npos
610 || s.find(
"__CFRunLoopDoObservers") != string::npos
611 || s.find(
"__CFRunLoopDoSource0") != string::npos
612 || s.find(
"__CFRunLoopDoSources0") != string::npos
613 || s.find(
"__CFRunLoopRun") != string::npos
614 || s.find(
"__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__") != string::npos
615 || s.find(
"__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__") != string::npos
616 || s.find(
"__CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__") != string::npos
617 || s.find(
"_CFXRegistrationPost") != string::npos
618 || s.find(
"_CFXNotification") != string::npos
619 || s.find(
"_NSWindowSendWindowDidMove") != string::npos
620 || s.find(
"_NSSendEventToObservers") != string::npos
621 || s.find(
"NSCarbonMenuImpl") != string::npos
622 || s.find(
"NSSLMMenuEventHandler") != string::npos
623 || s.find(
"CopyCarbonUIElementAttributeValue") != string::npos
624 || s.find(
"NSApplication(NSEvent) _") != string::npos
625 || s.find(
"NSApplication(NSAppleEventHandling) _") != string::npos
626 || s.find(
"withWindowOrderingObserverHeuristic") != string::npos
627 || s.find(
"aeProcessAppleEvent") != string::npos
628 || s.find(
"dispatchEventAndSendReply") != string::npos
629 || s.find(
"aeDispatchAppleEvent") != string::npos
630 || s.find(
"_NSAppleEventManagerGenericHandler") != string::npos
631 || s.find(
"NSWindow _") != string::npos
632 || s.find(
"HIServices") != string::npos
633 || s.find(
"HIToolbox") != string::npos
634 || s.find(
"RunCurrentEventLoopInMode") != string::npos
635 || s.find(
"ReceiveNextEventCommon") != string::npos
636 || s.find(
"_BlockUntilNextEventMatchingListInModeWithFilter") != string::npos
637 || s.find(
"_DPSNextEvent") != string::npos
638 || s.find(
"_pthread_wqthread") != string::npos
639 || s.find(
"qt_plugin_instance") != string::npos
640 || s.find(
"QWindowSystemInterface::") != string::npos
641 || s.find(
"QWidgetPrivate::") != string::npos
642 || s.find(
"QApplicationPrivate::") != string::npos
643 || s.find(
"QGuiApplicationPrivate::") != string::npos
644 || s.find(
"QCoreApplication::notifyInternal2") != string::npos
645 || s.find(
"QCoreApplicationPrivate::") != string::npos;
647 outputStrings.end());
651 return outputStrings;