2 * Copyright (c) 2018 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
28 #include <sys/dtrace.h>
29 #include <sys/dtrace_impl.h>
30 #include <kern/lock_group.h>
31 #include <kern/lock_stat.h>
38 #define SPIN_HELD_PREFIX "spin-held-"
39 #define SPIN_MISS_PREFIX "spin-miss-"
40 #define SPIN_SPIN_PREFIX "spin-spin-"
42 #define LOCKGROUPSTAT_AFRAMES 1
43 #define LOCKGROUPSTAT_LEN 64
45 static dtrace_provider_id_t lockprof_id
;
47 decl_lck_mtx_data(extern, lck_grp_lock
)
48 extern queue_head_t lck_grp_queue
;
49 extern unsigned int lck_grp_cnt
;
51 #define LOCKPROF_MAX 10000 /* maximum number of lockprof probes */
52 static uint32_t lockprof_count
; /* current number of lockprof probes */
59 {SPIN_HELD
, SPIN_HELD_PREFIX
, false},
60 {SPIN_MISS
, SPIN_MISS_PREFIX
, false},
61 {SPIN_SPIN
, SPIN_SPIN_PREFIX
, true},
65 const static int hold_defaults
[] = {
74 {100, "ms", NANOSEC
/ MILLISEC
}
77 typedef struct lockprof_probe
{
79 dtrace_id_t lockprof_id
;
80 uint64_t lockprof_limit
;
81 lck_grp_t
*lockprof_grp
;
85 lockprof_invoke(lck_grp_t
*grp
, lck_grp_stat_t
*stat
, uint64_t val
)
87 dtrace_probe(stat
->lgs_probeid
, (uintptr_t)grp
, val
, 0, 0, 0);
91 probe_create(int kind
, const char *suffix
, const char *grp_name
, uint64_t count
, uint64_t mult
)
93 char name
[LOCKGROUPSTAT_LEN
];
94 lck_mtx_lock(&lck_grp_lock
);
95 lck_grp_t
*grp
= (lck_grp_t
*)queue_first(&lck_grp_queue
);
96 uint64_t limit
= count
* mult
;
98 if (events
[kind
].time_event
) {
99 nanoseconds_to_absolutetime(limit
, &limit
);
102 for (unsigned int i
= 0; i
< lck_grp_cnt
; i
++, grp
= (lck_grp_t
*)queue_next((queue_entry_t
)grp
)) {
103 if (!grp_name
|| grp_name
[0] == '\0' || strcmp(grp_name
, grp
->lck_grp_name
) == 0) {
104 snprintf(name
, sizeof(name
), "%s%llu%s", events
[kind
].prefix
, count
, suffix
?: "");
106 if (dtrace_probe_lookup(lockprof_id
, grp
->lck_grp_name
, NULL
, name
) != 0) {
109 if (lockprof_count
>= LOCKPROF_MAX
) {
113 lockprof_probe_t
*probe
= kmem_zalloc(sizeof(lockprof_probe_t
), KM_SLEEP
);
114 probe
->lockprof_kind
= kind
;
115 probe
->lockprof_limit
= limit
;
116 probe
->lockprof_grp
= grp
;
118 probe
->lockprof_id
= dtrace_probe_create(lockprof_id
, grp
->lck_grp_name
, NULL
, name
,
119 LOCKGROUPSTAT_AFRAMES
, probe
);
124 lck_mtx_unlock(&lck_grp_lock
);
128 lockprof_provide(void *arg
, const dtrace_probedesc_t
*desc
)
131 size_t event_id
, i
, len
;
134 for (i
= 0; i
< sizeof(hold_defaults
) / sizeof(hold_defaults
[0]); i
++) {
135 probe_create(SPIN_HELD
, NULL
, NULL
, hold_defaults
[i
], 1);
136 probe_create(SPIN_MISS
, NULL
, NULL
, hold_defaults
[i
], 1);
138 for (i
= 0; i
< sizeof(cont_defaults
) / sizeof(cont_defaults
[0]); i
++) {
139 probe_create(SPIN_SPIN
, cont_defaults
[i
].suffix
, NULL
, cont_defaults
[i
].time
, cont_defaults
[i
].mult
);
144 const char *name
, *suffix
= NULL
;
145 hrtime_t val
= 0, mult
= 1;
151 { "us", NANOSEC
/ MICROSEC
},
152 { "usec", NANOSEC
/ MICROSEC
},
153 { "ms", NANOSEC
/ MILLISEC
},
154 { "msec", NANOSEC
/ MILLISEC
},
155 { "s", NANOSEC
/ SEC
},
156 { "sec", NANOSEC
/ SEC
},
160 name
= desc
->dtpd_name
;
162 for (event_id
= 0; events
[event_id
].prefix
!= NULL
; event_id
++) {
163 len
= strlen(events
[event_id
].prefix
);
165 if (strncmp(name
, events
[event_id
].prefix
, len
) != 0) {
171 if (events
[event_id
].prefix
== NULL
) {
177 * We need to start before any time suffix.
179 for (i
= strlen(name
); i
>= len
; i
--) {
180 if (name
[i
] >= '0' && name
[i
] <= '9') {
187 * Now determine the numerical value present in the probe name.
189 for (uint64_t m
= 1; i
>= len
; i
--) {
190 if (name
[i
] < '0' || name
[i
] > '9') {
194 val
+= (name
[i
] - '0') * m
;
202 if (events
[event_id
].time_event
) {
203 for (i
= 0, mult
= 0; suffixes
[i
].name
!= NULL
; i
++) {
204 if (strncasecmp(suffixes
[i
].name
, suffix
, strlen(suffixes
[i
].name
) + 1) == 0) {
205 mult
= suffixes
[i
].mult
;
209 if (suffixes
[i
].name
== NULL
) {
212 } else if (*suffix
!= '\0') {
216 probe_create(events
[event_id
].kind
, suffix
, desc
->dtpd_mod
, val
, mult
);
220 static lck_grp_stat_t
*
221 lockprof_stat(lck_grp_t
*grp
, int kind
)
225 return &grp
->lck_grp_stats
.lgss_spin_held
;
227 return &grp
->lck_grp_stats
.lgss_spin_miss
;
229 return &grp
->lck_grp_stats
.lgss_spin_spin
;
236 lockprof_enable(void *arg
, dtrace_id_t id
, void *parg
)
238 #pragma unused(arg, id, parg)
239 lockprof_probe_t
*probe
= (lockprof_probe_t
*)parg
;
240 lck_grp_t
*grp
= probe
->lockprof_grp
;
241 lck_grp_stat_t
*stat
;
247 if ((stat
= lockprof_stat(grp
, probe
->lockprof_kind
)) == NULL
) {
252 * lockprof_enable/disable are called with
255 if (stat
->lgs_limit
!= 0) {
259 stat
->lgs_limit
= probe
->lockprof_limit
;
260 stat
->lgs_enablings
++;
261 stat
->lgs_probeid
= probe
->lockprof_id
;
267 lockprof_disable(void *arg
, dtrace_id_t id
, void *parg
)
269 #pragma unused(arg, id)
270 lockprof_probe_t
*probe
= (lockprof_probe_t
*)parg
;
271 lck_grp_t
*grp
= probe
->lockprof_grp
;
272 lck_grp_stat_t
*stat
;
278 if ((stat
= lockprof_stat(grp
, probe
->lockprof_kind
)) == NULL
) {
282 if (stat
->lgs_limit
== 0 || stat
->lgs_enablings
== 0) {
287 stat
->lgs_enablings
--;
288 stat
->lgs_probeid
= 0;
292 lockprof_destroy(void *arg
, dtrace_id_t id
, void *parg
)
294 #pragma unused(arg, id)
295 lockprof_probe_t
*probe
= (lockprof_probe_t
*)parg
;
296 kmem_free(probe
, sizeof(lockprof_probe_t
));
301 lockprof_getargdesc(void *arg
, dtrace_id_t id
, void *parg
, dtrace_argdesc_t
*desc
)
303 #pragma unused(arg, id, parg)
304 const char *argdesc
= NULL
;
305 switch (desc
->dtargd_ndx
) {
307 argdesc
= "lck_grp_t*";
310 argdesc
= "uint64_t";
315 strlcpy(desc
->dtargd_native
, argdesc
, DTRACE_ARGTYPELEN
);
317 desc
->dtargd_ndx
= DTRACE_ARGNONE
;
320 static dtrace_pattr_t lockprof_attr
= {
321 { DTRACE_STABILITY_EVOLVING
, DTRACE_STABILITY_EVOLVING
, DTRACE_CLASS_COMMON
},
322 { DTRACE_STABILITY_UNSTABLE
, DTRACE_STABILITY_UNSTABLE
, DTRACE_CLASS_UNKNOWN
},
323 { DTRACE_STABILITY_PRIVATE
, DTRACE_STABILITY_PRIVATE
, DTRACE_CLASS_UNKNOWN
},
324 { DTRACE_STABILITY_EVOLVING
, DTRACE_STABILITY_EVOLVING
, DTRACE_CLASS_COMMON
},
325 { DTRACE_STABILITY_EVOLVING
, DTRACE_STABILITY_EVOLVING
, DTRACE_CLASS_COMMON
},
328 static dtrace_pops_t lockprof_pops
= {
329 .dtps_provide
= lockprof_provide
,
330 .dtps_provide_module
= NULL
,
331 .dtps_enable
= lockprof_enable
,
332 .dtps_disable
= lockprof_disable
,
333 .dtps_suspend
= NULL
,
335 .dtps_getargdesc
= lockprof_getargdesc
,
336 .dtps_getargval
= NULL
,
337 .dtps_usermode
= NULL
,
338 .dtps_destroy
= lockprof_destroy
340 #endif /* LOCK_STATS */
341 void lockprof_init(void);
346 dtrace_register("lockprof", &lockprof_attr
,
347 DTRACE_PRIV_KERNEL
, NULL
,
348 &lockprof_pops
, NULL
, &lockprof_id
);
349 #endif /* LOCK_STATS */