gdbで絶対秒を日付で表示する
gdbでデバッグ時に、対象のプログラムが内部で持っている絶対秒(Unix時間)を
日付に変換して表示する方法。
バグ等でcoreファイルから原因を調査する際に欲しくなったので作ってみた。
具体的には~/.gdbinitにユーザ定義のコマンドを作って実現する。
#普通にgdbでプログラムを動かしている場合は、glibcの関数等を呼び出して変換できるはず。
coreファイル出力用のソースコード
解析用のソースコードとcoreファイルが必要なので、
グローバル変数に時刻と項番を書き込んだらabort()で自爆する単純なプログラムを作る。
printime.c
#include <stdio.h> #include <stdlib.h> #include <sys/time.h> #define ENTRY_MAX 10 typedef struct { struct timeval tv; int entno; } entry_t; static entry_t ent[ENTRY_MAX]; int main(int argc, char* argv[]) { int i = 0; for(i = 0; i < ENTRY_MAX; i++) { gettimeofday(&ent[i].tv, NULL); ent[i].entno = i; } abort(); return 0; }
~/.gdbinitの作成
gdbinitにユーザコマンドを定義する際のフォーマットは次の通り。
define <コマンド名> #実行したい処理 <処理1> <処理2> ... end
↓みたいな秒とマイクロ秒の引数を渡すと日付で表示してくれるコマンドを作る。
(gdb) ShowClock <sec> <usec> 2017/04/14 00:45:12.246025(JST)
実際に作成したgdbinitは下記。
Unix時間から表示する各要素を求めるのがとっても面倒だった。
~/.gdbinit
# # Convert Unix-Time (tv_sec, tv_usec) to date(yy/mm/dd hh:mm:ss) # define ConvertUnixTime2Date set $__acdays1 = 31 set $__acdays2 = 59 set $__acdays3 = 90 set $__acdays4 = 120 set $__acdays5 = 151 set $__acdays6 = 181 set $__acdays7 = 212 set $__acdays8 = 243 set $__acdays9 = 273 set $__acdays10 = 304 set $__acdays11 = 334 set $__acdays12 = 365 set $__leapdays = ((($__year-1)/4) - (($__year-1)/100) + (($__year-1)/400)) - 477 set $__year = 1970 + (($__daycount - $__leapdays)/365) set $__restdays = $__daycount - ((($__year - 1970) * 365) + $__leapdays) if (($__year % 400 == 0) || (($__year % 4 == 0) && ($__year % 100 !=0 ))) set $__acdays2++ set $__acdays3++ set $__acdays4++ set $__acdays5++ set $__acdays6++ set $__acdays7++ set $__acdays8++ set $__acdays9++ set $__acdays10++ set $__acdays11++ set $__acdays12++ end if ($__restdays <= $__acdays1) set $__mon = 1 set $__day = $__restdays end if ($__acdays1 < $__restdays && $__restdays <= $__acdays2) set $__mon = 2 set $__day = $__restdays - $__acdays1 end if ($__acdays2 < $__restdays && $__restdays <= $__acdays3) set $__mon = 3 set $__day = $__restdays - $__acdays2 end if ($__acdays3 < $__restdays && $__restdays <= $__acdays4) set $__mon = 4 set $__day = $__restdays - $__acdays3 end if ($__acdays4 < $__restdays && $__restdays <= $__acdays5) set $__mon = 5 set $__day = $__restdays - $__acdays4 end if ($__acdays5 < $__restdays && $__restdays <= $__acdays6) set $__mon = 6 set $__day = $__restdays - $__acdays5 end if ($__acdays6 < $__restdays && $__restdays <= $__acdays7) set $__mon = 7 set $__day = $__restdays - $__acdays6 end if ($__acdays7 < $__restdays && $__restdays <= $__acdays8) set $__mon = 8 set $__day = $__restdays - $__acdays7 end if ($__acdays8 < $__restdays && $__restdays <= $__acdays9) set $__mon = 9 set $__day = $__restdays - $__acdays8 end if ($__acdays9 < $__restdays && $__restdays <= $__acdays10) set $__mon = 10 set $__day = $__restdays - $__acdays9 end if ($__acdays10 < $__restdays && $__restdays <= $__acdays11) set $__mon = 11 set $__day = $__restdays - $__acdays10 end if ($__acdays11 < $__restdays && $__restdays <= $__acdays12) set $__mon = 12 set $__day = $__restdays - $__acdays11 end end # # Show clock form Unix-Time # This command requires 2 arguments (tv_sec, tv_usec) # define ShowClock set $__tv_sec = $arg0 set $__tv_usec = $arg1 # Set Time-Zone(JST) set $__timezone = 9 set $__tv_sec = $__tv_sec + ($__timezone * 60 * 60) set $__mon = 0 set $__day = 0 set $__daycount = ($__tv_sec / 86400) + 1 set $__year = (1970 + ($__daycount / 365)) set $__hour = (($__tv_sec % 86400) / 3600) set $__min = ((($__tv_sec % 86400) / 60) % 60) set $__sec = (($__tv_sec % 86400) % 60) ConvertUnixTime2Date if ($__timezone == 0) printf "%04d/%02d/%02d %02d:%02d:%02d.%06d(UTC) ", \ $__year, $__mon, $__day, $__hour, $__min, $__sec, $__tv_usec end if ($__timezone == 9) printf "%04d/%02d/%02d %02d:%02d:%02d.%06d(JST) ", \ $__year, $__mon, $__day, $__hour, $__min, $__sec, $__tv_usec end end define ShowEntryDump set $__index = 0 while ($__index < 10) set $__ent = &ent[$__index] ShowClock $__ent->tv.tv_sec $__ent->tv.tv_usec printf "entno=%d\n", $__ent->entno set $__index++ end end
実行結果
$ ulimit -c unlimited $ ./printime $ gdb printime corefile $ (gdb) showentrydump
すごく処理が重いけどいい感じ。