如何将调试器附加到正在运行的Perl进程?

35

我有一个正在运行的Perl进程卡住了,我想用调试器查看问题所在。我不能重新启动该进程。我能否将调试器附加到正在运行的进程上?我知道可以使用gdb -p,但是gdb对我没有帮助。我尝试过Enbugger,但失败了:

$ perl -e 'while (1) {}'&
[1] 86836
$ gdb -p 86836
…
Attaching to process 86836.
Reading symbols for shared libraries . done
Reading symbols for shared libraries ............................. done
Reading symbols for shared libraries + done
0x000000010c1694c6 in Perl_pp_stub ()
(gdb) call (void*)Perl_eval_pv("require Enbugger;Enbugger->stop;",0)
perl(86836) malloc: *** error for object 0x3: pointer being realloc'd was not allocated
*** set a breakpoint in malloc_error_break to debug

Program received signal SIGABRT, Aborted.
0x00007fff8269d82a in __kill ()
The program being debugged was signaled while in a function called from GDB.
GDB remains in the frame where the signal was received.
To change this behavior use "set unwindonsignal on"
Evaluation of the expression containing the function (Perl_eval_pv) will be abandoned.
(gdb) 

我做错了吗?还有其他选项吗?


另外,如果您认为连接到正在运行的进程的调试器会对您有所帮助,您可以插入一个由SIGUSR1触发的调试器后门:

use Enbugger::OnError 'USR1';

然后您只需要执行kill -USR1 pid,您的进程就会跳入调试器。

4个回答

12

首先,如果您想使用gdb进行检查,请使用调试版perl。

请定义“stuck”是指什么。繁忙或非繁忙等待(高或低CPU),是否占用内存?使用while 1会导致繁忙等待。我通常在Perl_hfree_next_entry()中遇到HV损坏时出现繁忙等待(无限循环),自从5.15以来如此。非繁忙等待通常是在阻塞IO读取时进行等待。

我得到了正确的结果:

`0x00007fba15ab35c1 in Perl_runops_debug () at dump.c:2266`
`2266       } while ((PL_op = PL_op->op_ppaddr(aTHX)));`

使用Perl调试器,您可以检查所有内容,比简单的Perl调试器更全面。对于非线程化的Perl,您需要键入较少的内容。

`(gdb) p Perl_op_dump(PL_op)`

等等其他内容。

如果你需要处理Perl:在pp_stub函数内部进入Enbugger运行循环不是一个好主意,你应该在dump.c中设置一个断点以进入主运行循环。"0x3对象的错误"在eval时听起来像上下文中的内部损坏,所以你应该查看cx和stack指针。可能是因为你在错误的上下文中启动了它。


7

我从未使用过gdb,但也许你可以从strace中获取一些有用的信息?

strace -f -s512 -p <PID>

6

1

设置

  1. 从 CPAN 安装 Enbugger:
    cpan install Enbugger
    
  2. 将以下内容添加到你的 .gdbinit 文件中:
    define perl_eval
        call (void*)Perl_eval_pv((void*)Perl_get_context(), $arg0, 0)
    end
    
    define perl_stop
        perl_eval "Enbugger->stop"
        continue
    end
    
    define perl_init
        python import os
        python gdb.execute("set $tty=\"" + os.ttyname(0) + "\"")
        call open($tty, 0)
        set $tty_in=$
        call open($tty, 1)
        set $tty_out=$
        call (int) 'dup@plt'(0)
        set $old_stdin=$
        call (int) 'dup@plt'(1)
        set $old_stdout=$
        call (int) 'dup@plt'(2)
        set $old_stderr=$
        call (int) 'dup2@plt'($tty_in, 0)
        call (int) 'dup2@plt'($tty_out, 1)
        call (int) 'dup2@plt'($tty_out, 2)
        eval "perl_eval \"$ENV{PERLDB_OPTS}='TTY=%s'\"", $tty
        perl_eval "require Enbugger"
    end
    
    define attach_perl
        attach $arg0
        perl_init
        perl_stop
    end
    
    基本上,perl_init 为 Perl 调试器设置了终端。如果标准输入/标准输出/标准错误流被调试进程重定向,你将无法在没有上述设置的情况下与 Perl 调试器进行交互。如果需要,你可以从备份描述符 $old_stdin/$old_stdout/$old_stderr 恢复旧的重定向。

连接到 Perl 进程

  • 通过GDB根据PID附加到您的perl程序:

    (gdb) attach_perl 640368
    [Thread debugging using libthread_db enabled]
    Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
    0x00007fd0b1313984 in __GI___select (nfds=0, readfds=0x0, writefds=0x0, exceptfds=0x0, timeout=0x7fff19c97240) at ../sysdeps/unix/sysv/linux/select.c:69
    69      ../sysdeps/unix/sysv/linux/select.c: No such file or directory.
    $1 = 7
    $2 = 8
    $3 = 9
    $4 = 10
    $5 = 11
    $6 = 0
    $7 = 1
    $8 = 2
    $9 = (void *) 0x562cae6a7810
    $10 = (void *) 0x562cad300ee0
    
    从perl5db.pl版本1.60加载DB例程
    支持编辑器。
    
    输入h或'h h'获取帮助,或键入'man perldebug'获取更多帮助。
    
    $11 = (void *) 0x562cad6322d0
    My::SafeProcess::(lib/My/SafeProcess.pm:543):
    543:        $millis++ if $millis < 1000;
      DB<1>
  • 有时您在第3步中可能无法获得提示符,因此可以按Ctrl+C并运行perl_stop

    输入h或'h h'获取帮助,或键入'man perldebug'获取更多帮助。
    
    $11 = (void *) 0x560577ff45a0
    ^C
    Program received signal SIGINT, Interrupt.
    0x00007fd8f9ee25a7 in __GI___wait4 (pid=626998, stat_loc=0x7fffbbeedf1c, options=0, usage=0x0) at ../sysdeps/unix/sysv/linux/wait4.c:30
    30      in ../sysdeps/unix/sysv/linux/wait4.c
    (gdb) perl_stop
    main::((eval 20)[/home/midenok/src/mariadb/10.4/build/opt/mysql-test/mysql-test-run:5]:1):
    1:      Enbugger->stop
      DB<1>

    只要加载了Enbugger,您随时可以通过perl_stop从GDB进入perl调试器。

  • 您可能只需要知道堆栈跟踪:

      DB<1> T
     @ = DB::DB called from file '(eval 39)[/usr/lib/x86_64-linux-gnu/perl-base/IO/Select.pm:130]' line 1
    $ = eval 'Enbugger->stop' called from file '/usr/lib/x86_64-linux-gnu/perl-base/IO/Select.pm' line 130
    @ = IO::Select::can_read(ref(IO::Select), 1) called from file '/home/midenok/src/mariadb/10.4/src/mysql-test/mysql-test-run.pl' line 594
    @ = main::run_test_server(ref(IO::Socket::INET), ref(ARRAY), ref(HASH)) called from file '/home/midenok/src/mariadb/10.4/src/mysql-test/mysql-test-run.pl' line 485
    . = main::main() called from file '/home/midenok/src/mariadb/10.4/src/mysql-test/mysql-test-run.pl' line 343
    

    这里最新的执行点是Select.pm的第130行,请不要对注入的eval 'Enbugger->stop'感到惊讶,但有时您会看到普通的堆栈跟踪。

  • 从 Perl 进程分离

    在perl调试器中运行"continue":
      DB<1> c
    [从子进程641324分离后]
    [从子进程641326分离后]
    [从子进程641328分离后]
    [从子进程641329分离后]
    

    按下Ctrl+C然后Ctrl+D:
    ^C
    程序收到信号SIGINT,中断。
    0x00007fd0b1313984 in __GI___select (nfds=0, readfds=0x0, writefds=0x0, exceptfds=0x0, timeout=0x7fff19c97240) at ../sysdeps/unix/sysv/linux/select.c:69
    69      in ../sysdeps/unix/sysv/linux/select.c
    (gdb) quit
    从程序中分离:/usr/bin/perl,进程640368
    [Inferior 1 (process 640368) detached]
    

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接