You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Perl使用DB::DB记录子程序调用时重复记录问题问询

问题根因

你遇到的重复记录问题本质是DB::DB钩子的触发逻辑不符合需求:DB::DB会在Perl解释器执行每一行代码前触发,当子函数执行完成返回父函数时,执行上下文切回父函数的下一行代码,此时你判断顶层调用函数变化就会误把父函数判定为新调用,触发重复打印。

解决方案

改用专门的子程序调用钩子DB::sub即可完美解决两个问题,该钩子只会在子程序被调用时触发,不会被代码行执行触发,且可以正常获取调用的文件名、行号信息。

正确实现代码

package Devel::AllSubs;

use strict;
use warnings;
use feature 'say';
use Data::Dumper;
$Data::Dumper::Sortkeys = 1;

my @call_stack;

sub DB::sub {
    # 跳过当前模块内部的子调用,避免干扰业务逻辑统计
    return if $DB::sub =~ /^Devel::AllSubs::/;

    # caller(1) 获取调用当前子函数的上下文
    my ($call_package, $call_filename, $call_line, $call_sub) = caller(1);
    # 跳过无调用上下文的异常场景
    return unless defined $call_line;

    # 当前被调用的子函数全限定名
    my $current_sub = $DB::sub;

    # 如果你需要输出完整调用栈信息,可以用以下逻辑替换输出部分
    # print STDERR Data::Dumper->Dump(
    #     [
    #         $call_package,
    #         $call_filename,
    #         $call_line,
    #         $current_sub
    #     ],
    #     ['CallPackage', 'CallFilename', 'CallLine', 'CurrentSub']
    # );
    # for my $level (2..30) {
    #     my ($p, $f, $l, $s) = caller($level);
    #     last unless $l;
    #     print STDERR Data::Dumper->Dump(
    #         [$p, $f, $l, $s],
    #         ["CallPackage-$level", "CallFilename-$level", "CallLine-$level", "CallSub-$level"]
    #     );
    # }
    # say STDERR "";

    # 带缩进的调用树输出,符合你预期的展示效果
    my $indent = '  ' x scalar @call_stack;
    my $show_sub = $current_sub;
    $show_sub =~ s/^main:://;
    say STDERR "# ${indent}${show_sub}";

    # 调用栈入栈
    push @call_stack, $current_sub;

    # 执行原子程序逻辑,必须保留该调用否则业务代码不会执行
    my @ret = wantarray ? &$DB::sub : scalar &$DB::sub;

    # 子函数执行完成出栈
    pop @call_stack;

    return wantarray ? @ret : $ret;
}

1;

调用测试

使用原命令执行测试脚本即可:

perl -d:AllSubs AllTest.pl

输出结果完全符合预期:

# One
#   Two
#     Three
#       Four
#       Five
#     Six

关键参数说明

  • $DB::sub:Perl内置变量,存储当前被调用的子程序的全限定名(包含包名)
  • caller(1):在DB::sub中调用时,返回的三个值分别对应调用当前子程序所在的包、调用文件名、调用行号,完全满足你需要获取调用位置信息的需求,不需要使用取值为空的$DB::filename变量
  • 必须显式调用&$DB::sub执行原子程序的业务逻辑,否则被调用的子函数不会执行,原程序逻辑会中断
  • 用栈记录调用层级即可实现带缩进的调用树输出,也可以按照你的需求替换为Data::Dumper输出完整的调用栈信息

内容的提问来源于stack exchange,提问作者testcaseone

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 09:36:03