Alexander Kolbasov
2005-Oct-13 19:15 UTC
[dtrace-discuss] whatintr: script showing what CPU is interrupted by some device
#!/usr/bin/perl -w # # whatintr - what CPU is interrupted by some device? # # Sometimes it is useful to know what CPU is interrupted by a particular device. # The intrstat(1M) reports general interrupt statistics but it is not very # useful for scripts. This scripts shamelessly steals the D script from intrstat # and uses the Brendan Gregg technique of blending DTrace and Perl. # # USAGE: whatintr [-t time] dev ... # # For each argument it prints the CPU ID or -1 if it can not determine the # interrupted CPU. # # The script collects data using DTrace for the time interval specified. # The time is given in DTrace units. The default is 10 seconds. # # Examples: # # $whatintr bge0 # .. sleeps for 10 seconds # 1 # # See also: intrstat(1M). # # Requirements: The script needs enough privileges to run DTrace. # # Limitations: The script observes actual interrupts in the system. If a given # device is quite for the whole time period, its interrupt mapping can''t be # determined. Also the script assumes that a given device interrupts only one # CPU. # # Author: Aleexander Kolbasov <Alexander.Kolbasov at eng.sun.com> # # # CDDL HEADER START # The contents of this file are subject to the terms # of the Common Development and Distribution License # (the "License"). You may not use this file except # in compliance with the License. # # You can obtain a copy of the license at # usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing # permissions and limitations under the License. # # When distributing Covered Code, include this CDDL # HEADER in each file and include the License file at # usr/src/OPENSOLARIS.LICENSE. If applicable, # add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your # own identifying information: Portions Copyright [yyyy] # [name of copyright owner] # # CDDL HEADER END # # Copyright 2005 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # $Id: whatintr,v 1.3 2005/10/13 19:13:11 akolb Exp $ use strict; use Getopt::Std; use File::Basename; sub usage($) { print STDERR "usage: ", basename(shift, ".pl"), " [-t time] dev0 dev1 ...\n"; exit 0; } our ($opt_t, $opt_h); usage $0 if !getopts("ht:") || $opt_h; my $interval= $opt_t ? $opt_t : ''10s''; my $dtrace = <<END; /usr/sbin/dtrace -n '' #pragma D option quiet interrupt-start /arg0 != NULL/ { this->devi = (struct dev_info *)arg0; \@counts[stringof(`devnamesp[this->devi->devi_major].dn_name), this->devi->devi_instance, cpu] = count(); } tick-$interval { exit(0); } dtrace:::END { printa(\"%s %d %d %\@d\\n\", \@counts); }'' END # # Cleanup on signals # $SIG{INT} = ''IGNORE''; # Ctrl-C $SIG{QUIT} = ''IGNORE''; # Ctrl-\ $SIG{TERM} = ''IGNORE''; # TERM ### Open DTrace open(DTRACE,"$dtrace |") || die "can not start dtrace (perms?): $!\n"; my $intrmap; # For each device and instance reported by DTrace, build a hash indexed by # device name and instance number. while (<DTRACE>) { chomp; # Trim end of line next unless $_; # Skip empty lines # Extract device name, device instance and CPU ID. my ($dev, $instance, $cpu, undef) = split; $intrmap->{$dev}{$instance} = $cpu; } # We are done with DTrace close(DTRACE) || die "can not complete dtrace: $!\n"; # For each argument in the command line translate it into device and instance # and locate the mapping in the $intrmap. Use -1 if there is no mapping. foreach my $arg (@ARGV) { my ($dev, $instance) = ($arg =~ /^([^\d]+)(\d*)$/); $instance = 0 unless $instance; my $cpu = $intrmap->{$dev}{$instance} || -1; print "$cpu "; } print "\n";
Jonathan Adams
2005-Oct-13 19:45 UTC
[dtrace-discuss] whatintr: script showing what CPU is interrupted by some device
On Thu, Oct 13, 2005 at 12:15:10PM -0700, Alexander Kolbasov wrote:> my $interval= $opt_t ? $opt_t : ''10s''; > > my $dtrace = <<END; > /usr/sbin/dtrace -n '' > #pragma D option quiet > interrupt-start > /arg0 != NULL/ > { > this->devi = (struct dev_info *)arg0; > \@counts[stringof(`devnamesp[this->devi->devi_major].dn_name), > this->devi->devi_instance, cpu] = count(); > } > tick-$interval { exit(0); } > dtrace:::END { printa(\"%s %d %d %\@d\\n\", \@counts); }'' > ENDUsing tick-interval like this is not reliable. If anyone else is currently using the same probe, you exit(0) early. I''m also not sure why you''re fully specifying dtrace:::END, but not interrupt-start or tick-$interval. Cheers, - jonathan -- Jonathan Adams, Solaris Kernel Development
Alexander Kolbasov
2005-Oct-13 19:55 UTC
[dtrace-discuss] whatintr: script showing what CPU is interrupted by some device
>>>>> "Jonathan" == Jonathan Adams <jonathan.adams at sun.com> writes:Jonathan> On Thu, Oct 13, 2005 at 12:15:10PM -0700, Alexander Kolbasov wrote: >> my $interval= $opt_t ? $opt_t : ''10s''; >> >> my $dtrace = <<END; >> /usr/sbin/dtrace -n '' >> #pragma D option quiet >> interrupt-start >> /arg0 != NULL/ >> { this-> devi = (struct dev_info *)arg0; >> \@counts[stringof(`devnamesp[this->devi->devi_major].dn_name), this-> devi->devi_instance, cpu] = count(); >> } >> tick-$interval { exit(0); } >> dtrace:::END { printa(\"%s %d %d %\@d\\n\", \@counts); }'' >> END Jonathan> Using tick-interval like this is not reliable. If anyone else is currently Jonathan> using the same probe, you exit(0) early. Hmm, I thought that DTrace provided enough magic for this to work. Should I resourt to getting timestamp in the BEGIN probe and compare time stamps in tick probe? This will require ugly conversions of DTrace time units to nanoseconds. Is there any other reliable way to end script after certain amount of time? Jonathan> I''m also not sure why you''re fully specifying dtrace:::END, but not Jonathan> interrupt-start or tick-$interval. Just to visually separate it from the END delimiter line, but I could use different word there as well. - Alexander Kolbasov
Jonathan Adams
2005-Oct-13 20:24 UTC
[dtrace-discuss] whatintr: script showing what CPU is interrupted by some device
On Thu, Oct 13, 2005 at 12:55:20PM -0700, Alexander Kolbasov wrote:> >>>>> "Jonathan" == Jonathan Adams <jonathan.adams at sun.com> writes: > > Jonathan> On Thu, Oct 13, 2005 at 12:15:10PM -0700, Alexander Kolbasov wrote: > >> my $interval= $opt_t ? $opt_t : ''10s''; > >> > >> my $dtrace = <<END; > >> /usr/sbin/dtrace -n '' > >> #pragma D option quiet > >> interrupt-start > >> /arg0 != NULL/ > >> { > this-> devi = (struct dev_info *)arg0; > >> \@counts[stringof(`devnamesp[this->devi->devi_major].dn_name), > this-> devi->devi_instance, cpu] = count(); > >> } > >> tick-$interval { exit(0); } > >> dtrace:::END { printa(\"%s %d %d %\@d\\n\", \@counts); }'' > >> END > > Jonathan> Using tick-interval like this is not reliable. If anyone else is currently > Jonathan> using the same probe, you exit(0) early. > > Hmm, I thought that DTrace provided enough magic for this to > work. Should I resourt to getting timestamp in the BEGIN probe and > compare time stamps in tick probe? This will require ugly conversions > of DTrace time units to nanoseconds. Is there any other reliable way > to end script after certain amount of time?It mostly works; it''s just not 100% reliable. The problem is that probes are shared, and are "enabled" when the first D program which uses them is loaded. tick-* and profile-* probes are set to first fire one interval after they are enabled.[1] So if a second script comes along and uses an in-use probe, the first firing will be at some arbitrary time less than the interval. I think the best approach is to use the tick probe, but *measure* the actual time spent enabled: dtrace:::BEGIN { start = timestamp } ... dtrace:::END { printf("%d\n", timestamp - start); ... } that way, you know how much time was actually traced.> Jonathan> I''m also not sure why you''re fully specifying dtrace:::END, but not > Jonathan> interrupt-start or tick-$interval. > > Just to visually separate it from the END delimiter line, but I could use > different word there as well.<nod>; Note that the most stable way to write a D script is to always specify the provider. Cheers, - jonathan [1] In addition, after all the probes for the D program are enabled, dtrace runs the BEGIN probe, then makes all of the other probes live; this means that the first firing will *always* be short: % dtrace -qn ''BEGIN{start=timestamp} tick-1s{printf("%10d\n",timestamp-start);}'' 975193583 1975194248 2975192914 3975200580 ... % so there was a ~25msec delay between probe creation and the BEGIN probe firing. -- Jonathan Adams, Solaris Kernel Development
Alexander Kolbasov
2005-Oct-13 21:30 UTC
[dtrace-discuss] whatintr: script showing what CPU is interrupted by some device
Jonathan, thank you for a good explanation of profile provider pecularities. Here is the version which should take care of premature tick probes. ############ Cut ################################################ #!/usr/bin/perl -w # # whatintr - what CPU is interrupted by some device? # # CDDL HEADER START # The contents of this file are subject to the terms # of the Common Development and Distribution License # (the "License"). You may not use this file except # in compliance with the License. # # You can obtain a copy of the license at # usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing # permissions and limitations under the License. # # When distributing Covered Code, include this CDDL # HEADER in each file and include the License file at # usr/src/OPENSOLARIS.LICENSE. If applicable, # add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your # own identifying information: Portions Copyright [yyyy] # [name of copyright owner] # # CDDL HEADER END # # # # Copyright 2005 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # $Id: whatintr,v 1.4 2005/10/13 21:28:05 akolb Exp $ # # # Sometimes it is useful to know what CPU is interrupted by a particular device. # The intrstat(1M) reports general interrupt statistics but it is not very # useful for scripts. This scripts shamelessly steals the D script from intrstat # and uses the Brendan Gregg technique of blending DTrace and Perl. # # USAGE: whatintr [-t time] dev ... # # For each argument it prints the CPU ID or -1 if it can not determine the # interrupted CPU. # # The script collects data using DTrace for the time interval specified. # The time is given in DTrace units. The default is 10 seconds. # # Examples: # # $whatintr bge0 # .. sleeps for 10 seconds # 1 # # See also: intrstat(1M). # # Requirements: The script needs enough privileges to run DTrace. # # Limitations: The script observes actual interrupts in the system. If a given # device is quite for the whole time period, its interrupt mapping can''t be # determined. Also the script assumes that a given device interrupts only one # CPU. # # Note that the time specified with -t is not exact, the script may actually # spend more time collecting data. # # Author: Aleexander Kolbasov <Alexander.Kolbasov at eng.sun.com> # use strict; use Getopt::Std; use File::Basename; # Bunch of useful constants. use constant SEC => 1; use constant MILLISEC => 1000; use constant MICROSEC => 1000000; use constant NANOSEC => 1000000000; # Number of seconds in a minute, hour and day use constant MINUTE => 60; use constant HOUR => 60 * MINUTE; use constant DAY => 24 * HOUR; # Default time in seconds; use constant TDEFAULT => 10 * SEC; use constant TDEFAULT_S => ''10s''; sub time2ns($); sub usage($) { print STDERR "usage: ", basename(shift, ".pl"), " [-t time] dev0 dev1 ...\n"; exit 0; } our ($opt_t, $opt_h); usage $0 if !getopts("ht:") || $opt_h; my $interval= $opt_t ? $opt_t : TDEFAULT_S; my $interval_ns = time2ns($interval); # # Construct the actual D script. The tick probe may fire before the specified # interval if someone else had it enable, so make sure that the required # interval have actually passed. If the tick probe fires too early, use a backup # 1-sec tick probe to check whether it is time to bail out. # my $dtrace = <<END_OF_DTRACE; /usr/sbin/dtrace -n '' #pragma D option quiet dtrace:::BEGIN { start = timestamp; } sdt:::interrupt-start /arg0 != NULL/ { this->devi = (struct dev_info *)arg0; \@counts[stringof(`devnamesp[this->devi->devi_major].dn_name), this->devi->devi_instance, cpu] = count(); } profile:::tick-$interval, profile:::tick-1s /timestamp - start >= $interval_ns/ { exit(0); } dtrace:::END { printa(\"%s %d %d %\@d\\n\", \@counts); }'' END_OF_DTRACE # # Cleanup on signals # $SIG{INT} = ''IGNORE''; # Ctrl-C $SIG{QUIT} = ''IGNORE''; # Ctrl-\ $SIG{TERM} = ''IGNORE''; # TERM ### Open DTrace open(DTRACE,"$dtrace |") || die "can not start dtrace (perms?): $!\n"; my $intrmap; # For each device and instance reported by DTrace, build a hash indexed by # device name and instance number. while (<DTRACE>) { chomp; # Trim end of line next unless $_; # Skip empty lines # Extract device name, device instance and CPU ID. my ($dev, $instance, $cpu, undef) = split; $intrmap->{$dev}{$instance} = $cpu; } # We are done with DTrace close(DTRACE) || die "can not complete dtrace: $!\n"; # For each argument in the command line translate it into device and instance # and locate the mapping in the $intrmap. Use -1 if there is no mapping. foreach my $arg (@ARGV) { my ($dev, $instance) = ($arg =~ /^([^\d]+)(\d*)$/); $instance = 0 unless $instance; my $cpu = $intrmap->{$dev}{$instance} || -1; print "$cpu "; } print "\n"; # Convert DTrace time units to nanoseconds. sub time2ns($) { my $t = shift; # Split between time value and time unit my ($tval, $tunit) = ($t =~ /^(\d+)([^\d]*)/); # Use 1 second by default $tval = 1 unless $tval; $tunit = ''s'' unless $tunit; CASE: { $tval *= (NANOSEC / MILLISEC), last if ($tunit =~ /^(msec|ms)$/); $tval *= NANOSEC, last if ($tunit =~ /^(sec|s)$/); $tval *= NANOSEC * MINUTE, last if ($tunit =~ /^(min|m)$/); $tval *= NANOSEC * HOUR, last if ($tunit =~ /^(hour|h)$/); $tval *= NANOSEC * DAY, last if ($tunit =~ /^(day|d)$/); # Complain Invalid time units warn "invalid time unit $tunit, using default ", TDEFAULT_S, "\n"; $tval = TDEFAULT * NANOSEC; $interval = TDEFAULT_S; }; return ($tval); } ############ Cut ################################################