├── .travis.yml ├── LICENSE ├── README.md ├── appveyor.yml ├── bam.lua ├── include └── dbgtools │ ├── assert.h │ ├── callstack.h │ ├── debugger.h │ ├── fpe_ctrl.h │ ├── hw_breakpoint.h │ ├── static_assert.h │ └── static_assert_counter.h ├── src ├── assert.cpp ├── callstack.cpp ├── debugger.cpp ├── fpe_ctrl.cpp └── hw_breakpoint.cpp └── test ├── greatest.h ├── test_assert.cpp ├── test_callstack.c ├── test_callstack_cpp.cpp ├── test_debugger_present.c ├── test_fpe_ctrl.cpp ├── test_hw_breakpoint.c ├── test_static_assert.c ├── test_static_assert1.h ├── test_static_assert2.h └── test_static_assert_cpp.cpp /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | os: 3 | - linux 4 | - osx 5 | 6 | sudo: false 7 | 8 | env: 9 | matrix: 10 | - DBGTOOLS_CONFIG=debug 11 | - DBGTOOLS_CONFIG=release 12 | 13 | compiler: 14 | - gcc 15 | - clang 16 | 17 | install: 18 | - git clone https://github.com/matricks/bam.git 19 | - cd bam 20 | - ./make_unix.sh 21 | - cd .. 22 | 23 | script: 24 | - bam/bam compiler=$CC config=$DBGTOOLS_CONFIG 25 | - local/$DBGTOOLS_CONFIG/linux_x86_64/test_assert 26 | - local/$DBGTOOLS_CONFIG/linux_x86_64/test_callstack 27 | - local/$DBGTOOLS_CONFIG/linux_x86_64/test_callstack_cpp 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | dbgtools - platform independent wrapping of "nice to have" debug functions. 2 | 3 | version 0.1, october, 2013 4 | 5 | https://github.com/wc-duck/dbgtools 6 | 7 | Copyright (C) 2013- Fredrik Kihlander 8 | 9 | This software is provided 'as-is', without any express or implied 10 | warranty. In no event will the authors be held liable for any damages 11 | arising from the use of this software. 12 | 13 | Permission is granted to anyone to use this software for any purpose, 14 | including commercial applications, and to alter it and redistribute it 15 | freely, subject to the following restrictions: 16 | 17 | 1. The origin of this software must not be misrepresented; you must not 18 | claim that you wrote the original software. If you use this software 19 | in a product, an acknowledgment in the product documentation would be 20 | appreciated but is not required. 21 | 2. Altered source versions must be plainly marked as such, and must not be 22 | misrepresented as being the original software. 23 | 3. This notice may not be removed or altered from any source distribution. 24 | 25 | Fredrik Kihlander 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/wc-duck/dbgtools.svg?branch=master)](https://travis-ci.org/wc-duck/dbgtools) 2 | [![Build status](https://ci.appveyor.com/api/projects/status/ebe2yl78l9pv38rv)](https://ci.appveyor.com/project/wc-duck/dbgtools) 3 | 4 | # About: 5 | Platform independent wrapping of "nice to have" debug functions. 6 | 7 | * assert.h - implements a replacement for the standard assert() macro supporting callback at assert, and error-message with printf-format. 8 | * callstack.h - implements capturing of callstack/backtrace + translation of captured symbols into name, file, line and offset. 9 | * debugger.h - implements debugger_present to check if a debugger is attached to the process. 10 | * static_assert.h - defines the macro STATIC_ASSERT( condition, message_string ) in an "as good as possible way" depending on compiler features and support. It will try to use builtin support for static_assert and _Static_assert if possible. 11 | * fpe_ctrl.h - implements platform independent functions to get/set floating point exception and enable trapping of the same exceptions. 12 | * hw_breakpoint.h - implements platform independent hardware breakpoints. 13 | 14 | # Design: 15 | The files are designed to be able to be used by them self, only header and src should be needed by the user and all files 16 | should work by them self and compile with all common compilers without to many specific fixes. 17 | 18 | # Notes: 19 | * MSVC - callstack_symbols() require linking against Dbghelp.lib. 20 | * GCC/Clang - callstack_symbols() require -rdynamic to be sepcified as link-flag to get valid symbols. 21 | 22 | # Licence: 23 | 24 | ``` 25 | dbgtools - platform independent wrapping of "nice to have" debug functions. 26 | 27 | version 0.1, october, 2013 28 | 29 | Copyright (C) 2013- Fredrik Kihlander 30 | 31 | This software is provided 'as-is', without any express or implied 32 | warranty. In no event will the authors be held liable for any damages 33 | arising from the use of this software. 34 | 35 | Permission is granted to anyone to use this software for any purpose, 36 | including commercial applications, and to alter it and redistribute it 37 | freely, subject to the following restrictions: 38 | 39 | 1. The origin of this software must not be misrepresented; you must not 40 | claim that you wrote the original software. If you use this software 41 | in a product, an acknowledgment in the product documentation would be 42 | appreciated but is not required. 43 | 2. Altered source versions must be plainly marked as such, and must not be 44 | misrepresented as being the original software. 45 | 3. This notice may not be removed or altered from any source distribution. 46 | 47 | Fredrik Kihlander 48 | ``` 49 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | platform: 2 | - x86 3 | 4 | environment: 5 | matrix: 6 | - DBGTOOLS_PLATFORM: win32 7 | DBGTOOLS_CONFIG: debug 8 | - DBGTOOLS_PLATFORM: win32 9 | DBGTOOLS_CONFIG: release 10 | - DBGTOOLS_PLATFORM: winx64 11 | DBGTOOLS_CONFIG: debug 12 | - DBGTOOLS_PLATFORM: winx64 13 | DBGTOOLS_CONFIG: release 14 | 15 | install: 16 | - if [%DBGTOOLS_PLATFORM%]==[win32] call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86 17 | - if [%DBGTOOLS_PLATFORM%]==[winx64] call "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64 18 | - if [%DBGTOOLS_PLATFORM%]==[winx64] call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86_amd64 19 | - git clone https://github.com/matricks/bam.git 20 | - cd bam 21 | - make_win64_msvc.bat 22 | - cd .. 23 | - bam\bam.exe config=%DBGTOOLS_CONFIG% 24 | 25 | build: OFF -------------------------------------------------------------------------------- /bam.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | dbgtools - platform independent wrapping of "nice to have" debug functions. 3 | 4 | version 0.1, october, 2013 5 | 6 | Copyright (C) 2013- Fredrik Kihlander 7 | 8 | This software is provided 'as-is', without any express or implied 9 | warranty. In no event will the authors be held liable for any damages 10 | arising from the use of this software. 11 | 12 | Permission is granted to anyone to use this software for any purpose, 13 | including commercial applications, and to alter it and redistribute it 14 | freely, subject to the following restrictions: 15 | 16 | 1. The origin of this software must not be misrepresented; you must not 17 | claim that you wrote the original software. If you use this software 18 | in a product, an acknowledgment in the product documentation would be 19 | appreciated but is not required. 20 | 2. Altered source versions must be plainly marked as such, and must not be 21 | misrepresented as being the original software. 22 | 3. This notice may not be removed or altered from any source distribution. 23 | 24 | Fredrik Kihlander 25 | --]] 26 | 27 | BUILD_PATH = "local" 28 | 29 | function get_config() 30 | local config = ScriptArgs["config"] 31 | if config == nil then 32 | return "debug" 33 | end 34 | return config 35 | end 36 | 37 | function get_platform() 38 | local platform = ScriptArgs["platform"] 39 | if platform == nil then 40 | if family == "windows" then 41 | platform = "winx64" 42 | else 43 | platform = "linux_x86_64" 44 | end 45 | end 46 | return platform 47 | end 48 | 49 | function get_base_settings() 50 | local settings = {} 51 | 52 | settings._is_settingsobject = true 53 | settings.invoke_count = 0 54 | settings.debug = 0 55 | settings.optimize = 0 56 | SetCommonSettings(settings) 57 | 58 | -- add all tools 59 | for _, tool in pairs(_bam_tools) do 60 | tool(settings) 61 | end 62 | 63 | return settings 64 | end 65 | 66 | function set_compiler( settings, config ) 67 | if family == "windows" then 68 | compiler = "msvc" 69 | else 70 | compiler = ScriptArgs["compiler"] 71 | if compiler == nil then 72 | compiler = "gcc" 73 | end 74 | end 75 | 76 | InitCommonCCompiler(settings) 77 | if compiler == "msvc" then 78 | SetDriversCL( settings ) 79 | 80 | settings.link.flags:Add( "/NODEFAULTLIB:LIBCMT.LIB" ); 81 | settings.cc.defines:Add("_ITERATOR_DEBUG_LEVEL=0") 82 | if config == "release" then 83 | settings.cc.flags:Add( "/Ox" ) 84 | end 85 | 86 | elseif compiler == "gcc" then 87 | SetDriversGCC( settings ) 88 | settings.cc.flags:Add( "-Wconversion", "-Wextra", "-Wall", "-Werror", "-Wstrict-aliasing=2" ) 89 | settings.link.flags:Add( '-rdynamic' ) 90 | if config == "release" then 91 | settings.cc.flags:Add( "-O2" ) 92 | end 93 | elseif compiler == "clang" then 94 | SetDriversClang( settings ) 95 | settings.cc.flags:Add( "-Wconversion", "-Wextra", "-Wall", "-Werror", "-Wstrict-aliasing=2" ) 96 | settings.link.flags:Add( '-rdynamic' ) 97 | if config == "release" then 98 | settings.cc.flags:Add( "-O2" ) 99 | end 100 | end 101 | end 102 | 103 | config = get_config() 104 | platform = get_platform() 105 | settings = get_base_settings() 106 | set_compiler( settings, config ) 107 | TableLock( settings ) 108 | 109 | local output_path = PathJoin( BUILD_PATH, PathJoin( config, platform ) ) 110 | local output_func = function(settings, path) return PathJoin(output_path, PathFilename(PathBase(path)) .. settings.config_ext) end 111 | settings.cc.Output = output_func 112 | settings.lib.Output = output_func 113 | settings.link.Output = output_func 114 | 115 | settings.cc.defines:Add("DBG_TOOLS_ASSERT_ENABLE") 116 | settings.cc.includes:Add( 'include' ) 117 | 118 | local debugger_obj = Compile( settings, 'src/debugger.cpp' ) 119 | local callstack_obj = Compile( settings, 'src/callstack.cpp' ) 120 | local assert_obj = Compile( settings, 'src/assert.cpp' ) 121 | local fpe_ctrl_obj = Compile( settings, 'src/fpe_ctrl.cpp' ) 122 | local hw_breok_obj = Compile( settings, 'src/hw_breakpoint.cpp' ) 123 | 124 | Compile( settings, 'test/test_static_assert.c' ) 125 | Compile( settings, 'test/test_static_assert_cpp.cpp' ) 126 | 127 | Link( settings, 'test_debugger', debugger_obj, Compile( settings, 'test/test_debugger_present.c' ) ) 128 | Link( settings, 'test_callstack', callstack_obj, Compile( settings, 'test/test_callstack.c' ) ) 129 | Link( settings, 'test_callstack_cpp', callstack_obj, Compile( settings, 'test/test_callstack_cpp.cpp' ) ) 130 | Link( settings, 'test_assert', assert_obj, Compile( settings, 'test/test_assert.cpp' ) ) 131 | Link( settings, 'test_fpe_ctrl', fpe_ctrl_obj, Compile( settings, 'test/test_fpe_ctrl.cpp' ) ) 132 | Link( settings, 'test_hw_breakpoint', hw_breok_obj, Compile( settings, 'test/test_hw_breakpoint.c' ) ) 133 | -------------------------------------------------------------------------------- /include/dbgtools/assert.h: -------------------------------------------------------------------------------- 1 | /* 2 | dbgtools - platform independent wrapping of "nice to have" debug functions. 3 | 4 | version 0.1, october, 2013 5 | 6 | https://github.com/wc-duck/dbgtools 7 | 8 | Copyright (C) 2013- Fredrik Kihlander 9 | 10 | This software is provided 'as-is', without any express or implied 11 | warranty. In no event will the authors be held liable for any damages 12 | arising from the use of this software. 13 | 14 | Permission is granted to anyone to use this software for any purpose, 15 | including commercial applications, and to alter it and redistribute it 16 | freely, subject to the following restrictions: 17 | 18 | 1. The origin of this software must not be misrepresented; you must not 19 | claim that you wrote the original software. If you use this software 20 | in a product, an acknowledgment in the product documentation would be 21 | appreciated but is not required. 22 | 2. Altered source versions must be plainly marked as such, and must not be 23 | misrepresented as being the original software. 24 | 3. This notice may not be removed or altered from any source distribution. 25 | 26 | Fredrik Kihlander 27 | */ 28 | 29 | #ifndef DBGTOOLS_ASSERT_INCLUDED 30 | #define DBGTOOLS_ASSERT_INCLUDED 31 | 32 | /** 33 | * return value used by assert callback to determine action of assert. 34 | */ 35 | enum assert_action 36 | { 37 | ASSERT_ACTION_NONE, ///< continue execution after callback returns. 38 | ASSERT_ACTION_BREAK ///< break into debugger when callstack returns. 39 | }; 40 | 41 | /** 42 | * callback signature for callback to attach to assert. 43 | * 44 | * @param cond assert condition that failed as string. 45 | * @param msg assert message provided, or "" no one was provided. 46 | * @param file file where assert was triggered. 47 | * @param line line where assert was triggered. 48 | * @param userdata pointer passed together will callback to assert_register_callback. 49 | * 50 | * @return the action to take when callback returns. 51 | */ 52 | typedef assert_action (*assert_callback_t)( const char* cond, const char* msg, const char* file, unsigned int line, void* user_data ); 53 | 54 | /** 55 | * register a callback to call when an assert is triggered. 56 | */ 57 | void assert_register_callback( assert_callback_t callback, void* user_data ); 58 | 59 | /** 60 | * macro that "asserts" that a condition is true, if not it breaks into the debugger. 61 | * @note if ASSERT_ENABLE is not defined it expands to a noop. 62 | * 63 | * @example ASSERT( a == 1 ); // trigger if a != 1 64 | * @example ASSERT( a == 1, "a was not == to 1" ); // trigger if a != 1 and reports "a was not == to 1" 65 | * @example ASSERT( a == 1, "a == %d", a ); // trigger if a != 1 and reports "a == 1337" if a is 1337 that is! 66 | */ 67 | #define ASSERT(cond, ...) ((void)sizeof( cond )) 68 | 69 | /** 70 | * macro that "asserts" that a condition is true, if not it breaks into the debugger. 71 | * @note if ASSERT_ENABLE is not defined it expands to only the condition. 72 | */ 73 | #define VERIFY(cond, ...) ((void)(cond)) 74 | 75 | /** 76 | * macro inserting a breakpoint into the code that breaks into the debugger on most platforms. 77 | */ 78 | #define DBG_TOOLS_BREAKPOINT 79 | 80 | assert_action assert_call_trampoline( const char* file, unsigned int line, const char* cond ); 81 | assert_action assert_call_trampoline( const char* file, unsigned int line, const char* cond, const char* fmt, ... ); 82 | 83 | 84 | 85 | // ... private implementation ... 86 | 87 | #undef DBG_TOOLS_BREAKPOINT 88 | 89 | #if defined ( _MSC_VER ) 90 | # define DBG_TOOLS_BREAKPOINT __debugbreak() 91 | #elif defined( __GNUC__ ) 92 | # if defined(__i386__) || defined( __x86_64__ ) 93 | void inline __attribute__((always_inline)) _dbg_tools_gcc_break_helper() { __asm("int3"); } 94 | # define DBG_TOOLS_BREAKPOINT _dbg_tools_gcc_break_helper() 95 | # else 96 | # define DBG_TOOLS_BREAKPOINT __builtin_trap() 97 | # endif 98 | #else 99 | # define DBG_TOOLS_BREAKPOINT exit(1) 100 | #endif 101 | 102 | #ifdef DBG_TOOLS_ASSERT_ENABLE 103 | #undef ASSERT 104 | #undef VERIFY 105 | 106 | #if defined( _MSC_VER ) 107 | #define ASSERT(cond, ...) ( (void)( ( !(cond) ) && ( assert_call_trampoline( __FILE__, __LINE__, #cond, __VA_ARGS__ ) == ASSERT_ACTION_BREAK ) && ( DBG_TOOLS_BREAKPOINT, 1 ) ) ) 108 | #define VERIFY(cond, ...) ASSERT( cond, __VA_ARGS__ ) 109 | #elif defined( __GNUC__ ) 110 | #define ASSERT(cond, args...) ( (void)( ( __builtin_expect(!(cond), 0) ) && ( assert_call_trampoline( __FILE__, __LINE__, #cond, ##args ) == ASSERT_ACTION_BREAK ) && ( DBG_TOOLS_BREAKPOINT, 1 ) ) ) 111 | #define VERIFY(cond, args...) ASSERT( cond, ##args ) 112 | #endif 113 | #else 114 | inline void assert_register_callback( assert_callback_t, void* ) {} 115 | #endif // DBG_TOOLS_ASSERT_ENABLE 116 | 117 | #endif // DBGTOOLS_ASSERT_INCLUDED 118 | -------------------------------------------------------------------------------- /include/dbgtools/callstack.h: -------------------------------------------------------------------------------- 1 | /* 2 | dbgtools - platform independent wrapping of "nice to have" debug functions. 3 | 4 | https://github.com/wc-duck/dbgtools 5 | 6 | version 0.1, october, 2013 7 | 8 | Copyright (C) 2013- Fredrik Kihlander 9 | 10 | This software is provided 'as-is', without any express or implied 11 | warranty. In no event will the authors be held liable for any damages 12 | arising from the use of this software. 13 | 14 | Permission is granted to anyone to use this software for any purpose, 15 | including commercial applications, and to alter it and redistribute it 16 | freely, subject to the following restrictions: 17 | 18 | 1. The origin of this software must not be misrepresented; you must not 19 | claim that you wrote the original software. If you use this software 20 | in a product, an acknowledgment in the product documentation would be 21 | appreciated but is not required. 22 | 2. Altered source versions must be plainly marked as such, and must not be 23 | misrepresented as being the original software. 24 | 3. This notice may not be removed or altered from any source distribution. 25 | 26 | Fredrik Kihlander 27 | */ 28 | 29 | #ifndef DEBUG_CALLSTACK_H_INCLUDED 30 | #define DEBUG_CALLSTACK_H_INCLUDED 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif // __cplusplus 35 | 36 | typedef struct 37 | { 38 | const char* function; ///< name of function containing address of function. 39 | const char* file; ///< file where symbol is defined, might not work on all platforms. 40 | unsigned int line; ///< line in file where symbol is defined, might not work on all platforms. 41 | unsigned int offset; ///< offset from start of function where call was made. 42 | } callstack_symbol_t; 43 | 44 | /** 45 | * Generate a callstack from the current location in the code. 46 | * @param skip_frames number of frames to skip in output to addresses. 47 | * @param addresses is a pointer to a buffer where to store addresses in callstack. 48 | * @param num_addresses size of addresses. 49 | * @return number of addresses in callstack. 50 | */ 51 | int callstack( int skip_frames, void** addresses, int num_addresses ); 52 | 53 | /** 54 | * Translate addresses from, for example, callstack to symbol-names. 55 | * @param addresses list of pointers to translate. 56 | * @param out_syms list of callstack_symbol_t to fill with translated data, need to fit as many strings as there are ptrs in addresses. 57 | * @param num_addresses number of addresses in addresses 58 | * @param memory memory used to allocate strings stored in out_syms. 59 | * @param mem_size size of addresses. 60 | * @return number of addresses translated. 61 | * 62 | * @note On windows this will load dbghelp.dll dynamically from the following paths: 63 | * 1) same path as the current module (.exe) 64 | * 2) current working directory. 65 | * 3) the usual search-paths ( PATH etc ). 66 | * 67 | * Some thing to be wary of is that if you are using symbol-server functionality symsrv.dll MUST reside together with 68 | * the dbghelp.dll that is loaded as dbghelp.dll will only load that from the same path as where it self lives. 69 | * 70 | * @note On windows .pdb search paths will be set in the same way as dbghelp-defaults + the current module (.exe) dir, i.e.: 71 | * 1) same path as the current module (.exe) 72 | * 2) current working directory. 73 | * 3) The _NT_SYMBOL_PATH environment variable. 74 | * 4) The _NT_ALTERNATE_SYMBOL_PATH environment variable. 75 | * 76 | * @note On platforms that support it debug-output can be enabled by defining the environment variable DBGTOOLS_SYMBOL_DEBUG_OUTPUT. 77 | */ 78 | int callstack_symbols( void** addresses, callstack_symbol_t* out_syms, int num_addresses, char* memory, int mem_size ); 79 | 80 | #ifdef __cplusplus 81 | } 82 | #endif // __cplusplus 83 | 84 | #endif // DEBUG_CALLSTACK_H_INCLUDED 85 | -------------------------------------------------------------------------------- /include/dbgtools/debugger.h: -------------------------------------------------------------------------------- 1 | /* 2 | dbgtools - platform independent wrapping of "nice to have" debug functions. 3 | 4 | https://github.com/wc-duck/dbgtools 5 | 6 | version 0.1, october, 2013 7 | 8 | Copyright (C) 2013- Fredrik Kihlander 9 | 10 | This software is provided 'as-is', without any express or implied 11 | warranty. In no event will the authors be held liable for any damages 12 | arising from the use of this software. 13 | 14 | Permission is granted to anyone to use this software for any purpose, 15 | including commercial applications, and to alter it and redistribute it 16 | freely, subject to the following restrictions: 17 | 18 | 1. The origin of this software must not be misrepresented; you must not 19 | claim that you wrote the original software. If you use this software 20 | in a product, an acknowledgment in the product documentation would be 21 | appreciated but is not required. 22 | 2. Altered source versions must be plainly marked as such, and must not be 23 | misrepresented as being the original software. 24 | 3. This notice may not be removed or altered from any source distribution. 25 | 26 | Fredrik Kihlander 27 | */ 28 | 29 | #ifndef DEBUG_DEBUGGER_H_INCLUDED 30 | #define DEBUG_DEBUGGER_H_INCLUDED 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif // __cplusplus 35 | 36 | /** 37 | * Return non-zero if a debugger is currently attached to this process. 38 | * 39 | * @note this might on some platforms be quite slow. 40 | */ 41 | int debugger_present(); 42 | 43 | #ifdef __cplusplus 44 | } 45 | #endif // __cplusplus 46 | 47 | #endif // DEBUG_DEBUGGER_H_INCLUDED 48 | -------------------------------------------------------------------------------- /include/dbgtools/fpe_ctrl.h: -------------------------------------------------------------------------------- 1 | /* 2 | dbgtools - platform independent wrapping of "nice to have" debug functions. 3 | 4 | version 0.1, october, 2013 5 | 6 | Copyright (C) 2013- Fredrik Kihlander 7 | 8 | This software is provided 'as-is', without any express or implied 9 | warranty. In no event will the authors be held liable for any damages 10 | arising from the use of this software. 11 | 12 | Permission is granted to anyone to use this software for any purpose, 13 | including commercial applications, and to alter it and redistribute it 14 | freely, subject to the following restrictions: 15 | 16 | 1. The origin of this software must not be misrepresented; you must not 17 | claim that you wrote the original software. If you use this software 18 | in a product, an acknowledgment in the product documentation would be 19 | appreciated but is not required. 20 | 2. Altered source versions must be plainly marked as such, and must not be 21 | misrepresented as being the original software. 22 | 3. This notice may not be removed or altered from any source distribution. 23 | 24 | Fredrik Kihlander 25 | */ 26 | 27 | #ifndef DBGTOOLS_FPE_CTRL_INCLUDED 28 | #define DBGTOOLS_FPE_CTRL_INCLUDED 29 | 30 | #if defined( __cplusplus ) 31 | extern "C" { 32 | #endif 33 | 34 | #if defined( _MSC_VER ) 35 | #include 36 | #else 37 | #include 38 | #endif 39 | 40 | /** 41 | * Enumeration used together with fpe_* functions. 42 | */ 43 | enum fp_exception_type 44 | { 45 | #if defined( _MSC_VER ) 46 | fp_exception_invalid = _EM_INVALID, ///< ... 47 | fp_exception_div_by_zero = _EM_ZERODIVIDE, ///< ... 48 | fp_exception_overflow = _EM_OVERFLOW, ///< ... 49 | fp_exception_underflow = _EM_UNDERFLOW, ///< ... 50 | fp_exception_inexact = _EM_INEXACT, ///< ... 51 | #else 52 | fp_exception_invalid = FE_INVALID, ///< ... 53 | fp_exception_div_by_zero = FE_DIVBYZERO, ///< ... 54 | fp_exception_overflow = FE_OVERFLOW, ///< ... 55 | fp_exception_underflow = FE_UNDERFLOW, ///< ... 56 | fp_exception_inexact = FE_INEXACT, ///< ... 57 | #endif 58 | 59 | fp_exception_all = fp_exception_invalid | 60 | fp_exception_div_by_zero | 61 | fp_exception_overflow | 62 | fp_exception_underflow | 63 | fp_exception_inexact 64 | }; 65 | 66 | // get/set fp exception flag. 67 | 68 | /** 69 | * Clear the floating point exception register according to bitmask. 70 | * @param except bits to clear in floating point register. 71 | * @return 0 on success, otherwise -1 72 | */ 73 | int fpe_clear( unsigned int except ); 74 | 75 | /** 76 | * Try to raise floating point exceptions specified by except. 77 | * @note order exceptions are raised is undefined if except has multiple exceptions set. 78 | * @return 0 on success, otherwise -1 79 | */ 80 | int fpe_raise( unsigned int except ); 81 | 82 | /** 83 | * Return the bitwise or between except and currently set bits in floating point exception register. 84 | */ 85 | unsigned int fpe_test( unsigned int except ); 86 | 87 | // enable/disable interrupt. 88 | 89 | /** 90 | * Enables trap by raising SIGFPE when an floating point exception is raised. 91 | * 92 | * @param except bitfield of fp_exception_type to enable. 93 | * @return 0 on success, otherwise -1 94 | */ 95 | int fpe_enable_trap( unsigned int except ); 96 | 97 | /** 98 | * Disables trap by raising SIGFPE when an floating point exception is raised. 99 | * 100 | * @param except bitfield of fp_exception_type to disable. 101 | * @return 0 on success, otherwise -1 102 | */ 103 | int fpe_disable_trap( unsigned int except ); 104 | 105 | /** 106 | * Set what floating point exceptions that is trapped by raising SIGFPE. 107 | * 108 | * @param except bitfield of fp_exception_type to set, the non-set are cleared. 109 | * @return 0 on success, otherwise -1 110 | */ 111 | int fpe_set_trapped( unsigned int except ); 112 | 113 | /** 114 | * Return a bitfield of the currently trapped floating point exceptions. 115 | */ 116 | unsigned int fpe_get_trapped(); 117 | 118 | #if defined( __cplusplus ) 119 | } 120 | #endif 121 | 122 | 123 | #if defined( __cplusplus ) 124 | 125 | /** 126 | * Enable trap of floating point exception while object of this type is alive. 127 | * 128 | * @example 129 | * 130 | * { 131 | * // ... not capturing divide by zero ... 132 | * 133 | * { 134 | * fpe_enable_scope es( fp_exception_div_by_zero ); 135 | * 136 | * // ... capturing divide by zero exceptions ... 137 | * } 138 | * 139 | * // ... not capturing divide by zero ... 140 | * } 141 | */ 142 | class fpe_enable_scope 143 | { 144 | unsigned int old_except; 145 | 146 | /** 147 | * @param except_flags mask of floating point exceptions to trap in scope. 148 | */ 149 | fpe_enable_scope( unsigned int except_flags ) 150 | : old_except( fpe_get_trapped() ) 151 | { 152 | fpe_enable_trap( except_flags ); 153 | } 154 | 155 | ~fpe_enable_scope() 156 | { 157 | fpe_set_trapped( old_except ); 158 | } 159 | }; 160 | 161 | /** 162 | * Disable trap of floating point exception while object of this type is alive. 163 | * 164 | * @example 165 | * 166 | * { 167 | * // ... capturing divide by zero ... 168 | * 169 | * { 170 | * fpe_disable_scope es( fp_exception_div_by_zero ); 171 | * 172 | * // ... not capturing divide by zero exceptions ... 173 | * } 174 | * 175 | * // ... capturing divide by zero ... 176 | * } 177 | */ 178 | class fpe_disable_scope 179 | { 180 | unsigned int old_except; 181 | 182 | /** 183 | * 184 | * @param except_flags mask of floating point exceptions to not trap in scope. 185 | */ 186 | fpe_disable_scope( unsigned int except_flags ) 187 | : old_except( fpe_get_trapped() ) 188 | { 189 | fpe_disable_trap( except_flags ); 190 | } 191 | 192 | ~fpe_disable_scope() 193 | { 194 | fpe_set_trapped( old_except ); 195 | } 196 | }; 197 | 198 | #endif // defined( __cplusplus ) 199 | 200 | #endif // DBGTOOLS_FPE_CTRL_INCLUDED 201 | -------------------------------------------------------------------------------- /include/dbgtools/hw_breakpoint.h: -------------------------------------------------------------------------------- 1 | /* 2 | dbgtools - platform independent wrapping of "nice to have" debug functions. 3 | 4 | version 0.1, october, 2013 5 | 6 | https://github.com/wc-duck/dbgtools 7 | 8 | Copyright (C) 2013- Fredrik Kihlander 9 | 10 | This software is provided 'as-is', without any express or implied 11 | warranty. In no event will the authors be held liable for any damages 12 | arising from the use of this software. 13 | 14 | Permission is granted to anyone to use this software for any purpose, 15 | including commercial applications, and to alter it and redistribute it 16 | freely, subject to the following restrictions: 17 | 18 | 1. The origin of this software must not be misrepresented; you must not 19 | claim that you wrote the original software. If you use this software 20 | in a product, an acknowledgment in the product documentation would be 21 | appreciated but is not required. 22 | 2. Altered source versions must be plainly marked as such, and must not be 23 | misrepresented as being the original software. 24 | 3. This notice may not be removed or altered from any source distribution. 25 | 26 | Fredrik Kihlander 27 | */ 28 | 29 | #ifndef DBGTOOLS_HWBREAKPOINT_INCLUDED 30 | #define DBGTOOLS_HWBREAKPOINT_INCLUDED 31 | 32 | /** 33 | * Breakpoint type used in hw_breakpoint_set() 34 | */ 35 | enum hw_breakpoint_type 36 | { 37 | HW_BREAKPOINT_READ, 38 | HW_BREAKPOINT_WRITE, 39 | HW_BREAKPOINT_READWRITE, 40 | }; 41 | 42 | /** 43 | * Error codes from hw_breakpoint_set() 44 | */ 45 | #define HW_BREAKPOINT_ERROR_INVALID_ARG (-1) // invalid argument was passed to hw_breakpoint_set such as a bad size. 46 | #define HW_BREAKPOINT_ERROR_OUT_OF_SLOTS (-2) // to many hardware breakpoints are already set. 47 | #define HW_BREAKPOINT_ERROR_UNKNOWN (-3) // unknown error! ( please report bug-report or bug-fix :) ) 48 | #define HW_BREAKPOINT_ERROR_NOT_SUPPORTED (-4) // hardware breakpoints aren't supported on this platform. 49 | 50 | #ifdef __cplusplus 51 | extern "C" { 52 | #endif // __cplusplus 53 | 54 | /** 55 | * Set a hardware breakpoint at the specified memory-address that will trigger a SIGSEGV on access. 56 | * @param address to break on. 57 | * @param size of bytes to monitor, valid values are 1, 2, 4, 8 58 | * @param type type of hw-breakpoint. 59 | * @return value >= 0 on success, otherwise errorcode. 60 | */ 61 | int hw_breakpoint_set( void* address, unsigned int size, enum hw_breakpoint_type type ); 62 | 63 | /** 64 | * Clear a previously set hardware breakpoint set with hw_breakpoint_set(). 65 | * @param hwbp value returned by hw_breakpoint_set(). 66 | */ 67 | void hw_breakpoint_clear( int hwbp ); 68 | 69 | #ifdef __cplusplus 70 | } 71 | #endif // __cplusplus 72 | 73 | #if defined(__cplusplus) 74 | 75 | /** 76 | * Utility class to handle scoped hw_breakpoint_set/hw_breakpoint_clear 77 | */ 78 | class hw_breakpoint_enable_scope 79 | { 80 | int hwbp; 81 | 82 | hw_breakpoint_enable_scope( void* address, unsigned int size, enum hw_breakpoint_type type ) 83 | : hwbp( hw_breakpoint_set( address, size, type ) ) 84 | { 85 | } 86 | 87 | ~hw_breakpoint_enable_scope() 88 | { 89 | if( hwbp >= 0 ) 90 | hw_breakpoint_clear( hwbp ); 91 | } 92 | }; 93 | 94 | #endif // defined(__cplusplus) 95 | 96 | #endif // DBGTOOLS_HWBREAKPOINT_INCLUDED 97 | 98 | -------------------------------------------------------------------------------- /include/dbgtools/static_assert.h: -------------------------------------------------------------------------------- 1 | /* 2 | dbgtools - platform independent wrapping of "nice to have" debug functions. 3 | 4 | version 0.1, october, 2013 5 | 6 | https://github.com/wc-duck/dbgtools 7 | 8 | Copyright (C) 2013- Fredrik Kihlander 9 | 10 | This software is provided 'as-is', without any express or implied 11 | warranty. In no event will the authors be held liable for any damages 12 | arising from the use of this software. 13 | 14 | Permission is granted to anyone to use this software for any purpose, 15 | including commercial applications, and to alter it and redistribute it 16 | freely, subject to the following restrictions: 17 | 18 | 1. The origin of this software must not be misrepresented; you must not 19 | claim that you wrote the original software. If you use this software 20 | in a product, an acknowledgment in the product documentation would be 21 | appreciated but is not required. 22 | 2. Altered source versions must be plainly marked as such, and must not be 23 | misrepresented as being the original software. 24 | 3. This notice may not be removed or altered from any source distribution. 25 | 26 | Fredrik Kihlander 27 | */ 28 | 29 | #ifndef DBGTOOLS_STATIC_ASSERT_INCLUDED 30 | #define DBGTOOLS_STATIC_ASSERT_INCLUDED 31 | 32 | /** 33 | * Compile-time check that condition is true, if false a compile-time error will be generated. 34 | * This macro tries to use a built in variant of static assert to generate as good errors as possible but fall back to a generic implementation 35 | * if that is not available. 36 | * 37 | * @note msg will be ignored and an "as good as possible" message will get displayed instead if compiler has no built in support for static assert. 38 | * 39 | * @note macro do unfortunately not work within structs in its c-implementation =/ 40 | * @note on compilers that do not support __COUNTER__ static_assert_counter.h will be used to generate a counter. This however forces the user to include 41 | * static_assert.h in each file where it is used ( as should be done anyways ). 42 | * 43 | * @example STATIC_ASSERT( sizeof( int ) == 4, "size of int is not 4!" ); 44 | */ 45 | #define STATIC_ASSERT(cond, msg) 46 | #undef STATIC_ASSERT 47 | 48 | // ... clang ... 49 | #if defined( __clang__ ) 50 | # if defined( __cplusplus ) && __has_feature(cxx_static_assert) 51 | # define STATIC_ASSERT( cond, msg ) static_assert( cond, msg ) 52 | # elif __has_feature(c_static_assert) 53 | # define STATIC_ASSERT( cond, msg ) _Static_assert( cond, msg ) 54 | # endif 55 | 56 | // ... msvc ... 57 | #elif defined( _MSC_VER ) && ( defined(_MSC_VER) && (_MSC_VER >= 1600) ) 58 | # define STATIC_ASSERT( cond, msg ) static_assert( cond, msg ) 59 | 60 | // ... gcc ... 61 | #elif defined( __cplusplus ) 62 | # if __cplusplus >= 201103L || ( defined(_MSC_VER) && (_MSC_VER >= 1600) ) 63 | # define STATIC_ASSERT( cond, msg ) static_assert( cond, msg ) 64 | # endif 65 | #elif defined( __STDC__ ) 66 | # if defined( __STDC_VERSION__ ) 67 | # if __STDC_VERSION__ >= 201112L 68 | # define STATIC_ASSERT( cond, msg ) _Static_assert( cond, msg ) 69 | # else 70 | # define STATIC_ASSERT( cond, msg ) _Static_assert( cond, msg ) 71 | # endif 72 | # endif 73 | #endif 74 | 75 | /* if we couldn't detect a builtin static assert, lets define one! */ 76 | #ifndef STATIC_ASSERT 77 | # if defined( __COUNTER__ ) 78 | # define DBGTOOLS_STATIC_ASSERT_MACRO_TOKENS_DO(line) DBGTOOLS_STATIC_ASSERT_MACRO_TOKENS_DO1(__COUNTER__,line) 79 | # else 80 | # define DBGTOOLS_STATIC_ASSERT_MACRO_TOKENS_DO(line) DBGTOOLS_STATIC_ASSERT_MACRO_TOKENS_DO1(DBG_TOOLS_STATIC_ASSERT_COUNTER,line) 81 | # endif 82 | # define DBGTOOLS_STATIC_ASSERT_MACRO_TOKENS_DO1(count,line) DBGTOOLS_STATIC_ASSERT_MACRO_TOKENS_DO2(count,line) 83 | # define DBGTOOLS_STATIC_ASSERT_MACRO_TOKENS_DO2(count,line) static_assert_##count##_at_line_##line 84 | # 85 | # if defined( _MSC_VER ) 86 | # if defined( __cplusplus ) 87 | template struct DBG_TOOLS_STATIC_ASSERT_IMPL; 88 | template<> struct DBG_TOOLS_STATIC_ASSERT_IMPL {}; 89 | # define STATIC_ASSERT( cond, msg ) struct DBGTOOLS_STATIC_ASSERT_MACRO_TOKENS_DO( __LINE__ ) { DBG_TOOLS_STATIC_ASSERT_IMPL<(cond)> a; } 90 | # else 91 | # define STATIC_ASSERT( cond, msg ) enum { DBGTOOLS_STATIC_ASSERT_MACRO_TOKENS_DO( __LINE__ ) = 1 / (!!(cond)) } 92 | # endif 93 | # else 94 | # define STATIC_ASSERT( cond, msg ) typedef char DBGTOOLS_STATIC_ASSERT_MACRO_TOKENS_DO( __LINE__ )[ (cond) ? 1 : -1 ] __attribute__ ((unused)) 95 | # endif 96 | #endif 97 | 98 | #endif 99 | 100 | // This inclusion is outside the ifdef on purpose. 101 | #if !defined( __COUNTER__ ) 102 | # include "static_assert_counter.h" 103 | #endif 104 | -------------------------------------------------------------------------------- /include/dbgtools/static_assert_counter.h: -------------------------------------------------------------------------------- 1 | /* 2 | dbgtools - platform independent wrapping of "nice to have" debug functions. 3 | 4 | version 0.1, october, 2013 5 | 6 | https://github.com/wc-duck/dbgtools 7 | 8 | Copyright (C) 2013- Fredrik Kihlander 9 | 10 | This software is provided 'as-is', without any express or implied 11 | warranty. In no event will the authors be held liable for any damages 12 | arising from the use of this software. 13 | 14 | Permission is granted to anyone to use this software for any purpose, 15 | including commercial applications, and to alter it and redistribute it 16 | freely, subject to the following restrictions: 17 | 18 | 1. The origin of this software must not be misrepresented; you must not 19 | claim that you wrote the original software. If you use this software 20 | in a product, an acknowledgment in the product documentation would be 21 | appreciated but is not required. 22 | 2. Altered source versions must be plainly marked as such, and must not be 23 | misrepresented as being the original software. 24 | 3. This notice may not be removed or altered from any source distribution. 25 | 26 | Fredrik Kihlander 27 | */ 28 | 29 | /** 30 | * Auto-generated header implementing a counter that increases by each include of the file. 31 | * 32 | * This header will define the macro DBG_TOOLS_STATIC_ASSERT_COUNTER to be increased for each inclusion of the file. 33 | * 34 | * It has been generated with 3 amount of digits resulting in the counter wrapping around after 35 | * 10000 inclusions. 36 | * 37 | * Usage: 38 | * 39 | * #include "this_header.h" 40 | * int a = DBG_TOOLS_STATIC_ASSERT_COUNTER; // 0 41 | * #include "this_header.h" 42 | * int b = DBG_TOOLS_STATIC_ASSERT_COUNTER; // 1 43 | * #include "this_header.h" 44 | * int c = DBG_TOOLS_STATIC_ASSERT_COUNTER; // 2 45 | * #include "this_header.h" 46 | * int d = DBG_TOOLS_STATIC_ASSERT_COUNTER; // 3 47 | */ 48 | 49 | #ifndef DBG_TOOLS_STATIC_ASSERT_COUNTER 50 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_0 0 51 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_1 52 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_2 53 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_3 54 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_0 55 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_0 56 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_0 57 | #endif // DBG_TOOLS_STATIC_ASSERT_COUNTER 58 | 59 | #if !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D0_0 ) 60 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D0_0 61 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_0 62 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_0 0 63 | #elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D0_1 ) 64 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D0_1 65 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_0 66 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_0 1 67 | #elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D0_2 ) 68 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D0_2 69 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_0 70 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_0 2 71 | #elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D0_3 ) 72 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D0_3 73 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_0 74 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_0 3 75 | #elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D0_4 ) 76 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D0_4 77 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_0 78 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_0 4 79 | #elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D0_5 ) 80 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D0_5 81 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_0 82 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_0 5 83 | #elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D0_6 ) 84 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D0_6 85 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_0 86 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_0 6 87 | #elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D0_7 ) 88 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D0_7 89 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_0 90 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_0 7 91 | #elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D0_8 ) 92 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D0_8 93 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_0 94 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_0 8 95 | #elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D0_9 ) 96 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D0_9 97 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_0 98 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_0 9 99 | #else 100 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D0_1 101 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D0_2 102 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D0_3 103 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D0_4 104 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D0_5 105 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D0_6 106 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D0_7 107 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D0_8 108 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D0_9 109 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_0 110 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_0 0 111 | # if !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_0 ) 112 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_0 113 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_1 114 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_1 0 115 | # elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_1 ) 116 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_1 117 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_1 118 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_1 1 119 | # elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_2 ) 120 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_2 121 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_1 122 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_1 2 123 | # elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_3 ) 124 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_3 125 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_1 126 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_1 3 127 | # elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_4 ) 128 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_4 129 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_1 130 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_1 4 131 | # elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_5 ) 132 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_5 133 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_1 134 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_1 5 135 | # elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_6 ) 136 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_6 137 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_1 138 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_1 6 139 | # elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_7 ) 140 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_7 141 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_1 142 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_1 7 143 | # elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_8 ) 144 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_8 145 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_1 146 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_1 8 147 | # elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_9 ) 148 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_9 149 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_1 150 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_1 9 151 | # else 152 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_1 153 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_2 154 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_3 155 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_4 156 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_5 157 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_6 158 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_7 159 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_8 160 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D1_9 161 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_1 162 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_1 0 163 | # if !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_0 ) 164 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_0 165 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_2 166 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_2 0 167 | # elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_1 ) 168 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_1 169 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_2 170 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_2 1 171 | # elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_2 ) 172 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_2 173 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_2 174 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_2 2 175 | # elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_3 ) 176 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_3 177 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_2 178 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_2 3 179 | # elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_4 ) 180 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_4 181 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_2 182 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_2 4 183 | # elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_5 ) 184 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_5 185 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_2 186 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_2 5 187 | # elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_6 ) 188 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_6 189 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_2 190 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_2 6 191 | # elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_7 ) 192 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_7 193 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_2 194 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_2 7 195 | # elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_8 ) 196 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_8 197 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_2 198 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_2 8 199 | # elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_9 ) 200 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_9 201 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_2 202 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_2 9 203 | # else 204 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_1 205 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_2 206 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_3 207 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_4 208 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_5 209 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_6 210 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_7 211 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_8 212 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D2_9 213 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_2 214 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_2 0 215 | # if !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_0 ) 216 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_0 217 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_3 218 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_3 0 219 | # elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_1 ) 220 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_1 221 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_3 222 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_3 1 223 | # elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_2 ) 224 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_2 225 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_3 226 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_3 2 227 | # elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_3 ) 228 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_3 229 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_3 230 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_3 3 231 | # elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_4 ) 232 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_4 233 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_3 234 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_3 4 235 | # elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_5 ) 236 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_5 237 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_3 238 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_3 5 239 | # elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_6 ) 240 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_6 241 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_3 242 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_3 6 243 | # elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_7 ) 244 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_7 245 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_3 246 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_3 7 247 | # elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_8 ) 248 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_8 249 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_3 250 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_3 8 251 | # elif !defined( DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_9 ) 252 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_9 253 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_3 254 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_3 9 255 | # else 256 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_1 257 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_2 258 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_3 259 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_4 260 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_5 261 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_6 262 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_7 263 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_8 264 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_D3_9 265 | # undef DBG_TOOLS_STATIC_ASSERT_COUNTER_3 266 | # define DBG_TOOLS_STATIC_ASSERT_COUNTER_3 0 267 | # endif 268 | # endif 269 | # endif 270 | #endif 271 | 272 | #define DBG_TOOLS_STATIC_ASSERT_COUNTER_JOIN_DIGITS_MACRO_(digit0,digit1,digit2,digit3) digit0##digit1##digit2##digit3 273 | #define DBG_TOOLS_STATIC_ASSERT_COUNTER_JOIN_DIGITS_MACRO(digit0,digit1,digit2,digit3) DBG_TOOLS_STATIC_ASSERT_COUNTER_JOIN_DIGITS_MACRO_(digit0,digit1,digit2,digit3) 274 | #undef DBG_TOOLS_STATIC_ASSERT_COUNTER 275 | #define DBG_TOOLS_STATIC_ASSERT_COUNTER DBG_TOOLS_STATIC_ASSERT_COUNTER_JOIN_DIGITS_MACRO(DBG_TOOLS_STATIC_ASSERT_COUNTER_3,DBG_TOOLS_STATIC_ASSERT_COUNTER_2,DBG_TOOLS_STATIC_ASSERT_COUNTER_1,DBG_TOOLS_STATIC_ASSERT_COUNTER_0) 276 | 277 | -------------------------------------------------------------------------------- /src/assert.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | dbgtools - platform independent wrapping of "nice to have" debug functions. 3 | 4 | https://github.com/wc-duck/dbgtools 5 | 6 | version 0.1, october, 2013 7 | 8 | Copyright (C) 2013- Fredrik Kihlander 9 | 10 | This software is provided 'as-is', without any express or implied 11 | warranty. In no event will the authors be held liable for any damages 12 | arising from the use of this software. 13 | 14 | Permission is granted to anyone to use this software for any purpose, 15 | including commercial applications, and to alter it and redistribute it 16 | freely, subject to the following restrictions: 17 | 18 | 1. The origin of this software must not be misrepresented; you must not 19 | claim that you wrote the original software. If you use this software 20 | in a product, an acknowledgment in the product documentation would be 21 | appreciated but is not required. 22 | 2. Altered source versions must be plainly marked as such, and must not be 23 | misrepresented as being the original software. 24 | 3. This notice may not be removed or altered from any source distribution. 25 | 26 | Fredrik Kihlander 27 | */ 28 | 29 | #ifdef DBG_TOOLS_ASSERT_ENABLE 30 | #include 31 | 32 | #include 33 | #include 34 | 35 | assert_callback_t g_assert_callback = 0x0; 36 | void* g_assert_callback_data = 0x0; 37 | 38 | void assert_register_callback( assert_callback_t callback, void* user_data ) 39 | { 40 | g_assert_callback = callback; 41 | g_assert_callback_data = user_data; 42 | } 43 | 44 | assert_action assert_call_trampoline(const char* file, unsigned int line, const char* cond) 45 | { 46 | if( g_assert_callback != 0x0 ) 47 | return g_assert_callback(cond, "", file, line, g_assert_callback_data); 48 | return ASSERT_ACTION_BREAK; 49 | } 50 | 51 | assert_action assert_call_trampoline(const char* file, unsigned int line, const char* cond, const char* fmt, ...) 52 | { 53 | 54 | if( g_assert_callback == 0x0 ) 55 | return ASSERT_ACTION_BREAK; 56 | char buffer[2048]; 57 | va_list list; 58 | va_start( list, fmt ); 59 | vsnprintf(buffer, 2048, fmt, list); 60 | va_end(list); 61 | buffer[2048 - 1] = 0; 62 | return g_assert_callback( cond, buffer, file, line, g_assert_callback_data ); 63 | } 64 | #endif // DBG_TOOLS_ASSERT_ENABLE 65 | -------------------------------------------------------------------------------- /src/callstack.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | dbgtools - platform independent wrapping of "nice to have" debug functions. 3 | 4 | https://github.com/wc-duck/dbgtools 5 | 6 | version 0.1, october, 2013 7 | 8 | Copyright (C) 2013- Fredrik Kihlander 9 | 10 | This software is provided 'as-is', without any express or implied 11 | warranty. In no event will the authors be held liable for any damages 12 | arising from the use of this software. 13 | 14 | Permission is granted to anyone to use this software for any purpose, 15 | including commercial applications, and to alter it and redistribute it 16 | freely, subject to the following restrictions: 17 | 18 | 1. The origin of this software must not be misrepresented; you must not 19 | claim that you wrote the original software. If you use this software 20 | in a product, an acknowledgment in the product documentation would be 21 | appreciated but is not required. 22 | 2. Altered source versions must be plainly marked as such, and must not be 23 | misrepresented as being the original software. 24 | 3. This notice may not be removed or altered from any source distribution. 25 | 26 | Fredrik Kihlander 27 | */ 28 | 29 | #include 30 | 31 | #if defined( __unix__ ) || defined(unix) || defined(__unix) || ( defined(__APPLE__) && defined(__MACH__) ) 32 | # define DBG_TOOLS_CALLSTACK_UNIX 33 | #endif 34 | 35 | #include 36 | 37 | #if defined( DBG_TOOLS_CALLSTACK_UNIX ) || defined(_MSC_VER) 38 | typedef struct 39 | { 40 | char* out_ptr; 41 | const char* end_ptr; 42 | } callstack_string_buffer_t; 43 | 44 | static const char* alloc_string( callstack_string_buffer_t* buf, const char* str, size_t str_len ) 45 | { 46 | char* res; 47 | 48 | if( (size_t)(buf->end_ptr - buf->out_ptr) < str_len + 1 ) 49 | return ""; 50 | 51 | res = buf->out_ptr; 52 | buf->out_ptr += str_len + 1; 53 | memcpy( res, str, str_len ); 54 | res[str_len] = '\0'; 55 | return res; 56 | } 57 | #endif 58 | 59 | #if defined( DBG_TOOLS_CALLSTACK_UNIX ) 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | 67 | int callstack( int skip_frames, void** addresses, int num_addresses ) 68 | { 69 | ++skip_frames; 70 | void* trace[256]; 71 | int fetched = backtrace( trace, num_addresses + skip_frames ) - skip_frames; 72 | memcpy( addresses, trace + skip_frames, (size_t)fetched * sizeof(void*) ); 73 | return fetched; 74 | } 75 | 76 | #if defined(__linux) 77 | #include 78 | 79 | static uint16_t read_elf_type_from_self() 80 | { 81 | uint16_t type = 0; 82 | 83 | // lets check our own elf-type, first open our own exe! 84 | FILE* f = fopen("/proc/self/exe", "r"); 85 | if(f) 86 | { 87 | // we only need header-data at the start of the exe. 88 | // more to the point, 2 bytes at offset 0x10 that is 89 | // the elf-type. 90 | // https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header 91 | char elf_header[0x10 + 1] {0}; 92 | size_t read = fread(elf_header, sizeof(elf_header), 1, f); 93 | fclose(f); 94 | 95 | if(read == 1) 96 | memcpy(&type, elf_header + 0x10, sizeof(type)); 97 | } 98 | 99 | return type; 100 | } 101 | 102 | // detect if the current running exe is compiled as PIE. 103 | // https://en.wikipedia.org/wiki/Position-independent_code 104 | // if it does we need to translate all addresses that we are going to 105 | // lookup with the help of the address-maps in /proc/self/maps, if not 106 | // we should not do that. 107 | static int is_using_pie() 108 | { 109 | // elf-type for shared object/PIE-executable 110 | const uint16_t ET_DYN = 0x03; 111 | return read_elf_type_from_self() == ET_DYN; 112 | } 113 | 114 | typedef struct mmap_entry_t { 115 | struct mmap_entry_t *next; 116 | 117 | unsigned long range_start; 118 | unsigned long range_end; 119 | unsigned long file_offset; 120 | } mmap_entry_t; 121 | 122 | static mmap_entry_t *parse_mmaps(const char *maps_file) 123 | { 124 | FILE* maps = fopen(maps_file, "r"); 125 | if(!maps){ 126 | perror("fopen"); 127 | fprintf(stderr, "failed to open %s\n", maps_file); 128 | return NULL; 129 | } 130 | 131 | mmap_entry_t *first = NULL; 132 | mmap_entry_t *cur = NULL; 133 | 134 | while(!feof(maps)){ 135 | char buf[4096]; 136 | char *ret = fgets(buf, 4096, maps); 137 | if(!ret) 138 | break; 139 | 140 | mmap_entry_t *next = (mmap_entry_t*) malloc(sizeof(mmap_entry_t)); 141 | next->next = NULL; 142 | 143 | // from man `proc`: 144 | // address perms offset dev inode pathname 145 | sscanf(buf, "%lx-%lx %*s %lx", 146 | &next->range_start, 147 | &next->range_end, 148 | &next->file_offset 149 | ); 150 | 151 | if(!first){ 152 | first = next; 153 | cur = next; 154 | } else { 155 | cur->next = next; 156 | cur = next; 157 | } 158 | } 159 | 160 | fclose(maps); 161 | 162 | return first; 163 | } 164 | 165 | static void mmap_free(mmap_entry_t *mmap) 166 | { 167 | while(mmap){ 168 | mmap_entry_t *tmp = mmap->next; 169 | free(mmap); 170 | mmap = tmp; 171 | } 172 | } 173 | 174 | static void *mmap_translate(mmap_entry_t *mmap, void *addr) 175 | { 176 | for(; mmap != NULL; mmap = mmap->next){ 177 | unsigned long addr_i = (unsigned long)addr; 178 | if(addr_i >= mmap->range_start && addr_i <= mmap->range_end) 179 | return (void*) (addr_i - mmap->range_start + mmap->file_offset); 180 | } 181 | 182 | return addr; 183 | } 184 | 185 | static FILE* run_addr2line( void** addresses, int num_addresses, char* tmp_buffer, size_t tmp_buf_len ) 186 | { 187 | mmap_entry_t* list = is_using_pie() 188 | ? parse_mmaps("/proc/self/maps") 189 | : 0x0; 190 | 191 | size_t start = (size_t)snprintf( tmp_buffer, tmp_buf_len, "addr2line -e /proc/%u/exe", getpid() ); 192 | for( int i = 0; i < num_addresses; ++i ){ 193 | // Translate addresses out of potentially ASLR'd VMA space 194 | void *addr = mmap_translate(list, addresses[i]); 195 | 196 | start += (size_t)snprintf( tmp_buffer + start, tmp_buf_len - start, " %p", addr ); 197 | } 198 | 199 | mmap_free(list); 200 | 201 | return popen( tmp_buffer, "r" ); 202 | } 203 | #elif defined(__APPLE__) && defined(__MACH__) 204 | static FILE* run_addr2line( void** addresses, int num_addresses, char* tmp_buffer, size_t tmp_buf_len ) 205 | { 206 | size_t start = (size_t)snprintf( tmp_buffer, tmp_buf_len, "xcrun atos -p %u -l", getpid() ); 207 | for( int i = 0; i < num_addresses; ++i ) 208 | start += (size_t)snprintf( tmp_buffer + start, tmp_buf_len - start, " %p", addresses[i] ); 209 | 210 | return popen( tmp_buffer, "r" ); 211 | } 212 | #else 213 | # error "Unhandled platform" 214 | #endif 215 | 216 | 217 | static char* demangle_symbol( char* symbol, char* buffer, size_t buffer_size ) 218 | { 219 | int status; 220 | char* demangled_symbol = abi::__cxa_demangle( symbol, buffer, &buffer_size, &status ); 221 | return status != 0 ? symbol : demangled_symbol; 222 | } 223 | 224 | int callstack_symbols( void** addresses, callstack_symbol_t* out_syms, int num_addresses, char* memory, int mem_size ) 225 | { 226 | int num_translated = 0; 227 | callstack_string_buffer_t outbuf = { memory, memory + mem_size }; 228 | memset( out_syms, 0x0, (size_t)num_addresses * sizeof(callstack_symbol_t) ); 229 | 230 | char** syms = backtrace_symbols( addresses, num_addresses ); 231 | size_t tmp_buf_len = 1024 * 32; 232 | char* tmp_buffer = (char*)malloc( tmp_buf_len ); 233 | 234 | FILE* addr2line = run_addr2line( addresses, num_addresses, tmp_buffer, tmp_buf_len ); 235 | 236 | for( int i = 0; i < num_addresses; ++i ) 237 | { 238 | char* symbol = syms[i]; 239 | unsigned int offset = 0; 240 | 241 | // find function name and offset 242 | #if defined(__linux) 243 | char* name_start = strchr( symbol, '(' ); 244 | char* offset_start = name_start ? strchr( name_start, '+' ) : 0x0; 245 | 246 | if( name_start && offset_start ) 247 | { 248 | // zero terminate all strings 249 | ++name_start; 250 | *offset_start = '\0'; ++offset_start; 251 | } 252 | #elif defined(__APPLE__) && defined(__MACH__) 253 | char* name_start = 0x0; 254 | char* offset_start = strrchr( symbol, '+' ); 255 | if( offset_start ) 256 | { 257 | offset_start[-1] = '\0'; ++offset_start; 258 | name_start = strrchr( symbol, ' ' ); 259 | if( name_start ) 260 | ++name_start; 261 | } 262 | #else 263 | # error "Unhandled platform" 264 | #endif 265 | 266 | if( name_start && offset_start ) 267 | { 268 | offset = (unsigned int)strtoll( offset_start, 0x0, 16 ); 269 | symbol = demangle_symbol( name_start, tmp_buffer, tmp_buf_len ); 270 | } 271 | 272 | out_syms[i].function = alloc_string( &outbuf, symbol, strlen( symbol ) ); 273 | out_syms[i].offset = offset; 274 | out_syms[i].file = "failed to lookup file"; 275 | out_syms[i].line = 0; 276 | 277 | if( addr2line != 0x0 ) 278 | { 279 | if( fgets( tmp_buffer, (int)tmp_buf_len, addr2line ) != 0x0 ) 280 | { 281 | #if defined(__linux) 282 | char* line_start = strchr( tmp_buffer, ':' ); 283 | *line_start = '\0'; 284 | 285 | if( tmp_buffer[0] != '?' && tmp_buffer[1] != '?' ) 286 | out_syms[i].file = alloc_string( &outbuf, tmp_buffer, strlen( tmp_buffer ) ); 287 | out_syms[i].line = (unsigned int)strtoll( line_start + 1, 0x0, 10 ); 288 | #elif defined(__APPLE__) && defined(__MACH__) 289 | char* file_start = strrchr( tmp_buffer, '('); 290 | if( file_start ) 291 | { 292 | ++file_start; 293 | char* line_start = strchr( file_start, ':' ); 294 | if( line_start ) 295 | { 296 | *line_start = '\0'; 297 | ++line_start; 298 | 299 | out_syms[i].file = alloc_string( &outbuf, file_start, strlen( file_start ) ); 300 | out_syms[i].line = (unsigned int)strtoll( line_start, 0x0, 10 ); 301 | } 302 | } 303 | #else 304 | # error "Unhandled platform" 305 | #endif 306 | } 307 | } 308 | 309 | ++num_translated; 310 | } 311 | free( syms ); 312 | free( tmp_buffer ); 313 | fclose( addr2line ); 314 | return num_translated; 315 | } 316 | 317 | #elif defined(_MSC_VER) 318 | # if defined(__clang__) 319 | // when compiling with clang on windows, silence warnings from windows-code 320 | # pragma clang diagnostic push 321 | # pragma clang diagnostic ignored "-Wunknown-pragmas" 322 | # pragma clang diagnostic ignored "-Wignored-pragma-intrinsic" 323 | # endif 324 | # include 325 | # if defined(__clang__) 326 | # pragma clang diagnostic pop 327 | # endif 328 | #include 329 | 330 | int callstack( int skip_frames, void** addresses, int num_addresses ) 331 | { 332 | return RtlCaptureStackBackTrace( skip_frames + 1, num_addresses, addresses, 0 ); 333 | } 334 | 335 | typedef BOOL (__stdcall *SymInitialize_f)( _In_ HANDLE hProcess, _In_opt_ PCSTR UserSearchPath, _In_ BOOL fInvadeProcess ); 336 | typedef BOOL (__stdcall *SymFromAddr_f)( _In_ HANDLE hProcess, _In_ DWORD64 Address, _Out_opt_ PDWORD64 Displacement, _Inout_ PSYMBOL_INFO Symbol ); 337 | typedef BOOL (__stdcall *SymGetLineFromAddr64_f)( _In_ HANDLE hProcess, _In_ DWORD64 qwAddr, _Out_ PDWORD pdwDisplacement, _Out_ PIMAGEHLP_LINE64 Line64 ); 338 | typedef DWORD (__stdcall *SymSetOptions_f)( _In_ DWORD SymOptions ); 339 | 340 | static struct 341 | { 342 | int initialized; 343 | int init_ok; 344 | SymInitialize_f SymInitialize; 345 | SymFromAddr_f SymFromAddr; 346 | SymGetLineFromAddr64_f SymGetLineFromAddr64; 347 | SymSetOptions_f SymSetOptions; 348 | } 349 | dbghelp = { 0, 0, 0, 0, 0, 0 }; 350 | 351 | static HMODULE callstack_symbols_find_dbghelp() 352 | { 353 | // ... try to find dbghelp.dll in the same directory as the executable ... 354 | HMODULE mod; 355 | DWORD module_path_len; 356 | char module_path[4096]; 357 | 358 | module_path_len = GetModuleFileName((HMODULE)0, module_path, sizeof(module_path)); 359 | if (module_path_len > 0) 360 | { 361 | char* slash = strrchr(module_path, '\\'); 362 | if (slash) 363 | *(slash + 1) = '\0'; 364 | strncat(module_path, "dbghelp.dll", sizeof(module_path) - strlen(module_path) - 1); 365 | 366 | mod = LoadLibrary(module_path); 367 | if (mod) 368 | return mod; 369 | } 370 | 371 | // ... try to find dbghelp.dll in the current working directory ... 372 | module_path_len = GetCurrentDirectory( sizeof(module_path), module_path ); 373 | if (module_path_len > 0) 374 | { 375 | strncat(module_path, "\\dbghelp.dll", sizeof(module_path) - strlen(module_path) - 1); 376 | 377 | mod = LoadLibrary(module_path); 378 | if (mod) 379 | return mod; 380 | } 381 | 382 | // ... fallback to the "normal" search-paths ... 383 | return LoadLibrary("dbghelp.dll"); 384 | } 385 | 386 | static void callstack_symbols_build_search_path(char* search_path, int length) 387 | { 388 | // by default dbghelp will build a search-path by concatenating: 389 | // - The current working directory of the application 390 | // - The _NT_SYMBOL_PATH environment variable 391 | // - The _NT_ALTERNATE_SYMBOL_PATH environment variable 392 | // 393 | // let's do the same thing but also add the directory where the running 394 | // module lives. 395 | DWORD mod; 396 | char env_var[4096]; 397 | 398 | search_path[0] = '\0'; 399 | 400 | mod = GetModuleFileName((HMODULE)0, search_path, length); 401 | if (mod > 0) 402 | { 403 | char* slash = strrchr(search_path, '\\'); 404 | if(slash) 405 | *slash = '\0'; 406 | } 407 | else 408 | { 409 | search_path[0] = '\0'; 410 | } 411 | 412 | if (length > 2) 413 | { 414 | if (search_path[0] != '\0') 415 | strncat(search_path, ";", length); 416 | strncat(search_path, ".", length); 417 | } 418 | 419 | mod = GetEnvironmentVariable("_NT_SYMBOL_PATH", env_var, sizeof(env_var)); 420 | if (mod > 0 && mod < sizeof(env_var)) 421 | { 422 | if (search_path[0] != '\0') 423 | strncat(search_path, ";", length); 424 | strncat(search_path, env_var, length); 425 | } 426 | 427 | mod = GetEnvironmentVariable("_NT_ALTERNATE_SYMBOL_PATH", env_var, sizeof(env_var)); 428 | if (mod > 0 && mod < sizeof(env_var)) 429 | { 430 | if (search_path[0] != '\0') 431 | strncat(search_path, ";", length); 432 | strncat(search_path, env_var, length); 433 | } 434 | } 435 | 436 | static int callstack_symbols_initialize() 437 | { 438 | HANDLE process; 439 | HMODULE mod; 440 | BOOL res; 441 | DWORD err; 442 | 443 | if(!dbghelp.initialized) 444 | { 445 | mod = callstack_symbols_find_dbghelp(); 446 | if (mod) 447 | { 448 | dbghelp.SymInitialize = (SymInitialize_f)GetProcAddress(mod, "SymInitialize"); 449 | dbghelp.SymFromAddr = (SymFromAddr_f)GetProcAddress(mod, "SymFromAddr"); 450 | dbghelp.SymGetLineFromAddr64 = (SymGetLineFromAddr64_f)GetProcAddress(mod, "SymGetLineFromAddr64"); 451 | dbghelp.SymSetOptions = (SymSetOptions_f)GetProcAddress(mod, "SymSetOptions"); 452 | } 453 | 454 | // ... symbols fetched ok ... 455 | if( dbghelp.SymInitialize && 456 | dbghelp.SymFromAddr && 457 | dbghelp.SymGetLineFromAddr64 && 458 | dbghelp.SymSetOptions) 459 | { 460 | // ... enable debug-output if needed ... 461 | if (GetEnvironmentVariable("DBGTOOLS_SYMBOL_DEBUG_OUTPUT", 0, 0) > 0) 462 | dbghelp.SymSetOptions(SYMOPT_DEBUG); 463 | 464 | // ... generate the same search-paths for pdbs as default, just add our executable directory as well ... 465 | char search_path[4096]; 466 | callstack_symbols_build_search_path(search_path, sizeof(search_path)-1); 467 | 468 | // ... initialize sym system ... 469 | process = GetCurrentProcess(); 470 | res = dbghelp.SymInitialize(process, search_path, TRUE); 471 | 472 | dbghelp.init_ok = 1; 473 | if (res == 0) 474 | { 475 | err = GetLastError(); 476 | // ERROR_INVALID_PARAMETER seems to be returned IF symbols for a specific module could not be loaded. 477 | // However the lookup will still work for all other symbols so let us ignore this error! 478 | if (err != ERROR_INVALID_PARAMETER) 479 | dbghelp.init_ok = 0; 480 | } 481 | } 482 | } 483 | 484 | dbghelp.initialized = 1; 485 | return dbghelp.init_ok; 486 | } 487 | 488 | int callstack_symbols( void** addresses, callstack_symbol_t* out_syms, int num_addresses, char* memory, int mem_size ) 489 | { 490 | HANDLE process; 491 | DWORD64 offset; 492 | DWORD line_dis; 493 | BOOL res; 494 | IMAGEHLP_LINE64 line; 495 | PSYMBOL_INFO sym_info; 496 | char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; 497 | int num_translated = 0; 498 | callstack_string_buffer_t outbuf = { memory, memory + mem_size }; 499 | 500 | memset( out_syms, 0x0, (size_t)num_addresses * sizeof(callstack_symbol_t) ); 501 | 502 | 503 | 504 | if( !callstack_symbols_initialize() ) 505 | { 506 | out_syms[0].function = "failed to initialize dbghelp.dll"; 507 | out_syms[0].offset = 0; 508 | out_syms[0].file = ""; 509 | out_syms[0].line = 0; 510 | return 1; 511 | } 512 | 513 | process = GetCurrentProcess(); 514 | sym_info = (PSYMBOL_INFO)buffer; 515 | sym_info->SizeOfStruct = sizeof(SYMBOL_INFO); 516 | sym_info->MaxNameLen = MAX_SYM_NAME; 517 | 518 | line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); 519 | 520 | for( int i = 0; i < num_addresses; ++i ) 521 | { 522 | res = dbghelp.SymFromAddr( process, (DWORD64)addresses[i], &offset, sym_info ); 523 | if( res == 0 ) 524 | out_syms[i].function = "failed to lookup symbol"; 525 | else 526 | out_syms[i].function = alloc_string( &outbuf, sym_info->Name, sym_info->NameLen ); 527 | 528 | res = dbghelp.SymGetLineFromAddr64( process, (DWORD64)addresses[i], &line_dis, &line ); 529 | if( res == 0 ) 530 | { 531 | out_syms[i].offset = 0; 532 | out_syms[i].file = "failed to lookup file"; 533 | out_syms[i].line = 0; 534 | } 535 | else 536 | { 537 | out_syms[i].offset = (unsigned int)line_dis; 538 | out_syms[i].file = alloc_string( &outbuf, line.FileName, strlen( line.FileName ) ); 539 | out_syms[i].line = (unsigned int)line.LineNumber; 540 | } 541 | 542 | ++num_translated; 543 | } 544 | return num_translated; 545 | } 546 | 547 | #else 548 | 549 | int callstack( int skip_frames, void** addresses, int num_addresses ) 550 | { 551 | (void)skip_frames; (void)addresses; (void)num_addresses; 552 | return 0; 553 | } 554 | 555 | int callstack_symbols( void** addresses, callstack_symbol_t* out_syms, int num_addresses, char* memory, int mem_size ) 556 | { 557 | (void)addresses; (void)out_syms; (void)num_addresses; (void)memory; (void)mem_size; 558 | return 0; 559 | } 560 | 561 | #endif 562 | 563 | #if defined( DBG_TOOLS_CALLSTACK_UNIX ) 564 | # undef DBG_TOOLS_CALLSTACK_UNIX 565 | #endif 566 | -------------------------------------------------------------------------------- /src/debugger.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | dbgtools - platform independent wrapping of "nice to have" debug functions. 3 | 4 | https://github.com/wc-duck/dbgtools 5 | 6 | version 0.1, october, 2013 7 | 8 | Copyright (C) 2013- Fredrik Kihlander 9 | 10 | This software is provided 'as-is', without any express or implied 11 | warranty. In no event will the authors be held liable for any damages 12 | arising from the use of this software. 13 | 14 | Permission is granted to anyone to use this software for any purpose, 15 | including commercial applications, and to alter it and redistribute it 16 | freely, subject to the following restrictions: 17 | 18 | 1. The origin of this software must not be misrepresented; you must not 19 | claim that you wrote the original software. If you use this software 20 | in a product, an acknowledgment in the product documentation would be 21 | appreciated but is not required. 22 | 2. Altered source versions must be plainly marked as such, and must not be 23 | misrepresented as being the original software. 24 | 3. This notice may not be removed or altered from any source distribution. 25 | 26 | Fredrik Kihlander 27 | */ 28 | 29 | #include 30 | 31 | #if defined( __linux ) 32 | 33 | #include 34 | #include 35 | #include 36 | 37 | int debugger_present() 38 | { 39 | FILE* f = fopen( "/proc/self/status", "r" ); 40 | if( f == 0x0 ) 41 | return 0; 42 | 43 | int trace_pid = 0; 44 | 45 | char buffer[1024]; 46 | while(fgets(buffer, 1024, f) != 0x0) 47 | { 48 | if (strncmp(buffer, "TracerPid:", 10) == 0) 49 | { 50 | trace_pid = atoi(buffer + 10); 51 | break; 52 | } 53 | } 54 | 55 | fclose( f ); 56 | 57 | return trace_pid != 0; 58 | } 59 | 60 | #elif defined( _MSC_VER ) 61 | 62 | #include 63 | 64 | int debugger_present() 65 | { 66 | return IsDebuggerPresent(); 67 | } 68 | 69 | #elif defined( __APPLE__ ) 70 | 71 | #include 72 | #include 73 | 74 | int debugger_present() 75 | { 76 | int mib[4]; 77 | struct kinfo_proc info; 78 | size_t size; 79 | 80 | info.kp_proc.p_flag = 0; 81 | mib[0] = CTL_KERN; 82 | mib[1] = KERN_PROC; 83 | mib[2] = KERN_PROC_PID; 84 | mib[3] = getpid(); 85 | 86 | size = sizeof(info); 87 | sysctl( mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0) ; 88 | 89 | return (info.kp_proc.p_flag & P_TRACED) != 0; 90 | } 91 | #else 92 | 93 | int debugger_present() 94 | { 95 | return 0; 96 | } 97 | 98 | #endif 99 | -------------------------------------------------------------------------------- /src/fpe_ctrl.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | dbgtools - platform independent wrapping of "nice to have" debug functions. 3 | 4 | version 0.1, october, 2013 5 | 6 | Copyright (C) 2013- Fredrik Kihlander 7 | 8 | This software is provided 'as-is', without any express or implied 9 | warranty. In no event will the authors be held liable for any damages 10 | arising from the use of this software. 11 | 12 | Permission is granted to anyone to use this software for any purpose, 13 | including commercial applications, and to alter it and redistribute it 14 | freely, subject to the following restrictions: 15 | 16 | 1. The origin of this software must not be misrepresented; you must not 17 | claim that you wrote the original software. If you use this software 18 | in a product, an acknowledgment in the product documentation would be 19 | appreciated but is not required. 20 | 2. Altered source versions must be plainly marked as such, and must not be 21 | misrepresented as being the original software. 22 | 3. This notice may not be removed or altered from any source distribution. 23 | 24 | Fredrik Kihlander 25 | */ 26 | 27 | #include 28 | 29 | #if defined( __cplusplus ) 30 | extern "C" { 31 | #endif 32 | 33 | #if defined( __APPLE__ ) 34 | // These implementations of fegetexcept, feenableexcept and fedisableexcept is lifted from: 35 | // http://www-personal.umich.edu/~williams/archive/computation/fe-handling-example.c 36 | 37 | # if defined(__ppc__) || defined(__ppc64__) 38 | 39 | # define FE_EXCEPT_SHIFT 22 // shift flags right to get masks 40 | # define FM_ALL_EXCEPT FE_ALL_EXCEPT >> FE_EXCEPT_SHIFT 41 | 42 | static int fegetexcept() 43 | { 44 | fenv_t fenv; 45 | return ( fegetenv (&fenv) ? -1 : ( ( fenv & (FM_ALL_EXCEPT) ) << FE_EXCEPT_SHIFT ) ); 46 | } 47 | 48 | static int feenableexcept( int excepts ) 49 | { 50 | fenv_t fenv; 51 | unsigned int new_excepts = ((unsigned int)excepts & FE_ALL_EXCEPT) >> FE_EXCEPT_SHIFT, 52 | old_excepts; // all previous masks 53 | 54 | if ( fegetenv (&fenv) ) return -1; 55 | old_excepts = (fenv & FM_ALL_EXCEPT) << FE_EXCEPT_SHIFT; 56 | 57 | fenv = (fenv & ~new_excepts) | new_excepts; 58 | return ( fesetenv (&fenv) ? -1 : old_excepts ); 59 | } 60 | 61 | static int fedisableexcept( int excepts ) 62 | { 63 | fenv_t fenv; 64 | unsigned int still_on = ~( ((unsigned int)excepts & FE_ALL_EXCEPT) >> FE_EXCEPT_SHIFT ), 65 | old_excepts; // previous masks 66 | 67 | if ( fegetenv (&fenv) ) return -1; 68 | old_excepts = (fenv & FM_ALL_EXCEPT) << FE_EXCEPT_SHIFT; 69 | 70 | fenv &= still_on; 71 | return ( fesetenv (&fenv) ? -1 : (int)old_excepts ); 72 | } 73 | 74 | # elif defined(__i386__) || defined(__x86_64__) 75 | static int fegetexcept() 76 | { 77 | fenv_t fenv; 78 | return fegetenv (&fenv) ? -1 : (fenv.__control & FE_ALL_EXCEPT); 79 | } 80 | 81 | static int feenableexcept( int excepts ) 82 | { 83 | fenv_t fenv; 84 | unsigned int new_excepts = (unsigned int)excepts & FE_ALL_EXCEPT, 85 | old_excepts; // previous masks 86 | 87 | if ( fegetenv (&fenv) ) return -1; 88 | old_excepts = fenv.__control & FE_ALL_EXCEPT; 89 | 90 | // unmask 91 | fenv.__control &= ~new_excepts; 92 | fenv.__mxcsr &= ~(new_excepts << 7); 93 | 94 | return ( fesetenv (&fenv) ? -1 : (int)old_excepts ); 95 | } 96 | 97 | static int fedisableexcept( int excepts ) 98 | { 99 | fenv_t fenv; 100 | unsigned int new_excepts = excepts & FE_ALL_EXCEPT, 101 | old_excepts; // all previous masks 102 | 103 | if ( fegetenv (&fenv) ) return -1; 104 | old_excepts = fenv.__control & FE_ALL_EXCEPT; 105 | 106 | // mask 107 | fenv.__control |= new_excepts; 108 | fenv.__mxcsr |= new_excepts << 7; 109 | 110 | return ( fesetenv (&fenv) ? -1 : (int)old_excepts ); 111 | } 112 | # endif // PPC or INTEL enabling 113 | #endif 114 | 115 | #if defined( _GNU_SOURCE ) || defined( __APPLE__ ) 116 | 117 | #include 118 | 119 | // how to handle that this is an unknown pragma? 120 | // #pragma STDC FENV_ACCESS on 121 | 122 | int fpe_clear( unsigned int except ) 123 | { 124 | return feclearexcept( (int)except ); 125 | } 126 | 127 | int fpe_raise( unsigned int except ) 128 | { 129 | return feraiseexcept( (int)except ); 130 | } 131 | 132 | unsigned int fpe_test( unsigned int except ) 133 | { 134 | return (unsigned int)fetestexcept( (int)except ); 135 | } 136 | 137 | int fpe_enable_trap( unsigned int except ) 138 | { 139 | if( feclearexcept( (int)except ) < 0 ) 140 | return -1; 141 | 142 | return feenableexcept( (int)except ); 143 | } 144 | 145 | int fpe_disable_trap( unsigned int except ) 146 | { 147 | return fedisableexcept( (int)except ); 148 | } 149 | 150 | int fpe_set_trapped( unsigned int except ) 151 | { 152 | if( fpe_disable_trap( ~except ) < 0 ) 153 | return -1; 154 | return fpe_enable_trap( except ); 155 | } 156 | 157 | unsigned int fpe_get_trapped() 158 | { 159 | return (unsigned int)fegetexcept(); 160 | } 161 | 162 | #elif defined( _MSC_VER ) 163 | 164 | #include 165 | #pragma fenv_access(on) 166 | 167 | int fpe_clear( unsigned int except ) 168 | { 169 | _clearfp(); 170 | return 0; 171 | } 172 | 173 | int fpe_raise( unsigned int except ) 174 | { 175 | return -1; 176 | } 177 | 178 | unsigned int fpe_test( unsigned int except ) 179 | { 180 | return ( _statusfp() & except ) > 0; 181 | } 182 | 183 | int fpe_enable_trap( unsigned int except ) 184 | { 185 | unsigned int curr; 186 | errno_t err = _controlfp_s( &curr, ~except, _MCW_EM ); 187 | return err == 0 ? 0 : -1; 188 | } 189 | 190 | int fpe_disable_trap( unsigned int except ) 191 | { 192 | unsigned int curr; 193 | errno_t err = _controlfp_s( &curr, except, _MCW_EM ); 194 | return err == 0 ? 0 : -1; 195 | } 196 | 197 | int fpe_set_trapped( unsigned int except ) 198 | { 199 | if( fpe_disable_trap( ~except ) < 0 ) 200 | return -1; 201 | return fpe_enable_trap( except ); 202 | } 203 | 204 | unsigned int fpe_get_trapped() 205 | { 206 | unsigned int current_word = 0; 207 | _controlfp_s( ¤t_word, 0, 0 ); 208 | return ( ~current_word ) & fp_exception_all; 209 | } 210 | 211 | #else 212 | 213 | int fpe_clear( unsigned int /*except*/ ) { return -1; } 214 | int fpe_raise( unsigned int /*except*/ ) { return -1; } 215 | unsigned int fpe_test( unsigned int /*except*/ ) { return 0; } 216 | int fpe_enable_trap ( unsigned int /*except*/ ) { return -1; } 217 | int fpe_disable_trap ( unsigned int /*except*/ ) { return -1; } 218 | int fpe_get_trapped ( unsigned int /*except*/ ) { return -1; } 219 | 220 | #endif 221 | 222 | #if defined( __cplusplus ) 223 | } 224 | #endif 225 | -------------------------------------------------------------------------------- /src/hw_breakpoint.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | dbgtools - platform independent wrapping of "nice to have" debug functions. 3 | 4 | https://github.com/wc-duck/dbgtools 5 | 6 | version 0.1, october, 2013 7 | 8 | Copyright (C) 2013- Fredrik Kihlander 9 | 10 | This software is provided 'as-is', without any express or implied 11 | warranty. In no event will the authors be held liable for any damages 12 | arising from the use of this software. 13 | 14 | Permission is granted to anyone to use this software for any purpose, 15 | including commercial applications, and to alter it and redistribute it 16 | freely, subject to the following restrictions: 17 | 18 | 1. The origin of this software must not be misrepresented; you must not 19 | claim that you wrote the original software. If you use this software 20 | in a product, an acknowledgment in the product documentation would be 21 | appreciated but is not required. 22 | 2. Altered source versions must be plainly marked as such, and must not be 23 | misrepresented as being the original software. 24 | 3. This notice may not be removed or altered from any source distribution. 25 | 26 | Fredrik Kihlander 27 | */ 28 | 29 | /** 30 | * Based on: 31 | * linux: https://gist.github.com/jld/5d292c2c48eb07980562 32 | * windows: http://www.morearty.com/code/breakpoint 33 | */ 34 | 35 | #include 36 | 37 | /** 38 | * Currently the linux implementation depend on having linux-headers installed. 39 | * If you are on a compiler that do not support __has_include() but still have 40 | * linux-headers installed this can be manually defined. 41 | * 42 | * In the future I might add a fallback that just defines the used symbols from these 43 | * headers and "hope" for the best. 44 | */ 45 | #if defined(__linux) 46 | # if !defined(DBG_TOOLS_HW_BREAKPOINT_HAS_LINUX_HEADERS) 47 | # if defined(__has_include) 48 | # if __has_include() && __has_include() 49 | # define DBG_TOOLS_HW_BREAKPOINT_HAS_LINUX_HEADERS 50 | # endif 51 | # endif 52 | # endif 53 | #endif 54 | 55 | #if defined(DBG_TOOLS_HW_BREAKPOINT_HAS_LINUX_HEADERS) 56 | 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | 68 | extern "C" int hw_breakpoint_set( void* address, unsigned int size, enum hw_breakpoint_type type ) 69 | { 70 | struct perf_event_attr attr; 71 | struct f_owner_ex owner; 72 | int fd; 73 | 74 | memset(&attr, 0, sizeof(attr)); 75 | attr.size = sizeof(attr); 76 | attr.type = PERF_TYPE_BREAKPOINT; 77 | 78 | switch( type ) 79 | { 80 | case HW_BREAKPOINT_READ: attr.bp_type = HW_BREAKPOINT_R; break; 81 | case HW_BREAKPOINT_WRITE: attr.bp_type = HW_BREAKPOINT_W; break; 82 | case HW_BREAKPOINT_READWRITE: attr.bp_type = HW_BREAKPOINT_RW; break; 83 | } 84 | 85 | attr.bp_addr = (uint64_t)address; 86 | 87 | switch( size ) 88 | { 89 | case 1: 90 | case 2: 91 | case 4: 92 | case 8: 93 | attr.bp_len = size; 94 | break; 95 | default: 96 | return HW_BREAKPOINT_ERROR_INVALID_ARG; 97 | } 98 | 99 | attr.sample_period = 1; 100 | attr.precise_ip = 2; // request synchronous delivery 101 | attr.wakeup_events = 1; 102 | 103 | fd = (int)syscall(__NR_perf_event_open, &attr, 0, -1, -1, 104 | #if defined(PERF_FLAG_FD_CLOEXEC) 105 | PERF_FLAG_FD_CLOEXEC 106 | #else 107 | 0 108 | #endif 109 | ); 110 | 111 | if( fd < 0 ) 112 | { 113 | if( errno == ENOSPC ) 114 | return HW_BREAKPOINT_ERROR_OUT_OF_SLOTS; 115 | perror("perf_event_open"); 116 | return HW_BREAKPOINT_ERROR_UNKNOWN; 117 | } 118 | 119 | if (fcntl(fd, F_SETSIG, SIGSEGV) < 0) 120 | { 121 | perror("fcntl F_SETSIG"); 122 | close(fd); 123 | return HW_BREAKPOINT_ERROR_UNKNOWN; 124 | } 125 | 126 | owner.type = F_OWNER_TID; 127 | owner.pid = (pid_t)syscall(__NR_gettid); 128 | 129 | if (fcntl(fd, F_SETOWN_EX, &owner) < 0) 130 | { 131 | perror("fcntl F_SETOWN_EX"); 132 | close(fd); 133 | return HW_BREAKPOINT_ERROR_UNKNOWN; 134 | } 135 | 136 | if (fcntl(fd, F_SETFL, O_ASYNC) < 0) 137 | { 138 | perror("fcntl F_SETFL"); 139 | close(fd); 140 | return HW_BREAKPOINT_ERROR_UNKNOWN; 141 | } 142 | 143 | return fd; 144 | } 145 | 146 | extern "C" void hw_breakpoint_clear( int hwbp ) 147 | { 148 | close(hwbp); 149 | } 150 | 151 | #elif defined(_MSC_VER) 152 | 153 | #include 154 | 155 | inline DWORD64 hw_breakpoint_setbits(DWORD64 dw, int lowBit, int bits, int newValue) 156 | { 157 | int mask = (1 << bits) - 1; 158 | return (dw & ~(mask << lowBit)) | (newValue << lowBit); 159 | } 160 | 161 | extern "C" int hw_breakpoint_set( void* address, unsigned int size, enum hw_breakpoint_type type ) 162 | { 163 | switch( size ) 164 | { 165 | case 1: size = 0; break; // 00 166 | case 2: size = 1; break; // 01 167 | case 4: size = 3; break; // 11 168 | case 8: size = 2; break; // 10 169 | break; 170 | default: 171 | return HW_BREAKPOINT_ERROR_INVALID_ARG; 172 | } 173 | 174 | CONTEXT cxt; 175 | HANDLE thread = GetCurrentThread(); 176 | cxt.ContextFlags = CONTEXT_DEBUG_REGISTERS; 177 | 178 | // Read the register values 179 | if( !GetThreadContext(thread, &cxt) ) 180 | return HW_BREAKPOINT_ERROR_UNKNOWN; 181 | 182 | int reg_index; 183 | for( reg_index = 0; reg_index < 4; ++reg_index ) 184 | { 185 | if( ( cxt.Dr7 & ( 1 << (reg_index * 2) ) ) == 0 ) 186 | break; 187 | } 188 | 189 | switch( reg_index ) 190 | { 191 | case 0: cxt.Dr0 = (DWORD) address; break; 192 | case 1: cxt.Dr1 = (DWORD) address; break; 193 | case 2: cxt.Dr2 = (DWORD) address; break; 194 | case 3: cxt.Dr3 = (DWORD) address; break; 195 | default: 196 | return HW_BREAKPOINT_ERROR_OUT_OF_SLOTS; 197 | } 198 | 199 | int when; 200 | switch( type ) 201 | { 202 | case HW_BREAKPOINT_WRITE: when = 1; break; // 01 203 | case HW_BREAKPOINT_READ: when = 2; break; // 10 204 | case HW_BREAKPOINT_READWRITE: when = 3; break; // 11 205 | } 206 | 207 | cxt.Dr7 = hw_breakpoint_setbits(cxt.Dr7, 16 + (reg_index * 4), 2, when); 208 | cxt.Dr7 = hw_breakpoint_setbits(cxt.Dr7, 18 + (reg_index * 4), 2, size); 209 | cxt.Dr7 = hw_breakpoint_setbits(cxt.Dr7, reg_index * 2, 1, 1); 210 | 211 | if( !SetThreadContext(thread, &cxt) ) 212 | return HW_BREAKPOINT_ERROR_UNKNOWN; 213 | return reg_index; 214 | } 215 | 216 | extern "C" void hw_breakpoint_clear( int hwbp ) 217 | { 218 | CONTEXT cxt; 219 | HANDLE thread = GetCurrentThread(); 220 | cxt.ContextFlags = CONTEXT_DEBUG_REGISTERS; 221 | 222 | GetThreadContext(thread, &cxt); 223 | cxt.Dr7 = hw_breakpoint_setbits(cxt.Dr7, hwbp * 2, 1, 0); 224 | SetThreadContext(thread, &cxt); 225 | } 226 | 227 | #elif defined(__APPLE__) && defined(__x86_64__) && 0 // disabled until I get hold of an OSX-machine to test on. 228 | 229 | #include 230 | #include 231 | 232 | inline unsigned int hw_breakpoint_setbits(unsigned int dw, int lowBit, int bits, int newValue) 233 | { 234 | int mask = (1 << bits) - 1; 235 | return (dw & ~(mask << lowBit)) | (newValue << lowBit); 236 | } 237 | 238 | extern "C" int hw_breakpoint_set( void* address, unsigned int size, enum hw_breakpoint_type type ) 239 | { 240 | switch( size ) 241 | { 242 | case 1: size = 0; break; // 00 243 | case 2: size = 1; break; // 01 244 | case 4: size = 3; break; // 11 245 | case 8: size = 2; break; // 10 246 | break; 247 | default: 248 | return HW_BREAKPOINT_ERROR_INVALID_ARG; 249 | } 250 | 251 | thread_t target = get_target_thread(); 252 | struct x86_debug_state dr; 253 | mach_msg_type_number_t dr_count = x86_DEBUG_STATE_COUNT; 254 | 255 | kern_return_t rc = thread_get_state(target, x86_DEBUG_STATE, &dr, &dr_count); 256 | 257 | int reg_index; 258 | for( reg_index = 0; reg_index < 4; ++reg_index ) 259 | { 260 | if( ( dr.uds.ds32.__dr7 & ( 1 << (reg_index * 2) ) ) == 0 ) 261 | break; 262 | } 263 | 264 | switch( reg_index ) 265 | { 266 | case 0: dr.uds.ds32.__dr0 = (DWORD) address; break; 267 | case 1: dr.uds.ds32.__dr1 = (DWORD) address; break; 268 | case 2: dr.uds.ds32.__dr2 = (DWORD) address; break; 269 | case 3: dr.uds.ds32.__dr3 = (DWORD) address; break; 270 | default: 271 | return HW_BREAKPOINT_ERROR_OUT_OF_SLOTS; 272 | } 273 | 274 | int when; 275 | switch( type ) 276 | { 277 | case HW_BREAKPOINT_WRITE: when = 1; break; // 01 278 | case HW_BREAKPOINT_READ: when = 2; break; // 10 279 | case HW_BREAKPOINT_READWRITE: when = 3; break; // 11 280 | } 281 | 282 | dr.uds.ds32.__dr7 = hw_breakpoint_setbits(dr.uds.ds32.__dr7, 16 + (reg_index * 4), 2, when); 283 | dr.uds.ds32.__dr7 = hw_breakpoint_setbits(dr.uds.ds32.__dr7, 18 + (reg_index * 4), 2, size); 284 | dr.uds.ds32.__dr7 = hw_breakpoint_setbits(dr.uds.ds32.__dr7, reg_index * 2, 1, 1); 285 | 286 | thread_set_state(target, x86_DEBUG_STATE, &dr, &dr_count); 287 | return reg_index; 288 | } 289 | 290 | extern "C" void hw_breakpoint_clear( int hwbp ) 291 | { 292 | thread_t target = get_target_thread(); 293 | struct x86_debug_state dr; 294 | mach_msg_type_number_t dr_count = x86_DEBUG_STATE_COUNT; 295 | 296 | kern_return_t rc = thread_get_state(target, x86_DEBUG_STATE, &dr, &dr_count); 297 | dr.uds.ds32.__dr7 = hw_breakpoint_setbits(dr.uds.ds32.__dr7, hwbp * 2, 1, 0); 298 | thread_set_state(target, x86_DEBUG_STATE, &dr, &dr_count); 299 | } 300 | 301 | 302 | #else 303 | 304 | extern "C" int hw_breakpoint_set( void*, unsigned int, enum hw_breakpoint_type ) 305 | { 306 | return HW_BREAKPOINT_ERROR_NOT_SUPPORTED; 307 | } 308 | 309 | extern "C" void hw_breakpoint_clear( int ) 310 | { 311 | } 312 | 313 | #endif 314 | -------------------------------------------------------------------------------- /test/greatest.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Scott Vokes 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef GREATEST_H 18 | #define GREATEST_H 19 | 20 | #define GREATEST_VERSION_MAJOR 0 21 | #define GREATEST_VERSION_MINOR 9 22 | #define GREATEST_VERSION_PATCH 3 23 | 24 | /* A unit testing system for C, contained in 1 file. 25 | * It doesn't use dynamic allocation or depend on anything 26 | * beyond ANSI C89. */ 27 | 28 | 29 | /********************************************************************* 30 | * Minimal test runner template 31 | *********************************************************************/ 32 | #if 0 33 | 34 | #include "greatest.h" 35 | 36 | TEST foo_should_foo() { 37 | PASS(); 38 | } 39 | 40 | static void setup_cb(void *data) { 41 | printf("setup callback for each test case\n"); 42 | } 43 | 44 | static void teardown_cb(void *data) { 45 | printf("teardown callback for each test case\n"); 46 | } 47 | 48 | SUITE(suite) { 49 | /* Optional setup/teardown callbacks which will be run before/after 50 | * every test case in the suite. 51 | * Cleared when the suite finishes. */ 52 | SET_SETUP(setup_cb, voidp_to_callback_data); 53 | SET_TEARDOWN(teardown_cb, voidp_to_callback_data); 54 | 55 | RUN_TEST(foo_should_foo); 56 | } 57 | 58 | /* Add all the definitions that need to be in the test runner's main file. */ 59 | GREATEST_MAIN_DEFS(); 60 | 61 | int main(int argc, char **argv) { 62 | GREATEST_MAIN_BEGIN(); /* command-line arguments, initialization. */ 63 | RUN_SUITE(suite); 64 | GREATEST_MAIN_END(); /* display results */ 65 | } 66 | 67 | #endif 68 | /*********************************************************************/ 69 | 70 | 71 | #include 72 | #include 73 | #include 74 | #include 75 | 76 | 77 | /*********** 78 | * Options * 79 | ***********/ 80 | 81 | /* Default column width for non-verbose output. */ 82 | #ifndef GREATEST_DEFAULT_WIDTH 83 | #define GREATEST_DEFAULT_WIDTH 72 84 | #endif 85 | 86 | /* FILE *, for test logging. */ 87 | #ifndef GREATEST_STDOUT 88 | #define GREATEST_STDOUT stdout 89 | #endif 90 | 91 | /* Remove GREATEST_ prefix from most commonly used symbols? */ 92 | #ifndef GREATEST_USE_ABBREVS 93 | #define GREATEST_USE_ABBREVS 1 94 | #endif 95 | 96 | 97 | /********* 98 | * Types * 99 | *********/ 100 | 101 | /* Info for the current running suite. */ 102 | typedef struct greatest_suite_info { 103 | unsigned int tests_run; 104 | unsigned int passed; 105 | unsigned int failed; 106 | unsigned int skipped; 107 | 108 | /* timers, pre/post running suite and individual tests */ 109 | clock_t pre_suite; 110 | clock_t post_suite; 111 | clock_t pre_test; 112 | clock_t post_test; 113 | } greatest_suite_info; 114 | 115 | /* Type for a suite function. */ 116 | typedef void (greatest_suite_cb)(void); 117 | 118 | /* Types for setup/teardown callbacks. If non-NULL, these will be run 119 | * and passed the pointer to their additional data. */ 120 | typedef void (greatest_setup_cb)(void *udata); 121 | typedef void (greatest_teardown_cb)(void *udata); 122 | 123 | typedef enum { 124 | GREATEST_FLAG_VERBOSE = 0x01, 125 | GREATEST_FLAG_FIRST_FAIL = 0x02, 126 | GREATEST_FLAG_LIST_ONLY = 0x04 127 | } GREATEST_FLAG; 128 | 129 | typedef struct greatest_run_info { 130 | unsigned int flags; 131 | unsigned int tests_run; /* total test count */ 132 | 133 | /* Overall pass/fail/skip counts. */ 134 | unsigned int passed; 135 | unsigned int failed; 136 | unsigned int skipped; 137 | 138 | /* currently running test suite */ 139 | greatest_suite_info suite; 140 | 141 | /* info to print about the most recent failure */ 142 | const char *fail_file; 143 | unsigned int fail_line; 144 | const char *msg; 145 | 146 | /* current setup/teardown hooks and userdata */ 147 | greatest_setup_cb *setup; 148 | void *setup_udata; 149 | greatest_teardown_cb *teardown; 150 | void *teardown_udata; 151 | 152 | /* formatting info for ".....s...F"-style output */ 153 | unsigned int col; 154 | unsigned int width; 155 | 156 | /* only run a specific suite or test */ 157 | char *suite_filter; 158 | char *test_filter; 159 | 160 | /* overall timers */ 161 | clock_t begin; 162 | clock_t end; 163 | } greatest_run_info; 164 | 165 | /* Global var for the current testing context. 166 | * Initialized by GREATEST_MAIN_DEFS(). */ 167 | extern greatest_run_info greatest_info; 168 | 169 | 170 | /********************** 171 | * Exported functions * 172 | **********************/ 173 | 174 | void greatest_do_pass(const char *name); 175 | void greatest_do_fail(const char *name); 176 | void greatest_do_skip(const char *name); 177 | int greatest_pre_test(const char *name); 178 | void greatest_post_test(const char *name, int res); 179 | void greatest_usage(const char *name); 180 | void GREATEST_SET_SETUP_CB(greatest_setup_cb *cb, void *udata); 181 | void GREATEST_SET_TEARDOWN_CB(greatest_teardown_cb *cb, void *udata); 182 | 183 | 184 | /********** 185 | * Macros * 186 | **********/ 187 | 188 | /* Define a suite. */ 189 | #define GREATEST_SUITE(NAME) void NAME(void) 190 | 191 | /* Start defining a test function. 192 | * The arguments are not included, to allow parametric testing. */ 193 | #define GREATEST_TEST static int 194 | 195 | /* Run a suite. */ 196 | #define GREATEST_RUN_SUITE(S_NAME) greatest_run_suite(S_NAME, #S_NAME) 197 | 198 | /* Run a test in the current suite. */ 199 | #define GREATEST_RUN_TEST(TEST) \ 200 | do { \ 201 | if (greatest_pre_test(#TEST) == 1) { \ 202 | int res = TEST(); \ 203 | greatest_post_test(#TEST, res); \ 204 | } else if (GREATEST_LIST_ONLY()) { \ 205 | fprintf(GREATEST_STDOUT, " %s\n", #TEST); \ 206 | } \ 207 | } while (0) 208 | 209 | /* Run a test in the current suite with one void* argument, 210 | * which can be a pointer to a struct with multiple arguments. */ 211 | #define GREATEST_RUN_TEST1(TEST, ENV) \ 212 | do { \ 213 | if (greatest_pre_test(#TEST) == 1) { \ 214 | int res = TEST(ENV); \ 215 | greatest_post_test(#TEST, res); \ 216 | } else if (GREATEST_LIST_ONLY()) { \ 217 | fprintf(GREATEST_STDOUT, " %s\n", #TEST); \ 218 | } \ 219 | } while (0) 220 | 221 | /* If __VA_ARGS__ (C99) is supported, allow parametric testing 222 | * without needing to manually manage the argument struct. */ 223 | #if __STDC_VERSION__ >= 19901L 224 | #define GREATEST_RUN_TESTp(TEST, ...) \ 225 | do { \ 226 | if (greatest_pre_test(#TEST) == 1) { \ 227 | int res = TEST(__VA_ARGS__); \ 228 | greatest_post_test(#TEST, res); \ 229 | } else if (GREATEST_LIST_ONLY()) { \ 230 | fprintf(GREATEST_STDOUT, " %s\n", #TEST); \ 231 | } \ 232 | } while (0) 233 | #endif 234 | 235 | 236 | /* Check if the test runner is in verbose mode. */ 237 | #define GREATEST_IS_VERBOSE() (greatest_info.flags & GREATEST_FLAG_VERBOSE) 238 | #define GREATEST_LIST_ONLY() (greatest_info.flags & GREATEST_FLAG_LIST_ONLY) 239 | #define GREATEST_FIRST_FAIL() (greatest_info.flags & GREATEST_FLAG_FIRST_FAIL) 240 | #define GREATEST_FAILURE_ABORT() (greatest_info.suite.failed > 0 && GREATEST_FIRST_FAIL()) 241 | 242 | /* Message-less forms. */ 243 | #define GREATEST_PASS() GREATEST_PASSm(NULL) 244 | #define GREATEST_FAIL() GREATEST_FAILm(NULL) 245 | #define GREATEST_SKIP() GREATEST_SKIPm(NULL) 246 | #define GREATEST_ASSERT(COND) GREATEST_ASSERTm(#COND, COND) 247 | #define GREATEST_ASSERT_FALSE(COND) GREATEST_ASSERT_FALSEm(#COND, COND) 248 | #define GREATEST_ASSERT_EQ(EXP, GOT) GREATEST_ASSERT_EQm(#EXP " != " #GOT, EXP, GOT) 249 | #define GREATEST_ASSERT_STR_EQ(EXP, GOT) GREATEST_ASSERT_STR_EQm(#EXP " != " #GOT, EXP, GOT) 250 | 251 | /* The following forms take an additional message argument first, 252 | * to be displayed by the test runner. */ 253 | 254 | /* Fail if a condition is not true, with message. */ 255 | #define GREATEST_ASSERTm(MSG, COND) \ 256 | do { \ 257 | greatest_info.msg = MSG; \ 258 | greatest_info.fail_file = __FILE__; \ 259 | greatest_info.fail_line = __LINE__; \ 260 | if (!(COND)) { return -1; } \ 261 | greatest_info.msg = NULL; \ 262 | } while (0) 263 | 264 | #define GREATEST_ASSERT_FALSEm(MSG, COND) \ 265 | do { \ 266 | greatest_info.msg = MSG; \ 267 | greatest_info.fail_file = __FILE__; \ 268 | greatest_info.fail_line = __LINE__; \ 269 | if ((COND)) { return -1; } \ 270 | greatest_info.msg = NULL; \ 271 | } while (0) 272 | 273 | #define GREATEST_ASSERT_EQm(MSG, EXP, GOT) \ 274 | do { \ 275 | greatest_info.msg = MSG; \ 276 | greatest_info.fail_file = __FILE__; \ 277 | greatest_info.fail_line = __LINE__; \ 278 | if ((EXP) != (GOT)) { return -1; } \ 279 | greatest_info.msg = NULL; \ 280 | } while (0) 281 | 282 | #define GREATEST_ASSERT_STR_EQm(MSG, EXP, GOT) \ 283 | do { \ 284 | const char *exp_s = (EXP); \ 285 | const char *got_s = (GOT); \ 286 | greatest_info.msg = MSG; \ 287 | greatest_info.fail_file = __FILE__; \ 288 | greatest_info.fail_line = __LINE__; \ 289 | if (0 != strcmp(exp_s, got_s)) { \ 290 | fprintf(GREATEST_STDOUT, \ 291 | "Expected:\n####\n%s\n####\n", exp_s); \ 292 | fprintf(GREATEST_STDOUT, \ 293 | "Got:\n####\n%s\n####\n", got_s); \ 294 | return -1; \ 295 | } \ 296 | greatest_info.msg = NULL; \ 297 | } while (0) 298 | 299 | #define GREATEST_PASSm(MSG) \ 300 | do { \ 301 | greatest_info.msg = MSG; \ 302 | return 0; \ 303 | } while (0) 304 | 305 | #define GREATEST_FAILm(MSG) \ 306 | do { \ 307 | greatest_info.fail_file = __FILE__; \ 308 | greatest_info.fail_line = __LINE__; \ 309 | greatest_info.msg = MSG; \ 310 | return -1; \ 311 | } while (0) 312 | 313 | #define GREATEST_SKIPm(MSG) \ 314 | do { \ 315 | greatest_info.msg = MSG; \ 316 | return 1; \ 317 | } while (0) 318 | 319 | #define GREATEST_SET_TIME(NAME) \ 320 | NAME = clock(); \ 321 | if (NAME == (clock_t) -1) { \ 322 | fprintf(GREATEST_STDOUT, \ 323 | "clock error: %s\n", #NAME); \ 324 | exit(EXIT_FAILURE); \ 325 | } 326 | 327 | #define GREATEST_CLOCK_DIFF(C1, C2) \ 328 | fprintf(GREATEST_STDOUT, " (%lu ticks, %.3f sec)", \ 329 | (long unsigned int) (C2) - (long unsigned int)(C1), \ 330 | (double)((C2) - (C1)) / (1.0 * (double)CLOCKS_PER_SEC)) \ 331 | 332 | /* Include several function definitions in the main test file. */ 333 | #define GREATEST_MAIN_DEFS() \ 334 | \ 335 | /* Is FILTER a subset of NAME? */ \ 336 | static int greatest_name_match(const char *name, \ 337 | const char *filter) { \ 338 | size_t offset = 0; \ 339 | size_t filter_len = strlen(filter); \ 340 | while (name[offset] != '\0') { \ 341 | if (name[offset] == filter[0]) { \ 342 | if (0 == strncmp(&name[offset], filter, filter_len)) { \ 343 | return 1; \ 344 | } \ 345 | } \ 346 | offset++; \ 347 | } \ 348 | \ 349 | return 0; \ 350 | } \ 351 | \ 352 | int greatest_pre_test(const char *name) { \ 353 | if (!GREATEST_LIST_ONLY() \ 354 | && (!GREATEST_FIRST_FAIL() || greatest_info.suite.failed == 0) \ 355 | && (greatest_info.test_filter == NULL || \ 356 | greatest_name_match(name, greatest_info.test_filter))) { \ 357 | GREATEST_SET_TIME(greatest_info.suite.pre_test); \ 358 | if (greatest_info.setup) { \ 359 | greatest_info.setup(greatest_info.setup_udata); \ 360 | } \ 361 | return 1; /* test should be run */ \ 362 | } else { \ 363 | return 0; /* skipped */ \ 364 | } \ 365 | } \ 366 | \ 367 | void greatest_post_test(const char *name, int res) { \ 368 | GREATEST_SET_TIME(greatest_info.suite.post_test); \ 369 | if (greatest_info.teardown) { \ 370 | void *udata = greatest_info.teardown_udata; \ 371 | greatest_info.teardown(udata); \ 372 | } \ 373 | \ 374 | if (res < 0) { \ 375 | greatest_do_fail(name); \ 376 | } else if (res > 0) { \ 377 | greatest_do_skip(name); \ 378 | } else if (res == 0) { \ 379 | greatest_do_pass(name); \ 380 | } \ 381 | greatest_info.suite.tests_run++; \ 382 | greatest_info.col++; \ 383 | if (GREATEST_IS_VERBOSE()) { \ 384 | GREATEST_CLOCK_DIFF(greatest_info.suite.pre_test, \ 385 | greatest_info.suite.post_test); \ 386 | fprintf(GREATEST_STDOUT, "\n"); \ 387 | } else if (greatest_info.col % greatest_info.width == 0) { \ 388 | fprintf(GREATEST_STDOUT, "\n"); \ 389 | greatest_info.col = 0; \ 390 | } \ 391 | if (GREATEST_STDOUT == stdout) fflush(stdout); \ 392 | } \ 393 | \ 394 | static void greatest_run_suite(greatest_suite_cb *suite_cb, \ 395 | const char *suite_name) { \ 396 | if (greatest_info.suite_filter && \ 397 | !greatest_name_match(suite_name, greatest_info.suite_filter)) { \ 398 | return; \ 399 | } \ 400 | if (GREATEST_FIRST_FAIL() && greatest_info.failed > 0) { return; } \ 401 | greatest_info.suite.tests_run = 0; \ 402 | greatest_info.suite.failed = 0; \ 403 | greatest_info.suite.passed = 0; \ 404 | greatest_info.suite.skipped = 0; \ 405 | greatest_info.suite.pre_suite = 0; \ 406 | greatest_info.suite.post_suite = 0; \ 407 | greatest_info.suite.pre_test = 0; \ 408 | greatest_info.suite.post_test = 0; \ 409 | greatest_info.col = 0; \ 410 | fprintf(GREATEST_STDOUT, "\n* Suite %s:\n", suite_name); \ 411 | GREATEST_SET_TIME(greatest_info.suite.pre_suite); \ 412 | suite_cb(); \ 413 | GREATEST_SET_TIME(greatest_info.suite.post_suite); \ 414 | if (greatest_info.suite.tests_run > 0) { \ 415 | fprintf(GREATEST_STDOUT, \ 416 | "\n%u tests - %u pass, %u fail, %u skipped", \ 417 | greatest_info.suite.tests_run, \ 418 | greatest_info.suite.passed, \ 419 | greatest_info.suite.failed, \ 420 | greatest_info.suite.skipped); \ 421 | GREATEST_CLOCK_DIFF(greatest_info.suite.pre_suite, \ 422 | greatest_info.suite.post_suite); \ 423 | fprintf(GREATEST_STDOUT, "\n"); \ 424 | } \ 425 | greatest_info.setup = NULL; \ 426 | greatest_info.setup_udata = NULL; \ 427 | greatest_info.teardown = NULL; \ 428 | greatest_info.teardown_udata = NULL; \ 429 | greatest_info.passed += greatest_info.suite.passed; \ 430 | greatest_info.failed += greatest_info.suite.failed; \ 431 | greatest_info.skipped += greatest_info.suite.skipped; \ 432 | greatest_info.tests_run += greatest_info.suite.tests_run; \ 433 | } \ 434 | \ 435 | void greatest_do_pass(const char *name) { \ 436 | if (GREATEST_IS_VERBOSE()) { \ 437 | fprintf(GREATEST_STDOUT, "PASS %s: %s", \ 438 | name, greatest_info.msg ? greatest_info.msg : ""); \ 439 | } else { \ 440 | fprintf(GREATEST_STDOUT, "."); \ 441 | } \ 442 | greatest_info.suite.passed++; \ 443 | } \ 444 | \ 445 | void greatest_do_fail(const char *name) { \ 446 | if (GREATEST_IS_VERBOSE()) { \ 447 | fprintf(GREATEST_STDOUT, \ 448 | "FAIL %s: %s (%s:%u)", \ 449 | name, greatest_info.msg ? greatest_info.msg : "", \ 450 | greatest_info.fail_file, greatest_info.fail_line); \ 451 | } else { \ 452 | fprintf(GREATEST_STDOUT, "F"); \ 453 | greatest_info.col++; \ 454 | /* add linebreak if in line of '.'s */ \ 455 | if (greatest_info.col != 0) { \ 456 | fprintf(GREATEST_STDOUT, "\n"); \ 457 | greatest_info.col = 0; \ 458 | } \ 459 | fprintf(GREATEST_STDOUT, "FAIL %s: %s (%s:%u)\n", \ 460 | name, \ 461 | greatest_info.msg ? greatest_info.msg : "", \ 462 | greatest_info.fail_file, greatest_info.fail_line); \ 463 | } \ 464 | greatest_info.suite.failed++; \ 465 | } \ 466 | \ 467 | void greatest_do_skip(const char *name) { \ 468 | if (GREATEST_IS_VERBOSE()) { \ 469 | fprintf(GREATEST_STDOUT, "SKIP %s: %s", \ 470 | name, \ 471 | greatest_info.msg ? \ 472 | greatest_info.msg : "" ); \ 473 | } else { \ 474 | fprintf(GREATEST_STDOUT, "s"); \ 475 | } \ 476 | greatest_info.suite.skipped++; \ 477 | } \ 478 | \ 479 | void greatest_usage(const char *name) { \ 480 | fprintf(GREATEST_STDOUT, \ 481 | "Usage: %s [-hlfv] [-s SUITE] [-t TEST]\n" \ 482 | " -h print this Help\n" \ 483 | " -l List suites and their tests, then exit\n" \ 484 | " -f Stop runner after first failure\n" \ 485 | " -v Verbose output\n" \ 486 | " -s SUITE only run suite named SUITE\n" \ 487 | " -t TEST only run test named TEST\n", \ 488 | name); \ 489 | } \ 490 | \ 491 | void GREATEST_SET_SETUP_CB(greatest_setup_cb *cb, void *udata) { \ 492 | greatest_info.setup = cb; \ 493 | greatest_info.setup_udata = udata; \ 494 | } \ 495 | \ 496 | void GREATEST_SET_TEARDOWN_CB(greatest_teardown_cb *cb, \ 497 | void *udata) { \ 498 | greatest_info.teardown = cb; \ 499 | greatest_info.teardown_udata = udata; \ 500 | } \ 501 | \ 502 | greatest_run_info greatest_info 503 | 504 | /* Handle command-line arguments, etc. */ 505 | #define GREATEST_MAIN_BEGIN() \ 506 | do { \ 507 | int i = 0; \ 508 | memset(&greatest_info, 0, sizeof(greatest_info)); \ 509 | if (greatest_info.width == 0) { \ 510 | greatest_info.width = GREATEST_DEFAULT_WIDTH; \ 511 | } \ 512 | for (i = 1; i < argc; i++) { \ 513 | if (0 == strcmp("-t", argv[i])) { \ 514 | if (argc <= i + 1) { \ 515 | greatest_usage(argv[0]); \ 516 | exit(EXIT_FAILURE); \ 517 | } \ 518 | greatest_info.test_filter = argv[i+1]; \ 519 | i++; \ 520 | } else if (0 == strcmp("-s", argv[i])) { \ 521 | if (argc <= i + 1) { \ 522 | greatest_usage(argv[0]); \ 523 | exit(EXIT_FAILURE); \ 524 | } \ 525 | greatest_info.suite_filter = argv[i+1]; \ 526 | i++; \ 527 | } else if (0 == strcmp("-f", argv[i])) { \ 528 | greatest_info.flags |= GREATEST_FLAG_FIRST_FAIL; \ 529 | } else if (0 == strcmp("-v", argv[i])) { \ 530 | greatest_info.flags |= GREATEST_FLAG_VERBOSE; \ 531 | } else if (0 == strcmp("-l", argv[i])) { \ 532 | greatest_info.flags |= GREATEST_FLAG_LIST_ONLY; \ 533 | } else if (0 == strcmp("-h", argv[i])) { \ 534 | greatest_usage(argv[0]); \ 535 | exit(EXIT_SUCCESS); \ 536 | } else { \ 537 | fprintf(GREATEST_STDOUT, \ 538 | "Unknown argument '%s'\n", argv[i]); \ 539 | greatest_usage(argv[0]); \ 540 | exit(EXIT_FAILURE); \ 541 | } \ 542 | } \ 543 | } while (0); \ 544 | GREATEST_SET_TIME(greatest_info.begin) 545 | 546 | #define GREATEST_MAIN_END() \ 547 | do { \ 548 | if (!GREATEST_LIST_ONLY()) { \ 549 | GREATEST_SET_TIME(greatest_info.end); \ 550 | fprintf(GREATEST_STDOUT, \ 551 | "\nTotal: %u tests", greatest_info.tests_run); \ 552 | GREATEST_CLOCK_DIFF(greatest_info.begin, \ 553 | greatest_info.end); \ 554 | fprintf(GREATEST_STDOUT, "\n"); \ 555 | fprintf(GREATEST_STDOUT, \ 556 | "Pass: %u, fail: %u, skip: %u.\n", \ 557 | greatest_info.passed, \ 558 | greatest_info.failed, greatest_info.skipped); \ 559 | } \ 560 | return (greatest_info.failed > 0 \ 561 | ? EXIT_FAILURE : EXIT_SUCCESS); \ 562 | } while (0) 563 | 564 | /* Make abbreviations without the GREATEST_ prefix for the 565 | * most commonly used symbols. */ 566 | #if GREATEST_USE_ABBREVS 567 | #define TEST GREATEST_TEST 568 | #define SUITE GREATEST_SUITE 569 | #define RUN_TEST GREATEST_RUN_TEST 570 | #define RUN_TEST1 GREATEST_RUN_TEST1 571 | #define RUN_SUITE GREATEST_RUN_SUITE 572 | #define ASSERT GREATEST_ASSERT 573 | #define ASSERTm GREATEST_ASSERTm 574 | #define ASSERT_FALSE GREATEST_ASSERT_FALSE 575 | #define ASSERT_EQ GREATEST_ASSERT_EQ 576 | #define ASSERT_STR_EQ GREATEST_ASSERT_STR_EQ 577 | #define ASSERT_FALSEm GREATEST_ASSERT_FALSEm 578 | #define ASSERT_EQm GREATEST_ASSERT_EQm 579 | #define ASSERT_STR_EQm GREATEST_ASSERT_STR_EQm 580 | #define PASS GREATEST_PASS 581 | #define FAIL GREATEST_FAIL 582 | #define SKIP GREATEST_SKIP 583 | #define PASSm GREATEST_PASSm 584 | #define FAILm GREATEST_FAILm 585 | #define SKIPm GREATEST_SKIPm 586 | #define SET_SETUP GREATEST_SET_SETUP_CB 587 | #define SET_TEARDOWN GREATEST_SET_TEARDOWN_CB 588 | 589 | #if __STDC_VERSION__ >= 19901L 590 | #endif /* C99 */ 591 | #define RUN_TESTp GREATEST_RUN_TESTp 592 | #endif /* USE_ABBREVS */ 593 | 594 | #endif 595 | -------------------------------------------------------------------------------- /test/test_assert.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | assert_action assert_callback( const char* cond, const char* msg, const char* file, unsigned int line, void* /*user_data*/ ) 6 | { 7 | printf("assert fail at: %s( %u ): %s -> \"%s\"\n", file, line, cond, msg); 8 | 9 | // ... skip all asserts ... 10 | return ASSERT_ACTION_NONE; 11 | } 12 | 13 | int main( int, char** ) 14 | { 15 | assert_register_callback( assert_callback, 0x0 ); 16 | 17 | // ... standard assert ... 18 | ASSERT( false ); 19 | 20 | // ... assert with message ... 21 | ASSERT( false, "assert with message" ); 22 | 23 | // ... assert with message and args ... 24 | ASSERT( false, "assert with message %s %d", "a string", 1337 ); 25 | 26 | // ... assert is a statement ... 27 | if( true ) 28 | ASSERT( false, "is statement" ); 29 | else 30 | {} 31 | 32 | // ... assert is also a statement that can be used with the ternary operator ... 33 | true ? ASSERT( false, "in ternary op" ) : ASSERT( false, "also in ternary op" ); 34 | 35 | // ... assert should not warn for unused variable when not used ... 36 | int i_am_used = 1; 37 | ASSERT( i_am_used == 1 ); 38 | 39 | bool verified = false; 40 | VERIFY( verified = true ); 41 | 42 | if( !verified ) 43 | printf("NOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO1 ( ERROR ERROR ERROR )!\n"); 44 | 45 | verified = false; 46 | if( true ) 47 | VERIFY( verified = true ); 48 | 49 | if( !verified ) 50 | printf("NOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO2 ( ERROR ERROR ERROR )!\n"); 51 | 52 | return 0; 53 | } 54 | -------------------------------------------------------------------------------- /test/test_callstack.c: -------------------------------------------------------------------------------- 1 | /* 2 | Simple test-program of written in c for callstack() and callstack_symbols()from dbgtools. 3 | 4 | version 0.1, october, 2013 5 | 6 | Copyright (C) 2013- Fredrik Kihlander 7 | 8 | This software is provided 'as-is', without any express or implied 9 | warranty. In no event will the authors be held liable for any damages 10 | arising from the use of this software. 11 | 12 | Permission is granted to anyone to use this software for any purpose, 13 | including commercial applications, and to alter it and redistribute it 14 | freely, subject to the following restrictions: 15 | 16 | 1. The origin of this software must not be misrepresented; you must not 17 | claim that you wrote the original software. If you use this software 18 | in a product, an acknowledgment in the product documentation would be 19 | appreciated but is not required. 20 | 2. Altered source versions must be plainly marked as such, and must not be 21 | misrepresented as being the original software. 22 | 3. This notice may not be removed or altered from any source distribution. 23 | 24 | Fredrik Kihlander 25 | */ 26 | 27 | #include 28 | 29 | #include 30 | 31 | void print_callstack() 32 | { 33 | void* addresses[256]; 34 | int i; 35 | int num_addresses = callstack( 0, addresses, 256 ); 36 | 37 | callstack_symbol_t symbols[256]; 38 | char symbols_buffer[1024]; 39 | num_addresses = callstack_symbols( addresses, symbols, num_addresses, symbols_buffer, 1024 ); 40 | 41 | for( i = 0; i < num_addresses; ++i ) 42 | printf( "%3d) %-50s %s(%u)\n", i, symbols[i].function, symbols[i].file, symbols[i].line ); 43 | } 44 | 45 | typedef void (*cb)( int, int ); 46 | 47 | void func1( int, int ); 48 | void func2( int, int ); 49 | void func3( int, int ); 50 | 51 | cb funcs[] = { func1, func2, func3 }; 52 | 53 | void func1( int call_me, int depth ) 54 | { 55 | if( depth == 0 ) 56 | { 57 | print_callstack(); 58 | return; 59 | } 60 | funcs[call_me]( 2, depth - 1 ); 61 | } 62 | 63 | void func2( int call_me, int depth ) 64 | { 65 | if( depth == 0 ) 66 | { 67 | print_callstack(); 68 | return; 69 | } 70 | funcs[call_me]( 0, depth - 1 ); 71 | } 72 | 73 | void func3( int call_me, int depth ) 74 | { 75 | if( depth == 0 ) 76 | { 77 | print_callstack(); 78 | return; 79 | } 80 | funcs[call_me]( 1, depth - 1 ); 81 | } 82 | 83 | int main( int argc, const char** argv ) 84 | { 85 | (void)argc; (void) argv; 86 | func1( 1, 5 ); 87 | return 0; 88 | } 89 | -------------------------------------------------------------------------------- /test/test_callstack_cpp.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Simple test-program of written in c++ for callstack() and callstack_symbols()from dbgtools. 3 | 4 | version 0.1, october, 2013 5 | 6 | Copyright (C) 2013- Fredrik Kihlander 7 | 8 | This software is provided 'as-is', without any express or implied 9 | warranty. In no event will the authors be held liable for any damages 10 | arising from the use of this software. 11 | 12 | Permission is granted to anyone to use this software for any purpose, 13 | including commercial applications, and to alter it and redistribute it 14 | freely, subject to the following restrictions: 15 | 16 | 1. The origin of this software must not be misrepresented; you must not 17 | claim that you wrote the original software. If you use this software 18 | in a product, an acknowledgment in the product documentation would be 19 | appreciated but is not required. 20 | 2. Altered source versions must be plainly marked as such, and must not be 21 | misrepresented as being the original software. 22 | 3. This notice may not be removed or altered from any source distribution. 23 | 24 | Fredrik Kihlander 25 | */ 26 | 27 | #include 28 | 29 | #include 30 | 31 | void print_callstack() 32 | { 33 | void* addresses[256]; 34 | int num_addresses = callstack( 0, addresses, 256 ); 35 | 36 | callstack_symbol_t symbols[256]; 37 | char symbols_buffer[2048]; 38 | num_addresses = callstack_symbols( addresses, symbols, num_addresses, symbols_buffer, 2048 ); 39 | 40 | int i; 41 | for( i = 0; i < num_addresses; ++i ) 42 | printf( "%3d) %-50s %s(%u)\n", i, symbols[i].function, symbols[i].file, symbols[i].line ); 43 | } 44 | 45 | typedef void (*cb)( int, int ); 46 | 47 | cb funcs[3]; 48 | 49 | template < typename T > 50 | class my_class 51 | { 52 | public: 53 | void a_member_func( int call_me, int depth ) 54 | { 55 | if( depth == 0 ) 56 | { 57 | print_callstack(); 58 | return; 59 | } 60 | funcs[call_me]( 1, depth - 1 ); 61 | } 62 | 63 | static void a_static_func( int call_me, int depth ) 64 | { 65 | if( depth == 0 ) 66 | { 67 | print_callstack(); 68 | return; 69 | } 70 | funcs[call_me]( 2, depth - 1 ); 71 | } 72 | }; 73 | 74 | template< typename T > 75 | void a_static_func( int call_me, int depth ) 76 | { 77 | my_class c; 78 | c.a_member_func( call_me, depth ); 79 | } 80 | 81 | int main( int, const char** ) 82 | { 83 | funcs[0] = my_class::a_static_func; 84 | funcs[1] = a_static_func; 85 | funcs[2] = a_static_func; 86 | 87 | a_static_func( 0, 6 ); 88 | return 0; 89 | } 90 | -------------------------------------------------------------------------------- /test/test_debugger_present.c: -------------------------------------------------------------------------------- 1 | /* 2 | Simple test-program of debugger_present() from dbgtools. 3 | 4 | version 1.0, october, 2013 5 | 6 | Copyright (C) 2013- Fredrik Kihlander 7 | 8 | This software is provided 'as-is', without any express or implied 9 | warranty. In no event will the authors be held liable for any damages 10 | arising from the use of this software. 11 | 12 | Permission is granted to anyone to use this software for any purpose, 13 | including commercial applications, and to alter it and redistribute it 14 | freely, subject to the following restrictions: 15 | 16 | 1. The origin of this software must not be misrepresented; you must not 17 | claim that you wrote the original software. If you use this software 18 | in a product, an acknowledgment in the product documentation would be 19 | appreciated but is not required. 20 | 2. Altered source versions must be plainly marked as such, and must not be 21 | misrepresented as being the original software. 22 | 3. This notice may not be removed or altered from any source distribution. 23 | 24 | Fredrik Kihlander 25 | */ 26 | 27 | #include 28 | 29 | #include 30 | 31 | #if defined( __unix__ ) || defined(unix) || defined(__unix) || ( defined(__APPLE__) && defined(__MACH__) ) 32 | #include 33 | #elif defined( _MSC_VER ) 34 | #include 35 | #define sleep( x ) Sleep( x * 1000 ) 36 | #endif 37 | 38 | int main( int argc, const char** argv ) 39 | { 40 | (void)argc; (void)argv; 41 | for(;;) 42 | { 43 | printf("debugger: %s\n", debugger_present() ? "ATTACHED" : "NOT ATTACHED" ); 44 | sleep( 1 ); 45 | } 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /test/test_fpe_ctrl.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | dbgtools - platform independent wrapping of "nice to have" debug functions. 3 | 4 | version 0.1, october, 2013 5 | 6 | Copyright (C) 2013- Fredrik Kihlander 7 | 8 | This software is provided 'as-is', without any express or implied 9 | warranty. In no event will the authors be held liable for any damages 10 | arising from the use of this software. 11 | 12 | Permission is granted to anyone to use this software for any purpose, 13 | including commercial applications, and to alter it and redistribute it 14 | freely, subject to the following restrictions: 15 | 16 | 1. The origin of this software must not be misrepresented; you must not 17 | claim that you wrote the original software. If you use this software 18 | in a product, an acknowledgment in the product documentation would be 19 | appreciated but is not required. 20 | 2. Altered source versions must be plainly marked as such, and must not be 21 | misrepresented as being the original software. 22 | 3. This notice may not be removed or altered from any source distribution. 23 | 24 | Fredrik Kihlander 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "greatest.h" 35 | 36 | static float raise_fpe_invalid() { return sqrtf( -1.0f ); } 37 | static float raise_fpe_div_zero() { static volatile float z = 0.0f; return 1.0f / z; } 38 | static float raise_fpe_overflow() { return expf( 88.8f ); } 39 | static float raise_fpe_underflow() { return expf( -88.8f ); } 40 | static float raise_fpe_inexact() { static volatile float z = 3.0f; return 2.0f / z; } 41 | 42 | int flags_set_correctly() 43 | { 44 | fpe_clear( fp_exception_all ); 45 | GREATEST_ASSERT_FALSE( fpe_test( fp_exception_div_by_zero ) ); 46 | 47 | raise_fpe_invalid(); GREATEST_ASSERT( fpe_test( fp_exception_invalid ) ); fpe_clear( fp_exception_all ); 48 | raise_fpe_div_zero(); GREATEST_ASSERT( fpe_test( fp_exception_div_by_zero ) ); fpe_clear( fp_exception_all ); 49 | raise_fpe_overflow(); GREATEST_ASSERT( fpe_test( fp_exception_overflow ) ); fpe_clear( fp_exception_all ); 50 | raise_fpe_underflow(); GREATEST_ASSERT( fpe_test( fp_exception_underflow ) ); fpe_clear( fp_exception_all ); 51 | raise_fpe_inexact(); GREATEST_ASSERT( fpe_test( fp_exception_inexact ) ); fpe_clear( fp_exception_all ); 52 | 53 | return 0; 54 | } 55 | 56 | int raise_set_correctly() 57 | { 58 | fpe_clear( fp_exception_all ); 59 | 60 | fpe_raise( fp_exception_invalid ); GREATEST_ASSERT( fpe_test( fp_exception_invalid ) ); fpe_clear( fp_exception_all ); 61 | fpe_raise( fp_exception_div_by_zero ); GREATEST_ASSERT( fpe_test( fp_exception_div_by_zero ) ); fpe_clear( fp_exception_all ); 62 | fpe_raise( fp_exception_overflow ); GREATEST_ASSERT( fpe_test( fp_exception_overflow ) ); fpe_clear( fp_exception_all ); 63 | fpe_raise( fp_exception_underflow ); GREATEST_ASSERT( fpe_test( fp_exception_underflow ) ); fpe_clear( fp_exception_all ); 64 | fpe_raise( fp_exception_inexact ); GREATEST_ASSERT( fpe_test( fp_exception_inexact ) ); fpe_clear( fp_exception_all ); 65 | 66 | return 0; 67 | } 68 | 69 | int test_get_trapped() 70 | { 71 | fpe_enable_trap( fp_exception_all ); 72 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_invalid, fp_exception_invalid ); 73 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_div_by_zero, fp_exception_div_by_zero ); 74 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_overflow, fp_exception_overflow ); 75 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_underflow, fp_exception_underflow ); 76 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_inexact, fp_exception_inexact ); 77 | GREATEST_ASSERT_EQ( fpe_get_trapped() & (unsigned)~fp_exception_all, 0 ); 78 | fpe_disable_trap( fp_exception_all ); 79 | 80 | fpe_enable_trap( fp_exception_invalid ); 81 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_invalid, fp_exception_invalid ); 82 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_div_by_zero, 0 ); 83 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_overflow, 0 ); 84 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_underflow, 0 ); 85 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_inexact, 0 ); 86 | fpe_disable_trap( fp_exception_all ); 87 | 88 | fpe_enable_trap( fp_exception_div_by_zero ); 89 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_invalid, 0 ); 90 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_div_by_zero, fp_exception_div_by_zero ); 91 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_overflow, 0 ); 92 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_underflow, 0 ); 93 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_inexact, 0 ); 94 | fpe_disable_trap( fp_exception_all ); 95 | 96 | fpe_enable_trap( fp_exception_overflow ); 97 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_invalid, 0 ); 98 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_div_by_zero, 0 ); 99 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_overflow, fp_exception_overflow ); 100 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_underflow, 0 ); 101 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_inexact, 0 ); 102 | fpe_disable_trap( fp_exception_all ); 103 | 104 | fpe_enable_trap( fp_exception_underflow ); 105 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_invalid, 0 ); 106 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_div_by_zero, 0 ); 107 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_overflow, 0 ); 108 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_underflow, fp_exception_underflow ); 109 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_inexact, 0 ); 110 | fpe_disable_trap( fp_exception_all ); 111 | 112 | fpe_enable_trap( fp_exception_inexact ); 113 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_invalid, 0 ); 114 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_div_by_zero, 0 ); 115 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_overflow, 0 ); 116 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_underflow, 0 ); 117 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_inexact, fp_exception_inexact ); 118 | fpe_disable_trap( fp_exception_all ); 119 | 120 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_invalid, 0 ); 121 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_div_by_zero, 0 ); 122 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_overflow, 0 ); 123 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_underflow, 0 ); 124 | GREATEST_ASSERT_EQ( fpe_get_trapped() & fp_exception_inexact, 0 ); 125 | 126 | return 0; 127 | } 128 | 129 | #include 130 | 131 | #if defined( _MSC_VER ) 132 | // TODO: generalize this! 133 | void sigfpe_handler( int sig, int fpe ) 134 | { 135 | if( sig != SIGFPE ) 136 | printf( "no SIGFPE!?!\n" ); 137 | else 138 | { 139 | switch( fpe ) 140 | { 141 | case FPE_INVALID: printf("raised invalid!\n"); break; 142 | case FPE_ZERODIVIDE: printf("raised div by zero!\n"); break; 143 | case FPE_OVERFLOW: printf("raised overflow!\n"); break; 144 | case FPE_UNDERFLOW: printf("raised underflow!\n"); break; 145 | case FPE_INEXACT: printf("raised inexact!\n"); break; 146 | } 147 | } 148 | exit( 0 ); 149 | } 150 | #else 151 | void sigfpe_handler( int /*sig*/ ) 152 | { 153 | printf("raised!\n"); 154 | exit( 0 ); 155 | } 156 | #endif 157 | 158 | GREATEST_SUITE(fpe_ctrl) 159 | { 160 | RUN_TEST( flags_set_correctly ); 161 | RUN_TEST( raise_set_correctly ); 162 | RUN_TEST( test_get_trapped ); 163 | } 164 | 165 | extern SUITE(fpe_ctrl); 166 | 167 | GREATEST_MAIN_DEFS(); 168 | 169 | int main( int argc, char** argv ) 170 | { 171 | GREATEST_MAIN_BEGIN(); 172 | RUN_SUITE( fpe_ctrl ); 173 | GREATEST_MAIN_END(); 174 | 175 | /* 176 | if( argc > 1 ) 177 | { 178 | signal( SIGFPE, (void(*)( int ))sigfpe_handler ); 179 | 180 | fpe_enable_trap( fp_exception_all ); 181 | 182 | if( strcmp( argv[1], "invalid" ) == 0 ) raise_fpe_invalid(); 183 | if( strcmp( argv[1], "divzero" ) == 0 ) raise_fpe_div_zero(); 184 | if( strcmp( argv[1], "overflow" ) == 0 ) raise_fpe_overflow(); 185 | if( strcmp( argv[1], "inexact" ) == 0 ) raise_fpe_inexact(); 186 | if( strcmp( argv[1], "underflow" ) == 0 ) raise_fpe_underflow(); 187 | 188 | printf("fail!!!\n"); 189 | return 1; 190 | } 191 | 192 | printf("success!\n");*/ 193 | return 0; 194 | } 195 | -------------------------------------------------------------------------------- /test/test_hw_breakpoint.c: -------------------------------------------------------------------------------- 1 | /* 2 | Simpl test-program of hw_breakpoint_set() from dbgtools. 3 | 4 | version 1.0, october, 2013 5 | 6 | Copyright (C) 2013- Fredrik Kihlander 7 | 8 | This software is provided 'as-is', without any express or implied 9 | warranty. In no event will the authors be held liable for any damages 10 | arising from the use of this software. 11 | 12 | Permission is granted to anyone to use this software for any purpose, 13 | including commercial applications, and to alter it and redistribute it 14 | freely, subject to the following restrictions: 15 | 16 | 1. The origin of this software must not be misrepresented; you must not 17 | claim that you wrote the original software. If you use this software 18 | in a product, an acknowledgment in the product documentation would be 19 | appreciated but is not required. 20 | 2. Altered source versions must be plainly marked as such, and must not be 21 | misrepresented as being the original software. 22 | 3. This notice may not be removed or altered from any source distribution. 23 | 24 | Fredrik Kihlander 25 | */ 26 | 27 | #include 28 | 29 | #include 30 | #include 31 | 32 | #if defined( __unix__ ) || defined(unix) || defined(__unix) || ( defined(__APPLE__) && defined(__MACH__) ) 33 | #include 34 | #elif defined( _MSC_VER ) 35 | #include 36 | #define sleep( x ) Sleep( x * 1000 ) 37 | #endif 38 | 39 | int main( int argc, const char** argv ) 40 | { 41 | int hwbp; 42 | int break_size = 4; 43 | int break_index = 16; 44 | int break_offset = -1; 45 | char memory[256]; 46 | 47 | if( argc > 1 ) 48 | break_size = atoi( argv[1] ); 49 | if( argc > 2 ) 50 | break_offset = atoi( argv[2] ); 51 | 52 | if( break_offset < 0 ) 53 | break_offset = break_size - 1; 54 | 55 | printf("installing breakpoint of size %d on index %d\n", break_size, break_index); 56 | hwbp = hw_breakpoint_set( &memory[break_index], (unsigned int)break_size, HW_BREAKPOINT_WRITE ); 57 | if( hwbp < 0 ) 58 | { 59 | printf("failed to install hwbp!\n"); 60 | } 61 | else 62 | { 63 | printf( "write before breakpoint at index %d...\n", break_index-1 ); 64 | memory[break_index-1]++; 65 | 66 | printf( "write after breakpoint at index %d...\n", break_index+break_size ); 67 | memory[break_index+break_size]++; 68 | 69 | printf( "write at end of breakpoint at index %d...\n", break_index+break_offset ); 70 | memory[break_index+break_offset]++; 71 | 72 | hw_breakpoint_clear(hwbp); 73 | } 74 | 75 | printf("exit normaly!?!\n"); 76 | 77 | return 0; 78 | } 79 | -------------------------------------------------------------------------------- /test/test_static_assert.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "test_static_assert1.h" 4 | #include "test_static_assert2.h" 5 | 6 | enum { 7 | test_var1 = 1337, 8 | test_var2 = 7331, 9 | 10 | test_var1_cmp = 1337, 11 | test_var2_cmp = 7331, 12 | }; 13 | 14 | /* ... globally ... */ 15 | STATIC_ASSERT( test_var1 == test_var1_cmp, "static assert fail" ); 16 | STATIC_ASSERT( test_var2 == test_var2_cmp, "static assert fail" ); 17 | 18 | int func() 19 | { 20 | /* ... in func ... */ 21 | STATIC_ASSERT( test_var1 == test_var1_cmp, "static assert fail" ); 22 | STATIC_ASSERT( test_var2 == test_var2_cmp, "static assert fail" ); 23 | return 1; 24 | } 25 | -------------------------------------------------------------------------------- /test/test_static_assert1.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_STATIC_ASSERT1_H_INCLUDED 2 | #define TEST_STATIC_ASSERT1_H_INCLUDED 3 | 4 | #include 5 | 6 | STATIC_ASSERT( __LINE__ == 6, "line 6" ); 7 | 8 | #endif // TEST_STATIC_ASSERT1_H_INCLUDED 9 | -------------------------------------------------------------------------------- /test/test_static_assert2.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_STATIC_ASSERT2_H_INCLUDED 2 | #define TEST_STATIC_ASSERT2_H_INCLUDED 3 | 4 | #include 5 | 6 | STATIC_ASSERT( __LINE__ == 6, "line 6" ); 7 | 8 | #endif // TEST_STATIC_ASSERT2_H_INCLUDED 9 | -------------------------------------------------------------------------------- /test/test_static_assert_cpp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "test_static_assert1.h" 4 | #include "test_static_assert2.h" 5 | 6 | enum 7 | { 8 | test_var1 = 1337, 9 | test_var2 = 7331, 10 | 11 | test_var1_cmp = 1337, 12 | test_var2_cmp = 7331, 13 | }; 14 | 15 | // ... globally ... 16 | STATIC_ASSERT( test_var1 == test_var1_cmp, "static assert fail" ); 17 | STATIC_ASSERT( test_var2 == test_var2_cmp, "static assert fail" ); 18 | 19 | struct test 20 | { 21 | int dummy; 22 | 23 | // ... in struct ... 24 | STATIC_ASSERT( test_var1 == test_var1_cmp, "static assert fail" ); 25 | STATIC_ASSERT( test_var2 == test_var2_cmp, "static assert fail" ); 26 | }; 27 | 28 | STATIC_ASSERT( sizeof( struct test ) == sizeof(int), "static assert add size to struct!" ); 29 | 30 | int func() 31 | { 32 | // ... in func ... 33 | STATIC_ASSERT( test_var1 == test_var1_cmp, "static assert fail" ); 34 | STATIC_ASSERT( test_var2 == test_var2_cmp, "static assert fail" ); 35 | return 1; 36 | } 37 | 38 | --------------------------------------------------------------------------------