tags:

views:

252

answers:

8

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.

+3  A: 

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.

fd
Article on debugging perl: http://sunsite.ualberta.ca/Documentation/Misc/perl-5.6.1/pod/perldebtut.html
fd
+4  A: 

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.

Bron Gondwana
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
$ find . -name '*.pm' | xargs cat | wc -l187903That'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
+1  A: 

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
+4  A: 

This article explains how to hook into perl print function: http://stackoverflow.com/questions/387702/how-can-i-hook-into-perls-print

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

Igor Krivokon
+4  A: 

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
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
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
+6  A: 

Use Debug::Trace ( http://search.cpan.org/~jv/Debug-Trace-0.04/Trace.pm )

#!/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
rpkelly
+3  A: 

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
+12  A: 

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
Excellent. Thanks for introducing this module.
Sinan Ünür
I've just released <a href="http://search.cpan.org/dist/Devel-Ditto/">Devel::Ditto</a> which, I hope does what you want:<code><pre>$ 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?</pre></code>
hexten