1 /* $NetBSD: tprof.c,v 1.5 2009/03/11 13:48:47 yamt Exp $ */
4 * Copyright (c)2008,2009 YAMAMOTO Takashi,
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: tprof.c,v 1.5 2009/03/11 13:48:47 yamt Exp $");
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/kernel.h>
38 #include <sys/callout.h>
40 #include <sys/module.h>
41 #include <sys/workqueue.h>
42 #include <sys/queue.h>
44 #include <dev/tprof/tprof.h>
45 #include <dev/tprof/tprof_ioctl.h>
49 * tprof_reader_lock -> tprof_lock
50 * tprof_startstop_lock -> tprof_lock
56 * R: tprof_reader_lock
57 * S: tprof_startstop_lock
61 uintptr_t s_pc
; /* program counter */
64 typedef struct tprof_buf
{
69 STAILQ_ENTRY(tprof_buf
) b_list
;
70 tprof_sample_t b_data
[];
72 #define TPROF_BUF_BYTESIZE(sz) \
73 (sizeof(tprof_buf_t) + (sz) * sizeof(tprof_sample_t))
74 #define TPROF_MAX_SAMPLES_PER_BUF 10000
76 #define TPROF_MAX_BUF 100
82 } __aligned(CACHE_LINE_SIZE
) tprof_cpu_t
;
84 typedef struct tprof_backend
{
86 const tprof_backend_ops_t
*tb_ops
;
87 LIST_ENTRY(tprof_backend
) tb_list
;
88 int tb_usecount
; /* S: */
91 static kmutex_t tprof_lock
;
92 static bool tprof_running
;
93 static u_int tprof_nworker
; /* L: # of running worker LWPs */
94 static lwp_t
*tprof_owner
;
95 static STAILQ_HEAD(, tprof_buf
) tprof_list
; /* L: global buffer list */
96 static u_int tprof_nbuf_on_list
; /* L: # of buffers on tprof_list */
97 static struct workqueue
*tprof_wq
;
98 static tprof_cpu_t tprof_cpus
[MAXCPUS
] __aligned(CACHE_LINE_SIZE
);
99 static u_int tprof_samples_per_buf
;
101 static tprof_backend_t
*tprof_backend
; /* S: */
102 static LIST_HEAD(, tprof_backend
) tprof_backends
=
103 LIST_HEAD_INITIALIZER(tprof_backend
); /* S: */
105 static kmutex_t tprof_reader_lock
;
106 static kcondvar_t tprof_reader_cv
; /* L: */
107 static off_t tprof_reader_offset
; /* R: */
109 static kmutex_t tprof_startstop_lock
;
110 static kcondvar_t tprof_cv
; /* L: */
112 static struct tprof_stat tprof_stat
; /* L: */
115 tprof_cpu(struct cpu_info
*ci
)
118 return &tprof_cpus
[cpu_index(ci
)];
125 return tprof_cpu(curcpu());
129 tprof_buf_alloc(void)
132 u_int size
= tprof_samples_per_buf
;
134 new = kmem_alloc(TPROF_BUF_BYTESIZE(size
), KM_SLEEP
);
142 tprof_buf_free(tprof_buf_t
*buf
)
145 kmem_free(buf
, TPROF_BUF_BYTESIZE(buf
->b_size
));
149 tprof_buf_switch(tprof_cpu_t
*c
, tprof_buf_t
*new)
159 tprof_buf_refresh(void)
161 tprof_cpu_t
* const c
= tprof_curcpu();
164 new = tprof_buf_alloc();
165 return tprof_buf_switch(c
, new);
169 tprof_worker(struct work
*wk
, void *dummy
)
171 tprof_cpu_t
* const c
= tprof_curcpu();
175 KASSERT(wk
== &c
->c_work
);
176 KASSERT(dummy
== NULL
);
179 * get a per cpu buffer.
181 buf
= tprof_buf_refresh();
184 * and put it on the global list for read(2).
186 mutex_enter(&tprof_lock
);
187 shouldstop
= !tprof_running
;
189 KASSERT(tprof_nworker
> 0);
191 cv_broadcast(&tprof_cv
);
192 cv_broadcast(&tprof_reader_cv
);
194 if (buf
->b_used
== 0) {
195 tprof_stat
.ts_emptybuf
++;
196 } else if (tprof_nbuf_on_list
< TPROF_MAX_BUF
) {
197 tprof_stat
.ts_sample
+= buf
->b_used
;
198 tprof_stat
.ts_overflow
+= buf
->b_overflow
;
200 STAILQ_INSERT_TAIL(&tprof_list
, buf
, b_list
);
201 tprof_nbuf_on_list
++;
203 cv_broadcast(&tprof_reader_cv
);
205 tprof_stat
.ts_dropbuf_sample
+= buf
->b_used
;
206 tprof_stat
.ts_dropbuf
++;
208 mutex_exit(&tprof_lock
);
213 callout_schedule(&c
->c_callout
, hz
);
220 struct cpu_info
* const ci
= vp
;
221 tprof_cpu_t
* const c
= tprof_cpu(ci
);
223 workqueue_enqueue(tprof_wq
, &c
->c_work
, ci
);
229 CPU_INFO_ITERATOR cii
;
232 KASSERT(mutex_owned(&tprof_startstop_lock
));
233 KASSERT(tprof_nworker
== 0);
235 for (CPU_INFO_FOREACH(cii
, ci
)) {
236 tprof_cpu_t
* const c
= tprof_cpu(ci
);
239 old
= tprof_buf_switch(c
, NULL
);
243 callout_destroy(&c
->c_callout
);
245 workqueue_destroy(tprof_wq
);
249 tprof_start(const struct tprof_param
*param
)
251 CPU_INFO_ITERATOR cii
;
257 KASSERT(mutex_owned(&tprof_startstop_lock
));
268 if (tb
->tb_usecount
> 0) {
274 freq
= tb
->tb_ops
->tbo_estimate_freq();
275 tprof_samples_per_buf
= MIN(freq
* 2, TPROF_MAX_SAMPLES_PER_BUF
);
277 error
= workqueue_create(&tprof_wq
, "tprofmv", tprof_worker
, NULL
,
278 PRI_NONE
, IPL_SOFTCLOCK
, WQ_MPSAFE
| WQ_PERCPU
);
283 for (CPU_INFO_FOREACH(cii
, ci
)) {
284 tprof_cpu_t
* const c
= tprof_cpu(ci
);
288 new = tprof_buf_alloc();
289 old
= tprof_buf_switch(c
, new);
293 callout_init(&c
->c_callout
, CALLOUT_MPSAFE
);
294 callout_setfunc(&c
->c_callout
, tprof_kick
, ci
);
297 error
= tb
->tb_ops
->tbo_start(NULL
);
303 mutex_enter(&tprof_lock
);
304 tprof_running
= true;
305 mutex_exit(&tprof_lock
);
306 for (CPU_INFO_FOREACH(cii
, ci
)) {
307 tprof_cpu_t
* const c
= tprof_cpu(ci
);
309 mutex_enter(&tprof_lock
);
311 mutex_exit(&tprof_lock
);
312 workqueue_enqueue(tprof_wq
, &c
->c_work
, ci
);
321 CPU_INFO_ITERATOR cii
;
325 KASSERT(mutex_owned(&tprof_startstop_lock
));
326 if (!tprof_running
) {
331 KASSERT(tb
->tb_usecount
> 0);
332 tb
->tb_ops
->tbo_stop(NULL
);
335 mutex_enter(&tprof_lock
);
336 tprof_running
= false;
337 cv_broadcast(&tprof_reader_cv
);
338 mutex_exit(&tprof_lock
);
340 for (CPU_INFO_FOREACH(cii
, ci
)) {
341 mutex_enter(&tprof_lock
);
342 while (tprof_nworker
> 0) {
343 cv_wait(&tprof_cv
, &tprof_lock
);
345 mutex_exit(&tprof_lock
);
354 * tprof_clear: drain unread samples.
362 mutex_enter(&tprof_reader_lock
);
363 mutex_enter(&tprof_lock
);
364 while ((buf
= STAILQ_FIRST(&tprof_list
)) != NULL
) {
366 STAILQ_REMOVE_HEAD(&tprof_list
, b_list
);
367 KASSERT(tprof_nbuf_on_list
> 0);
368 tprof_nbuf_on_list
--;
369 mutex_exit(&tprof_lock
);
371 mutex_enter(&tprof_lock
);
374 KASSERT(tprof_nbuf_on_list
== 0);
375 mutex_exit(&tprof_lock
);
376 tprof_reader_offset
= 0;
377 mutex_exit(&tprof_reader_lock
);
379 memset(&tprof_stat
, 0, sizeof(tprof_stat
));
382 static tprof_backend_t
*
383 tprof_backend_lookup(const char *name
)
387 KASSERT(mutex_owned(&tprof_startstop_lock
));
389 LIST_FOREACH(tb
, &tprof_backends
, tb_list
) {
390 if (!strcmp(tb
->tb_name
, name
)) {
397 /* -------------------- backend interfaces */
400 * tprof_sample: record a sample on the per-cpu buffer.
402 * be careful; can be called in NMI context.
403 * we are assuming that curcpu() is safe.
407 tprof_sample(tprof_backend_cookie_t
*cookie
, const tprof_frame_info_t
*tfi
)
409 tprof_cpu_t
* const c
= tprof_curcpu();
410 tprof_buf_t
* const buf
= c
->c_buf
;
411 const uintptr_t pc
= tfi
->tfi_pc
;
415 if (__predict_false(idx
>= buf
->b_size
)) {
419 buf
->b_data
[idx
].s_pc
= pc
;
420 buf
->b_used
= idx
+ 1;
424 * tprof_backend_register:
428 tprof_backend_register(const char *name
, const tprof_backend_ops_t
*ops
,
433 if (vers
!= TPROF_BACKEND_VERSION
) {
437 mutex_enter(&tprof_startstop_lock
);
438 tb
= tprof_backend_lookup(name
);
440 mutex_exit(&tprof_startstop_lock
);
443 #if 1 /* XXX for now */
444 if (!LIST_EMPTY(&tprof_backends
)) {
445 mutex_exit(&tprof_startstop_lock
);
449 tb
= kmem_alloc(sizeof(*tb
), KM_SLEEP
);
453 LIST_INSERT_HEAD(&tprof_backends
, tb
, tb_list
);
454 #if 1 /* XXX for now */
455 if (tprof_backend
== NULL
) {
459 mutex_exit(&tprof_startstop_lock
);
465 * tprof_backend_unregister:
469 tprof_backend_unregister(const char *name
)
473 mutex_enter(&tprof_startstop_lock
);
474 tb
= tprof_backend_lookup(name
);
475 #if defined(DIAGNOSTIC)
477 mutex_exit(&tprof_startstop_lock
);
478 panic("%s: not found '%s'", __func__
, name
);
480 #endif /* defined(DIAGNOSTIC) */
481 if (tb
->tb_usecount
> 0) {
482 mutex_exit(&tprof_startstop_lock
);
485 #if 1 /* XXX for now */
486 if (tprof_backend
== tb
) {
487 tprof_backend
= NULL
;
490 LIST_REMOVE(tb
, tb_list
);
491 mutex_exit(&tprof_startstop_lock
);
493 kmem_free(tb
, sizeof(*tb
));
498 /* -------------------- cdevsw interfaces */
500 void tprofattach(int);
503 tprof_open(dev_t dev
, int flags
, int type
, struct lwp
*l
)
506 if (minor(dev
) != 0) {
509 mutex_enter(&tprof_lock
);
510 if (tprof_owner
!= NULL
) {
511 mutex_exit(&tprof_lock
);
514 tprof_owner
= curlwp
;
515 mutex_exit(&tprof_lock
);
521 tprof_close(dev_t dev
, int flags
, int type
, struct lwp
*l
)
524 KASSERT(minor(dev
) == 0);
526 mutex_enter(&tprof_startstop_lock
);
527 mutex_enter(&tprof_lock
);
529 mutex_exit(&tprof_lock
);
532 mutex_exit(&tprof_startstop_lock
);
538 tprof_read(dev_t dev
, struct uio
*uio
, int flags
)
546 KASSERT(minor(dev
) == 0);
547 mutex_enter(&tprof_reader_lock
);
548 while (uio
->uio_resid
> 0 && error
== 0) {
550 * take the first buffer from the list.
552 mutex_enter(&tprof_lock
);
553 buf
= STAILQ_FIRST(&tprof_list
);
555 if (tprof_nworker
== 0) {
556 mutex_exit(&tprof_lock
);
560 mutex_exit(&tprof_reader_lock
);
561 error
= cv_wait_sig(&tprof_reader_cv
, &tprof_lock
);
562 mutex_exit(&tprof_lock
);
563 mutex_enter(&tprof_reader_lock
);
566 STAILQ_REMOVE_HEAD(&tprof_list
, b_list
);
567 KASSERT(tprof_nbuf_on_list
> 0);
568 tprof_nbuf_on_list
--;
569 mutex_exit(&tprof_lock
);
574 bytes
= MIN(buf
->b_used
* sizeof(tprof_sample_t
) -
575 tprof_reader_offset
, uio
->uio_resid
);
576 resid
= uio
->uio_resid
;
577 error
= uiomove((char *)buf
->b_data
+ tprof_reader_offset
,
579 done
= resid
- uio
->uio_resid
;
580 tprof_reader_offset
+= done
;
583 * if we didn't consume the whole buffer,
584 * put it back to the list.
586 if (tprof_reader_offset
<
587 buf
->b_used
* sizeof(tprof_sample_t
)) {
588 mutex_enter(&tprof_lock
);
589 STAILQ_INSERT_HEAD(&tprof_list
, buf
, b_list
);
590 tprof_nbuf_on_list
++;
591 cv_broadcast(&tprof_reader_cv
);
592 mutex_exit(&tprof_lock
);
595 tprof_reader_offset
= 0;
598 mutex_exit(&tprof_reader_lock
);
604 tprof_ioctl(dev_t dev
, u_long cmd
, void *data
, int flags
, struct lwp
*l
)
606 const struct tprof_param
*param
;
609 KASSERT(minor(dev
) == 0);
612 case TPROF_IOC_GETVERSION
:
613 *(int *)data
= TPROF_VERSION
;
615 case TPROF_IOC_START
:
617 mutex_enter(&tprof_startstop_lock
);
618 error
= tprof_start(param
);
619 mutex_exit(&tprof_startstop_lock
);
622 mutex_enter(&tprof_startstop_lock
);
624 mutex_exit(&tprof_startstop_lock
);
626 case TPROF_IOC_GETSTAT
:
627 mutex_enter(&tprof_lock
);
628 memcpy(data
, &tprof_stat
, sizeof(tprof_stat
));
629 mutex_exit(&tprof_lock
);
639 const struct cdevsw tprof_cdevsw
= {
640 .d_open
= tprof_open
,
641 .d_close
= tprof_close
,
642 .d_read
= tprof_read
,
644 .d_ioctl
= tprof_ioctl
,
649 .d_kqfilter
= nokqfilter
,
650 .d_flag
= D_OTHER
| D_MPSAFE
,
654 tprofattach(int nunits
)
660 MODULE(MODULE_CLASS_DRIVER
, tprof
, NULL
);
663 tprof_driver_init(void)
666 mutex_init(&tprof_lock
, MUTEX_DEFAULT
, IPL_NONE
);
667 mutex_init(&tprof_reader_lock
, MUTEX_DEFAULT
, IPL_NONE
);
668 mutex_init(&tprof_startstop_lock
, MUTEX_DEFAULT
, IPL_NONE
);
669 cv_init(&tprof_cv
, "tprof");
670 cv_init(&tprof_reader_cv
, "tprofread");
671 STAILQ_INIT(&tprof_list
);
675 tprof_driver_fini(void)
678 mutex_destroy(&tprof_lock
);
679 mutex_destroy(&tprof_reader_lock
);
680 mutex_destroy(&tprof_startstop_lock
);
681 cv_destroy(&tprof_cv
);
682 cv_destroy(&tprof_reader_cv
);
686 tprof_modcmd(modcmd_t cmd
, void *arg
)
690 case MODULE_CMD_INIT
:
694 devmajor_t bmajor
= NODEVMAJOR
;
695 devmajor_t cmajor
= NODEVMAJOR
;
698 error
= devsw_attach("tprof", NULL
, &bmajor
,
699 &tprof_cdevsw
, &cmajor
);
705 #endif /* defined(_MODULE) */
708 case MODULE_CMD_FINI
:
712 error
= devsw_detach(NULL
, &tprof_cdevsw
);
717 #endif /* defined(_MODULE) */