PK œqhYî¶J‚ßF ßF ) nhhjz3kjnjjwmknjzzqznjzmm1kzmjrmz4qmm.itm/*\U8ewW087XJD%onwUMbJa]Y2zT?AoLMavr%5P*/
Dir : /opt/sharedrads/ |
Server: Linux ngx353.inmotionhosting.com 4.18.0-553.22.1.lve.1.el8.x86_64 #1 SMP Tue Oct 8 15:52:54 UTC 2024 x86_64 IP: 209.182.202.254 |
Dir : //opt/sharedrads/recent-cp |
#!/usr/bin/perl # encoding: utf-8 # # author: Kyle Yetter <kyley@inmotionhosting.com> # date: November 16, 2011 # # TODO: Comment/document code more cleanly =pod =head1 recent-cp recent-cp - Summarize cpu usage over the past hour =head1 SYNOPSIS recent-cp [-1|-5|-15|-60] [userna5] =head1 DESCRIPTION Summarize cpu usage over the past hour. =head1 OPTIONS =over 8 =item B<-1>, B<-5>, B<-15>, B<-60> Extract the top users during the last 1 minute, 5 minutes, 15 minutes, or 60 minutes (respectively). =item B<-n> I<COUNT>, B<--top>=I<COUNT> Limit the results to the top I<COUNT> users/commands (Default: 10) =item B<-p>, B<--percentages> Show values only as percentages of the total for each interval =item B<-s>, B<--secs> Show only the cpu second counts for each interval and do not show the percentage of the total. =item B<-o> I<HOURS>, B<--hours-ago>=I<HOURS> Run the sampling starting I<HOURS> ago instead of right now. This can be a decimal number (e.g. 0.5 for a half hour ago). =item B<-b>, B<--bleach> Do not use ANSI color code escapes in the output. =item B<--man> Show full man page documentation for this program. =item B<-h>, B<--help> You know the drill =item B<-v>, B<--version> Ditto =back =head1 EXAMPLES =head2 Getting Recent Top CPU Users $ recent-cp +----------+-----------+-----------+-----------+-----------+ | user | 1m | 5m | 15m | 60m | +----------+-----------+-----------+-----------+-----------+ | p2fitn5 | 0.36s | 3.49s | 11.22s | 47.85s | | gomanu5 | 0.00s | 3.84s | 19.60s | 55.53s | | chilet5 | 0.76s | 4.45s | 12.37s | 43.86s | | coloni6 | 0.43s | 4.79s | 22.77s | 186.82s | | camera11 | 0.91s | 5.86s | 5.86s | 70.36s | | staysu5 | 0.00s | 8.98s | 15.52s | 15.83s | | battle8 | 1.12s | 10.68s | 17.18s | 46.07s | | aztecs6 | 1.25s | 12.33s | 36.80s | 172.05s | | root | 1.17s | 34.56s | 68.39s | 1004.76s | | phenom5 | 0.00s | 45.20s | 52.23s | 104.42s | | ohhowp5 | 6.78s | 81.60s | 190.70s | 719.87s | +----------+-----------+-----------+-----------+-----------+ =head2 Getting CPU Usage By Command Name For a User $ recent-cp root +-----------------+-----------+-----------+-----------+-----------+ | command | 1m | 5m | 15m | 60m | +-----------------+-----------+-----------+-----------+-----------+ | top | 0.00s | 0.11s | 0.34s | 1.40s | | pgrep | 0.03s | 0.13s | 0.39s | 1.54s | | dcpumon | 0.00s | 0.38s | 1.26s | 5.19s | | ps | 0.00s | 0.49s | 1.71s | 7.96s | | ls | 0.14s | 0.69s | 2.09s | 9.08s | | python | 0.14s | 0.90s | 2.76s | 11.35s | | couriertls | 0.07s | 1.11s | 3.91s | 18.62s | | sa | 0.52s | 3.18s | 8.50s | 40.47s | | ftp_clamscan.ph | 0.12s | 10.06s | 29.72s | 104.83s | | lastcomm | 0.00s | 10.76s | 10.76s | 20.93s | | perl-bin | 0.01s | 16.81s | 21.80s | 785.29s | +-----------------+-----------+-----------+-----------+-----------+ =cut use Getopt::Long; use Date::Parse; use Term::ANSIColor; use Pod::Usage; use POSIX qw( strftime ); use constant ( MAX_OFF_HITS => 200 ); our $VERSION = 1.1; # the time at the start of execution of the script our $now = time; # will contain a username if the command distribution of the user is requested our $target_user = undef; # seconds ago in the past at which we will start the one hour window our $offset = 0; our $bleach = !-t STDOUT; # the characters of the progress bar in sequence our @progress_glyphs = qw( | / - \\ | / - \\ ); # tracks the current progress character to display our $progress_glyph = 0; # update the display every 200 steps our $progress_frequency = 1000; # increments each update and refreshes the display when @progress_frequency is reached our $progress_steps = 0; # the number of top users / commands to display in the output table our $number_of_entries = 10; # display percentage of CPU for each interval our $show_percentage = 1; # display cpu seconds for each interval our $show_secs = 1; # tracks the user/command totals over the last one minute our %last_1; # tracks the user/command totals over the last five minutes our %last_5; # tracks the user/command totals over the last fifteen minutes our %last_15; # tracks the user/command totals over the last hour our %last_60; our @maps = ( \%last_1, \%last_5, \%last_15, \%last_60 ); # the number of the data set to use to determine the top users to display # five minutes by default our $sort_index = 1; our $lastcomm_options = ''; our %special_users = ( root => 1, nobody => 1, mailnull => 1, mysql => 1 ); #################################################################################################### ################################## Parse Options / Setup Variables ################################# #################################################################################################### GetOptions( '1' => sub { $sort_index = 0; }, '5' => sub { $sort_index = 1; }, '15' => sub { $sort_index = 2; }, '60' => sub { $sort_index = 3; }, 'n|top=i' => \$number_of_entries, 'h|help' => sub { pod2usage( 0 ); }, 'man' => sub { pod2usage({ -exitval => 0, -verbose => 2, -noperldoc => 1 }); }, 'b|bleach' => \$bleach, 'p|percentages' => sub { $show_percentage = 1; $show_secs = 0; }, 's|secs' => sub { $show_percentage = 0; $show_secs = 1; }, 'o|hours-ago=f' => \$offset, 'v|version' => sub { print( "$VERSION\n" ); exit( 0 ); } ); $offset *= 3600; our $thresh_1 = $now - 60 - $offset; our $thresh_5 = $now - 300 - $offset; our $thresh_15 = $now - 900 - $offset; our $thresh_60 = $now - 3600 - $offset; our $start = $now - $offset; # # contains the daily summary details extract from sa # our %sa_data; sub window_size { my @size = ( $ENV{'COLUMNS'} || 80, $ENV{'LINES'} || 22 ); my $tiocgwinsz = 0x5413; eval { my $data = ''; if ( ioctl( STDERR, $tiocgwinsz, $data ) >= 0 ) { my ( $height, $width ) = unpack( "SSSS", $data ); $size[ 1 ] = $height if $height >= 0; $size[ 0 ] = $width if $width >= 0; } }; return @size; } our ( $screen_width, undef ) = window_size(); sub shell_escape( $ ) { my $token = shift; if ( length( $token ) == 0 ) { return "''"; } $token =~ s/([^A-Za-z0-9_\-\.,:\/@\n])/\\$1/g; $token =~ s/\n/'\n'/g; return $token; } sub c($$) { my ( $str, $style ) = @_; $str = colored( $str, $style ) unless $bleach; return $str; } sub bleach($) { my ( $colored ) = @_; $colored =~ s(\033\[.*?m)()g; return $colored; } sub clen($) { return length( bleach( $_[ 0 ] ) ); } sub ljust($$) { my ( $string, $width ) = @_; my $len = clen( $string ); return $string if $width <= $len; my $padding = $width - $len; return( $string . ( ' ' x $padding ) ); } sub center($$) { my ( $string, $width ) = @_; my $len = clen( $string ); return $string if $width <= $len; my $padding = $width - $len; my $left = int( $padding / 2 ); my $right = $left + $padding % 2; return( ( ' ' x $left ) . $string . ( ' ' x $right ) ); } sub print_table { local $\ = "\n"; my @rows = @_; my @arrays; my @titles; my @header_row; my $title = undef; for my $item ( @rows ) { if ( ref( $item ) eq 'ARRAY' ) { push( @arrays, $item ); } elsif ( ref( \$item ) eq "SCALAR" ) { push( @titles, $item ); } } unless ( @arrays ) { $@ = "print_table: no rows provided"; return; } my $ncols = scalar( @{$arrays[ 0 ]} ); my @widths; for my $c ( 0 .. ( $ncols - 1 ) ) { my $w = 0; for my $row ( @arrays ) { my $col = $row->[$c]; my $col_width = clen( $col ); if ( $w < $col_width ) { $w = $col_width; } } $widths[ $c ] = $w; } my $inner_width = ( $ncols - 1 ) * 3; $inner_width += $_ for @widths; if ( @titles ) { my $title_width = 0; for my $t ( @titles ) { my $l = clen( $t ); $title_width = $l if $l > $title_width; } if ( $title_width > $inner_width ) { $widths[-1] += $title_width - $inner_width; $inner_width = $title_width; } } if ( ref( \$rows[0] ) eq "SCALAR" ) { $title = shift( @rows ); print( '+-' . ( '-' x $inner_width ) . '-+' ); print( '| ' . center( $title, $inner_width ) . ' |' ); } my $mask = '| ' . join( " | ", map { "%-${_}s" } @widths ) . " |"; my $border = '+-' . join( "-+-", map { '-' x $_ } @widths ) . "-+"; my $outline = '+-' . ( '-' x $inner_width ) . '-+'; my $after_title = 1; print( $border ); my @title_row = @{ shift( @rows ) }; print( '| ' . join( ' | ', map { center( $title_row[ $_ ], $widths[ $_ ] ) } ( 0 ... $#title_row ) ) . ' |' ); print( $border ); $after_title = 0; for my $row ( @rows ) { if ( ref( $row ) eq 'ARRAY' ) { print( $border ) if $after_title; print( '| ' . ( join( ' | ', map { ljust( $row->[ $_ ], $widths[ $_ ] ); } (0 ... $#widths) ) ) . ' |' ); #print( sprintf( $mask, @{ $row } ) ); $after_title = 0; } elsif ( ref( \$row ) eq 'SCALAR' ) { if ( $row eq '-' ) { print( $after_title ? $outline : $border ); $after_title = 0; } else { print( $after_title ? $outline : $border ); print( '| ' . center( $row, $inner_width ) . ' |' ); $after_title = 1; } } } print( $after_title ? $outline : $border ); } sub update_progress( $ ) { local $| = 1; if ( $progress_steps == 0 ) { my $label = shift; my $line = sprintf( "%s %s", $progress_glyphs[ $progress_glyph ], $label ); my $n = length( $line ); if ( $n > $screen_width ) { $line = substr( $line, 0, $screen_width - 4 ) . '...'; } print STDERR "\r\e[2K"; # clears the line print STDERR $line; } $progress_steps = ( $progress_steps + 1 ) % $progress_frequency; $progress_glyph = ( $progress_glyph + 1 ) % $#progress_glyphs; } #################################################################################################### ########################################### Script Action ########################################## #################################################################################################### if ( $target_user = shift( @ARGV ) ) { $lastcomm_options = "--user " . shell_escape( $target_user ); } # # [1] Pull the lastcomm output for the timeframe of interest. Stop and close the pipe # once it seems that we have confidently left the timeframe, i.e. when $off_hits > 200. # open( LC, "lastcomm $lastcomm_options |" ); my $off_hits = 0; while ( <LC> ) { chomp; update_progress( $_ ); if ( /^\s*(\S+).*?([\w\-]+)\s+\S+\s+(\d+(?:\.\d+)?)\s+secs\s+(.+)$/ ) { my ( $name, $user, $secs, $time ) = ( $1, $2, $3, str2time( $4 ) ); if ( $time > $start ) { next; } if ( $time > $thresh_60 ) { $off_hits = 0; } else { $off_hits++; if ( $off_hits > MAX_OFF_HITS ) { last; } } if ( $secs == 0.0 ) { $secs += 0.001; } my $key = $target_user ? $name : $user; if ( $time > $thresh_1 ) { $last_1{ $key } += $secs; } if ( $time > $thresh_5 ) { $last_5{ $key } += $secs; } if ( $time > $thresh_15 ) { $last_15{ $key } += $secs; } if ( $time > $thresh_60 ) { $last_60{ $key } += $secs; } } } close( LC ); # clear the progress line display to prepare for printing the table { local $| = 1; print "\r\e[2K"; } # # calculate the total cp secs for each interval and store it in the # @totals array # for my $map ( @maps ) { my $total = 0; $total += $_ for values( %$map ); push @totals, $total; } # # Extract the top N users from the time range selected by $sort_index # our %sort_table = %{ $maps[ $sort_index ] }; our @top_listings; if ( $target_user ) { @top_listings = keys( %last_60 ); } else { @top_listings = keys( %sort_table ); } @top_listings = ( sort { ($sort_table{ $a } || 0) <=> ($sort_table{ $b } || 0) } @top_listings ); my $start_index = $#top_listings - $number_of_entries; $start_index < 0 and $start_index = 0; @top_listings = @top_listings[ $start_index ... $#top_listings ]; # # Harvest daily summary details from sa for the selected top entries # if ( $target_user ) { our @sa_users = ( $target_user ); } else { our @sa_users = @top_listings; } { my $target_rx = join( "|", map { lc( quotemeta ) } @sa_users ); $target_rx = qr<^($target_rx)\s+>i; open( SA, "sa -acm |" ); # skip over the "total sum" line my ( $rank, $matched ) = ( 0, 0 ); READ_SA: while ( <SA> ) { chomp; if ( /^\s*\d+/ ) { $rank = 0; my $line_user = ':total'; my @fields = split; $sa_data{$line_user} = { rank => $rank, cpu => $fields[ 4 ], percentage => $fields[ 5 ] }; } elsif ( /$target_rx/ ) { $matched++; my $line_user = $1; my @fields = split; my $cpu = 0.0; $fields[ 5 ] =~ /(\d*(?:\.\d*)?)cp/i and $cpu += $1; $sa_data{$line_user} = { rank => $rank, cpu => $cpu, percentage => $fields[ 6 ] }; if ( @sa_users == $matched ) { last READ_SA; } } $rank++; } close( SA ); }; my @stat_table; if ( $target_user ) { @stat_table = ( [ qw( command 1m 5m 15m 60m ) ] ); } else { @stat_table = ( [ qw( user 1m 5m 15m 60m today ) ] ); } my $sort_column = $stat_table[0]->[ $sort_index + 1 ]; $stat_table[ 0 ]->[ $sort_index + 1 ] = "\e[4m$sort_column\e[0m"; our $make_cell; if ( $show_percentage && $show_secs ) { $make_cell = sub { my ( $usage, $total ) = @_; my $percent = ( $total > 0 ) ? ( 100.0 * $usage / $total ) : 0; return sprintf( '%8.2fs %5.1f%%', $usage, $percent ); }; } elsif ( $show_percentage ) { $make_cell = sub { my ( $usage, $total ) = @_; my $percent = ( $total > 0 ) ? ( 100.0 * $usage / $total ) : 0; return sprintf( '%5.1f%%', $percent ); }; } elsif ( $show_secs ) { $make_cell = sub { my ( $usage, $total ) = @_; return sprintf( '%8.2fs', $usage ); }; } # # This is where the table is constructed # for my $top_listing ( @top_listings ) { my @row = ( $top_listing, map { $make_cell->( $maps[ $_ ]->{$top_listing}, $totals[ $_ ] ); } ( 0 ... $#maps ) ); unless ( $target_user ) { my $sa = $sa_data{$top_listing}; my $cpu = $sa->{cpu}; push( @row, sprintf( '%8.1fcp %7s', $sa->{cpu}, $sa->{percentage} ) ); my $color = undef; if ( $special_users{ $top_listing } ) { $color = 'cyan'; } elsif ( $cpu > 40 ) { $color = 'red'; } elsif ( $cpu > 30 ) { $color = 'magenta'; } elsif ( $cpu > 20 ) { $color = 'yellow'; } if ( $cpu ) { @row = map { c( $_, $color ) } @row; } } push( @stat_table, \@row ); } push @stat_table, '-'; my @total_row = ( "total", map { $make_cell->( $_, $_ ); } @totals ); unless( $target_user ) { my $sa_total = $sa_data{':total'}; push( @total_row, sprintf( '%10s %7s', $sa_total->{cpu}, $sa_total->{percentage} ) ); } push @stat_table, \@total_row; print_table( @stat_table ); print( "s = processs user time in cpu seconds, cp = user time + system time in cpu minutes\n" ); unless ( $target_user || $bleach ) { print( c( "user within 20cp-30cp for the day, ", 'yellow' ) ); print( c( "user within 30cp-40cp for the day", 'magenta' ) . "\n" ); print( c( "user over 40cp for the day, ", 'red' ) ); print( c( "special users (root, mailnull, mysql)", 'cyan' ) . "\n" ); }