/** * @file dbg.h * @brief デバッグ関係の処理(ログ出力等の簡易なもののみ) * @author tenk* * @note * - ヘッダinclueのみで利用可能. * - この版は主に win or linux(unix) 用を想定. * - ERR_ASSERT,ERR_PUTS あたりを書換ることで、別ターゲット対応. * - winの場合は _WINDOWSマクロ定義で GUI としてデバッグ出力も可能. * - DBG_系は、DEBUG時のみチェック(NDEBUG定義で消える) * ERR_系は、DEBUG,RELEASE時も有効 * ただし DBG_MACRO_ON を定義している場合は、NDEBUG 設定でもマクロoff * しないようになる. * - assert(x)マクロを、ここで定義している DBG_ASSERTに置換する. * ※が、include順番等により、再度乗っ取られたりするので、注意. */ /* - 主な命令. ※ ??? は DBG or ERR DBG_STATIC_ASSERT(x) コンパイル時のassertチェック. ERR_IS_RAM_PTR(p) p がRAM領域を指しているか否かを返す. ???_ABORT() アボート終了.(あるいはブレーク) ???_PRINTF(x) ログprintf書式出力. 例) DBG_PRINTF(("%s %d\n",f,l)); ???_ABORTMSG(x) ログ出力&アボート. 例) DBG_ABORTMSG(("%s %d\n",f,l)); ???_FAIL(a) ソースファイル、行番号、メッセージを表示. ???_BAD_ROUTE() 来てはいけないルートに置く. DBG_CHECK(x) 式 x が真か? 偽なら警告としてログ出力. DBG_ASSERT(x) 式 x が真か? 偽ならログ出力して停止. DBG_ASSERT_MSG(x, s) 式 x が真か? 偽なら sをログ出力して停止. DBG_ASSERT_PTR(p) RAMアドレスの簡易なチェック. NULL はアウト. DBG_ASSERT_PTR0(p) RAMアドレスの簡易なチェック. NULLもok. DBG_DUMP(a,sz) メモリダンプ. DBG_DIFF(a,b,sz,dspN) メモリ比較. a,bアドレスからszバイト. 違った場合はdspN個までは表示. DBG_LIM_I(a, mi, ma) 整数変数 aが mi<= a <= ma を満たすか? DBG_LIM_U(a, ma) 符号無し変数 a が a <= ma を満たすか? DBG_LIM_LL(a, mi, ma) long long 整数変数 aが mi<= a <= ma を満たすか? DBG_LIM_BOOL(a) 変数a が0or1か? DBG_LIM_F(a, mi, ma) float変数 aが mi<= a <= ma を満たすか? DBG_LIM_D(a, mi, ma) double変数 aが mi<= a <= ma を満たすか? DBG_LIM_LD(a, mi, ma) long double変数 aが mi<= a <= ma を満たすか? DBG_EQ_I(a, b) a=b か? DBG_EQ_LL(a, b) a=b か? DBG_EQ_F(a, b, d) float型で a==b±d か? DBG_EQ_D(a, b, d) double型で a==b±d か? DBG_EQ_LD(a, b, d) long double型で a==b±d か? DBG_EQ_MEM(a, b, l) 指定アドレスのメモリがlバイト同じか? DBG_LIMIT(a, mi, ma) c++時にstrstream使用の範囲チェック. DBG_EQ(a, b) c++時にa==bをチェック. DBG_PUT_CLASS_SIZE(c) class c のバイト数をエラーログ出力する DBG_COUNT_CLASS(NMAX) メンバーに記述することでclass生成回数を限定. DBG_CHK_NEST_CALL(T) メンバー関数内に記す. 呼出がネストしているか? */ #ifndef DBG_H_INCLUDED #define DBG_H_INCLUDED #include <stdarg.h> #include <stdio.h> #include <string.h> #ifndef assert #include <assert.h> #endif #if defined(NDEBUG) && defined(DBG_MACRO_ON) == 0 #define DBG_MACRO_OFF // DBG_系マクロを空にする場合に定義 #endif // ########################################################################### // デバッグマクロ内で使うマクロ // ########################################################################### // 2つの引数を連結して1つのラベルを生成 #define ERR_STR_CAT(a,b) ERR_STR_CAT_2(a,b) #define ERR_STR_CAT_2(a,b) ERR_STR_CAT_3(a##b) #define ERR_STR_CAT_3(x) x // __func__ がなさそうならがあればそれを使う(結局C99になってなかったのねん...) //#if (defined __STDC_VERSION__ == 0 || __STDC_VERSION__ < 199901L) // #define __func__ "" // __FILE__ "(" DBG_I2STR(__LINE__) ")" //#endif #ifdef _WIN32 #define DBG_LLONG __int64 #define DBG_ULLONG unsigned __int64 #if defined __cplusplus == 0 && __STDC_VERSION__ < 199901L #define inline __inline #endif #else #define DBG_LLONG long long #define DBG_ULLONG unsigned long long #endif // ########################################################################### // エラー用処理, マクロ ※ ERR_ で始まるものは NDEBUGでも実行される. // ########################################################################### // ポインタとしておかしな値なら真( 環境依存なので、ここでは大雑把に定義 ) #if defined _WIN64 #define ERR_IS_RAM_PTR(p) ((size_t)(p) >= 0x10000 && (size_t)(p) <= 0xFFFF000000000000LL) #elif defined _WIN32 #define ERR_IS_RAM_PTR(p) ((size_t)(p) >= 0x10000 && (size_t)(p) <= 0xF0000000) #else #define ERR_IS_RAM_PTR(p) ((size_t)(p) >= 0x1000 && (size_t)(p) <= 0xFffff000) #endif #define ERR_IS_RAM_PTR0(p) ((p) == 0 || ERR_IS_RAM_PTR(p)) #ifdef _WINDOWS // GUIアプリの時 static inline int err_abort() { DebugBreak(); return 0; } #define ERR_ABORT() err_abort() //DebugBreak() #define ERR_PUTS(s) OutputDebugString(s) #define ERR_VSNPRINTF(b,l,f,a) _vsnprintf(b,l,f,a) #else // コマンドライン・アプリのとき. #include <stdlib.h> #ifdef NDEBUG static inline int err_abort() { exit(1); return 0; } #else static inline int err_abort() { abort(); return 0; } #endif #ifndef ERR_ABORT #define ERR_ABORT() err_abort() //#define ERR_ABORT() ((*(char*)0) = 0) #endif #ifndef ERR_PUTS #define ERR_PUTS(s) fputs(s, stderr) #endif #ifndef ERR_VSNPRINTF #define ERR_VSNPRINTF(b,l,f,a) vsnprintf(b,l,f,a) #endif #endif #define ERR_PRINTF(x) err_printf x #define ERR_ABORTMSG(s) (DBG_PRINTF(s) && ERR_ABORT()) #define ERR_FAIL(a) ERR_ABORTMSG(("%s(%d): %s",__FILE__,__LINE__,a)) #define ERR_BAD_ROUTE() ERR_FAIL("ERROR: Bad route!\n") #define ERR_STREAM(msg) do { \ char _buf_[1030]; \ std::strstream _ss_(_buf_,sizeof _buf_);\ _ss_ << msg << std::ends; \ ERR_PUTS(_buf_); \ } while (0) static inline int err_printf(const char* f, ...) { char b[0x4000]; va_list a; va_start(a,f); ERR_VSNPRINTF(b, (sizeof b), f, a); va_end(a); ERR_PUTS(b); return 1; } #if 0 // defined __cplusplus && !(defined __WATCOMC__) // やっぱりアライメントチェックは止め. #include <cstddef> // ポインタのアライメントチェック. template<typename T> bool err_check_ptr_align(T* p) { p; return 1; } template<> bool err_check_ptr_align<short>(short* p) { return !( (std::size_t)p & (sizeof(short)-1) ); } template<> bool err_check_ptr_align<unsigned short>(unsigned short* p) { return !( (std::size_t)p & (sizeof(unsigned short)-1) ); } template<> bool err_check_ptr_align<int>(int* p) { return !( (std::size_t)p & (sizeof(int)-1) ); } template<> bool err_check_ptr_align<unsigned>(unsigned* p) { return !( (std::size_t)p & (sizeof(unsigned)-1) ); } template<> bool err_check_ptr_align<long>(long* p) { return !( (std::size_t)p & (sizeof(long)-1) ); } template<> bool err_check_ptr_align<unsigned long>(unsigned long* p) { return !( (std::size_t)p & (sizeof(unsigned long)-1) ); } template<> bool err_check_ptr_align<DBG_LLONG>(DBG_LLONG* p) { return !( (std::size_t)p & (sizeof(DBG_LLONG)-1) ); } template<> bool err_check_ptr_align<DBG_ULLONG>(DBG_ULLONG* p) { return !( (std::size_t)p & (sizeof(DBG_ULLONG)-1) ); } template<> bool err_check_ptr_align<float>(float* p) { return !( (std::size_t)p & (sizeof(float)-1) ); } template<> bool err_check_ptr_align<double>(double* p) { return !( (std::size_t)p & (sizeof(double)-1) ); } template<> bool err_check_ptr_align<long double>(long double* p) { enum { N = sizeof(long double) }; if (N == 4 || N == 8 || N == 16) return !( (std::size_t)p & (N-1) ); else if (N == 12) return !( (std::size_t)p & (4-1) ); else if (N == 10) return !( (std::size_t)p & (2-1) ); return 1; } template<typename T> bool err_check_ptr_align(T** p) { return !( (std::size_t)p & (sizeof(T*)-1) ); } #else #define err_check_ptr_align(p) 1 #endif // __cplusplus // ########################################################################### // デバッグマクロ ※ DBG_ で始まるマクロは NDEBUG で実コードの生成無し. // ########################################################################### // コンパイル時assert. ※ 実行コードに影響しないので、debug,release関係なく使える. #define DBG_STATIC_ASSERT(cc) typedef char ERR_STR_CAT(Static_Assert_L,__LINE__)[(cc) ? 1/*OK*/ : -1/*ERROR*/]; \ enum { ERR_STR_CAT(STATIC_ASSERT_CHECK_L,__LINE__) = sizeof( ERR_STR_CAT(Static_Assert_L,__LINE__) ) } // _DEBUGコンパイル時のみチェックするマクロ. #if defined(DBG_MACRO_OFF) // リリース時 チェックしない #define DBG_CHECK(x) #define DBG_ASSERT(x) // 式 x が真か? 偽ならログ出力して停止. #define DBG_ASSERT_MSG(x, s) // 式 x が真か? 偽なら s をログ出力&停止. #define DBG_ASSERT_PTR(p) // アドレスが正常な範囲か? NULL駄目. #define DBG_ASSERT_PTR0(p) // アドレスが正常な範囲か? NULLもok. #define DBG_PRINTF(s) // デバッグログ出力 #define DBG_M() // ソースファイル名と行番号をログ出力 #define DBG_DUMP(a,sz) // メモリダンプ. #define DBG_DIFF(a,b,sz,dspN) // メモリ比較. #define DBG_FAIL(a) // 絶対にきてはいけない箇所に配置. #define DBG_BAD_ROUTE() // 絶対にきてはいけないルートに配置. #else #undef assert #define assert(x) DBG_ASSERT(x) #define DBG_CHECK(x) (!(x) && ERR_PRINTF(("%s (%d): warning: (%s) is false.\n", __FILE__, __LINE__, #x)) ) #define DBG_ASSERT(x) (!(x) && ERR_ABORTMSG(("%s (%d): ERROR: (%s) is false.\n", __FILE__, __LINE__, #x)) ) #define DBG_ASSERT_MSG(x, s) (!(x) && ERR_ABORTMSG(("%s (%d): ERROR: (%s) is false.\n\t%s\n", __FILE__, __LINE__, #x, s)) ) #define DBG_ASSERT_PTR(p) (!(ERR_IS_RAM_PTR (p) && err_check_ptr_align(p)) && ERR_ABORTMSG(("%s (%d): ERROR: %s(%p) is bad pointer.\n", __FILE__, __LINE__, #p, p)) ) #define DBG_ASSERT_PTR0(p) (!(ERR_IS_RAM_PTR0(p) && err_check_ptr_align(p)) && ERR_ABORTMSG(("%s (%d): ERROR: %s(%p) is bad pointer.\n", __FILE__, __LINE__, #p, p)) ) #define DBG_PRINTF(s) ERR_PRINTF(s) #define DBG_M() DBG_PRINTF(("%-14s(%5d):\n", __FILE__, __LINE__)) #define DBG_DUMP(a,sz) dbg_dump(a,sz) #define DBG_DIFF(a,b,sz,dspN) dbg_diff(a,b,sz,dspN) #define DBG_FAIL(a) ERR_FAIL(a) #define DBG_BAD_ROUTE() ERR_BAD_ROUTE() #endif static inline void dbg_dump(const void *addr, int sz) { const unsigned char *s = (unsigned char *) addr; int i; sz = (sz > 0) ? (sz + 15) / 16 : 16; for (i = 0; i < sz; ++i) { ERR_PRINTF(("%08x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", s,s[0],s[1],s[2],s[3],s[4],s[5],s[6],s[7],s[8],s[9],s[10],s[11],s[12],s[13],s[14],s[15])); s += 16; } } static inline void dbg_diff(const void *left, const void *right, int sz, int dspNum) { const unsigned char *l = (unsigned char *) left; const unsigned char *r = (unsigned char *) right; unsigned n = 0; int i; dspNum = dspNum < 1 ? 10 : dspNum; ERR_PRINTF(("Memory compare %#x, %#x, %d(%#x) bytes\n", l, r, sz, sz)); for (i = 0; i < sz; ++i, ++l, ++r) { if (*l != *r) { if ((int)(n) < dspNum) { ERR_PRINTF(("%04x(%5d) [%08x %02x(%3d)] [%08x %02x(%3d)]\n" , i, i, l, *l, *l, r, *r, *r)); } ++n; } } ERR_PRINTF(("compare %d bytes, diff. %d bytes\n", sz, n)); } // =========================================================================== // 各種チェック用 // =========================================================================== #if defined( DBG_MACRO_OFF ) // defined(NDEBUG) // リリース時 - - - - - - #define DBG_LIM_I(a, mi, ma) //整数変数 aが mi<= a <= ma を満たすか? #define DBG_LIM_U(a, ma) //符号無し変数 a が a <= ma を満たすか? #define DBG_LIM_LL(a, mi, ma) //long long変数aが mi<= a <= ma を満たすか? #define DBG_LIM_BOOL(a) //変数a が0or1か? #define DBG_LIM_F(a, mi, ma) //float変数 aが mi<= a <= ma を満たすか? #define DBG_LIM_D(a, mi, ma) //double変数 aが mi<= a <= ma を満たすか? #define DBG_LIM_LD(a, mi, ma) //long double変数aが mi<=a<=ma を満たすか? #define DBG_EQ_I(a, b) // a=b か? #define DBG_EQ_LL(a, b) // a=b か? #define DBG_EQ_F(a, b, d) // float型で a==b±d か? #define DBG_EQ_D(a, b, d) // double型で a==b±d か? #define DBG_EQ_LD(a, b, d) // long double型で a==b±d か? #define DBG_EQ_MEM(a, b, l) // 指定アドレスのメモリがlバイト同じか? #ifdef __cplusplus #define DBG_LIMIT(a, mi, ma) // 範囲チェック. strstreamを用いる. #define DBG_EQ(a, b) // a==b か否か? #define DBG_PUT_CLASS_SIZE(c) // class c のバイト数をエラーログ出力する #define DBG_COUNT_CLASS(N) typedef int ERR_STR_CAT(dbg_count_class_,N) #define DBG_CHK_NEST_CALL(T) #define DBG_IS_NEST_CALL(T) (0) template<typename T, typename S> T* dbg_chk_ptr_cast(S* p) { return static_cast<T*>(p); } #endif // __cplusplus #else // デバッグ中 - - - - - - - - - - - - - - - #define DBG_LIM_BOOL(a) ( ((int)(a)!= 0 && (int)(a)!=1 ) && ERR_ABORTMSG(("%s (%d): ERROR: %s=%d(%#x), Out of range[%d,%d]\n", __FILE__, __LINE__, #a,(a),(a), 0 , 1 )) ) #define DBG_LIM_I(a, mi, ma) ( ((a) < (mi) || (ma) < (a)) && ERR_ABORTMSG(("%s (%d): ERROR: %s=%d(%#x), Out of range[%d,%d]\n", __FILE__, __LINE__, #a,(a),(a), (mi), (ma) )) ) #define DBG_LIM_U(a, ma) ( (!((unsigned)(a) <= (ma))) && ERR_ABORTMSG(("%s (%d): ERROR: %s=%d(%#x), Out of range[%d,%d]\n", __FILE__, __LINE__, #a,(a),(a), 0 , (ma) )) ) #define DBG_LIM_LL(a, mi, ma) ( ((a) < (mi) || (ma) < (a)) && ERR_ABORTMSG(("%s (%d): ERROR: %s=%#llx, Out of range[%llx,%llx]\n", __FILE__, __LINE__, #a,(DBG_ULLONG)(a), (DBG_ULLONG)(mi), (DBG_ULLONG)(ma) )) ) #define DBG_LIM_F(a, mi, ma) do {const float *aa=&(a); aa; if ((a) < (mi) || (ma) < (a)) ERR_ABORTMSG(("%s (%d): ERROR: %s=%g, Out of range[%g,%g]\n", __FILE__, __LINE__, #a,(double)(a), (double)(mi), (double)(ma) ));} while(0) #define DBG_LIM_D(a, mi, ma) do {const double *aa=&(a); aa; if ((a) < (mi) || (ma) < (a)) ERR_ABORTMSG(("%s (%d): ERROR: %s=%g, Out of range[%g,%g]\n", __FILE__, __LINE__, #a,(double)(a), (double)(mi), (double)(ma) ));} while(0) #define DBG_LIM_LD(a, mi, ma) do {const long double *aa=&(a); aa; if ((a) < (mi) || (ma) < (a)) ERR_ABORTMSG(("%s (%d): ERROR: %s=%Lg, Out of range[%Lg,%Lg]\n", __FILE__, __LINE__, #a,(long double)(a), (long double)(mi), (long double)(ma) ));} while(0) #define DBG_EQ_I(a, b) do {if ((a) != (b)) ERR_ABORTMSG(("%s (%d): ERROR: %s(%d) != %s(%d)\n", __FILE__, __LINE__, #a, (a), #b, (b) )); } while (0) #define DBG_EQ_LL(a, b) do {if ((a) != (b)) ERR_ABORTMSG(("%s (%d): ERROR: %s(%#llx) != %s(%#llx)\n", __FILE__, __LINE__, #a, (DBG_ULLONG)(a), #b, (DBG_ULLONG)(b) )); } while (0) #define DBG_EQ_F(a, b, d) do {const float *aa=&(a); aa; if (!((a) >= (float)(b)-(d) && (a) <= (float)(b)+(d))) ERR_ABORTMSG(("%s (%d): ERROR: %s(%g) != %s(%g)\n", __FILE__, __LINE__, #a, (double)(a), #b, (double)(b) )); } while (0) #define DBG_EQ_D(a, b, d) do {const double *aa=&(a); aa; if (!((a) >= (double)(b)-(d) && (a) <= (double)(b)+(d))) ERR_ABORTMSG(("%s (%d): ERROR: %s(%g) != %s(%g)\n", __FILE__, __LINE__, #a, (double)(a), #b, (double)(b) )); } while (0) #define DBG_EQ_LD(a, b, d) do {const long double *aa=&(a); aa; if (!((a) >= (long double)(b)-(d) && (a) <= (long double)(b)+(d))) ERR_ABORTMSG(("%s (%d): ERROR: %s(%Lg) != %s(%Lg)\n", __FILE__, __LINE__, #a, (long double)(a), #b, (long double)(b) )); } while (0) #define DBG_EQ_MEM(a, b, l) ( (memcmp((a),(b),(l))) && ERR_ABORTMSG(("%s (%d): ERROR: %s != %s (%dbytes)\n", __FILE__, __LINE__, #a, #b, l)) ) #define DBG_PUT_CLASS_SIZE(c) DBG_PRINTF(("%s %#x(%d)bytes\n", #c, sizeof(c), sizeof(c))) #ifdef __cplusplus // 範囲チェック. strstream を使い、型の分類はコンパイラに任せる. #define DBG_LIMIT(a, mi, ma) do { \ if (! ((mi) <= (a) && (a) <= (ma)) ) { \ ERR_STREAM(__FILE__<<" ("<<__LINE__<<"): ERROR: "<<#a<<"="<<a \ << ", Out of range[" << mi << " , " << ma << "]\n"); \ ERR_ABORT(); \ } \ } while (0) #define DBG_EQ(a, b) do { \ if (! ((a) == (b))) { \ ERR_STREAM(__FILE__ << " (" << __LINE__ << "): ERROR: " \ <<#a<< '(' <<a<< ") != " <<#b<< '('<<b<< ")\n"); \ ERR_ABORT(); \ } \ } while (0) #else #define DBG_LIMIT(a, mi, ma) DBG_ASSERT((mi)<=(a)&&(a)<=(ma)) #define DBG_EQ(a, b) DBG_ASSERT((a) == (b)) #endif #ifdef __cplusplus /** クラスメンバに記述することで、クラスの生成回数をNMAX回までに限定する. * ※ 1バイト以上の実体が発生してしまうので、デバッグ時とリリース時で * struct/classサイズが変動してもよい場合にのみ使える. */ #define DBG_COUNT_CLASS(NMAX) \ class DBG_Count_Class { \ static int add_count(int add) { \ static int num = 0; \ num += add; \ return num; \ } \ public: \ DBG_Count_Class() { \ int n = add_count(1); \ if (n > NMAX) \ ERR_ABORTMSG(("%s (%d): ERROR: too many call constructor.(%d > %d)\n"\ , __FILE__, __LINE__, n, NMAX)); \ } \ ~DBG_Count_Class() { \ int n = add_count(-1); \ if (n < 0) \ ERR_ABORTMSG(("%s (%d): ERROR: too many call destructor.\n" \ , __FILE__, __LINE__)); \ } \ }; \ DBG_Count_Class ERR_STR_CAT(dbg_count_class_,NMAX) // 簡易な再入禁止チェック. チェック箇所別に適当な型Tを設定すること. // 別スレッドや割込からの呼び出し等をきっちり捕捉できるわけでないが、 // ざっくりと、全くしないよりかはましな程度にチェック. #define DBG_CHK_NEST_CALL(T) dbg_nest_call_check<T> \ dbg_nest_call_check___(__FILE__, __LINE__) #define DBG_IS_NEST_CALL(T) dbg_nest_call_check<T>::isNested() template<typename T> class dbg_nest_call_check { public: dbg_nest_call_check(const char* fn, unsigned l) { if (s_cnt_++) ERR_PRINTF(("%s (%d) : ERROR: bad nest call\n", fn, l)); } ~dbg_nest_call_check() { --s_cnt_; } static bool isNested() { return s_cnt_ > 1; } private: static volatile int s_cnt_; }; template<typename T> volatile int dbg_nest_call_check<T>::s_cnt_ = 0; template<typename T, typename S> T* dbg_chk_ptr_cast(S* p) {T* t = dynamic_cast<T*>(p); DBG_ASSERT_PTR(t); return t;} #endif // __cplusplus #endif // --------------------- #endif // DBG_H_INCLUDED