9

How can I find the source location of a print statement in Perl?

#!/usr/bin/perl

foo();
bar();

sub foo {
  print "foo\n";
}

sub bar {
  print "bar\n";
}

The output being:

>perl test.pl 
foo
bar

I'd like to somehow find be able to see (or something like)

>perl test.pl 
main::foo> foo
main::bar> bar

The reason for this is I'm trying to track down some rouge output, and cannot find its location in a large code base.

brian d foy
  • 129,424
  • 31
  • 207
  • 592
Matthew Watson
  • 14,083
  • 9
  • 62
  • 82

8 Answers8

15

Try this:

#!/usr/bin/env perl

use strict;
use warnings;
use Tie::STDOUT print => sub {
  my ( $pkg, $file, $line ) = caller(2);
  print "$pkg, $file, $line :: ", @_;
};

print "Hello, World\n";

Which gives:

$ perl tp.pl
main, tp.pl, 10 :: Hello, World

Update: I've just released Devel::Ditto:

$ perl -MDevel::Ditto myprog.pl
[main, t/myprog.pl, 9] This is regular text
[main, t/myprog.pl, 10] This is a warning
[MyPrinter, t/lib/MyPrinter.pm, 7] Hello, World
[MyPrinter, t/lib/MyPrinter.pm, 8] Whappen?
hexten
  • 1,169
  • 7
  • 10
  • Excellent. Thanks for introducing this module. – Sinan Ünür May 25 '09 at 14:41
  • I've just released Devel::Ditto which, I hope does what you want:
    $ perl -MDevel::Ditto myprog.pl
    [main, t/myprog.pl, 9] This is regular text
    [main, t/myprog.pl, 10] This is a warning
    [MyPrinter, t/lib/MyPrinter.pm, 7] Hello, World
    [MyPrinter, t/lib/MyPrinter.pm, 8] Whappen?
    – hexten May 25 '09 at 16:26
8

Use Debug::Trace ( https://metacpan.org/pod/Debug::Trace )

#!/usr/bin/perl

foo();
bar();

sub foo {
  print "foo\n";
}

sub bar {
  print "bar\n";
}

This program, saved as test.pl and called as:

perl -MDebug::Trace=foo,bar test.pl

Prints out:

TRACE:  main::foo() called at test.pl line 3 package main
foo
TRACE:  main::foo() returned
TRACE:  main::bar() called at test.pl line 4 package main
bar
TRACE:  main::bar() returned
Suic
  • 2,441
  • 1
  • 17
  • 30
rpkelly
  • 2,076
  • 1
  • 20
  • 19
5

I knew source filters could be useful for something:

C:\Temp> cat DebugFilter.pm
package DebugFilter;

use strict;
use warnings;

use Filter::Simple;

FILTER_ONLY
    code_no_comments => sub {
        s/print/main::mydebugfn();print/g
    };

1;
__END__

C:\Temp> cat DebugPrint.pm
package DebugPrint;

use strict;
use warnings;

use base qw( Exporter );

our @EXPORT = qw( mydebugfn );

sub mydebugfn {
    my ( $pkg, $fn, $line, $sub ) = caller(2);
    warn "print called from: ${sub}(${fn}:${line})\n";
}

1;
__END__

C:\Temp> cat t.pl
#!/usr/bin/perl

use strict;
use warnings;

use DebugFilter;

sub test {
    print STDOUT "print in test\n";
    return;
}

test();

Here is the output:

C:\Temp> perl -MDebugPrint t.pl
print called from: main::test(t.pl:13)
print in test
Sinan Ünür
  • 116,958
  • 15
  • 196
  • 339
  • That's a solution I wouldn't have thought of :) However, does it handle prints in libraries? Does it know to filter only code, and not data (such as strings that contain the word 'print'? – Nathan Fellman May 25 '09 at 06:56
  • I only tried simple tests. At a minimum, you will need to use DebugFilter in any source file where you want to trace but that is the only modification that needs to be made to source files (if everything works as it should). The FILTER_ONLY code_no_comments ensures that the only substitutions are made in code: http://search.cpan.org/~smueller/Filter-Simple-0.84/lib/Filter/Simple.pm#Filtering_only_the_code_parts_of_source_code – Sinan Ünür May 25 '09 at 07:04
4

Copas: I can't reply to your comment directly (not yet "cool" enough) but the basic problem is that any decent sized project is not one .pl file. It's a bunch of modules all being pulled in together.

Your code:

a) doesn't address anything outside the one .pl file b) doesn't handle cases where print(...) has brackets or different spacing. c) doesn't cope with cases where the first argument to print is a function call or variable rather than a double-quoted string. d) consider: $object->log("Am about to print " . foo($var) . " to the console"); - your regex would cause a syntax error, making this: $object->log("Am about to print "Line 1084: . $foo($var) . " to the console");

That said, it's viable for simple scripts. It's not a BAD idea (worst case, your newsource.pl doesn't compile) - but it's not going to fix this particular problem unless it's all in one file.

  • The majority of perl scripts I encounter are short anything over a few hundred lines is usually done with a compiled language in my corner of corporate America. The question didn't specify that this was a large project so I envisioned the types of things I see at work daily. Still, post redacted thanks for your input. – Copas May 26 '09 at 04:52
  • $ find . -name '*.pm' | xargs cat | wc -l 187903 That's my work repository. 188k lines. From the question I figured it wasn't going to be trivial or it would be quicker just to check for them all by hand rather than typing out the question on SO! – Bron Gondwana May 31 '09 at 00:50
4

This article explains how to hook into perl print function: How can I hook into Perl's print?

You may need it, if you don't want to replace all the print statements in your source code with something else.

Community
  • 1
  • 1
Igor Krivokon
  • 10,145
  • 1
  • 37
  • 41
4

I am going to leave the source filter alternative up for reference because it is geared towards tracing the invocation of a single function.

However, the solution seems to be to use Devel::Trace. This will presumably generate a lot of output which you can redirect to a file and then grep for the offending output.

C:\Temp> perl -d:Trace t.pl
>> t.pl:10: T::test();
>> T.pm:5:     print "in T::test\n";
in T::test
>> c:/opt/perl/lib/Config.pm:63: sub DESTROY { }
Sinan Ünür
  • 116,958
  • 15
  • 196
  • 339
3

You could try stepping through your code with the debugger (perl -d).

I was going to suggest overriding print and using:

($package, $filename, $line) = caller;

...to print out the extra info, but it turns out print is one of the builtins that can't be overridden.

Mike Tunnicliffe
  • 10,674
  • 3
  • 31
  • 46
1

You could try using Hook::LexWrap to dig down into what's calling what in your codebase. It does some pretty evil things internally, so won't work for all codebases.

singingfish
  • 3,136
  • 22
  • 25