314 lines
9.1 KiB
C++
314 lines
9.1 KiB
C++
//------------------------------------------------------------------------
|
|
// Project : SDK Base
|
|
// Version : 1.0
|
|
//
|
|
// Category : Helpers
|
|
// Filename : base/source/fdebug.cpp
|
|
// Created by : Steinberg, 1995
|
|
// Description : There are 2 levels of debugging messages:
|
|
// DEVELOPMENT During development
|
|
// RELEASE Program is shipping.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
// LICENSE
|
|
// (c) 2018, Steinberg Media Technologies GmbH, All Rights Reserved
|
|
//-----------------------------------------------------------------------------
|
|
// Redistribution and use in source and binary forms, with or without modification,
|
|
// are permitted provided that the following conditions are met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright notice,
|
|
// this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
// this list of conditions and the following disclaimer in the documentation
|
|
// and/or other materials provided with the distribution.
|
|
// * Neither the name of the Steinberg Media Technologies nor the names of its
|
|
// contributors may be used to endorse or promote products derived from this
|
|
// software without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
|
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
|
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
|
// OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "base/source/fdebug.h"
|
|
|
|
#if DEVELOPMENT
|
|
|
|
#include <assert.h>
|
|
#include <cstdarg>
|
|
#include <cstdio>
|
|
|
|
#if SMTG_OS_WINDOWS
|
|
#ifndef _WIN32_WINNT
|
|
#define _WIN32_WINNT 0x0400
|
|
#endif
|
|
#include <intrin.h>
|
|
#include <windows.h>
|
|
#define vsnprintf _vsnprintf
|
|
#define snprintf _snprintf
|
|
|
|
#elif SMTG_OS_MACOS
|
|
#include <errno.h>
|
|
#include <mach/mach_init.h>
|
|
#include <mach/mach_time.h>
|
|
#include <new>
|
|
#include <signal.h>
|
|
#include <stdbool.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
static bool AmIBeingDebugged (void);
|
|
|
|
#define THREAD_ALLOC_WATCH 0 // check allocations on specific threads
|
|
|
|
#if THREAD_ALLOC_WATCH
|
|
mach_port_t watchThreadID = 0;
|
|
#endif
|
|
|
|
#endif
|
|
|
|
AssertionHandler gAssertionHandler = nullptr;
|
|
AssertionHandler gPreAssertionHook = nullptr;
|
|
DebugPrintLogger gDebugPrintLogger = nullptr;
|
|
|
|
namespace boost {
|
|
// Define the boost assertion handler to redirect to our assertion handler,
|
|
// otherwise it just calls abort(). Note that we don't need to include any boost
|
|
// headers for this, it just provides the handler.
|
|
void assertion_failed (char const* expr, char const* function, char const* file, long line)
|
|
{
|
|
#if DEVELOPMENT
|
|
char message[512];
|
|
snprintf (message, 512, "%s at %s, %s:%ld", expr, function, file, line);
|
|
if (gAssertionHandler)
|
|
{
|
|
FDebugBreak (message);
|
|
}
|
|
else
|
|
{
|
|
assert (!(const char *)message);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
static const int kDebugPrintfBufferSize = 10000;
|
|
static bool neverDebugger = false; // so I can switch it off in the debugger...
|
|
|
|
//--------------------------------------------------------------------------
|
|
static void printDebugString (const char* string)
|
|
{
|
|
if (!string)
|
|
return;
|
|
|
|
if (gDebugPrintLogger)
|
|
{
|
|
gDebugPrintLogger (string);
|
|
}
|
|
else
|
|
{
|
|
#if SMTG_OS_MACOS
|
|
fprintf (stderr, "%s", string);
|
|
#elif SMTG_OS_WINDOWS
|
|
OutputDebugStringA (string);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// printf style debugging output
|
|
//--------------------------------------------------------------------------
|
|
void FDebugPrint (const char* format, ...)
|
|
{
|
|
char string[kDebugPrintfBufferSize];
|
|
va_list marker;
|
|
va_start (marker, format);
|
|
vsnprintf (string, kDebugPrintfBufferSize, format, marker);
|
|
|
|
printDebugString (string);
|
|
}
|
|
|
|
#if SMTG_OS_WINDOWS
|
|
#define AmIBeingDebugged IsDebuggerPresent
|
|
#endif
|
|
|
|
#if SMTG_OS_LINUX
|
|
#include <signal.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
//--------------------------------------------------------------------------
|
|
static inline bool AmIBeingDebugged ()
|
|
{
|
|
// TODO: check if GDB or LLDB is attached
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
//--------------------------------------------------------------------------
|
|
// printf style debugging output
|
|
//--------------------------------------------------------------------------
|
|
void FDebugBreak (const char* format, ...)
|
|
{
|
|
char string[kDebugPrintfBufferSize];
|
|
va_list marker;
|
|
va_start (marker, format);
|
|
vsnprintf (string, kDebugPrintfBufferSize, format, marker);
|
|
|
|
printDebugString (string);
|
|
|
|
// The Pre-assertion hook is always called, even if we're not running in the debugger,
|
|
// so that we can log asserts without displaying them
|
|
if (gPreAssertionHook)
|
|
{
|
|
gPreAssertionHook (string);
|
|
}
|
|
|
|
if (neverDebugger)
|
|
return;
|
|
if (AmIBeingDebugged ())
|
|
{
|
|
// do not crash if no debugger present
|
|
// If there is an assertion handler defined then let this override the UI
|
|
// and tell us whether we want to break into the debugger
|
|
bool breakIntoDebugger = true;
|
|
if (gAssertionHandler && gAssertionHandler (string) == false)
|
|
{
|
|
breakIntoDebugger = false;
|
|
}
|
|
|
|
if (breakIntoDebugger)
|
|
{
|
|
#if SMTG_OS_WINDOWS
|
|
__debugbreak (); // intrinsic version of DebugBreak()
|
|
#elif __ppc64__ || __ppc__ || __arm__
|
|
kill (getpid (), SIGINT);
|
|
#elif __i386__ || __x86_64__
|
|
{
|
|
__asm__ volatile ("int3");
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
void FPrintLastError (const char* file, int line)
|
|
{
|
|
#if SMTG_OS_WINDOWS
|
|
LPVOID lpMessageBuffer;
|
|
FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
|
|
GetLastError (), MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
(LPSTR)&lpMessageBuffer, 0, NULL);
|
|
FDebugPrint ("%s(%d) : %s\n", file, line, lpMessageBuffer);
|
|
LocalFree (lpMessageBuffer);
|
|
#endif
|
|
|
|
#if SMTG_OS_MACOS
|
|
#if !__MACH__
|
|
extern int errno;
|
|
#endif
|
|
FDebugPrint ("%s(%d) : Errno %d\n", file, line, errno);
|
|
#endif
|
|
}
|
|
|
|
#if SMTG_OS_MACOS
|
|
|
|
//------------------------------------------------------------------------
|
|
void* operator new (size_t size, int, const char* file, int line)
|
|
{
|
|
#if THREAD_ALLOC_WATCH
|
|
mach_port_t threadID = mach_thread_self ();
|
|
if (watchThreadID == threadID)
|
|
{
|
|
FDebugPrint ("Watched Thread Allocation : %s (Line:%d)\n", file ? file : "Unknown", line);
|
|
}
|
|
#endif
|
|
try
|
|
{
|
|
return ::operator new (size);
|
|
}
|
|
catch (std::bad_alloc exception)
|
|
{
|
|
FDebugPrint ("bad_alloc exception : %s (Line:%d)", file ? file : "Unknown", line);
|
|
}
|
|
return (void*)-1;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void* operator new[] (size_t size, int, const char* file, int line)
|
|
{
|
|
#if THREAD_ALLOC_WATCH
|
|
mach_port_t threadID = mach_thread_self ();
|
|
if (watchThreadID == threadID)
|
|
{
|
|
FDebugPrint ("Watched Thread Allocation : %s (Line:%d)\n", file ? file : "Unknown", line);
|
|
}
|
|
#endif
|
|
try
|
|
{
|
|
return ::operator new[] (size);
|
|
}
|
|
catch (std::bad_alloc exception)
|
|
{
|
|
FDebugPrint ("bad_alloc exception : %s (Line:%d)", file ? file : "Unknown", line);
|
|
}
|
|
return (void*)-1;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void operator delete (void* p, int, const char* file, int line)
|
|
{
|
|
::operator delete (p);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void operator delete[] (void* p, int, const char* file, int line)
|
|
{
|
|
::operator delete[] (p);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// from Technical Q&A QA1361 (http://developer.apple.com/qa/qa2004/qa1361.html)
|
|
//------------------------------------------------------------------------
|
|
bool AmIBeingDebugged (void)
|
|
// Returns true if the current process is being debugged (either
|
|
// running under the debugger or has a debugger attached post facto).
|
|
{
|
|
int mib[4];
|
|
struct kinfo_proc info;
|
|
size_t size;
|
|
|
|
// Initialize the flags so that, if sysctl fails for some bizarre
|
|
// reason, we get a predictable result.
|
|
|
|
info.kp_proc.p_flag = 0;
|
|
|
|
// Initialize mib, which tells sysctl the info we want, in this case
|
|
// we're looking for information about a specific process ID.
|
|
|
|
mib[0] = CTL_KERN;
|
|
mib[1] = KERN_PROC;
|
|
mib[2] = KERN_PROC_PID;
|
|
mib[3] = getpid ();
|
|
|
|
// Call sysctl.
|
|
|
|
size = sizeof (info);
|
|
sysctl (mib, sizeof (mib) / sizeof (*mib), &info, &size, NULL, 0);
|
|
|
|
// We're being debugged if the P_TRACED flag is set.
|
|
return ((info.kp_proc.p_flag & P_TRACED) != 0);
|
|
}
|
|
|
|
#endif // SMTG_OS_MACOS
|
|
|
|
#endif // DEVELOPMENT
|