1235368Sgnn#!/usr/bin/perl -w
2235368Sgnn#
3235368Sgnn# hotuser - sample on-CPU user-level functions and libraries.
4235368Sgnn#           Written using Perl and DTrace (Solaris 10 03/05)
5235368Sgnn#
6235368Sgnn# This samples the on-CPU function at 1001 Hertz, for a simple yet
7235368Sgnn# effective user-level profiling tool for sampling exclusive function time.
8235368Sgnn# The output will identify which function is on the CPU the most - which
9235368Sgnn# is the hottest. See Notes/ALLexclusive_notes.txt for an explanation of
10235368Sgnn# exclusive time.
11235368Sgnn#
12235368Sgnn# $Id: hotuser 65 2007-10-04 11:09:40Z brendan $
13235368Sgnn#
14235368Sgnn# USAGE:        hotuser [-hl] { -c command | -p PID }
15235368Sgnn#
16235368Sgnn#		-h              # help
17235368Sgnn#		-l              # match libraries, not functions
18235368Sgnn#		-p PID          # examine this PID
19235368Sgnn#		-c command      # run and examine this command
20235368Sgnn#       eg,
21235368Sgnn#		hotuser -p 81   # sample user functions from PID 81
22235368Sgnn#		hotuser -lp 81  # sample user libraries from PID 81
23235368Sgnn#		hotuser -p `pgrep -n Xorg`	# sample Xorg
24235368Sgnn#
25235368Sgnn# FIELDS:
26235368Sgnn#		FUNCTION        Function name
27235368Sgnn#		LIBRARY         Library name
28235368Sgnn#		COUNT           Number of samples
29235368Sgnn#		PCNT            Percentage of total samples
30235368Sgnn#
31235368Sgnn# COPYRIGHT: Copyright (c) 2006 Brendan Gregg.
32235368Sgnn#
33235368Sgnn# CDDL HEADER START
34235368Sgnn#
35235368Sgnn#  The contents of this file are subject to the terms of the
36235368Sgnn#  Common Development and Distribution License, Version 1.0 only
37235368Sgnn#  (the "License").  You may not use this file except in compliance
38235368Sgnn#  with the License.
39235368Sgnn#
40235368Sgnn#  You can obtain a copy of the license at Docs/cddl1.txt
41235368Sgnn#  or http://www.opensolaris.org/os/licensing.
42235368Sgnn#  See the License for the specific language governing permissions
43235368Sgnn#  and limitations under the License.
44235368Sgnn#
45235368Sgnn# CDDL HEADER END
46235368Sgnn#
47235368Sgnn# Author: Brendan Gregg  [Sydney, Australia]
48235368Sgnn#
49235368Sgnn# 29-Jun-2006	Brendan Gregg	Created this.
50235368Sgnn# 29-Jun-2006	   "      "	Last update.
51235368Sgnn#
52235368Sgnn
53235368Sgnnuse strict;
54235368Sgnnuse Getopt::Std;
55235368Sgnn
56235368Sgnn#
57235368Sgnn# Command Line Arguments
58235368Sgnn#
59235368Sgnnmy $args;
60235368Sgnnusage() if defined $ARGV[0] and $ARGV[0] eq "--help";
61235368Sgnngetopts('c:hlp:') or usage();
62235368Sgnnusage() if defined $main::opt_h and $main::opt_h;
63235368Sgnnmy $libs = defined $main::opt_l and $main::opt_l ? 1 : 0;
64235368Sgnnif (defined $main::opt_c) {
65235368Sgnn    $args = "-c $main::opt_c";
66235368Sgnn}
67235368Sgnnelsif (defined $main::opt_p) {
68235368Sgnn    $args = "-p $main::opt_p";
69235368Sgnn}
70235368Sgnnelse {
71235368Sgnn    usage();
72235368Sgnn}
73235368Sgnn
74235368Sgnn#
75235368Sgnn# Cleanup on signals
76235368Sgnn#
77235368Sgnn$SIG{INT} = \&cleanupsig;    # Ctrl-C
78235368Sgnn$SIG{QUIT} = \&cleanupsig;   # Ctrl-\
79235368Sgnn$SIG{TERM} = \&cleanupsig;   # TERM
80235368Sgnn
81235368Sgnn#
82235368Sgnn# Declare DTrace script
83235368Sgnn#
84235368Sgnnmy $dtrace = <<END;
85235368Sgnn/usr/sbin/dtrace -n '
86235368Sgnn	#pragma D option quiet
87235368Sgnn	profile:::profile-1001hz
88235368Sgnn	/pid == \$target/ 
89235368Sgnn	{
90235368Sgnn		\@pc[arg1] = count();
91235368Sgnn	}
92235368Sgnn	dtrace:::END
93235368Sgnn	{
94235368Sgnn		printa("OUT: %A %\@d\\n", \@pc);
95235368Sgnn	}
96235368Sgnn' '$args'
97235368SgnnEND
98235368Sgnn
99235368Sgnn#
100235368Sgnn# Run DTrace, process output
101235368Sgnn#
102235368Sgnnmy %Count;
103235368Sgnnmy $total;
104235368Sgnnopen DTRACE, "$dtrace |" or die "ERROR1: Can't run dtrace (perms?): $!\n";
105235368Sgnnprint "Sampling... Hit Ctrl-C to end.\n";
106235368Sgnnwhile (my $line = <DTRACE>) {
107235368Sgnn    next if $line =~ /^\s*$/;
108235368Sgnn    next if $line !~ /^OUT: /;
109235368Sgnn    my ($tag, $addr, $count) = split ' ', $line;
110235368Sgnn    my ($name, $offset) = split /\+/, $addr;
111235368Sgnn    next if $name eq "0x0";
112235368Sgnn    $name =~ s/\`.*// if $libs;
113235368Sgnn    $Count{$name} += $count;
114235368Sgnn    $total += $count;
115235368Sgnn}
116235368Sgnnclose DTRACE;
117235368Sgnn
118235368Sgnn#
119235368Sgnn# Print final report
120235368Sgnn#
121235368Sgnnprintf "\n%-52s %8s %6s\n", $libs ? "LIBRARY" : "FUNCTION", "COUNT", "PCNT";
122235368Sgnnforeach my $name (sort { $Count{$a} <=> $Count{$b} } keys %Count) {
123235368Sgnn    printf "%-52s %8d %5.1f%%\n", $name, $Count{$name},
124235368Sgnn                                  100 * $Count{$name} / ($total ? $total : 1);
125235368Sgnn}
126235368Sgnn
127235368Sgnn#
128235368Sgnn# Subroutines
129235368Sgnn#
130235368Sgnnsub cleanupsig {
131235368Sgnn}
132235368Sgnnsub usage {
133235368Sgnn    print STDERR "USAGE: hotuser [-hl] { -c command | -p PID }\n";
134235368Sgnn    print STDERR "   eg,\n";
135235368Sgnn    print STDERR "       hotuser -p 81     # sample user funcs for PID 81\n";
136235368Sgnn    print STDERR "       hotuser -lp 81    # sample user libs for PID 81\n";
137235368Sgnn    print STDERR "       hotuser -p `pgrep -n Xorg`     # sample Xorg\n";
138235368Sgnn    exit 1;
139235368Sgnn}
140