xref: /titanic_53/usr/src/uts/common/dtrace/profile.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate #include <sys/errno.h>
30*7c478bd9Sstevel@tonic-gate #include <sys/stat.h>
31*7c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
32*7c478bd9Sstevel@tonic-gate #include <sys/conf.h>
33*7c478bd9Sstevel@tonic-gate #include <sys/systm.h>
34*7c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
35*7c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
36*7c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h>
37*7c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
38*7c478bd9Sstevel@tonic-gate #include <sys/strsubr.h>
39*7c478bd9Sstevel@tonic-gate #include <sys/dtrace.h>
40*7c478bd9Sstevel@tonic-gate #include <sys/cyclic.h>
41*7c478bd9Sstevel@tonic-gate #include <sys/atomic.h>
42*7c478bd9Sstevel@tonic-gate 
43*7c478bd9Sstevel@tonic-gate static dev_info_t *profile_devi;
44*7c478bd9Sstevel@tonic-gate static dtrace_provider_id_t profile_id;
45*7c478bd9Sstevel@tonic-gate 
46*7c478bd9Sstevel@tonic-gate /*
47*7c478bd9Sstevel@tonic-gate  * Regardless of platform, there are five artificial frames in the case of the
48*7c478bd9Sstevel@tonic-gate  * profile provider:
49*7c478bd9Sstevel@tonic-gate  *
50*7c478bd9Sstevel@tonic-gate  *	profile_fire
51*7c478bd9Sstevel@tonic-gate  *	cyclic_expire
52*7c478bd9Sstevel@tonic-gate  *	cyclic_fire
53*7c478bd9Sstevel@tonic-gate  *	[ cbe ]
54*7c478bd9Sstevel@tonic-gate  *	[ locore ]
55*7c478bd9Sstevel@tonic-gate  *
56*7c478bd9Sstevel@tonic-gate  * On amd64, there are two frames associated with locore:  one in locore, and
57*7c478bd9Sstevel@tonic-gate  * another in common interrupt dispatch code.  (i386 has not been modified to
58*7c478bd9Sstevel@tonic-gate  * use this common layer.)  Further, on i386, the interrupted instruction
59*7c478bd9Sstevel@tonic-gate  * appears as its own stack frame.  All of this means that we need to add one
60*7c478bd9Sstevel@tonic-gate  * frame for amd64, and then take one away for both amd64 and i386.
61*7c478bd9Sstevel@tonic-gate  *
62*7c478bd9Sstevel@tonic-gate  * On SPARC, the picture is further complicated because the compiler
63*7c478bd9Sstevel@tonic-gate  * optimizes away tail-calls -- so the following frames are optimized away:
64*7c478bd9Sstevel@tonic-gate  *
65*7c478bd9Sstevel@tonic-gate  * 	profile_fire
66*7c478bd9Sstevel@tonic-gate  *	cyclic_expire
67*7c478bd9Sstevel@tonic-gate  *
68*7c478bd9Sstevel@tonic-gate  * This gives three frames.  However, on DEBUG kernels, the cyclic_expire
69*7c478bd9Sstevel@tonic-gate  * frame cannot be tail-call eliminated, yielding four frames in this case.
70*7c478bd9Sstevel@tonic-gate  *
71*7c478bd9Sstevel@tonic-gate  * All of the above constraints lead to the mess below.  Yes, the profile
72*7c478bd9Sstevel@tonic-gate  * provider should ideally figure this out on-the-fly by hiting one of its own
73*7c478bd9Sstevel@tonic-gate  * probes and then walking its own stack trace.  This is complicated, however,
74*7c478bd9Sstevel@tonic-gate  * and the static definition doesn't seem to be overly brittle.  Still, we
75*7c478bd9Sstevel@tonic-gate  * allow for a manual override in case we get it completely wrong.
76*7c478bd9Sstevel@tonic-gate  */
77*7c478bd9Sstevel@tonic-gate #ifdef __amd64
78*7c478bd9Sstevel@tonic-gate #define	PROF_ARTIFICIAL_FRAMES	7
79*7c478bd9Sstevel@tonic-gate #else
80*7c478bd9Sstevel@tonic-gate #ifdef __i386
81*7c478bd9Sstevel@tonic-gate #define	PROF_ARTIFICIAL_FRAMES	6
82*7c478bd9Sstevel@tonic-gate #else
83*7c478bd9Sstevel@tonic-gate #ifdef __sparc
84*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
85*7c478bd9Sstevel@tonic-gate #define	PROF_ARTIFICIAL_FRAMES	4
86*7c478bd9Sstevel@tonic-gate #else
87*7c478bd9Sstevel@tonic-gate #define	PROF_ARTIFICIAL_FRAMES	3
88*7c478bd9Sstevel@tonic-gate #endif
89*7c478bd9Sstevel@tonic-gate #endif
90*7c478bd9Sstevel@tonic-gate #endif
91*7c478bd9Sstevel@tonic-gate #endif
92*7c478bd9Sstevel@tonic-gate 
93*7c478bd9Sstevel@tonic-gate #define	PROF_NAMELEN		15
94*7c478bd9Sstevel@tonic-gate 
95*7c478bd9Sstevel@tonic-gate #define	PROF_PROFILE		0
96*7c478bd9Sstevel@tonic-gate #define	PROF_TICK		1
97*7c478bd9Sstevel@tonic-gate #define	PROF_PREFIX_PROFILE	"profile-"
98*7c478bd9Sstevel@tonic-gate #define	PROF_PREFIX_TICK	"tick-"
99*7c478bd9Sstevel@tonic-gate 
100*7c478bd9Sstevel@tonic-gate typedef struct profile_probe {
101*7c478bd9Sstevel@tonic-gate 	char		prof_name[PROF_NAMELEN];
102*7c478bd9Sstevel@tonic-gate 	dtrace_id_t	prof_id;
103*7c478bd9Sstevel@tonic-gate 	int		prof_kind;
104*7c478bd9Sstevel@tonic-gate 	hrtime_t	prof_interval;
105*7c478bd9Sstevel@tonic-gate 	cyclic_id_t	prof_cyclic;
106*7c478bd9Sstevel@tonic-gate } profile_probe_t;
107*7c478bd9Sstevel@tonic-gate 
108*7c478bd9Sstevel@tonic-gate typedef struct profile_probe_percpu {
109*7c478bd9Sstevel@tonic-gate 	hrtime_t	profc_expected;
110*7c478bd9Sstevel@tonic-gate 	hrtime_t	profc_interval;
111*7c478bd9Sstevel@tonic-gate 	profile_probe_t	*profc_probe;
112*7c478bd9Sstevel@tonic-gate } profile_probe_percpu_t;
113*7c478bd9Sstevel@tonic-gate 
114*7c478bd9Sstevel@tonic-gate hrtime_t	profile_interval_min = NANOSEC / 5000;		/* 5000 hz */
115*7c478bd9Sstevel@tonic-gate int		profile_aframes = 0;				/* override */
116*7c478bd9Sstevel@tonic-gate 
117*7c478bd9Sstevel@tonic-gate static int profile_rates[] = {
118*7c478bd9Sstevel@tonic-gate     97, 199, 499, 997, 1999,
119*7c478bd9Sstevel@tonic-gate     4001, 4999, 0, 0, 0,
120*7c478bd9Sstevel@tonic-gate     0, 0, 0, 0, 0,
121*7c478bd9Sstevel@tonic-gate     0, 0, 0, 0, 0
122*7c478bd9Sstevel@tonic-gate };
123*7c478bd9Sstevel@tonic-gate 
124*7c478bd9Sstevel@tonic-gate static int profile_ticks[] = {
125*7c478bd9Sstevel@tonic-gate     1, 10, 100, 500, 1000,
126*7c478bd9Sstevel@tonic-gate     5000, 0, 0, 0, 0,
127*7c478bd9Sstevel@tonic-gate     0, 0, 0, 0, 0
128*7c478bd9Sstevel@tonic-gate };
129*7c478bd9Sstevel@tonic-gate 
130*7c478bd9Sstevel@tonic-gate /*
131*7c478bd9Sstevel@tonic-gate  * profile_max defines the upper bound on the number of profile probes that
132*7c478bd9Sstevel@tonic-gate  * can exist (this is to prevent malicious or clumsy users from exhausing
133*7c478bd9Sstevel@tonic-gate  * system resources by creating a slew of profile probes). At mod load time,
134*7c478bd9Sstevel@tonic-gate  * this gets its value from PROFILE_MAX_DEFAULT or profile-max-probes if it's
135*7c478bd9Sstevel@tonic-gate  * present in the profile.conf file.
136*7c478bd9Sstevel@tonic-gate  */
137*7c478bd9Sstevel@tonic-gate #define	PROFILE_MAX_DEFAULT	1000	/* default max. number of probes */
138*7c478bd9Sstevel@tonic-gate static uint32_t profile_max;		/* maximum number of profile probes */
139*7c478bd9Sstevel@tonic-gate static uint32_t profile_total;	/* current number of profile probes */
140*7c478bd9Sstevel@tonic-gate 
141*7c478bd9Sstevel@tonic-gate static void
142*7c478bd9Sstevel@tonic-gate profile_fire(void *arg)
143*7c478bd9Sstevel@tonic-gate {
144*7c478bd9Sstevel@tonic-gate 	profile_probe_percpu_t *pcpu = arg;
145*7c478bd9Sstevel@tonic-gate 	profile_probe_t *prof = pcpu->profc_probe;
146*7c478bd9Sstevel@tonic-gate 	hrtime_t late;
147*7c478bd9Sstevel@tonic-gate 
148*7c478bd9Sstevel@tonic-gate 	late = dtrace_gethrtime() - pcpu->profc_expected;
149*7c478bd9Sstevel@tonic-gate 	pcpu->profc_expected += pcpu->profc_interval;
150*7c478bd9Sstevel@tonic-gate 
151*7c478bd9Sstevel@tonic-gate 	dtrace_probe(prof->prof_id, CPU->cpu_profile_pc,
152*7c478bd9Sstevel@tonic-gate 	    CPU->cpu_profile_upc, late, 0, 0);
153*7c478bd9Sstevel@tonic-gate }
154*7c478bd9Sstevel@tonic-gate 
155*7c478bd9Sstevel@tonic-gate static void
156*7c478bd9Sstevel@tonic-gate profile_tick(void *arg)
157*7c478bd9Sstevel@tonic-gate {
158*7c478bd9Sstevel@tonic-gate 	profile_probe_t *prof = arg;
159*7c478bd9Sstevel@tonic-gate 
160*7c478bd9Sstevel@tonic-gate 	dtrace_probe(prof->prof_id, CPU->cpu_profile_pc,
161*7c478bd9Sstevel@tonic-gate 	    CPU->cpu_profile_upc, 0, 0, 0);
162*7c478bd9Sstevel@tonic-gate }
163*7c478bd9Sstevel@tonic-gate 
164*7c478bd9Sstevel@tonic-gate static void
165*7c478bd9Sstevel@tonic-gate profile_create(hrtime_t interval, const char *name, int kind)
166*7c478bd9Sstevel@tonic-gate {
167*7c478bd9Sstevel@tonic-gate 	profile_probe_t *prof;
168*7c478bd9Sstevel@tonic-gate 
169*7c478bd9Sstevel@tonic-gate 	if (interval < profile_interval_min)
170*7c478bd9Sstevel@tonic-gate 		return;
171*7c478bd9Sstevel@tonic-gate 
172*7c478bd9Sstevel@tonic-gate 	if (dtrace_probe_lookup(profile_id, NULL, NULL, name) != 0)
173*7c478bd9Sstevel@tonic-gate 		return;
174*7c478bd9Sstevel@tonic-gate 
175*7c478bd9Sstevel@tonic-gate 	atomic_add_32(&profile_total, 1);
176*7c478bd9Sstevel@tonic-gate 	if (profile_total > profile_max) {
177*7c478bd9Sstevel@tonic-gate 		atomic_add_32(&profile_total, -1);
178*7c478bd9Sstevel@tonic-gate 		return;
179*7c478bd9Sstevel@tonic-gate 	}
180*7c478bd9Sstevel@tonic-gate 
181*7c478bd9Sstevel@tonic-gate 	prof = kmem_zalloc(sizeof (profile_probe_t), KM_SLEEP);
182*7c478bd9Sstevel@tonic-gate 	(void) strcpy(prof->prof_name, name);
183*7c478bd9Sstevel@tonic-gate 	prof->prof_interval = interval;
184*7c478bd9Sstevel@tonic-gate 	prof->prof_cyclic = CYCLIC_NONE;
185*7c478bd9Sstevel@tonic-gate 	prof->prof_kind = kind;
186*7c478bd9Sstevel@tonic-gate 	prof->prof_id = dtrace_probe_create(profile_id,
187*7c478bd9Sstevel@tonic-gate 	    NULL, NULL, name,
188*7c478bd9Sstevel@tonic-gate 	    profile_aframes ? profile_aframes : PROF_ARTIFICIAL_FRAMES, prof);
189*7c478bd9Sstevel@tonic-gate }
190*7c478bd9Sstevel@tonic-gate 
191*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
192*7c478bd9Sstevel@tonic-gate static void
193*7c478bd9Sstevel@tonic-gate profile_provide(void *arg, const dtrace_probedesc_t *desc)
194*7c478bd9Sstevel@tonic-gate {
195*7c478bd9Sstevel@tonic-gate 	int i, j, rate, kind;
196*7c478bd9Sstevel@tonic-gate 	hrtime_t val = 0, mult = 1, len;
197*7c478bd9Sstevel@tonic-gate 	const char *name, *suffix = NULL;
198*7c478bd9Sstevel@tonic-gate 
199*7c478bd9Sstevel@tonic-gate 	const struct {
200*7c478bd9Sstevel@tonic-gate 		char *prefix;
201*7c478bd9Sstevel@tonic-gate 		int kind;
202*7c478bd9Sstevel@tonic-gate 	} types[] = {
203*7c478bd9Sstevel@tonic-gate 		{ PROF_PREFIX_PROFILE, PROF_PROFILE },
204*7c478bd9Sstevel@tonic-gate 		{ PROF_PREFIX_TICK, PROF_TICK },
205*7c478bd9Sstevel@tonic-gate 		{ NULL, NULL }
206*7c478bd9Sstevel@tonic-gate 	};
207*7c478bd9Sstevel@tonic-gate 
208*7c478bd9Sstevel@tonic-gate 	const struct {
209*7c478bd9Sstevel@tonic-gate 		char *name;
210*7c478bd9Sstevel@tonic-gate 		hrtime_t mult;
211*7c478bd9Sstevel@tonic-gate 	} suffixes[] = {
212*7c478bd9Sstevel@tonic-gate 		{ "ns", 	NANOSEC / NANOSEC },
213*7c478bd9Sstevel@tonic-gate 		{ "nsec",	NANOSEC / NANOSEC },
214*7c478bd9Sstevel@tonic-gate 		{ "us",		NANOSEC / MICROSEC },
215*7c478bd9Sstevel@tonic-gate 		{ "usec",	NANOSEC / MICROSEC },
216*7c478bd9Sstevel@tonic-gate 		{ "ms",		NANOSEC / MILLISEC },
217*7c478bd9Sstevel@tonic-gate 		{ "msec",	NANOSEC / MILLISEC },
218*7c478bd9Sstevel@tonic-gate 		{ "s",		NANOSEC / SEC },
219*7c478bd9Sstevel@tonic-gate 		{ "sec",	NANOSEC / SEC },
220*7c478bd9Sstevel@tonic-gate 		{ "m",		NANOSEC * (hrtime_t)60 },
221*7c478bd9Sstevel@tonic-gate 		{ "min",	NANOSEC * (hrtime_t)60 },
222*7c478bd9Sstevel@tonic-gate 		{ "h",		NANOSEC * (hrtime_t)(60 * 60) },
223*7c478bd9Sstevel@tonic-gate 		{ "hour",	NANOSEC * (hrtime_t)(60 * 60) },
224*7c478bd9Sstevel@tonic-gate 		{ "d",		NANOSEC * (hrtime_t)(24 * 60 * 60) },
225*7c478bd9Sstevel@tonic-gate 		{ "day",	NANOSEC * (hrtime_t)(24 * 60 * 60) },
226*7c478bd9Sstevel@tonic-gate 		{ "hz",		0 },
227*7c478bd9Sstevel@tonic-gate 		{ NULL }
228*7c478bd9Sstevel@tonic-gate 	};
229*7c478bd9Sstevel@tonic-gate 
230*7c478bd9Sstevel@tonic-gate 	if (desc == NULL) {
231*7c478bd9Sstevel@tonic-gate 		char n[PROF_NAMELEN];
232*7c478bd9Sstevel@tonic-gate 
233*7c478bd9Sstevel@tonic-gate 		/*
234*7c478bd9Sstevel@tonic-gate 		 * If no description was provided, provide all of our probes.
235*7c478bd9Sstevel@tonic-gate 		 */
236*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < sizeof (profile_rates) / sizeof (int); i++) {
237*7c478bd9Sstevel@tonic-gate 			if ((rate = profile_rates[i]) == 0)
238*7c478bd9Sstevel@tonic-gate 				continue;
239*7c478bd9Sstevel@tonic-gate 
240*7c478bd9Sstevel@tonic-gate 			(void) snprintf(n, PROF_NAMELEN, "%s%d",
241*7c478bd9Sstevel@tonic-gate 			    PROF_PREFIX_PROFILE, rate);
242*7c478bd9Sstevel@tonic-gate 			profile_create(NANOSEC / rate, n, PROF_PROFILE);
243*7c478bd9Sstevel@tonic-gate 		}
244*7c478bd9Sstevel@tonic-gate 
245*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < sizeof (profile_ticks) / sizeof (int); i++) {
246*7c478bd9Sstevel@tonic-gate 			if ((rate = profile_ticks[i]) == 0)
247*7c478bd9Sstevel@tonic-gate 				continue;
248*7c478bd9Sstevel@tonic-gate 
249*7c478bd9Sstevel@tonic-gate 			(void) snprintf(n, PROF_NAMELEN, "%s%d",
250*7c478bd9Sstevel@tonic-gate 			    PROF_PREFIX_TICK, rate);
251*7c478bd9Sstevel@tonic-gate 			profile_create(NANOSEC / rate, n, PROF_TICK);
252*7c478bd9Sstevel@tonic-gate 		}
253*7c478bd9Sstevel@tonic-gate 
254*7c478bd9Sstevel@tonic-gate 		return;
255*7c478bd9Sstevel@tonic-gate 	}
256*7c478bd9Sstevel@tonic-gate 
257*7c478bd9Sstevel@tonic-gate 	name = desc->dtpd_name;
258*7c478bd9Sstevel@tonic-gate 
259*7c478bd9Sstevel@tonic-gate 	for (i = 0; types[i].prefix != NULL; i++) {
260*7c478bd9Sstevel@tonic-gate 		len = strlen(types[i].prefix);
261*7c478bd9Sstevel@tonic-gate 
262*7c478bd9Sstevel@tonic-gate 		if (strncmp(name, types[i].prefix, len) != 0)
263*7c478bd9Sstevel@tonic-gate 			continue;
264*7c478bd9Sstevel@tonic-gate 		break;
265*7c478bd9Sstevel@tonic-gate 	}
266*7c478bd9Sstevel@tonic-gate 
267*7c478bd9Sstevel@tonic-gate 	if (types[i].prefix == NULL)
268*7c478bd9Sstevel@tonic-gate 		return;
269*7c478bd9Sstevel@tonic-gate 
270*7c478bd9Sstevel@tonic-gate 	kind = types[i].kind;
271*7c478bd9Sstevel@tonic-gate 	j = strlen(name) - len;
272*7c478bd9Sstevel@tonic-gate 
273*7c478bd9Sstevel@tonic-gate 	/*
274*7c478bd9Sstevel@tonic-gate 	 * We need to start before any time suffix.
275*7c478bd9Sstevel@tonic-gate 	 */
276*7c478bd9Sstevel@tonic-gate 	for (j = strlen(name); j >= len; j--) {
277*7c478bd9Sstevel@tonic-gate 		if (name[j] >= '0' && name[j] <= '9')
278*7c478bd9Sstevel@tonic-gate 			break;
279*7c478bd9Sstevel@tonic-gate 		suffix = &name[j];
280*7c478bd9Sstevel@tonic-gate 	}
281*7c478bd9Sstevel@tonic-gate 
282*7c478bd9Sstevel@tonic-gate 	ASSERT(suffix != NULL);
283*7c478bd9Sstevel@tonic-gate 
284*7c478bd9Sstevel@tonic-gate 	/*
285*7c478bd9Sstevel@tonic-gate 	 * Now determine the numerical value present in the probe name.
286*7c478bd9Sstevel@tonic-gate 	 */
287*7c478bd9Sstevel@tonic-gate 	for (; j >= len; j--) {
288*7c478bd9Sstevel@tonic-gate 		if (name[j] < '0' || name[j] > '9')
289*7c478bd9Sstevel@tonic-gate 			return;
290*7c478bd9Sstevel@tonic-gate 
291*7c478bd9Sstevel@tonic-gate 		val += (name[j] - '0') * mult;
292*7c478bd9Sstevel@tonic-gate 		mult *= (hrtime_t)10;
293*7c478bd9Sstevel@tonic-gate 	}
294*7c478bd9Sstevel@tonic-gate 
295*7c478bd9Sstevel@tonic-gate 	if (val == 0)
296*7c478bd9Sstevel@tonic-gate 		return;
297*7c478bd9Sstevel@tonic-gate 
298*7c478bd9Sstevel@tonic-gate 	/*
299*7c478bd9Sstevel@tonic-gate 	 * Look-up the suffix to determine the multiplier.
300*7c478bd9Sstevel@tonic-gate 	 */
301*7c478bd9Sstevel@tonic-gate 	for (i = 0, mult = 0; suffixes[i].name != NULL; i++) {
302*7c478bd9Sstevel@tonic-gate 		if (strcasecmp(suffixes[i].name, suffix) == 0) {
303*7c478bd9Sstevel@tonic-gate 			mult = suffixes[i].mult;
304*7c478bd9Sstevel@tonic-gate 			break;
305*7c478bd9Sstevel@tonic-gate 		}
306*7c478bd9Sstevel@tonic-gate 	}
307*7c478bd9Sstevel@tonic-gate 
308*7c478bd9Sstevel@tonic-gate 	if (suffixes[i].name == NULL && *suffix != '\0')
309*7c478bd9Sstevel@tonic-gate 		return;
310*7c478bd9Sstevel@tonic-gate 
311*7c478bd9Sstevel@tonic-gate 	if (mult == 0) {
312*7c478bd9Sstevel@tonic-gate 		/*
313*7c478bd9Sstevel@tonic-gate 		 * The default is frequency-per-second.
314*7c478bd9Sstevel@tonic-gate 		 */
315*7c478bd9Sstevel@tonic-gate 		val = NANOSEC / val;
316*7c478bd9Sstevel@tonic-gate 	} else {
317*7c478bd9Sstevel@tonic-gate 		val *= mult;
318*7c478bd9Sstevel@tonic-gate 	}
319*7c478bd9Sstevel@tonic-gate 
320*7c478bd9Sstevel@tonic-gate 	profile_create(val, name, kind);
321*7c478bd9Sstevel@tonic-gate }
322*7c478bd9Sstevel@tonic-gate 
323*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
324*7c478bd9Sstevel@tonic-gate static void
325*7c478bd9Sstevel@tonic-gate profile_destroy(void *arg, dtrace_id_t id, void *parg)
326*7c478bd9Sstevel@tonic-gate {
327*7c478bd9Sstevel@tonic-gate 	profile_probe_t *prof = parg;
328*7c478bd9Sstevel@tonic-gate 
329*7c478bd9Sstevel@tonic-gate 	ASSERT(prof->prof_cyclic == CYCLIC_NONE);
330*7c478bd9Sstevel@tonic-gate 	kmem_free(prof, sizeof (profile_probe_t));
331*7c478bd9Sstevel@tonic-gate 
332*7c478bd9Sstevel@tonic-gate 	ASSERT(profile_total >= 1);
333*7c478bd9Sstevel@tonic-gate 	atomic_add_32(&profile_total, -1);
334*7c478bd9Sstevel@tonic-gate }
335*7c478bd9Sstevel@tonic-gate 
336*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
337*7c478bd9Sstevel@tonic-gate static void
338*7c478bd9Sstevel@tonic-gate profile_online(void *arg, cpu_t *cpu, cyc_handler_t *hdlr, cyc_time_t *when)
339*7c478bd9Sstevel@tonic-gate {
340*7c478bd9Sstevel@tonic-gate 	profile_probe_t *prof = arg;
341*7c478bd9Sstevel@tonic-gate 	profile_probe_percpu_t *pcpu;
342*7c478bd9Sstevel@tonic-gate 
343*7c478bd9Sstevel@tonic-gate 	pcpu = kmem_zalloc(sizeof (profile_probe_percpu_t), KM_SLEEP);
344*7c478bd9Sstevel@tonic-gate 	pcpu->profc_probe = prof;
345*7c478bd9Sstevel@tonic-gate 
346*7c478bd9Sstevel@tonic-gate 	hdlr->cyh_func = profile_fire;
347*7c478bd9Sstevel@tonic-gate 	hdlr->cyh_arg = pcpu;
348*7c478bd9Sstevel@tonic-gate 	hdlr->cyh_level = CY_HIGH_LEVEL;
349*7c478bd9Sstevel@tonic-gate 
350*7c478bd9Sstevel@tonic-gate 	when->cyt_interval = prof->prof_interval;
351*7c478bd9Sstevel@tonic-gate 	when->cyt_when = dtrace_gethrtime() + when->cyt_interval;
352*7c478bd9Sstevel@tonic-gate 
353*7c478bd9Sstevel@tonic-gate 	pcpu->profc_expected = when->cyt_when;
354*7c478bd9Sstevel@tonic-gate 	pcpu->profc_interval = when->cyt_interval;
355*7c478bd9Sstevel@tonic-gate }
356*7c478bd9Sstevel@tonic-gate 
357*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
358*7c478bd9Sstevel@tonic-gate static void
359*7c478bd9Sstevel@tonic-gate profile_offline(void *arg, cpu_t *cpu, void *oarg)
360*7c478bd9Sstevel@tonic-gate {
361*7c478bd9Sstevel@tonic-gate 	profile_probe_percpu_t *pcpu = oarg;
362*7c478bd9Sstevel@tonic-gate 
363*7c478bd9Sstevel@tonic-gate 	ASSERT(pcpu->profc_probe == arg);
364*7c478bd9Sstevel@tonic-gate 	kmem_free(pcpu, sizeof (profile_probe_percpu_t));
365*7c478bd9Sstevel@tonic-gate }
366*7c478bd9Sstevel@tonic-gate 
367*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
368*7c478bd9Sstevel@tonic-gate static void
369*7c478bd9Sstevel@tonic-gate profile_enable(void *arg, dtrace_id_t id, void *parg)
370*7c478bd9Sstevel@tonic-gate {
371*7c478bd9Sstevel@tonic-gate 	profile_probe_t *prof = parg;
372*7c478bd9Sstevel@tonic-gate 	cyc_omni_handler_t omni;
373*7c478bd9Sstevel@tonic-gate 	cyc_handler_t hdlr;
374*7c478bd9Sstevel@tonic-gate 	cyc_time_t when;
375*7c478bd9Sstevel@tonic-gate 
376*7c478bd9Sstevel@tonic-gate 	ASSERT(prof->prof_interval != 0);
377*7c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&cpu_lock));
378*7c478bd9Sstevel@tonic-gate 
379*7c478bd9Sstevel@tonic-gate 	if (prof->prof_kind == PROF_TICK) {
380*7c478bd9Sstevel@tonic-gate 		hdlr.cyh_func = profile_tick;
381*7c478bd9Sstevel@tonic-gate 		hdlr.cyh_arg = prof;
382*7c478bd9Sstevel@tonic-gate 		hdlr.cyh_level = CY_HIGH_LEVEL;
383*7c478bd9Sstevel@tonic-gate 
384*7c478bd9Sstevel@tonic-gate 		when.cyt_interval = prof->prof_interval;
385*7c478bd9Sstevel@tonic-gate 		when.cyt_when = dtrace_gethrtime() + when.cyt_interval;
386*7c478bd9Sstevel@tonic-gate 	} else {
387*7c478bd9Sstevel@tonic-gate 		ASSERT(prof->prof_kind == PROF_PROFILE);
388*7c478bd9Sstevel@tonic-gate 		omni.cyo_online = profile_online;
389*7c478bd9Sstevel@tonic-gate 		omni.cyo_offline = profile_offline;
390*7c478bd9Sstevel@tonic-gate 		omni.cyo_arg = prof;
391*7c478bd9Sstevel@tonic-gate 	}
392*7c478bd9Sstevel@tonic-gate 
393*7c478bd9Sstevel@tonic-gate 	if (prof->prof_kind == PROF_TICK) {
394*7c478bd9Sstevel@tonic-gate 		prof->prof_cyclic = cyclic_add(&hdlr, &when);
395*7c478bd9Sstevel@tonic-gate 	} else {
396*7c478bd9Sstevel@tonic-gate 		prof->prof_cyclic = cyclic_add_omni(&omni);
397*7c478bd9Sstevel@tonic-gate 	}
398*7c478bd9Sstevel@tonic-gate }
399*7c478bd9Sstevel@tonic-gate 
400*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
401*7c478bd9Sstevel@tonic-gate static void
402*7c478bd9Sstevel@tonic-gate profile_disable(void *arg, dtrace_id_t id, void *parg)
403*7c478bd9Sstevel@tonic-gate {
404*7c478bd9Sstevel@tonic-gate 	profile_probe_t *prof = parg;
405*7c478bd9Sstevel@tonic-gate 
406*7c478bd9Sstevel@tonic-gate 	ASSERT(prof->prof_cyclic != CYCLIC_NONE);
407*7c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&cpu_lock));
408*7c478bd9Sstevel@tonic-gate 
409*7c478bd9Sstevel@tonic-gate 	cyclic_remove(prof->prof_cyclic);
410*7c478bd9Sstevel@tonic-gate 	prof->prof_cyclic = CYCLIC_NONE;
411*7c478bd9Sstevel@tonic-gate }
412*7c478bd9Sstevel@tonic-gate 
413*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
414*7c478bd9Sstevel@tonic-gate static int
415*7c478bd9Sstevel@tonic-gate profile_usermode(void *arg, dtrace_id_t id, void *parg)
416*7c478bd9Sstevel@tonic-gate {
417*7c478bd9Sstevel@tonic-gate 	return (CPU->cpu_profile_pc == 0);
418*7c478bd9Sstevel@tonic-gate }
419*7c478bd9Sstevel@tonic-gate 
420*7c478bd9Sstevel@tonic-gate static dtrace_pattr_t profile_attr = {
421*7c478bd9Sstevel@tonic-gate { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
422*7c478bd9Sstevel@tonic-gate { DTRACE_STABILITY_UNSTABLE, DTRACE_STABILITY_UNSTABLE, DTRACE_CLASS_UNKNOWN },
423*7c478bd9Sstevel@tonic-gate { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
424*7c478bd9Sstevel@tonic-gate { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
425*7c478bd9Sstevel@tonic-gate { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
426*7c478bd9Sstevel@tonic-gate };
427*7c478bd9Sstevel@tonic-gate 
428*7c478bd9Sstevel@tonic-gate static dtrace_pops_t profile_pops = {
429*7c478bd9Sstevel@tonic-gate 	profile_provide,
430*7c478bd9Sstevel@tonic-gate 	NULL,
431*7c478bd9Sstevel@tonic-gate 	profile_enable,
432*7c478bd9Sstevel@tonic-gate 	profile_disable,
433*7c478bd9Sstevel@tonic-gate 	NULL,
434*7c478bd9Sstevel@tonic-gate 	NULL,
435*7c478bd9Sstevel@tonic-gate 	NULL,
436*7c478bd9Sstevel@tonic-gate 	NULL,
437*7c478bd9Sstevel@tonic-gate 	profile_usermode,
438*7c478bd9Sstevel@tonic-gate 	profile_destroy
439*7c478bd9Sstevel@tonic-gate };
440*7c478bd9Sstevel@tonic-gate 
441*7c478bd9Sstevel@tonic-gate static int
442*7c478bd9Sstevel@tonic-gate profile_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
443*7c478bd9Sstevel@tonic-gate {
444*7c478bd9Sstevel@tonic-gate 	switch (cmd) {
445*7c478bd9Sstevel@tonic-gate 	case DDI_ATTACH:
446*7c478bd9Sstevel@tonic-gate 		break;
447*7c478bd9Sstevel@tonic-gate 	case DDI_RESUME:
448*7c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
449*7c478bd9Sstevel@tonic-gate 	default:
450*7c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
451*7c478bd9Sstevel@tonic-gate 	}
452*7c478bd9Sstevel@tonic-gate 
453*7c478bd9Sstevel@tonic-gate 	if (ddi_create_minor_node(devi, "profile", S_IFCHR, 0,
454*7c478bd9Sstevel@tonic-gate 	    DDI_PSEUDO, NULL) == DDI_FAILURE ||
455*7c478bd9Sstevel@tonic-gate 	    dtrace_register("profile", &profile_attr,
456*7c478bd9Sstevel@tonic-gate 	    DTRACE_PRIV_KERNEL | DTRACE_PRIV_USER, 0,
457*7c478bd9Sstevel@tonic-gate 	    &profile_pops, NULL, &profile_id) != 0) {
458*7c478bd9Sstevel@tonic-gate 		ddi_remove_minor_node(devi, NULL);
459*7c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
460*7c478bd9Sstevel@tonic-gate 	}
461*7c478bd9Sstevel@tonic-gate 
462*7c478bd9Sstevel@tonic-gate 	profile_max = ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
463*7c478bd9Sstevel@tonic-gate 	    "profile-max-probes", PROFILE_MAX_DEFAULT);
464*7c478bd9Sstevel@tonic-gate 
465*7c478bd9Sstevel@tonic-gate 	ddi_report_dev(devi);
466*7c478bd9Sstevel@tonic-gate 	profile_devi = devi;
467*7c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
468*7c478bd9Sstevel@tonic-gate }
469*7c478bd9Sstevel@tonic-gate 
470*7c478bd9Sstevel@tonic-gate static int
471*7c478bd9Sstevel@tonic-gate profile_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
472*7c478bd9Sstevel@tonic-gate {
473*7c478bd9Sstevel@tonic-gate 	switch (cmd) {
474*7c478bd9Sstevel@tonic-gate 	case DDI_DETACH:
475*7c478bd9Sstevel@tonic-gate 		break;
476*7c478bd9Sstevel@tonic-gate 	case DDI_SUSPEND:
477*7c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
478*7c478bd9Sstevel@tonic-gate 	default:
479*7c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
480*7c478bd9Sstevel@tonic-gate 	}
481*7c478bd9Sstevel@tonic-gate 
482*7c478bd9Sstevel@tonic-gate 	if (dtrace_unregister(profile_id) != 0)
483*7c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
484*7c478bd9Sstevel@tonic-gate 
485*7c478bd9Sstevel@tonic-gate 	ddi_remove_minor_node(devi, NULL);
486*7c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
487*7c478bd9Sstevel@tonic-gate }
488*7c478bd9Sstevel@tonic-gate 
489*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
490*7c478bd9Sstevel@tonic-gate static int
491*7c478bd9Sstevel@tonic-gate profile_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
492*7c478bd9Sstevel@tonic-gate {
493*7c478bd9Sstevel@tonic-gate 	int error;
494*7c478bd9Sstevel@tonic-gate 
495*7c478bd9Sstevel@tonic-gate 	switch (infocmd) {
496*7c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
497*7c478bd9Sstevel@tonic-gate 		*result = (void *)profile_devi;
498*7c478bd9Sstevel@tonic-gate 		error = DDI_SUCCESS;
499*7c478bd9Sstevel@tonic-gate 		break;
500*7c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
501*7c478bd9Sstevel@tonic-gate 		*result = (void *)0;
502*7c478bd9Sstevel@tonic-gate 		error = DDI_SUCCESS;
503*7c478bd9Sstevel@tonic-gate 		break;
504*7c478bd9Sstevel@tonic-gate 	default:
505*7c478bd9Sstevel@tonic-gate 		error = DDI_FAILURE;
506*7c478bd9Sstevel@tonic-gate 	}
507*7c478bd9Sstevel@tonic-gate 	return (error);
508*7c478bd9Sstevel@tonic-gate }
509*7c478bd9Sstevel@tonic-gate 
510*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
511*7c478bd9Sstevel@tonic-gate static int
512*7c478bd9Sstevel@tonic-gate profile_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
513*7c478bd9Sstevel@tonic-gate {
514*7c478bd9Sstevel@tonic-gate 	return (0);
515*7c478bd9Sstevel@tonic-gate }
516*7c478bd9Sstevel@tonic-gate 
517*7c478bd9Sstevel@tonic-gate static struct cb_ops profile_cb_ops = {
518*7c478bd9Sstevel@tonic-gate 	profile_open,		/* open */
519*7c478bd9Sstevel@tonic-gate 	nodev,			/* close */
520*7c478bd9Sstevel@tonic-gate 	nulldev,		/* strategy */
521*7c478bd9Sstevel@tonic-gate 	nulldev,		/* print */
522*7c478bd9Sstevel@tonic-gate 	nodev,			/* dump */
523*7c478bd9Sstevel@tonic-gate 	nodev,			/* read */
524*7c478bd9Sstevel@tonic-gate 	nodev,			/* write */
525*7c478bd9Sstevel@tonic-gate 	nodev,			/* ioctl */
526*7c478bd9Sstevel@tonic-gate 	nodev,			/* devmap */
527*7c478bd9Sstevel@tonic-gate 	nodev,			/* mmap */
528*7c478bd9Sstevel@tonic-gate 	nodev,			/* segmap */
529*7c478bd9Sstevel@tonic-gate 	nochpoll,		/* poll */
530*7c478bd9Sstevel@tonic-gate 	ddi_prop_op,		/* cb_prop_op */
531*7c478bd9Sstevel@tonic-gate 	0,			/* streamtab  */
532*7c478bd9Sstevel@tonic-gate 	D_NEW | D_MP		/* Driver compatibility flag */
533*7c478bd9Sstevel@tonic-gate };
534*7c478bd9Sstevel@tonic-gate 
535*7c478bd9Sstevel@tonic-gate static struct dev_ops profile_ops = {
536*7c478bd9Sstevel@tonic-gate 	DEVO_REV,		/* devo_rev, */
537*7c478bd9Sstevel@tonic-gate 	0,			/* refcnt  */
538*7c478bd9Sstevel@tonic-gate 	profile_info,		/* get_dev_info */
539*7c478bd9Sstevel@tonic-gate 	nulldev,		/* identify */
540*7c478bd9Sstevel@tonic-gate 	nulldev,		/* probe */
541*7c478bd9Sstevel@tonic-gate 	profile_attach,		/* attach */
542*7c478bd9Sstevel@tonic-gate 	profile_detach,		/* detach */
543*7c478bd9Sstevel@tonic-gate 	nodev,			/* reset */
544*7c478bd9Sstevel@tonic-gate 	&profile_cb_ops,	/* driver operations */
545*7c478bd9Sstevel@tonic-gate 	NULL,			/* bus operations */
546*7c478bd9Sstevel@tonic-gate 	nodev			/* dev power */
547*7c478bd9Sstevel@tonic-gate };
548*7c478bd9Sstevel@tonic-gate 
549*7c478bd9Sstevel@tonic-gate /*
550*7c478bd9Sstevel@tonic-gate  * Module linkage information for the kernel.
551*7c478bd9Sstevel@tonic-gate  */
552*7c478bd9Sstevel@tonic-gate static struct modldrv modldrv = {
553*7c478bd9Sstevel@tonic-gate 	&mod_driverops,		/* module type (this is a pseudo driver) */
554*7c478bd9Sstevel@tonic-gate 	"Profile Interrupt Tracing",	/* name of module */
555*7c478bd9Sstevel@tonic-gate 	&profile_ops,		/* driver ops */
556*7c478bd9Sstevel@tonic-gate };
557*7c478bd9Sstevel@tonic-gate 
558*7c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
559*7c478bd9Sstevel@tonic-gate 	MODREV_1,
560*7c478bd9Sstevel@tonic-gate 	(void *)&modldrv,
561*7c478bd9Sstevel@tonic-gate 	NULL
562*7c478bd9Sstevel@tonic-gate };
563*7c478bd9Sstevel@tonic-gate 
564*7c478bd9Sstevel@tonic-gate int
565*7c478bd9Sstevel@tonic-gate _init(void)
566*7c478bd9Sstevel@tonic-gate {
567*7c478bd9Sstevel@tonic-gate 	return (mod_install(&modlinkage));
568*7c478bd9Sstevel@tonic-gate }
569*7c478bd9Sstevel@tonic-gate 
570*7c478bd9Sstevel@tonic-gate int
571*7c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
572*7c478bd9Sstevel@tonic-gate {
573*7c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
574*7c478bd9Sstevel@tonic-gate }
575*7c478bd9Sstevel@tonic-gate 
576*7c478bd9Sstevel@tonic-gate int
577*7c478bd9Sstevel@tonic-gate _fini(void)
578*7c478bd9Sstevel@tonic-gate {
579*7c478bd9Sstevel@tonic-gate 	return (mod_remove(&modlinkage));
580*7c478bd9Sstevel@tonic-gate }
581