naru.jpn.com/wordpress

Programming, Computing etc.

lldbでPerl5の実行中の情報を調べる

lldb の練習も兼ねて, 前回ビルドしたデバッグ情報付きの perl5 を使ってブレークポイントを張ったりスタックトレースを見たりしてみます.

プロセスIDから attach してバックトレースを調べる

前回のプログラム

print "start loop program.\n";
while (1) {
  print "loop.\n";
  sleep(1);
}

を実行します.

$ ./output/bin/perl5.31.9 ./sample.pl

ps でプロセスのID(PID)を調べます.

$ ps
  PID TTY           TIME CMD
58175 ttys003    0:00.00 ./output/bin/perl5.31.9 ./sample.pl

lldb を起動して, -p オプションでPIDを指定して attach します.

$ lldb
(lldb) attach -p 58175
Process 58175 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
    frame #0: 0x00007fff65463bba libsystem_kernel.dylib`__semwait_signal + 10
libsystem_kernel.dylib`__semwait_signal:
->  0x7fff65463bba <+10>: jae    0x7fff65463bc4            ; <+20>
    0x7fff65463bbc <+12>: movq   %rax, %rdi
    0x7fff65463bbf <+15>: jmp    0x7fff6546268d            ; cerror
    0x7fff65463bc4 <+20>: retq   
Target 0: (perl5.31.9) stopped.

Executable module set to "/Users/naru/repo/perl/perl-5.31.9/output/bin/perl5.31.9".
Architecture set to: x86_64h-apple-macosx-.

bt でバックトレースを確認.

$ (lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
  * frame #0: 0x00007fff65463bba libsystem_kernel.dylib`__semwait_signal + 10
    frame #1: 0x00007fff653e70fa libsystem_c.dylib`nanosleep + 196
    frame #2: 0x00007fff653e6f62 libsystem_c.dylib`sleep + 41
    frame #3: 0x0000000101f48390 perl5.31.9`Perl_pp_sleep at pp_sys.c:4867:11
    frame #4: 0x0000000101ea44dd perl5.31.9`Perl_runops_standard at run.c:41:26
    frame #5: 0x0000000101d7e584 perl5.31.9`S_run_body(oldscope=1) at perl.c:2764:2
    frame #6: 0x0000000101d7dfd6 perl5.31.9`perl_run(my_perl=0x00007fe56cc01750) at perl.c:2687:2
    frame #7: 0x0000000101d3c54a perl5.31.9`main(argc=2, argv=0x00007ffeedec4ae0, env=0x00007ffeedec4af8) at perlmain.c:127:9
    frame #8: 0x00007fff653207fd libdyld.dylib`start + 1

sleep の真っ最中らしいことが分かります. pp_sys.c にある Perl_pp_sleep が呼ばれているらしい.

関数名からブレークポイントを張る

先ほどの関数 Perl_pp_sleep にブレークポイントを張って処理を再開してみます.

$ (lldb) breakpoint set -n Perl_pp_sleep
Breakpoint 1: where = perl5.31.9`Perl_pp_sleep + 46 at pp_sys.c:4850:5, address = 0x0000000101f481de
$ (lldb) continue
Process 58175 resuming
Process 58175 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000101f481de perl5.31.9`Perl_pp_sleep at pp_sys.c:4850:5
   4847	
   4848	PP(pp_sleep)
   4849	{
-> 4850	    dSP; dTARGET;
   4851	    Time_t lasttime;
   4852	    Time_t when;
   4853	
Target 0: (perl5.31.9) stopped.

これで実行中のコードの箇所を調べることができるようになりました.

ちなみに, PP(pp_sleep) は pp.h(L.11) で定義されています.

#define PP(s) OP * Perl_##s(pTHX)

Perl のソースコードはマクロが多用されています. PP ってなんでしょうね.

参考にさせていただいたサイト

Perlの言語実装の研究
Perlの中をgdbで覗く