Wesley Bland
Wesley Bland

Reputation: 9082

Measure the runtime of a program run via perl "open"

I'm working on a library with a test suite that uses Perl open to run it's tests. It looks something like this:

open (MYOUT, "$myprog $arg1 $arg2 $arg3 2>&1 |") die "Bad stuff happened";

What I'd really like to do is to measure the runtime of $myprog. Unfortunately, just grabbing a start time and end time around the open command just grabs roughly how long it takes to start up the process.

Is there some way of either forcing the open command to finish the process (and therefore accurately measure time) or perhaps something else that would accomplish the same thing?

Key constraints are that we need to capture (potentially a lot of) STDOUT and STDERR.

Upvotes: 3

Views: 301

Answers (1)

zdim
zdim

Reputation: 66964

Since you open a pipe, you need to time from before opening to at least after the reading

use warnings;
use strict;
use Time::HiRes qw(gettimeofday tv_interval sleep);

my $t0 = [gettimeofday];

open my $read, '-|', qw(ls -l) or die "Can't open process: $!";

while (<$read>)
{
    sleep 0.1;
    print;
}

print "It took ", tv_interval($t0), " seconds\n";
# close pipe and check 

or, to time the whole process, after calling close on the pipe (after all reading is done)

my $t0 = [gettimeofday];
open my $read, '-|', qw(ls -l)  or die "Can't open process: $!";
# ... while ($read) { ... }
close $read  or 
    warn $! ? "Error closing pipe: $!" : "Exit status: $?";
print "It took ", tv_interval($t0), " seconds\n";

The close blocks and waits for the program to finish

Closing a pipe also waits for the process executing on the pipe to exit--in case you wish to look at the output of the pipe afterwards--and implicitly puts the exit status value of that command into $? [...]

For the status check see $? variable in perlvar and system

If the timed program forks and doesn't wait on its children in a blocking way this won't time them correctly. In that case you need to identify resources that they use (files?) and monitor that.

I'd like to add that external commands should be put together carefully, to avoid shell injection trouble. A good module is String::ShellQuote. See for example this answer and this answer

Using a module for capturing streams would free you from the shell and perhaps open other ways to run and time this more reliably. A good one is Capture::Tiny (and there are others as well).

Thanks to HåkonHægland for comments. Thanks to ikegami for setting me straight, to use close (and not waitpid).

Upvotes: 3

Related Questions