代码之家  ›  专栏  ›  技术社区  ›  frans

如何从外部检查正在运行的bash脚本(即不在专用shell中运行它)

  •  0
  • frans  · 技术社区  · 4 年前

    想象一下任意 已在运行 shell脚本( bash , sh ,..)它被困在一个无休止的循环中,你想“深入研究”(即获取堆栈跟踪或脚本中的当前位置)来了解发生了什么。

    在里面 蟒蛇 当我遇到奇怪的行为(在CPU使用、无休止的循环等方面)时,我的典型方法是让脚本允许我在运行时“查看”它。例如,我会扩展给定的脚本,比如

    def fib(n):
        return 0 if n == 0 else 1 if n < 3 else fib(n-1) + fib(n-2)
    
    print(fib(38))  # might run long
    

    处理一些信号,例如。 SIGPOLL 像这样:

    import signal, traceback, os
    
    def fib(n):
        return 0 if n == 0 else 1 if n < 3 else fib(n-1) + fib(n-2)
    
    signal.signal(signal.SIGPOLL, lambda sig, frame: print(
        "\n".join(traceback.format_stack(frame))))
    print(f"This might take long - run `kill -{signal.SIGPOLL} {os.getpid()}` to look into stack")
    
    print(fib(38))  # might run long
    

    现在我可以跑步了 kill -SIGPOLL <PID> 每当我发现这个脚本运行的时间长得惊人时,我就会从另一个终端。我仍然需要先在专用终端中运行它才能看到输出,但我可以很容易地修改代码以写入文件或类似文件。

    这可能吗 猛击 / sh 也也许是内置的?

    最好的情况是,我一开始甚至不必修改脚本,但如果除了在一个额外的shell中执行它之外,还有其他方法 strace 或 -x 如果设置好了,我会接受:)

    以下是我有时使用的内容-它在某些情况下对我有帮助,但不会给我有关正在执行的代码的详细信息,如行号、调用堆栈等,并且脚本必须在额外的终端中运行才能看到输出:

    function toggle_tracing {
        if [ -z "$TRACE_ENABLED" ]; then
            TRACE_ENABLED=1
            set -x
        else
            unset TRACE_ENABLED
            set +x
        fi
    }
    
    trap toggle_tracing USR1
    echo "run 'kill -SIGUSR1 $BASHPID' to activate tracing"
    
    # do something really time consuming
    while true; do
        find ~ > /dev/null
    done
    

    为了使这种方法发挥作用,我必须提前做好准备(脚本需要修改并在专用终端或tmux会话中运行),这对于调查意外和不经常发生的事件来说是不好的。

    你也可以跑步 strace -p <PID> ,它的优点是不局限于(shell-)脚本,但由于您只获得系统IO,因此您必须是幸运的 和 非常了解可执行文件以猜测其内部状态。

    0 回复  |  直到 4 年前
        1
  •  1
  •   frans    4 年前

    一种方法 以某种方式 符合我的要求是组合设置 -x 随需应变 和 strace ,因为跟踪将被写入 STDERR 我可以从外面追踪到。

    准备:修改脚本 可以 在将来遇到问题时,请在启动时运行以下代码:

    function toggle_tracing {
        if [ -z "$TRACE_ENABLED" ]; then
            TRACE_ENABLED=1
            set -x
        else
            unset TRACE_ENABLED
            set +x
        fi
    }
    
    trap toggle_tracing USR1
    echo "run 'kill -SIGUSR1 $BASHPID' to activate tracing"
    

    当您现在发现进程占用CPU数小时时,您可以通过发送信号将其设置为跟踪模式,并开始使用strace监控它

    # sudo might not be necessary
    sudo kill -SIGUSR1 <PID>
    sudo strace -p <PID> 2>&1 | grep "write(2, \"" 
    

    这将导致命令打印到您运行的终端 strace 在里面

    write(2, "+ true\n", 7)                 = 7
    write(2, "+ find /home/itsme\n", 20)   = 20
    write(2, "+ true\n", 7)                 = 7
    write(2, "+ find /home/itsme\n", 20)   = 20
    write(2, "+ true\n", 7)                 = 7
    write(2, "+ find /home/itsme\n", 20)   = 20
    write(2, "+ true\n", 7)                 = 7
    write(2, "+ find /home/itsme\n", 20)   = 20
    write(2, "+ true\n", 7)                 = 7
    write(2, "+ find /home/itsme\n", 20)   = 20
    ...
    

    (不过仍然没有行号)

    顺便说一句,积分归我的一个朋友所有,他懒得建立自己的账户。