17#include <mach/mach_time.h>
18#include <mach-o/dyld.h>
20#include <objc/objc-runtime.h>
26#include <sys/sysctl.h>
31#include <CoreFoundation/CoreFoundation.h>
33#include <CoreGraphics/CoreGraphics.h>
34#include <CoreText/CoreText.h>
49 if (
auto ep = std::current_exception())
52 std::rethrow_exception(ep);
54 catch (std::exception
const &e)
57 const char *typeName =
typeid(e).name();
58 char *unmangled = abi::__cxa_demangle(typeName, 0, 0, &status);
62 VUserLog(
"Terminating due to uncaught %s: \"%s\"", typeName, e.what());
66 VUserLog(
"Terminating due to uncaught exception of unknown type");
71 VUserLog(
"Terminating because std::terminate was called (no exception data available)");
82 static mach_timebase_info_data_t info = {0,0};
85 kern_return_t ret = mach_timebase_info(&info);
86 if (ret != KERN_SUCCESS)
88 VUserLog(
"Failed to get mach timebase: %d", ret);
95 return (
double)mach_absolute_time() * info.numer / (info.denom * NSEC_PER_SEC);
115typedef std::map<std::string, VuoLogProfileEntry> VuoLogProfileType;
116static VuoLogProfileType *VuoLogProfile;
117static dispatch_queue_t VuoLogProfileQueue;
126static void __attribute__((constructor)) VuoLog_init(
void)
135 char executablePath[PATH_MAX + 1];
136 uint32_t size =
sizeof(executablePath);
137 if (!_NSGetExecutablePath(executablePath, &size))
141 VuoLogProfileQueue = dispatch_queue_create(
"VuoLogProfile", NULL);
142 VuoLogProfile =
new VuoLogProfileType;
146#if defined(VUO_PROFILE) || defined(DOXYGEN)
152 dispatch_sync(VuoLogProfileQueue, ^{
153 VuoLogProfileType::iterator i = VuoLogProfile->find(name);
154 if (i != VuoLogProfile->end())
156 i->second.total += time;
157 if (time < i->second.min)
158 i->second.min = time;
159 if (time > i->second.max)
160 i->second.max = time;
164 (*VuoLogProfile)[name] = (VuoLogProfileEntry){time, time, time, 1};
171extern "C" void __attribute__((destructor)) VuoLog_dumpProfile(
void)
173 dispatch_sync(VuoLogProfileQueue, ^{
175 for (VuoLogProfileType::iterator i = VuoLogProfile->begin(); i != VuoLogProfile->end(); ++i)
176 fprintf(stderr,
"%30s %12.9f s (%7.4f%%) (min %12.9f, max %12.9f, avg %12.9f, count %lld)\n",
179 i->second.total * 100. / totalRuntime,
182 i->second.total / i->second.count,
197#define VuoCrashReport_alignment __attribute__((aligned(8)))
214VuoCrashReport_infoType VuoCrashReport __attribute__((section(
"__DATA,__crash_info"))) = { 4, NULL, NULL, NULL, NULL, NULL, NULL };
216void VuoLog_statusF(
const char *moduleName,
const char *file,
const unsigned int linenumber,
const char *function,
const char *format, ...)
218 static dispatch_once_t statusInitialized = 0;
219 static dispatch_queue_t statusQueue;
220 dispatch_once(&statusInitialized, ^{
221 statusQueue = dispatch_queue_create(
"VuoLogStatus", NULL);
224 char *message =
nullptr;
228 va_start(args, format);
229 vasprintf(&message, format, args);
233 dispatch_sync(statusQueue, ^{
234 if (VuoCrashReport.message2)
235 free(VuoCrashReport.message2);
238 VuoCrashReport.message2 = message;
240 VuoCrashReport.message2 =
nullptr;
259 Class NSProcessInfoClass = objc_getClass(
"NSProcessInfo");
260 id processInfo = ((id(*)(id, SEL))objc_msgSend)((
id)NSProcessInfoClass, sel_getUid(
"processInfo"));
263 operatingSystemVersionType operatingSystemVersionFunc = (operatingSystemVersionType)objc_msgSend_stret;
265 operatingSystemVersionType operatingSystemVersionFunc = (operatingSystemVersionType)objc_msgSend;
267 NSOperatingSystemVersion operatingSystemVersion = operatingSystemVersionFunc(processInfo, sel_getUid(
"operatingSystemVersion"));
268 return operatingSystemVersion;
281 const struct mach_header_64 *mh =
reinterpret_cast<const mach_header_64 *
>(mh32);
284 if (mh->flags & MH_DYLIB_IN_CACHE)
288 Dl_info info{
"",
nullptr,
"",
nullptr};
289 dladdr((
void *)vmaddr_slide, &info);
292 if (strstr(info.dli_fname,
"/DVTInstrumentsFoundation.framework/"))
308 mib[2] = KERN_PROC_PID;
311 struct kinfo_proc info;
312 info.kp_proc.p_flag = 0;
313 size_t size =
sizeof(info);
314 if (sysctl(mib,
sizeof(mib) /
sizeof(*mib), &info, &size, NULL, 0) == 0
315 && info.kp_proc.p_flag & P_TRACED)
321 static once_flag once;
322 call_once(once, []() {
332extern struct mach_header __dso_handle;
334void VuoLog(
const char *moduleName,
const char *file,
const unsigned int linenumber,
const char *function,
const char *format, ...)
338 fprintf(stderr,
"VuoLog() error: Invalid 'moduleName' argument (%p). You may need to rebuild the module calling VuoLog().\n", moduleName);
344 fprintf(stderr,
"VuoLog() error: Invalid 'file' argument (%p). You may need to rebuild the module calling VuoLog().\n", file);
350 fprintf(stderr,
"VuoLog() error: Invalid 'function' argument (%p). You may need to rebuild the module calling VuoLog().\n", function);
356 fprintf(stderr,
"VuoLog() error: Invalid 'format' argument (%p). You may need to rebuild the module calling VuoLog().\n", format);
363 va_start(args, format);
364 int size = vsnprintf(NULL, 0, format, args);
367 char *formattedString = (
char *)malloc(size+1);
368 va_start(args, format);
369 vsnprintf(formattedString, size+1, format, args);
372 char *formattedFunction = NULL;
376 if (function && function[0] ==
'_' && function[1] ==
'_')
378 int actualFunctionLength = atoi(function + 2);
379 if (actualFunctionLength)
380 formattedFunction = strndup(function + 3 + (
int)log10(actualFunctionLength), actualFunctionLength);
382 formattedFunction = strndup(function + 2, strrchr(function + 2,
'_') - (function + 2));
387 if (strncmp(formattedFunction,
"_ZN", 3) == 0)
390 int len, priorLen = 0;
391 while ((len = atoi(formattedFunction + pos)))
393 pos += 1 + (int)log10(len) + len;
396 char *f2 = strndup(formattedFunction + pos - priorLen, priorLen);
397 free(formattedFunction);
398 formattedFunction = f2;
403 const char *blockInvokePos = strstr(function,
"_block_invoke");
405 formattedFunction = strndup(function, blockInvokePos - function);
411 const char *f = formattedFunction ? formattedFunction : function;
412 size_t fLen = strlen(f);
413 if (f[fLen - 1] !=
']')
415 size_t mallocSize = fLen + 2 + 1;
416 char *f2 = (
char *)malloc(mallocSize);
417 strlcpy(f2, f, mallocSize);
418 strlcat(f2,
"()", mallocSize);
419 if (formattedFunction)
420 free(formattedFunction);
421 formattedFunction = f2;
425 const char *formattedFile = file;
428 formattedFile =
"(unknown)";
431 if (
const char *lastSlash = strrchr(formattedFile,
'/'))
432 formattedFile = lastSlash + 1;
437 static double priorTime = 0;
438 const char *separator =
"";
439 if (priorTime > 0 && time - priorTime > 0.5)
444 pthread_t thread = pthread_self();
445 char threadName[256];
447 strcpy(threadName,
"main");
450 bzero(threadName, 256);
451 int ret = pthread_getname_np(thread, threadName, 256);
452 if (!(ret == 0 && strlen(threadName)))
453 snprintf(threadName, 256,
"%llx", (uint64_t)thread);
457 const char *mainColor =
"\033[97m";
459 const char *metadataColor =
"\033[38;5;249m";
461 const char *separatorColor =
"\033[90m";
465 int pidColor = getpid() % 144 + 88;
469 : ((uint64_t)thread) % 144 + 88;
471 fprintf(stderr,
"%s%s[%s%8.3fs%s] %s%12.12s%s:%s%-12.12s %s[\033[38;5;%dm%5d%s:\033[38;5;%dm%-8.8s%s] %s%20.20s%s:%s%-4u %32.32s %s%s\033[0m\n",
481 moduleName ? moduleName :
"Vuo",
494 formattedFunction ? formattedFunction : function,
504 typedef void (*vuoMacOsLogInternalType)(
void *dso,
void *log, uint8_t type,
const char *message, ...);
505 typedef void (*vuoMacOsLogImplType)(
void *dso,
void *log, uint8_t type,
const char *format, uint8_t *buf, uint32_t size);
506 static vuoMacOsLogInternalType vuoMacOsLogInternal = NULL;
507 static vuoMacOsLogImplType vuoMacOsLogImpl = NULL;
508 static dispatch_once_t once = 0;
509 static bool debuggerAttached =
false;
510 dispatch_once(&once, ^{
513 if (operatingSystemVersion.majorVersion == 10 && operatingSystemVersion.minorVersion == 12)
515 vuoMacOsLogInternal = (vuoMacOsLogInternalType)dlsym(RTLD_SELF,
"_os_log_internal");
516 else if ((operatingSystemVersion.majorVersion == 10 && operatingSystemVersion.minorVersion > 12)
517 || operatingSystemVersion.majorVersion > 10)
519 vuoMacOsLogImpl = (vuoMacOsLogImplType)dlsym(RTLD_SELF,
"_os_log_impl");
522 if (!debuggerAttached)
524 void *log = os_log_create(
"org.vuo", formattedFile);
526 if (vuoMacOsLogInternal)
527 vuoMacOsLogInternal(&__dso_handle, log, 0 ,
"%{public}41s:%-4u %{public}s", formattedFunction ? formattedFunction : function, linenumber, formattedString);
528 else if (vuoMacOsLogImpl)
530 char *formattedForOsLog;
531 asprintf(&formattedForOsLog,
"%41s:%-4u %s", formattedFunction ? formattedFunction : function, linenumber, formattedString);
534 uint8_t logFormatDescriptor[12] = {
541 memcpy(logFormatDescriptor + 4, &formattedForOsLog, 8);
543 vuoMacOsLogImpl(&__dso_handle, log, 0 ,
"%{public}s", logFormatDescriptor,
sizeof(logFormatDescriptor));
545 free(formattedForOsLog);
553 static dispatch_once_t historyInitialized = 0;
554 dispatch_once(&historyInitialized, ^{
563 char *formattedPrefixedString;
564 asprintf(&formattedPrefixedString,
"t=%8.4fs %27.27s:%-4u %41.41s %s", time, formattedFile, linenumber, formattedFunction ? formattedFunction : function, formattedString);
565 free(formattedString);
566 if (formattedFunction)
567 free(formattedFunction);
589 if (VuoCrashReport.message)
590 free(VuoCrashReport.message);
598 char *message = (
char *)malloc(size);
603 strlcat(message,
"\n", size);
607 VuoCrashReport.message = message;
622 static dispatch_once_t checked = 0;
623 static bool debug =
false;
624 dispatch_once(&checked, ^{
625 debug = CFPreferencesGetAppBooleanValue(CFSTR(
"debug"), CFSTR(
"org.vuo.Editor"), NULL);
638 backtrace.erase(backtrace.begin());
641 for (
auto line : backtrace)
642 fprintf(stderr,
"%3d %s\n", i++, line.c_str());
652 size_t replacementLen = strlen(replacement);
654 regcomp(®ex, substringToRemove, REG_EXTENDED);
656 regmatch_t pmatch[nmatch];
657 while (!regexec(®ex, wholeString, nmatch, pmatch, 0))
659 strncpy(wholeString + pmatch[0].rm_so, replacement, replacementLen);
660 memmove(wholeString + pmatch[0].rm_so + replacementLen, wholeString + pmatch[0].rm_eo, strlen(wholeString + pmatch[0].rm_eo) + 1);
671 size_t size = backtrace(array, 100);
672 char **strings = backtrace_symbols(array, size);
673 vector<string> outputStrings;
676 for (
size_t i = 1; i < size; i++)
679 const int lineNumberLen = 4;
680 string trimmedLine = strings[i] + lineNumberLen;
682 const int libraryLen = 36;
683 const int addressLen = 19;
684 string symbol = strings[i] + lineNumberLen + libraryLen + addressLen;
685 string instructionOffset = symbol.substr(symbol.find(
' '));
686 symbol = symbol.substr(0, symbol.find(
' '));
689 char *unmangled = abi::__cxa_demangle(symbol.c_str(),
nullptr,
nullptr, &status);
694 VuoLog_replaceString(unmangled,
"basic_string<char, char_traits<char>, allocator<char> >",
"string");
699 outputStrings.push_back(trimmedLine.substr(0, libraryLen + addressLen) + unmangled + instructionOffset);
703 outputStrings.push_back(trimmedLine);
710 auto last = outputStrings.back();
711 if (last.substr(0, 4) ==
"??? "
712 && last.find(
" 0x0 + ") != string::npos)
713 outputStrings.pop_back();
715 last = outputStrings.back();
716 if (last.substr(0, 13) ==
"libdyld.dylib"
717 && last.find(
" start + ") != string::npos)
718 outputStrings.pop_back();
721 remove_if(outputStrings.begin(), outputStrings.end(),
722 [](
const string &s) {
723 return s.find(
"_dispatch_queue_override_invoke") != string::npos
724 || s.find(
"_dispatch_root_queue_drain") != string::npos
725 || s.find(
"_dispatch_worker_thread2") != string::npos
726 || s.find(
"_dispatch_lane_serial_drain") != string::npos
727 || s.find(
"_dispatch_lane_barrier_sync_invoke_and_complete") != string::npos
728 || s.find(
"_dispatch_lane_invoke") != string::npos
729 || s.find(
"_dispatch_workloop_worker_thread") != string::npos
730 || s.find(
"_dispatch_continuation_pop") != string::npos
731 || s.find(
"_dispatch_main_queue_callback_4CF") != string::npos
732 || s.find(
"__CFRunLoopDoObservers") != string::npos
733 || s.find(
"__CFRunLoopDoSource0") != string::npos
734 || s.find(
"__CFRunLoopDoSources0") != string::npos
735 || s.find(
"__CFRunLoopRun") != string::npos
736 || s.find(
"__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__") != string::npos
737 || s.find(
"__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__") != string::npos
738 || s.find(
"__CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__") != string::npos
739 || s.find(
"_CFXRegistrationPost") != string::npos
740 || s.find(
"_CFXNotification") != string::npos
741 || s.find(
"_NSWindowSendWindowDidMove") != string::npos
742 || s.find(
"_NSSendEventToObservers") != string::npos
743 || s.find(
"NSCarbonMenuImpl") != string::npos
744 || s.find(
"NSSLMMenuEventHandler") != string::npos
745 || s.find(
"CopyCarbonUIElementAttributeValue") != string::npos
746 || s.find(
"NSApplication(NSEvent) _") != string::npos
747 || s.find(
"NSApplication(NSAppleEventHandling) _") != string::npos
748 || s.find(
"withWindowOrderingObserverHeuristic") != string::npos
749 || s.find(
"aeProcessAppleEvent") != string::npos
750 || s.find(
"dispatchEventAndSendReply") != string::npos
751 || s.find(
"aeDispatchAppleEvent") != string::npos
752 || s.find(
"_NSAppleEventManagerGenericHandler") != string::npos
753 || s.find(
"NSWindow _") != string::npos
754 || s.find(
"HIServices") != string::npos
755 || s.find(
"HIToolbox") != string::npos
756 || s.find(
"RunCurrentEventLoopInMode") != string::npos
757 || s.find(
"ReceiveNextEventCommon") != string::npos
758 || s.find(
"_BlockUntilNextEventMatchingListInModeWithFilter") != string::npos
759 || s.find(
"_DPSNextEvent") != string::npos
760 || s.find(
"_pthread_wqthread") != string::npos
761 || s.find(
"qt_plugin_instance") != string::npos
762 || s.find(
"QWindowSystemInterface::") != string::npos
763 || s.find(
"QWidgetPrivate::") != string::npos
764 || s.find(
"QApplicationPrivate::") != string::npos
765 || s.find(
"QGuiApplicationPrivate::") != string::npos
766 || s.find(
"QCoreApplication::notifyInternal2") != string::npos
767 || s.find(
"QCoreApplicationPrivate::") != string::npos;
769 outputStrings.end());
773 return outputStrings;