158 lines
3.4 KiB
Perl
Executable File
158 lines
3.4 KiB
Perl
Executable File
#!/usr/bin/perl
|
|
|
|
use warnings;
|
|
use strict;
|
|
use Time::HiRes;
|
|
use Proc::ProcessTable;
|
|
|
|
use Pod::Usage;
|
|
use Getopt::Long qw(:config auto_help);
|
|
use IO::Handle;
|
|
|
|
my %opt = (
|
|
interval => 1,
|
|
num_steps => 3,
|
|
);
|
|
|
|
GetOptions( \%opt, 'process_id|pid|p', 'help|?', 'interval=f', 'num_steps=i' ) or pod2usage(2);
|
|
|
|
pod2usage( -exitval => 0, -verbose => 2 ) if ( $opt{help} );
|
|
pod2usage(2) unless ( @ARGV && @ARGV > 1 );
|
|
|
|
my ( $log_fn, @cmd ) = @ARGV;
|
|
|
|
my $poll_intervall = int($opt{interval} * 1000 * 1000);
|
|
my $num_steps = $opt{num_steps};
|
|
my $script_start_time = [ Time::HiRes::gettimeofday() ];
|
|
|
|
my $pid;
|
|
|
|
if ( $opt{process_id} ) {
|
|
$pid = shift @cmd;
|
|
} else {
|
|
$SIG{CHLD} = 'IGNORE';
|
|
|
|
$pid = fork;
|
|
die "cannot fork" unless defined $pid;
|
|
}
|
|
|
|
if ( $pid == 0 ) {
|
|
#child
|
|
|
|
system(@cmd);
|
|
exit;
|
|
|
|
} else {
|
|
#main
|
|
|
|
my $time_point = 1;
|
|
my @start_time;
|
|
my @cpu_time;
|
|
|
|
my $ppt = Proc::ProcessTable->new;
|
|
|
|
say STDERR "tracking PID $pid";
|
|
my $log_fh;
|
|
if ( $log_fn eq '-' ) {
|
|
$log_fh = \*STDOUT;
|
|
} else {
|
|
open $log_fh, '>', $log_fn or die "Can't open filehandle: $!";
|
|
}
|
|
|
|
print $log_fh join( "\t", qw/tp time pids rss vsz pcpu/ ), "\n";
|
|
|
|
while ( kill( 0, $pid ) ) {
|
|
my $t = Time::HiRes::tv_interval($script_start_time);
|
|
my $pt = parse_ppt( $ppt->table );
|
|
|
|
my @pids;
|
|
my $sum_rss = 0;
|
|
my $sum_vsz = 0;
|
|
my $sum_cpu = 0;
|
|
my $sum_start = 0;
|
|
|
|
my %childs = map { $_ => 1 } subproc_ids( $pid, $pt );
|
|
$childs{$pid}++ if ( $opt{process_id} );
|
|
for my $p (@$pt) {
|
|
#[0] pid
|
|
#[1] ppid
|
|
#[2] rss
|
|
#[3] size
|
|
#[4] time
|
|
#[5] start
|
|
if ( $childs{ $p->[0] } ) {
|
|
$sum_rss += $p->[2];
|
|
$sum_vsz += $p->[3];
|
|
# utime + stime (cutime and cstime not needed, because we iterate through children
|
|
$sum_cpu += $p->[4];
|
|
push @pids, $p->[0];
|
|
}
|
|
}
|
|
|
|
# calc pct cpu per interval:
|
|
# we need seconds since
|
|
#https://stackoverflow.com/questions/16726779/how-do-i-get-the-total-cpu-usage-of-an-application-from-proc-pid-stat
|
|
|
|
#pctcpu = ( 100.0f * sum over all (prs->utime + prs->stime ) * 1/1e6 ) / (time(NULL) - prs->start_time);
|
|
|
|
shift @cpu_time if ( @cpu_time > $num_steps );
|
|
push @cpu_time, $sum_cpu;
|
|
|
|
shift @start_time if ( @start_time > $num_steps );
|
|
push @start_time, $t;
|
|
|
|
my $ratio = 0;
|
|
if ( @start_time >= $num_steps ) {
|
|
my $diff_cpu = ( $cpu_time[-1] - $cpu_time[0] ) / 1e6;
|
|
my $diff_start = ( $start_time[-1] - $start_time[0] );
|
|
$ratio = $diff_cpu / $diff_start if ( $diff_start > 0 );
|
|
}
|
|
print $log_fh join( "\t", $time_point, $t, join( ",", @pids ), $sum_rss, $sum_vsz, $ratio ), "\n";
|
|
$log_fh->flush;
|
|
|
|
Time::HiRes::usleep($poll_intervall);
|
|
$time_point++;
|
|
}
|
|
$log_fh->close;
|
|
}
|
|
|
|
sub parse_ppt {
|
|
my $ppt_table = shift;
|
|
|
|
my @table = map { [ $_->pid, $_->ppid, $_->rss, $_->size, $_->time, $_->start ] } @$ppt_table;
|
|
return \@table;
|
|
}
|
|
|
|
sub subproc_ids {
|
|
my ( $pid, $procs ) = @_;
|
|
#[ pid, parentid ]
|
|
my @childs;
|
|
for my $c ( grep { $_->[1] == $pid } @$procs ) {
|
|
push @childs, $c->[0];
|
|
push @childs, subproc_ids( $c->[0], $procs );
|
|
}
|
|
return @childs;
|
|
}
|
|
|
|
__END__
|
|
|
|
=head1 NAME
|
|
|
|
ppt_profile_cmd.pl - track the cpu and memory usage of a command
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
ppt_profile_cmd.pl [OPTIONS] <log file> <command|process_id> [<arg1> <arg2> ... <argn>]
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
=head1 OPTIONS
|
|
|
|
=head1 SEE ALSO
|
|
|
|
=head1 AUTHOR
|
|
|
|
jw bargsten, C<< <jwb at cpan dot org> >>
|
|
|
|
=cut
|