# $Header: emdprocesschars.pl 02-may-2007.03:02:07 bguhatha Exp $
#
# emdprocesschars.pl
# 
# Copyright (c) 2002, 2007, Oracle. All rights reserved.  
#
#    NAME
#      emdprocesschars.pl 
#
#    DESCRIPTION
#      Script that provides an API to get at certain characteristics of
#      a process P.
#
#    NOTES
#      USAGE: emdprocesschars.pl <process_number>
#      Where process_number is the process we want to look at
#
#    MODIFIED   (MM/DD/YY)
#    bguhatha    05/02/07 - Backport bguhatha_bug-5365614 from main
#    nsharma     02/01/07 - AIX bug 5257669
#    nsharma     12/18/06 - Bug 5719319 on HP, remove obsoleted methods
#    cvaishna    04/04/07 - Bug Fix : 5694782
#    cvaishna    04/10/07 - XbranchMerge cvaishna_bug-5694782 from main
#    kganapat    09/01/05 - Add getPIDS subroutine 
#    sksaha      07/26/05 - Adding subroutine getThreadCount 
#    aaitghez    04/25/05 - bug 3731255. pe code changes into main 
#    aaitghez    11/04/04 - bug 3843569. PLE changes 
#    aaitghez    03/24/04 - 
#    aaitghez    03/19/04 - bug 3511020 
#    mbhoopat    03/10/04 - linux port 
#    skumar      01/13/04 - 10G changes for emd procstats to exclude threads
#    rzkrishn    01/08/04 - having cmdresult as local 
#    gachen      12/03/03 - 3292538 
#    njagathe    10/28/03 - Stop collecting uptime 
#    aaitghez    05/13/03 - bug 2916669
#    aaitghez    01/20/03 - bug 2755430
#    klmichae    08/20/02 - fix warnings
#    aaitghez    08/26/02 - add getPhysicalMemoryUsagePerct
#    aaitghez    08/21/02 - let getFileHandles take list
#    aaitghez    07/16/02 - add no child capabilities
#    aaitghez    07/26/02 - don't examine this process while getting statistics
#    xxu         06/25/02 - remove /usr/local/bin/perl
#    aaitghez    06/24/02 - add number of threads held by child processes
#    vnukal      06/21/02 - Enumerate all procs in getFileHandles
#    aaitghez    06/13/02 - aaitghez_bug-2254629
#    aaitghez    05/29/02 - Creation
# 

$stat_offset=2;

sub countFileHandlesOfType {
    (my $pid) = @_;
    $cmd = "ls -l /proc/$pid/fd | wc -l";
    my $r = `$cmd` or warn "Could not execute $cmd: $!";
    $r =~ s/\s//g;
    return $r;
}#end countFileHandlesOfType

				
############################################
# getProcessCharsRec (
#   <process id>,
#   <include children>,
#   <number of columns in result>, (includes pid and ppid cols)
#   <get file handles boolean>,
#   <the vector of results of the ps command>,
#   <the result vector>,
##############################################

my %commands = ();

sub getProcessCharsRec
{
    my $pid = shift(@_);
    my $include_children = shift(@_);
    my $num_cols = shift(@_);
    my $get_file_handles = shift(@_);
    my $resarray = shift(@_);
    my $ret_array = shift(@_);
    my $fields = shift(@_);
    my $size = 0;
    my $ignore = '';

    my $numrows = @$resarray/$num_cols;
    ##print "NUM ROWS = $numrows - ".scalar(@$resarray)." - $num_cols\n";
    my $row = 0;
    for($row = 0; $row < $numrows; $row++)
    {
	my $col;
	$parent_id = @$resarray[($num_cols*$row)];
	$process_id = @$resarray[($num_cols*$row)+1];

	if($process_id == $pid)
	{
	    if ($include_children != 1 || $commands{$parent_id} ne $commands{$pid})
	    {

	        #this process
	        ##print "THIS PROCESS\n";
	        for($col = $stat_offset ; $col < $num_cols; $col++)
	        {
                    if ($^O eq "dec_osf")
                    {
                      if (@$fields[$col] eq "vsz" || @$fields[$col] eq "rss")
                      {
                        $size = @$resarray[($num_cols*$row)+$col];
                        if ($size =~ /K/) # KB
                        {
                            ($size, $ignore) = split('K', $size);
                        }
                        elsif ($size =~ /M/) # MB
                        {
                            ($size, $ignore) = split('M', $size);
                            $size *= 1024;
                        }
                        elsif ($size =~ /G/) # GB
                        {
                            ($size, $ignore) = split('G', $size);
                            $size *= 1024 * 1024;
                        }
                        @$resarray[($num_cols*$row)+$col] = $size;
                      }
                    }
		    ##print "VALUE = @$resarray[($num_cols*$row)+$col]\n";
		    @$ret_array[$col-$stat_offset] = @$ret_array[$col-$stat_offset ] + @$resarray[($num_cols*$row)+$col];
		    if($get_file_handles == 1)
		    {
		        ##print "INCLUDE FILE HANDLES\n";
		        @$ret_array[$num_cols-$stat_offset] = 
			    countFileHandlesOfType($process_id);
		    }
	        }
	    }
	}
	if($include_children == 1 && $parent_id == $pid)
	{
	    #we want to loop for the kids
	    ##print "KIDS".scalar(@$resarray)."\n";
	    getProcessCharsRec($process_id, $include_children, $num_cols, 
			       $get_file_handles,
			       \@$resarray, \@$ret_array, \@$fields);
	}
    }
}

#########################################
# getProcessChars (
#  <include_file_handles boolean>
#  <include_chidlren boolean>
#  <process id>
#  <vector of stats to collect>
#  <result to return things in>
#  <what file type handles to collect>
# )
#########################################

sub getProcessChars {
    my $get_file_handles = shift(@_);
    my $include_children = shift(@_);
    my $pid = shift(@_);

    my $stats = shift(@_);
    my $result = shift(@_);
    my @cmdresult = ();
    my @fields = ();

    #always get the pid and the ppid
    
    
    my $args = "ppid pid";
    #create ps argument string
    foreach $stat (@$stats)
    {
	$args .= " $stat";
    }
    
    if ($^O eq "darwin")
    {
	$args .= " command";
    }
    else
    {
	$args .= " args";
    }

    if ( $^O eq "linux" )
    {
	$cmd = "ps -www -eo '$args'";
    }
    elsif ($^O eq "SunOS" or $^O eq "solaris")
    {
        $cmd = "/usr/bin/ps -ef -o '$args'";
    }
    elsif ( $^O eq "aix" )
    {
	$cmd = "/usr/sysv/bin/ps -A -o '$args'";
    }
    elsif ( $^O eq "darwin" )
    {
	$cmd = "ps awwxo '$args'";
    }
    else
    {
	$cmd = "ps -ef -o '$args'";
    }
    my $r = `$cmd` or die "Could not execute $cmd: $!";
    
    # now we have list
    # of all the information we need.  
    @lines = split("\n", $r);
    #get rid of text
    shift(@lines);
    
    #number of processes running
    $numlines = @lines;
    $numcols = scalar(@$stats) + $stat_offset;
    my $numargs = scalar(@$stats) + 3;
    $row = 0;
    foreach $line (@lines)
    {
	@tokens = split(" ",$line, $numargs);
	$col = 0; 
	foreach $token (@tokens)
	{
	    @cmdresult[($row*$numcols)+$col] = $token;
	    $col++;
	}
	
	$row++;
        $commands{$tokens[1]} = $tokens[$numargs-1];
    }

    @fields = split(' ', $args);

    getProcessCharsRec($pid, $include_children, $numcols, $get_file_handles, \@cmdresult, \@$result, \@fields);
    
}

#Effects: Returns the uptime of process P in seconds.  i.e. the time it has 
#         been running.

sub getUptime {		 
    my $pid = shift(@_);
    $cmd = "ps -p $pid -o \"pid etime\" ";
    my $r = `$cmd` or die "Could not execute ps";
    @lines = split("\n", $r);	 
    foreach $line (@lines) 
    {	
	@stats = split(" ", $line);
	if($stats[0] == $pid)
	{
	    # this process
	    return $stats[1];
	}
    }
}# end getUptime

# Effects: Returns the sum of all the Open Filehandles (OF) for process 
#          P of the type passed in.

sub getFileHandles {
    (my $pid, my $include_child_procs) = @_;
    if($pid eq $$)
    {
	# we want to exclude this process
	return 0;
    }
   
    $cmd = "ps -A -o \"ppid pid\" ";
    my $file_count = 0;
    my $r = `$cmd` or die "Could not execute ps";
    @lines = split("\n", $r);
    foreach $line (@lines) 
    {	
	@stats = split(" ", $line);
	if($stats[1] eq $pid)
	{
	    # this process
	    $file_count += countFileHandlesOfType_HP($stats[1]);
	}
	if($include_child_procs == 1 && $stats[0] eq $pid)
	{
	    # child process
	    $file_count += getFileHandles($stats[1], $include_child_procs);
	}
    }
    return $file_count;	       
}#end getFileHandles

sub countFileHandlesOfType_HP {
    (my $pid) = @_;
    $cmd = "$ENV{EMDROOT}/bin/nmupm nof $pid";
    my $r = `$cmd`;
    if ($r =~ /em_result=(\d*)/)
    {
        return $1;
    } else {
    return 0; 
    }
}#end countFileHandlesOfType_HP

# Effects: Returns the number of threads created by process P and its 
#          child processes if include_children is //              TRUE.  If 
#          include_children is FALSE, returns just the number of threads 
#          created by process P.

sub getThreads {
    my $pid = shift(@_);
    my $include_child_procs = shift(@_);
    $cmd = "ps -p $pid -o \"ppid pid nlwp\" ";
    my $thread_count = 0;
    my $r = `$cmd` or die "Could not execute ps";
    @lines = split("\n", $r);
    foreach $line (@lines) 
    {	
	@stats = split(" ", $line);
	if($stats[1] eq $pid)
	{
	    # this process
	    $thread_count += $stats[2];
	}
	if($include_child_procs == 1 && $stats[0] eq $pid)
	{
	    # child process
	    $thread_count += getThreads($stats[1], 0);
	}
    }
    return $thread_count;	   
}#end getThreads

# Effects: Returns the number of threads among the list of 
#          threads sent as argument, which belong to a particular process

sub getThreadCount_LNX 
{
    my $pid = shift(@_);
    my $threads = shift(@_);
    my $thread_count = 0;

    @lines = split("\n", $threads);

    foreach $line (@lines)
    {
      @stats = split(" ", $line);
      if($stats[1] eq $pid)
      {
        # this process
        $thread_count = $thread_count + 1;
      }
      if($stats[0] eq $pid)
      {
        # child process
        $thread_count += getThreadCount_LNX($stats[1], $threads);
      }
    }
    return $thread_count;
}

# Effects: Returns the Agent PID and Parent PID from emctl status agent output  
#          emct status agent output sent as argument 

sub getPIDS{
    my $x = shift(@_);
    my @lines = split("\n", $x);
    my $size = @lines;
    my @pids;

    my $count = 0;

    for (my $i=0; $i < $size; $i++)
    {
      ( $preStr , $postStr) = split ( /: / , @lines[$i]);

      if($preStr eq "Agent Process ID  ")
      {
        $pids[0] = $postStr;
      }
      if($preStr eq "Parent Process ID ")
      {
        $pids[1] = $postStr;
      }
    }

    return @pids;
}


1;