#!/usr/bin/perl
#
# intrtime - Sample and print interrput handler times.
#	 Written using DTrace (Solaris 10 x86 build 63).
#
# Time spent by the kernel servicing interrupts is not easily
#  meausrable under Unix, intrtime gives a break down of the
#  interrupt types and times spent services each.
#
# 01-Mar-2005, ver 0.84  (check for newer versions)
#
#
# USAGE: intrtime [secs]
#
#	intrtime	# default output, 1 second
#	intrtime 10	# sample for 10 seconds
# 	
# FIELDS:
#
#	Interrput	Interrput name 
#	Time(ns)	Total time spent in this interrput (ns)
#	Time%		Percent this time is of TOTAL(dur)
#	TOTAL(int)	Total time spent in interrupts
#	TOTAL(dur)	Duration for this sample
#
# NOTE: This currently does not count the number of CPUs, so
#  Time% should really be divided by the number of CPUs handling
#  interrupts.
#
# SEE ALSO: Chapter 22, Solaris Dynamic Tracing Guide, docs.sun.com.
#           intrstat (and I wish I had discovered this sooner!)
#
# Standard Disclaimer: This is freeware, use at your own risk.
#
# Author: Brendan Gregg  [Sydney, Australia]
#
# 27-Feb-2005	Brendan Gregg	Created this.
# 01-Mar-2005	   "	  "	Now using device major names.
# 


### Process Arguments
&usage() if $ARGV[0] =~ /^-/;
&usage() if $ARGV[0] =~ /^\D/;
$secs = $ARGV[0] || 1;
$| = 1;

### name_to_major to major_to_name
open(MAJOR,"/etc/name_to_major") || 
 die("ERROR1: Can't read /etc/name_to_major: $!\n");
chomp(@Lines = <MAJOR>);
close MAJOR;
foreach $line (@Lines) {
	($name,$num) = split(' ',$line);
	$Major_to_name{$num} = $name;
}


#
#  DTrace Script 
#
$intr_d = '/usr/sbin/dtrace -qn \' 
 /* This measures interrput thread times */
 dtrace:::BEGIN {
	self->begin = timestamp;
	secs = ' . $secs . ';
 }

 profile:::tick-1sec
 /secs > 0/
 {
	secs--;
 }
 profile:::tick-1sec
 /secs == 0/
 {
	exit(0);
 }

 sdt:::interrupt-start { 
	self->start = timestamp;
 }

 sdt:::interrupt-complete { 
	this->delta = timestamp - self->start;
	this->devinfo = (struct dev_info *)arg0;
	major = this->devinfo == 0 ? -1 : this->devinfo->devi_major;
	@Ints[major] = sum(this->delta);
	@Totals["TOTAL(int)"] = sum(this->delta);
	self->start = 0;
 }

 dtrace:::END {
	@Totals["TOTAL(dur)"] = sum(timestamp - self->begin);
	printa("%12d %16@d\n",@Ints);
	printa("%12s %16@d\n",@Totals);
 }\'';


#
#  Execute DTrace
#
@Lines = `$intr_d`;


#
#  Print Report
#
foreach $line (@Lines) {
	$line =~ s/^  *//;
	($major,$ns) = split(' ',$line);
	$totaldur = $ns if $major eq "TOTAL(dur)";
}

printf("%12s %16s %7s\n","Interrupt","Time(ns)","%Time");

foreach $line (@Lines) {
	next if $line =~ /^\s*$/;
	($major,$ns) = split(' ',$line);
	if ($major !~ /^\s*TOTAL/) {
		next if $major < 0 || $major > 65536;
		# translate majors to device names,
		$name = $Major_to_name{$major};
		# (the answerbook has an elegant dtrace method for this)
	} else {
		$name = $major;
	}
	printf("%12s %16s %7.2f\n",$name,$ns,($ns*100/$totaldur));
}


# usage - print usage and exit.
#
sub usage {
	print STDERR "USAGE: intrtime [secs]\n";
	print STDERR "   eg,\n";
	print STDERR "       intrtime       # default output, 1 second\n";
	print STDERR "       intrtime 10    # sample for 10 seconds\n";
	exit(1);
}

