2 * Copyright (c) 2019 Apple 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>
39 #define SPIN_HELD_PREFIX "spin-held-"
40 #define SPIN_MISS_PREFIX "spin-miss-"
41 #define SPIN_SPIN_PREFIX "spin-spin-"
43 #define LOCKGROUPSTAT_AFRAMES 1
44 #define LOCKGROUPSTAT_LEN 64
46 static dtrace_provider_id_t lockprof_id
;
48 decl_lck_mtx_data(extern, lck_grp_lock
);
49 extern queue_head_t lck_grp_queue
;
50 extern unsigned int lck_grp_cnt
;
52 #define LOCKPROF_MAX 10000 /* maximum number of lockprof probes */
53 static uint32_t lockprof_count
; /* current number of lockprof probes */
60 {SPIN_HELD
, SPIN_HELD_PREFIX
, false},
61 {SPIN_MISS
, SPIN_MISS_PREFIX
, false},
62 {SPIN_SPIN
, SPIN_SPIN_PREFIX
, true},
66 const static int hold_defaults
[] = {
75 {100, "ms", NANOSEC
/ MILLISEC
}
78 typedef struct lockprof_probe
{
80 dtrace_id_t lockprof_id
;
81 uint64_t lockprof_limit
;
82 lck_grp_t
*lockprof_grp
;
86 lockprof_invoke(lck_grp_t
*grp
, lck_grp_stat_t
*stat
, uint64_t val
)
88 dtrace_probe(stat
->lgs_probeid
, (uintptr_t)grp
, val
, 0, 0, 0);
92 probe_create(int kind
, const char *suffix
, const char *grp_name
, uint64_t count
, uint64_t mult
)
94 char name
[LOCKGROUPSTAT_LEN
];
95 lck_mtx_lock(&lck_grp_lock
);
96 lck_grp_t
*grp
= (lck_grp_t
*)queue_first(&lck_grp_queue
);
97 uint64_t limit
= count
* mult
;
99 if (events
[kind
].time_event
) {
100 nanoseconds_to_absolutetime(limit
, &limit
);
103 for (unsigned int i
= 0; i
< lck_grp_cnt
; i
++, grp
= (lck_grp_t
*)queue_next((queue_entry_t
)grp
)) {
104 if (!grp_name
|| grp_name
[0] == '\0' || strcmp(grp_name
, grp
->lck_grp_name
) == 0) {
105 snprintf(name
, sizeof(name
), "%s%llu%s", events
[kind
].prefix
, count
, suffix
?: "");
107 if (dtrace_probe_lookup(lockprof_id
, grp
->lck_grp_name
, NULL
, name
) != 0) {
110 if (lockprof_count
>= LOCKPROF_MAX
) {
114 lockprof_probe_t
*probe
= kmem_zalloc(sizeof(lockprof_probe_t
), KM_SLEEP
);
115 probe
->lockprof_kind
= kind
;
116 probe
->lockprof_limit
= limit
;
117 probe
->lockprof_grp
= grp
;
119 probe
->lockprof_id
= dtrace_probe_create(lockprof_id
, grp
->lck_grp_name
, NULL
, name
,
120 LOCKGROUPSTAT_AFRAMES
, probe
);
125 lck_mtx_unlock(&lck_grp_lock
);
129 lockprof_provide(void *arg
, const dtrace_probedesc_t
*desc
)
132 size_t event_id
, i
, len
;
135 for (i
= 0; i
< sizeof(hold_defaults
) / sizeof(hold_defaults
[0]); i
++) {
136 probe_create(SPIN_HELD
, NULL
, NULL
, hold_defaults
[i
], 1);
137 probe_create(SPIN_MISS
, NULL
, NULL
, hold_defaults
[i
], 1);
139 for (i
= 0; i
< sizeof(cont_defaults
) / sizeof(cont_defaults
[0]); i
++) {
140 probe_create(SPIN_SPIN
, cont_defaults
[i
].suffix
, NULL
, cont_defaults
[i
].time
, cont_defaults
[i
].mult
);
145 const char *name
, *suffix
= NULL
;
146 hrtime_t val
= 0, mult
= 1;
152 { "us", NANOSEC
/ MICROSEC
},
153 { "usec", NANOSEC
/ MICROSEC
},
154 { "ms", NANOSEC
/ MILLISEC
},
155 { "msec", NANOSEC
/ MILLISEC
},
156 { "s", NANOSEC
/ SEC
},
157 { "sec", NANOSEC
/ SEC
},
161 name
= desc
->dtpd_name
;
163 for (event_id
= 0; events
[event_id
].prefix
!= NULL
; event_id
++) {
164 len
= strlen(events
[event_id
].prefix
);
166 if (strncmp(name
, events
[event_id
].prefix
, len
) != 0) {
172 if (events
[event_id
].prefix
== NULL
) {
178 * We need to start before any time suffix.
180 for (i
= strlen(name
); i
>= len
; i
--) {
181 if (name
[i
] >= '0' && name
[i
] <= '9') {
188 * Now determine the numerical value present in the probe name.
190 for (uint64_t m
= 1; i
>= len
; i
--) {
191 if (name
[i
] < '0' || name
[i
] > '9') {
195 val
+= (name
[i
] - '0') * m
;
203 if (events
[event_id
].time_event
) {
204 for (i
= 0, mult
= 0; suffixes
[i
].name
!= NULL
; i
++) {
205 if (strncasecmp(suffixes
[i
].name
, suffix
, strlen(suffixes
[i
].name
) + 1) == 0) {
206 mult
= suffixes
[i
].mult
;
210 if (suffixes
[i
].name
== NULL
) {
213 } else if (*suffix
!= '\0') {
217 probe_create(events
[event_id
].kind
, suffix
, desc
->dtpd_mod
, val
, mult
);
221 static lck_grp_stat_t
*
222 lockprof_stat(lck_grp_t
*grp
, int kind
)
226 return &grp
->lck_grp_stats
.lgss_spin_held
;
228 return &grp
->lck_grp_stats
.lgss_spin_miss
;
230 return &grp
->lck_grp_stats
.lgss_spin_spin
;
237 lockprof_enable(void *arg
, dtrace_id_t id
, void *parg
)
239 #pragma unused(arg, id, parg)
240 lockprof_probe_t
*probe
= (lockprof_probe_t
*)parg
;
241 lck_grp_t
*grp
= probe
->lockprof_grp
;
242 lck_grp_stat_t
*stat
;
248 if ((stat
= lockprof_stat(grp
, probe
->lockprof_kind
)) == NULL
) {
253 * lockprof_enable/disable are called with
256 if (stat
->lgs_limit
!= 0) {
260 stat
->lgs_limit
= probe
->lockprof_limit
;
261 stat
->lgs_enablings
++;
262 stat
->lgs_probeid
= probe
->lockprof_id
;
268 lockprof_disable(void *arg
, dtrace_id_t id
, void *parg
)
270 #pragma unused(arg, id)
271 lockprof_probe_t
*probe
= (lockprof_probe_t
*)parg
;
272 lck_grp_t
*grp
= probe
->lockprof_grp
;
273 lck_grp_stat_t
*stat
;
279 if ((stat
= lockprof_stat(grp
, probe
->lockprof_kind
)) == NULL
) {
283 if (stat
->lgs_limit
== 0 || stat
->lgs_enablings
== 0) {
288 stat
->lgs_enablings
--;
289 stat
->lgs_probeid
= 0;
293 lockprof_destroy(void *arg
, dtrace_id_t id
, void *parg
)
295 #pragma unused(arg, id)
296 lockprof_probe_t
*probe
= (lockprof_probe_t
*)parg
;
297 kmem_free(probe
, sizeof(lockprof_probe_t
));
302 lockprof_getargdesc(void *arg
, dtrace_id_t id
, void *parg
, dtrace_argdesc_t
*desc
)
304 #pragma unused(arg, id, parg)
305 const char *argdesc
= NULL
;
306 switch (desc
->dtargd_ndx
) {
308 argdesc
= "lck_grp_t*";
311 argdesc
= "uint64_t";
316 strlcpy(desc
->dtargd_native
, argdesc
, DTRACE_ARGTYPELEN
);
318 desc
->dtargd_ndx
= DTRACE_ARGNONE
;
321 static dtrace_pattr_t lockprof_attr
= {
322 { DTRACE_STABILITY_EVOLVING
, DTRACE_STABILITY_EVOLVING
, DTRACE_CLASS_COMMON
},
323 { DTRACE_STABILITY_UNSTABLE
, DTRACE_STABILITY_UNSTABLE
, DTRACE_CLASS_UNKNOWN
},
324 { DTRACE_STABILITY_PRIVATE
, DTRACE_STABILITY_PRIVATE
, DTRACE_CLASS_UNKNOWN
},
325 { DTRACE_STABILITY_EVOLVING
, DTRACE_STABILITY_EVOLVING
, DTRACE_CLASS_COMMON
},
326 { DTRACE_STABILITY_EVOLVING
, DTRACE_STABILITY_EVOLVING
, DTRACE_CLASS_COMMON
},
329 static dtrace_pops_t lockprof_pops
= {
330 .dtps_provide
= lockprof_provide
,
331 .dtps_provide_module
= NULL
,
332 .dtps_enable
= lockprof_enable
,
333 .dtps_disable
= lockprof_disable
,
334 .dtps_suspend
= NULL
,
336 .dtps_getargdesc
= lockprof_getargdesc
,
337 .dtps_getargval
= NULL
,
338 .dtps_usermode
= NULL
,
339 .dtps_destroy
= lockprof_destroy
341 #endif /* LOCK_STATS */
342 void lockprof_init(void);
347 dtrace_register("lockprof", &lockprof_attr
,
348 DTRACE_PRIV_KERNEL
, NULL
,
349 &lockprof_pops
, NULL
, &lockprof_id
);
350 #endif /* LOCK_STATS */