[comp.lang.perl] 'pt'... simple process tree in Perl

merlyn@iwarp.intel.com (Randal Schwartz) (03/08/90)

Here's a simple little goodie to sort the output of a process listing
so that it shows all parents immediately before their offspring.
If someone wants to make it a tree, feel free.

==================================================snip here
#!/local/merlyn/bin/perl

@x = split(/\n/,`ps lagxc`);
$header = shift @x;
$offpid = index($header,'  PID');
$offppid = index($header,' PPID');

for (@x) {
	$pid = sprintf("%05d", 0 + substr($_,$offpid,5));
	$ppid = sprintf("%05d", 0 + substr($_,$offppid,5));
	$text{$pid} = $_;
	$parent{$pid} = $ppid;
	$children{$ppid} .= "$pid " if $pid > 0;
}

print "$header\n";
for $each (sort keys (text)) {
	next unless ($each == 0) || undef $text{$parent{$each}};
	@x = ($each);
	while ($#x >= $[) {
		$_ = shift(@x);
		print "$text{$_}\n";
		@x = (sort split(/ /, $children{$_}),@x);
	}
}

================================================== snip here

Just another Perl hacker,
-- 
/=Randal L. Schwartz, Stonehenge Consulting Services (503)777-0095 ==========\
| on contract to Intel's iWarp project, Beaverton, Oregon, USA, Sol III      |
| merlyn@iwarp.intel.com ...!any-MX-mailer-like-uunet!iwarp.intel.com!merlyn |
\=Cute Quote: "Welcome to Portland, Oregon, home of the California Raisins!"=/

pvo@sapphire.OCE.ORST.EDU (Paul O'Neill) (03/12/90)

In article <1990Mar7.190901.3862@iwarp.intel.com> merlyn@iwarp.intel.com (Randal Schwartz) writes:
>Here's a simple little goodie to sort the output of a process listing
>so that it shows all parents immediately before their offspring.
>If someone wants to make it a tree, feel free.
>

OK.  Don't mind if I do.
A picture's worth a Kword.  This gave me a new feeling for the soul of
my machine.

---------------------------------------------------------------
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by sapphire!pvo on Sun Mar 11 12:14:50 PST 1990
# Contents:  pt_tree
 
echo x - pt_tree
sed 's/^@//' > "pt_tree" <<'@//E*O*F pt_tree//'
#!/usr/bin/perl -P

;#	pt_tree
;#	
;#	splice together Gisle Aas' ``pstree''
;#	and Randal Swartz ``pt''
;#	to plot process tree
;#
;#	Paul O'Neill
;#	11 mar 90

;# NAME
;#     pstree(1) - produce directory map in PostScript
;#
;# SYNOPSIS
;#     pstree [-f] [dirname]
;#
;# DESCRIPTION
;#     The output from this program is a PostScript program that will
;#     produce a "map" of the directory tree from the current directory
;#     and down. If a dirname is given the directory map from the given
;#     directory and down is produced. The page size is assumed to be A4.
;#
;# OPTIONS
;#     -f    Include ordinary files in the map. Without this flag only
;#           the overall directory structure is shown.
;#
;# SEE ALSO
;#     ls(1), perl(1), postscript(7)
;#
;# AUTHOR
;#     Gisle Aas, Norwegian Computing Centre, 1990
;#
;# BUGS
;#     Pstree truncates all directory names to 14 characters.
;# 
;# NOTES
;#     PostScript is a trademark of Adobe Systems, Incorporated.
;#     Perl is written by Larry Wall and is distributed under the
;#     terms of the GNU General Public License.
;#
;# CHANGES
;#     Jan 22 1990, Gisle Aas, NCC
;#        The right way to get rid of "." and ".." from directories is
;#        now used. Proper handling of symbolic links: tree doesn't
;#        follow symbolic links to directories any more.
;#     Jan 23 1990, Gisle Aas, NCC
;#        Added the '-f' option to include ordinary files in dir map.
;#


#define A4_TOP            792
#define A4_RIGHT_EDGE     612
#define TB_MARGIN         40
#define LEFT_MARGIN       60
;# #define FONT              "AvantGarde-Book"
#define FONT              "Times-Roman"
#define FONT_SIZE         10
#define DIR_LEVEL_INDENT  90

$y = A4_TOP - TB_MARGIN;
$prev_level = 0;

select(STDERR);
$|=1;

open(tmp,"+>/tmp/tree$$") || die "Can't create temporary file";
unlink("/tmp/tree$$");
select(tmp);

print  "/s {show} bind def\n";
print  "/m {moveto} bind def\n";
printf "/%s findfont %d scalefont setfont\n",FONT,FONT_SIZE;
print  "0.1 setlinewidth\n";

@@x = split(/\n/,`ps lagxc`);
$header = shift @x;
$offpid = index($header,'  PID');
$offppid = index($header,' PPID');
$offcmd = index($header,' COMMAND');

for (@x) {
        $pid = sprintf("%05d", 0 + substr($_,$offpid,5));
        $ppid = sprintf("%05d", 0 + substr($_,$offppid,5));
	$cmd = sprintf("%s", substr($_,$offcmd,14));
	$command{$pid} = $cmd;
        $text{$pid} = $_;
        $parent{$pid} = $ppid;
        $children{$ppid} .= "$pid " if $pid > 0;
}

;# for $each (sort keys (children)) {
;#     print STDERR "$each	$children{$each}\n";
;# }
;# print STDERR "\n\n";
;# 
;# @x = sort keys (children);	# @x = all parents
;# print STDERR "@x\n\n";
;# 
;# @y = sort keys (parent);	# @y = all processes
;# print STDERR "@y\n\n";

$level = 0;
$maxlevel = 0;
while( do find_a_child('00000') ) {
    $maxlevel = $level if $level > $maxlevel;
    $level = 0;
}
print "showpage\n";
seek(tmp,0,0); # rewind the temporary file

select(STDOUT);
print "%!\n";
if ($y < TB_MARGIN) {
   $page_size = (A4_TOP - 2*TB_MARGIN);
   $scale_factor = (A4_TOP - 2*TB_MARGIN)/((A4_TOP - TB_MARGIN ) - $y);
   printf "%.1f %.3f translate\n", LEFT_MARGIN,
                                   (-$y)*$scale_factor + TB_MARGIN;
   printf "%.5f dup scale\n", $scale_factor;
} elsif( $maxlevel > 5 ) {
   $scale_factor = 6/($maxlevel+1);
   printf "%.1f 0 translate\n", LEFT_MARGIN;
   printf "%.5f dup scale\n", $scale_factor;
} else {
   printf "%.1f 0 translate\n", LEFT_MARGIN;
};

;# copy temporary file to standard out
while (<tmp>) {
   print;
}
exit;




sub find_a_child
{
    local($me) = shift;
    local($a_child);

    @kids = split(/ /, $children{$me});
    $a_child = shift(@kids);
    $level++ if $a_child;
    do find_a_child($a_child) if $a_child;
    if(! $a_child) {
	&emitt(substr($command{$me},0,14), $level);
;#	print STDERR "$me	$level\n";
	@kids = split(/ /,$children{$parent{$me}});
	shift(@kids);
	$children{$parent{$me}} = join(' ',@kids);
    }
    $level;
}


;# Uses the following global variables:
;#    $y          : current vertical position (initial value = 'top of page')
;#    $prev_level : the level reportet last time on emit (init value = 0)
;#    @top        : current top position at different levels
;#    @bottom     : current bottom position at different levels
;#    @pos        : string of positions at different levels
sub emitt
{
   local($text) = shift;
   local($level) = shift;

   ;# Do some substitutions on the $text so that it can be used as a
   ;# PostScript string constant.
   $text =~ s/\\/\\134/g;
   $text =~ s/\(/\\050/g;
   $text =~ s/\)/\\051/g;

   if ($level == $prev_level) {
      &write($level,$y,$text);
      $pos[$level] .= " $y";
      $bottom[$level] = $y;
      $y -= FONT_SIZE;
   }
   elsif ($level > $prev_level) {
      &write($level,$y,$text);
      local($i);
      for ($i=$prev_level+1;$i<$level;$i++) {
          $pos[$i] = '';
      }
      $pos[$level] = "$y";
      $top[$level] = $y;
      $bottom[$level] = $y;
      $y -= FONT_SIZE;
   }
   elsif ($level == ($prev_level - 1)) {
      local($ypos) = ($top[$level+1] - $bottom[$level+1]) / 2 + 
                     $bottom[$level+1];
      &write($level,$ypos,$text);
      &lines($level,$ypos,$pos[$level+1],$text);
      if ($pos[$level]) {
         $pos[$level] .= " $ypos";
         $bottom[$level] = $ypos;
      }
      else {
         $pos[$level] = "$ypos";
         $top[$level] = $ypos;
         $bottom[$level] = $ypos;
      }
   }
   else {
      die "Humm..., jump from level $prev_level to level $level";
   }
   $prev_level = $level;
}

sub write
{
   local($x,$y,$text) = @_;
   $x = $x * DIR_LEVEL_INDENT;
   printf "%.f %.f m(%s)s\n", $x, $y, $text;
}

sub lines
{
   local($x,$y,$to,$text) = @_;
   local(@to) = split(/ /,$to);
   $x = $x * DIR_LEVEL_INDENT;
   $y += FONT_SIZE/3;
   printf "($text) stringwidth pop %.1f add %.1f m\n",$x+1,$y;
   printf "[";
   for (@to) { printf " %.1f", $_ + FONT_SIZE/3; }
   printf "]\n";
   printf "{gsave %.1f exch lineto stroke grestore} forall\n",
          $x+DIR_LEVEL_INDENT-4;
}
@//E*O*F pt_tree//
chmod u=rwx,g=rx,o=rx pt_tree
 
exit 0