#!/usr/bin/perl -w
use strict;
use Getopt::Std;
use vars qw($VERSION $opt_h $opt_x $opt_n $opt_v $opt_e $lord $hdr @lines %ppidlut %pidname %metric @pids);

($VERSION) = ('$Revision: 1.4 $' =~ /([\d\.]+)/); 

getopts('hxnve:');
if ($opt_v) {
	die "xmemusage - process memory usage tool\n\t" . q$Id: xmemusage.pl,v 1.4 2002/10/03 20:06:40 piersk Exp $ . "\n";
}
if ($opt_h) {
	require Pod::Usage;
	Pod::Usage::pod2usage(-verbose => 2);
}

my $lord; # the PID of the process that owns all other processes
my $shownames = $opt_n;
my $autoxdu = $opt_x;
my $execit = $opt_e || 'xdu';

if ($^O eq 'linux') {
	# Linux systems - procps version 2
	($hdr, @lines) = `/bin/ps -Ao pid,ppid,vsize,cmd`;
	$lord = 1;
} elsif ($^O eq 'solaris') {
	# solaris using normal ps(1)
	($hdr, @lines) = `/usr/bin/ps -Ao pid,ppid,vsz,comm`;
	$lord = 0;
} else {
	die "unsupported operating system - please update me!";
}

foreach (@lines) {
	chomp;
	if (m/^\s*(\d+)\s+(\d+)\s+(\S+)\s+(.*)$/) {
		my $pid = $1;
		push @pids, $pid;
		$ppidlut{$1} = $2;
		$metric{$1} = $3;
		my $tpidname = $4 || '<defunct>';
		$tpidname =~ s/\s.*$//;
#		$tpidname =~ s!^.*/!!;
		$tpidname =~ tr:/:%:;
		$pidname{$pid} = $tpidname || 'NONAME';
	} else {
		warn "No match for <$_>";
	}
}

if ($autoxdu) {
	open(XDU, '|'.$execit) or die "Cannot open pipe to xdu - $!";
	select XDU;
}

foreach (@pids) {
	my @bits = reverse(pid2heir($_));
	my $shortname = $pidname{$_};
	my $str = join('/', '', @bits, $shortname);
	print "$metric{$_}\t$str\n";
}

sub pid2heir {
	my $pid = shift;
	if (($pid == $lord) || (! exists($ppidlut{$pid}))) {
		return ($lord . ($shownames ? '.'.$pidname{$lord} : ''));
	} else {
		return ($pid . ($shownames ? '.'.$pidname{$pid} : ''), pid2heir( $ppidlut{$pid} ));
	}
}

=pod

=head1 NAME

xmemusage - show heirarchy of processes' memory usage, including children

=head1 SYNOPSIS

	xmemusage [ -h | -v ] [ -n ] [ -x [ -e PROGRAM ] ]
	
	-h - show this help
	-v - show the version
	-n - use PID and name in the 'directory' name
	-x - automatically execute xdu(1) and pass it input
	-e - specify the program to use instead of 'xdu', e.g. a full path or a clone of xdu. Requires -x

I recommend you simply use 'xmemusage -xn' if you have xdu installed (see below).

If you want to filter out certain processes, try this sort of construction:

	xmemusage.pl -n | grep -v oracle | xdu

=head1 DESCRIPTION

The utility xdu shows the size of directories including their children and files - very useful
for finding out what is using disk space. We can do a similar thing for processes - each process
itself uses memory and its children, if any, use memory too.

By default this program prints a du-like listing to STDOUT, suitable for saving to a file or piping to xdu.
The -x option invokes xdu automatically.

=head1 XDU

Originally written by Phil Dykstra, available from http://sd.wareonearth.com/~phil/xdu/ - although 
there is at least one enhanced version, http://xdiskusage.sourceforge.net/

This program is not incredibly useful without xdu, or similar, itself.

=head1 EXAMPLES

Default output, edited as an example, with just the PIDs as 'folder' names:

	0       /0/sched
	2264    /0/1/%etc%init
	0       /0/2/pageout
	2960    /0/1/193/17935/17938/%usr%local%bin%fvwm2
	3848    /0/1/203/%usr%lib%autofs%automountd
	1872    /0/1/21808/%usr%lib%nfs%lockd
	2208    /0/1/193/16210/16211/20131/20133/bash
	8920    /0/1/285/%usr%lib%ab2%dweb%sunos5%bin%dwhttpd
	9536    /0/1/285/1229/%usr%lib%ab2%dweb%sunos5%bin%dwhttpd
	1920    /0/1/578/713/830/832/893/896/928/929/10791/10792/10809/telnet

So, the name of the process is the 'filename', and its PID is the name of the 'folder' it
appears to be in.

The root process is PID 0 and is called 'sched'. PID 2 is 'pageout' and it is a direct child of
sched. /usr/lib/nfs/lockd is PID 1872 and is a child of init, PID 1.

This example has the -n option on so we have the names, aswell as PIDs, of all processes in the 'path':

	3000    /0.sched/1.%etc%init/11519.rxvt/11521.ssh/ssh
	3928    /0.sched/1.%etc%init/481.%usr%lib%snmp%snmpdx/502.mibiisa/mibiisa
	2232    /0.sched/1.%etc%init/8089.rxvt/8091.bash/bash
	2416    /0.sched/1.%etc%init/25392.rxvt/25394.tcsh/tcsh
	11616   /0.sched/1.%etc%init/373.dbsnmp/dbsnmp
	4904    /0.sched/1.%etc%init/578.%usr%dt%bin%dtlogin/%usr%dt%bin%dtlogin
	2144    /0.sched/1.%etc%init/297.%usr%dt%bin%sdt_shell/300.-bash/-bash
	2272    /0.sched/1.%etc%init/1518.rxvt/rxvt

=head1 PREREQUISITES

Getopt::Std, Pod::Usage (for the usage/help message only) - or just put this
file through perldoc or whatever POD viewer you like.

=head1 COREQUISITES

xdu or similar program. This program has only been tested on Linux and Solaris.

=begin comment

=pod OSNAMES

Unix - tested on Linux and Solaris only.

=pod SCRIPT CATEGORIES

UNIX/System_administration

=pod README

See what processes are using memory with this usage of xdu to see usage in a hierarchy of parent
and child processes.

=end comment

=head1 VERSION

$Id: xmemusage.pl,v 1.4 2002/10/03 20:06:40 piersk Exp $

=cut
