perf: Shutdown profiler/sampling on kpctl close
[akaros.git] / kern / drivers / dev / kprof.c
1 /*
2  * This file is part of the UCB release of Plan 9. It is subject to the license
3  * terms in the LICENSE file found in the top-level directory of this
4  * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
5  * part of the UCB release of Plan 9, including this file, may be copied,
6  * modified, propagated, or distributed except according to the terms contained
7  * in the LICENSE file.
8  */
9
10 #include <ros/profiler_records.h>
11 #include <arch/time.h>
12 #include <vfs.h>
13 #include <slab.h>
14 #include <kmalloc.h>
15 #include <kref.h>
16 #include <atomic.h>
17 #include <kthread.h>
18 #include <string.h>
19 #include <stdio.h>
20 #include <assert.h>
21 #include <error.h>
22 #include <pmap.h>
23 #include <smp.h>
24 #include <time.h>
25 #include <circular_buffer.h>
26 #include <umem.h>
27 #include <profiler.h>
28 #include <kprof.h>
29 #include <ros/procinfo.h>
30
31 #define KTRACE_BUFFER_SIZE (128 * 1024)
32 #define TRACE_PRINTK_BUFFER_SIZE (8 * 1024)
33
34 enum {
35         Kprofdirqid = 0,
36         Kprofdataqid,
37         Kprofctlqid,
38         Kptraceqid,
39         Kprintxqid,
40         Kmpstatqid,
41         Kmpstatrawqid,
42 };
43
44 struct trace_printk_buffer {
45         atomic_t in_use;
46         char buffer[TRACE_PRINTK_BUFFER_SIZE];
47 };
48
49 struct kprof {
50         qlock_t lock;
51         struct alarm_waiter *alarms;
52         bool mpstat_ipi;
53         bool profiling;
54         bool opened;
55         char *pdata;
56         size_t psize;
57 };
58
59 struct dev kprofdevtab;
60 struct dirtab kproftab[] = {
61         {".",                   {Kprofdirqid,           0, QTDIR}, 0,   DMDIR|0550},
62         {"kpdata",              {Kprofdataqid},         0,      0600},
63         {"kpctl",               {Kprofctlqid},          0,      0600},
64         {"kptrace",             {Kptraceqid},           0,      0600},
65         {"kprintx",             {Kprintxqid},           0,      0600},
66         {"mpstat",              {Kmpstatqid},           0,      0600},
67         {"mpstat-raw",  {Kmpstatrawqid},        0,      0600},
68 };
69
70 static struct kprof kprof;
71 static bool ktrace_init_done = FALSE;
72 static spinlock_t ktrace_lock = SPINLOCK_INITIALIZER_IRQSAVE;
73 static struct circular_buffer ktrace_data;
74 static char ktrace_buffer[KTRACE_BUFFER_SIZE];
75 static int kprof_timer_period = 1000;
76 static char kprof_control_usage[128];
77
78 static size_t mpstat_len(void)
79 {
80         size_t each_row = 7 + NR_CPU_STATES * 26;
81
82         return each_row * (num_cores + 1) + 1;
83 }
84
85 static size_t mpstatraw_len(void)
86 {
87         size_t header_row = 27 + NR_CPU_STATES * 7 + 1;
88         size_t cpu_row = 7 + NR_CPU_STATES * 17;
89
90         return header_row + cpu_row * num_cores + 1;
91 }
92
93 static char *devname(void)
94 {
95         return kprofdevtab.name;
96 }
97
98 static void kprof_alarm_handler(struct alarm_waiter *waiter,
99                                 struct hw_trapframe *hw_tf)
100 {
101         int coreid = core_id();
102         struct timer_chain *tchain = &per_cpu_info[coreid].tchain;
103
104         profiler_add_hw_sample(hw_tf, PROF_MKINFO(PROF_DOM_TIMER,
105                                                                                           kprof_timer_period));
106         reset_alarm_rel(tchain, waiter, kprof_timer_period);
107 }
108
109 static struct chan *kprof_attach(char *spec)
110 {
111         if (!kprof.alarms)
112                 error(ENOMEM, ERROR_FIXME);
113
114         return devattach(devname(), spec);
115 }
116
117 static void kprof_enable_timer(int coreid, int on_off)
118 {
119         struct timer_chain *tchain = &per_cpu_info[coreid].tchain;
120         struct alarm_waiter *waiter = &kprof.alarms[coreid];
121
122         if (on_off) {
123                 /* Per CPU waiters already inited.  Will set/reset each time (1 ms
124                  * default). */
125                 reset_alarm_rel(tchain, waiter, kprof_timer_period);
126         } else {
127                 /* Since the alarm handler runs and gets reset within IRQ context, then
128                  * we should never fail to cancel the alarm if it was already running
129                  * (tchain locks synchronize us).  But it might not be set at all, which
130                  * is fine. */
131                 unset_alarm(tchain, waiter);
132         }
133 }
134
135 static void kprof_profdata_clear(void)
136 {
137         kfree(kprof.pdata);
138         kprof.pdata = NULL;
139         kprof.psize = 0;
140 }
141
142 static void kprof_start_profiler(void)
143 {
144         ERRSTACK(2);
145
146         qlock(&kprof.lock);
147         if (waserror()) {
148                 qunlock(&kprof.lock);
149                 nexterror();
150         }
151         if (!kprof.profiling) {
152                 profiler_setup();
153                 if (waserror()) {
154                         kprof.profiling = FALSE;
155                         profiler_cleanup();
156                         nexterror();
157                 }
158                 profiler_control_trace(1);
159                 kprof.profiling = TRUE;
160                 kprof_profdata_clear();
161                 poperror();
162         }
163         poperror();
164         qunlock(&kprof.lock);
165 }
166
167 static void kprof_fetch_profiler_data(void)
168 {
169         size_t psize = kprof.psize + profiler_size();
170         size_t read_amt;
171         char *ndata = krealloc(kprof.pdata, psize, MEM_WAIT);
172
173         if (!ndata)
174                 error(ENOMEM, ERROR_FIXME);
175         kprof.pdata = ndata;
176         /* psize includes a snapshot of the profiler's size.  It might grow in the
177          * meantime, but we'll only ever grab as much as we saw originally.  This is
178          * fine.  The important thing is that we only grab contiguous records. */
179         while (kprof.psize < psize) {
180                 read_amt = profiler_read(kprof.pdata + kprof.psize,
181                                          psize - kprof.psize);
182                 /* We are the only reader - we must always get whatever was in the
183                  * queue. */
184                 assert(read_amt);
185                 kprof.psize += read_amt;
186         }
187 }
188
189 static void kprof_stop_profiler(void)
190 {
191         ERRSTACK(1);
192
193         qlock(&kprof.lock);
194         if (waserror()) {
195                 qunlock(&kprof.lock);
196                 nexterror();
197         }
198         if (kprof.profiling) {
199                 profiler_control_trace(0);
200                 kprof_fetch_profiler_data();
201                 profiler_cleanup();
202
203                 kprof.profiling = FALSE;
204         }
205         poperror();
206         qunlock(&kprof.lock);
207 }
208
209 static void kprof_flush_profiler(void)
210 {
211         ERRSTACK(1);
212
213         qlock(&kprof.lock);
214         if (waserror()) {
215                 qunlock(&kprof.lock);
216                 nexterror();
217         }
218         if (kprof.profiling) {
219                 profiler_trace_data_flush();
220                 kprof_fetch_profiler_data();
221         }
222         poperror();
223         qunlock(&kprof.lock);
224 }
225
226 static void kprof_init(void)
227 {
228         int i;
229         ERRSTACK(1);
230
231         profiler_init();
232
233         qlock_init(&kprof.lock);
234         kprof.profiling = FALSE;
235         kprof.opened = FALSE;
236         kprof.pdata = NULL;
237         kprof.psize = 0;
238
239         kprof.alarms = kzmalloc(sizeof(struct alarm_waiter) * num_cores,
240                                 MEM_WAIT);
241         if (!kprof.alarms)
242                 error(ENOMEM, ERROR_FIXME);
243         if (waserror()) {
244                 kfree(kprof.alarms);
245                 kprof.alarms = NULL;
246                 nexterror();
247         }
248         for (i = 0; i < num_cores; i++)
249                 init_awaiter_irq(&kprof.alarms[i], kprof_alarm_handler);
250
251         for (i = 0; i < ARRAY_SIZE(kproftab); i++)
252                 kproftab[i].length = 0;
253
254         kprof.mpstat_ipi = TRUE;
255         kproftab[Kmpstatqid].length = mpstat_len();
256         kproftab[Kmpstatrawqid].length = mpstatraw_len();
257
258         strlcpy(kprof_control_usage, "clear|start|stop|flush|timer",
259                 sizeof(kprof_control_usage));
260         profiler_append_configure_usage(kprof_control_usage,
261                                         sizeof(kprof_control_usage));
262
263         poperror();
264 }
265
266 static void kprof_shutdown(void)
267 {
268         kprof_stop_profiler();
269         kprof_profdata_clear();
270
271         kfree(kprof.alarms);
272         kprof.alarms = NULL;
273 }
274
275 static void kprofclear(void)
276 {
277         qlock(&kprof.lock);
278         kprof_profdata_clear();
279         qunlock(&kprof.lock);
280 }
281
282 static struct walkqid *kprof_walk(struct chan *c, struct chan *nc, char **name,
283                                   int nname)
284 {
285         return devwalk(c, nc, name, nname, kproftab, ARRAY_SIZE(kproftab), devgen);
286 }
287
288 static size_t kprof_profdata_size(void)
289 {
290         return kprof.pdata != NULL ? kprof.psize : profiler_size();
291 }
292
293 static long kprof_profdata_read(void *dest, long size, int64_t off)
294 {
295         qlock(&kprof.lock);
296         if (kprof.pdata && off < kprof.psize) {
297                 size = MIN(kprof.psize - off, size);
298                 memcpy(dest, kprof.pdata + off, size);
299         } else {
300                 size = 0;
301         }
302         qunlock(&kprof.lock);
303
304         return size;
305 }
306
307 static int kprof_stat(struct chan *c, uint8_t *db, int n)
308 {
309         kproftab[Kprofdataqid].length = kprof_profdata_size();
310         kproftab[Kptraceqid].length = kprof_tracedata_size();
311
312         return devstat(c, db, n, kproftab, ARRAY_SIZE(kproftab), devgen);
313 }
314
315 static struct chan *kprof_open(struct chan *c, int omode)
316 {
317         if (c->qid.type & QTDIR) {
318                 if (openmode(omode) != O_READ)
319                         error(EPERM, ERROR_FIXME);
320         }
321         switch ((int) c->qid.path) {
322         case Kprofctlqid:
323                 /* We have one global profiler.  Only one FD may be opened at a time for
324                  * it.  If we ever have separate profilers, we can create the profiler
325                  * here, and every open would get a separate instance. */
326                 qlock(&kprof.lock);
327                 if (kprof.opened) {
328                         qunlock(&kprof.lock);
329                         error(EBUSY, "Global profiler is already open");
330                 }
331                 kprof.opened = TRUE;
332                 qunlock(&kprof.lock);
333                 break;
334         }
335         c->mode = openmode(omode);
336         c->flag |= COPEN;
337         c->offset = 0;
338         return c;
339 }
340
341 static void kprof_close(struct chan *c)
342 {
343         if (c->flag & COPEN) {
344                 switch ((int) c->qid.path) {
345                 case Kprofctlqid:
346                         kprof_stop_profiler();
347                         kprof.opened = FALSE;
348                         break;
349                 }
350         }
351 }
352
353 static long mpstat_read(void *va, long n, int64_t off)
354 {
355         size_t bufsz = mpstat_len();
356         char *buf = kmalloc(bufsz, MEM_WAIT);
357         int len = 0;
358         struct per_cpu_info *pcpui;
359         uint64_t cpu_total;
360         struct timespec ts;
361
362         /* the IPI interferes with other cores, might want to disable that. */
363         if (kprof.mpstat_ipi)
364                 send_broadcast_ipi(I_POKE_CORE);
365
366         len += snprintf(buf + len, bufsz - len, "  CPU: ");
367         for (int j = 0; j < NR_CPU_STATES; j++)
368                 len += snprintf(buf + len, bufsz - len, "%23s%s", cpu_state_names[j],
369                                 j != NR_CPU_STATES - 1 ? "   " : "  \n");
370
371         for (int i = 0; i < num_cores; i++) {
372                 pcpui = &per_cpu_info[i];
373                 cpu_total = 0;
374                 len += snprintf(buf + len, bufsz - len, "%5d: ", i);
375                 for (int j = 0; j < NR_CPU_STATES; j++)
376                         cpu_total += pcpui->state_ticks[j];
377                 cpu_total = MAX(cpu_total, 1);  /* for the divide later */
378                 for (int j = 0; j < NR_CPU_STATES; j++) {
379                         ts = tsc2timespec(pcpui->state_ticks[j]);
380                         len += snprintf(buf + len, bufsz - len, "%10d.%06d (%3d%%)%s",
381                                         ts.tv_sec, ts.tv_nsec / 1000,
382                                         MIN((pcpui->state_ticks[j] * 100) / cpu_total, 100),
383                                         j != NR_CPU_STATES - 1 ? ", " : " \n");
384                 }
385         }
386         n = readstr(off, va, n, buf);
387         kfree(buf);
388         return n;
389 }
390
391 static long mpstatraw_read(void *va, long n, int64_t off)
392 {
393         size_t bufsz = mpstatraw_len();
394         char *buf = kmalloc(bufsz, MEM_WAIT);
395         int len = 0;
396         struct per_cpu_info *pcpui;
397
398         /* could spit it all out in binary, though then it'd be harder to process
399          * the data across a mnt (if we export #K).  probably not a big deal. */
400
401         /* header line: version, num_cores, tsc freq, state names */
402         len += snprintf(buf + len, bufsz - len, "v%03d %5d %16llu", 1, num_cores,
403                         __proc_global_info.tsc_freq);
404         for (int j = 0; j < NR_CPU_STATES; j++)
405                 len += snprintf(buf + len, bufsz - len, " %6s", cpu_state_names[j]);
406         len += snprintf(buf + len, bufsz - len, "\n");
407
408         for (int i = 0; i < num_cores; i++) {
409                 pcpui = &per_cpu_info[i];
410                 len += snprintf(buf + len, bufsz - len, "%5d: ", i);
411                 for (int j = 0; j < NR_CPU_STATES; j++) {
412                         len += snprintf(buf + len, bufsz - len, "%16llx%s",
413                                         pcpui->state_ticks[j],
414                                         j != NR_CPU_STATES - 1 ? " " : "\n");
415                 }
416         }
417         n = readstr(off, va, n, buf);
418         kfree(buf);
419         return n;
420 }
421
422 static long kprof_read(struct chan *c, void *va, long n, int64_t off)
423 {
424         uint64_t w, *bp;
425         char *a, *ea;
426         uintptr_t offset = off;
427         uint64_t pc;
428
429         switch ((int) c->qid.path) {
430         case Kprofdirqid:
431                 return devdirread(c, va, n, kproftab, ARRAY_SIZE(kproftab), devgen);
432         case Kprofdataqid:
433                 n = kprof_profdata_read(va, n, off);
434                 break;
435         case Kptraceqid:
436                 n = kprof_tracedata_read(va, n, off);
437                 break;
438         case Kprintxqid:
439                 n = readstr(offset, va, n, printx_on ? "on" : "off");
440                 break;
441         case Kmpstatqid:
442                 n = mpstat_read(va, n, offset);
443                 break;
444         case Kmpstatrawqid:
445                 n = mpstatraw_read(va, n, offset);
446                 break;
447         default:
448                 n = 0;
449                 break;
450         }
451         return n;
452 }
453
454 static void kprof_manage_timer(int coreid, struct cmdbuf *cb)
455 {
456         if (!strcmp(cb->f[2], "on")) {
457                 kprof_enable_timer(coreid, 1);
458         } else if (!strcmp(cb->f[2], "off")) {
459                 kprof_enable_timer(coreid, 0);
460         } else {
461                 error(EFAIL, "timer needs on|off");
462         }
463 }
464
465 static long kprof_write(struct chan *c, void *a, long n, int64_t unused)
466 {
467         ERRSTACK(1);
468         struct cmdbuf *cb = parsecmd(a, n);
469
470         if (waserror()) {
471                 kfree(cb);
472                 nexterror();
473         }
474         switch ((int) c->qid.path) {
475         case Kprofctlqid:
476                 if (cb->nf < 1)
477                         error(EFAIL, kprof_control_usage);
478                 if (profiler_configure(cb))
479                         break;
480                 if (!strcmp(cb->f[0], "clear")) {
481                         kprofclear();
482                 } else if (!strcmp(cb->f[0], "timer")) {
483                         if (cb->nf < 3)
484                                 error(EFAIL, "timer {{all, N} {on, off}, period USEC}");
485                         if (!strcmp(cb->f[1], "period")) {
486                                 kprof_timer_period = strtoul(cb->f[2], 0, 10);
487                         } else if (!strcmp(cb->f[1], "all")) {
488                                 for (int i = 0; i < num_cores; i++)
489                                         kprof_manage_timer(i, cb);
490                         } else {
491                                 int pcoreid = strtoul(cb->f[1], 0, 10);
492
493                                 if (pcoreid >= num_cores)
494                                         error(EFAIL, "No such coreid %d", pcoreid);
495                                 kprof_manage_timer(pcoreid, cb);
496                         }
497                 } else if (!strcmp(cb->f[0], "start")) {
498                         kprof_start_profiler();
499                 } else if (!strcmp(cb->f[0], "flush")) {
500                         kprof_flush_profiler();
501                 } else if (!strcmp(cb->f[0], "stop")) {
502                         kprof_stop_profiler();
503                 } else {
504                         error(EFAIL, kprof_control_usage);
505                 }
506                 break;
507         case Kprofdataqid:
508                 profiler_add_trace((uintptr_t) strtoul(a, 0, 0), 0);
509                 break;
510         case Kptraceqid:
511                 if (a && (n > 0)) {
512                         char *uptr = user_strdup_errno(current, a, n);
513
514                         if (uptr) {
515                                 trace_printk(false, "%s", uptr);
516                                 user_memdup_free(current, uptr);
517                         } else {
518                                 n = -1;
519                         }
520                 }
521                 break;
522         case Kprintxqid:
523                 if (!strncmp(a, "on", 2))
524                         set_printx(1);
525                 else if (!strncmp(a, "off", 3))
526                         set_printx(0);
527                 else if (!strncmp(a, "toggle", 6))
528                         set_printx(2);
529                 else
530                         error(EFAIL, "Invalid option to Kprintx %s\n", a);
531                 break;
532         case Kmpstatqid:
533         case Kmpstatrawqid:
534                 if (cb->nf < 1)
535                         error(EFAIL, "Bad mpstat option (reset|ipi|on|off)");
536                 if (!strcmp(cb->f[0], "reset")) {
537                         for (int i = 0; i < num_cores; i++)
538                                 reset_cpu_state_ticks(i);
539                 } else if (!strcmp(cb->f[0], "on")) {
540                         /* TODO: enable the ticks */ ;
541                 } else if (!strcmp(cb->f[0], "off")) {
542                         /* TODO: disable the ticks */ ;
543                 } else if (!strcmp(cb->f[0], "ipi")) {
544                         if (cb->nf < 2)
545                                 error(EFAIL, "Need another arg: ipi [on|off]");
546                         if (!strcmp(cb->f[1], "on"))
547                                 kprof.mpstat_ipi = TRUE;
548                         else if (!strcmp(cb->f[1], "off"))
549                                 kprof.mpstat_ipi = FALSE;
550                         else
551                                 error(EFAIL, "ipi [on|off]");
552                 } else {
553                         error(EFAIL, "Bad mpstat option (reset|ipi|on|off)");
554                 }
555                 break;
556         default:
557                 error(EBADFD, ERROR_FIXME);
558         }
559         kfree(cb);
560         poperror();
561         return n;
562 }
563
564 size_t kprof_tracedata_size(void)
565 {
566         return circular_buffer_size(&ktrace_data);
567 }
568
569 size_t kprof_tracedata_read(void *data, size_t size, size_t offset)
570 {
571         spin_lock_irqsave(&ktrace_lock);
572         if (likely(ktrace_init_done))
573                 size = circular_buffer_read(&ktrace_data, data, size, offset);
574         else
575                 size = 0;
576         spin_unlock_irqsave(&ktrace_lock);
577
578         return size;
579 }
580
581 void kprof_tracedata_write(const char *pretty_buf, size_t len)
582 {
583         spin_lock_irqsave(&ktrace_lock);
584         if (unlikely(!ktrace_init_done)) {
585                 circular_buffer_init(&ktrace_data, sizeof(ktrace_buffer),
586                                      ktrace_buffer);
587                 ktrace_init_done = TRUE;
588         }
589         circular_buffer_write(&ktrace_data, pretty_buf, len);
590         spin_unlock_irqsave(&ktrace_lock);
591 }
592
593 static struct trace_printk_buffer *kprof_get_printk_buffer(void)
594 {
595         static struct trace_printk_buffer boot_tpb;
596         static struct trace_printk_buffer *cpu_tpbs;
597         static atomic_t alloc_done;
598
599         if (unlikely(!num_cores))
600                 return &boot_tpb;
601         if (unlikely(!cpu_tpbs)) {
602                 /* Poor man per-CPU data structure. I really do no like littering global
603                  * data structures with module specific data.
604                  * We cannot take the ktrace_lock to protect the kzmalloc() call, as
605                  * that might trigger printk()s, and we would reenter here.
606                  * Let only one core into the kzmalloc() path, and let the others get
607                  * the boot_tpb until finished.
608                  */
609                 if (!atomic_cas(&alloc_done, 0, 1))
610                         return &boot_tpb;
611                 cpu_tpbs = kzmalloc(num_cores * sizeof(struct trace_printk_buffer), 0);
612         }
613
614         return cpu_tpbs + core_id_early();
615 }
616
617 void trace_vprintk(bool btrace, const char *fmt, va_list args)
618 {
619         struct print_buf {
620                 char *ptr;
621                 char *top;
622         };
623
624         void emit_print_buf_str(struct print_buf *pb, const char *str, ssize_t size)
625         {
626                 if (size < 0) {
627                         for (; *str && (pb->ptr < pb->top); str++)
628                                 *(pb->ptr++) = *str;
629                 } else {
630                         for (; (size > 0) && (pb->ptr < pb->top); str++, size--)
631                                 *(pb->ptr++) = *str;
632                 }
633         }
634
635         void bt_print(void *opaque, const char *str)
636         {
637                 struct print_buf *pb = (struct print_buf *) opaque;
638
639                 emit_print_buf_str(pb, "\t", 1);
640                 emit_print_buf_str(pb, str, -1);
641         }
642
643         static const size_t bufsz = TRACE_PRINTK_BUFFER_SIZE;
644         static const size_t usr_bufsz = (3 * bufsz) / 8;
645         static const size_t kp_bufsz = bufsz - usr_bufsz;
646         struct trace_printk_buffer *tpb = kprof_get_printk_buffer();
647         struct timespec ts_now = { 0, 0 };
648         struct print_buf pb;
649         char *usrbuf = tpb->buffer, *kpbuf = tpb->buffer + usr_bufsz;
650         const char *utop, *uptr;
651         char hdr[64];
652
653         if (!atomic_cas(&tpb->in_use, 0, 1))
654                 return;
655         if (likely(__proc_global_info.tsc_freq))
656                 ts_now = tsc2timespec(read_tsc());
657         snprintf(hdr, sizeof(hdr), "[%lu.%09lu]:cpu%d: ", ts_now.tv_sec,
658                  ts_now.tv_nsec, core_id_early());
659
660         pb.ptr = usrbuf + vsnprintf(usrbuf, usr_bufsz, fmt, args);
661         pb.top = usrbuf + usr_bufsz;
662
663         if (pb.ptr[-1] != '\n')
664                 emit_print_buf_str(&pb, "\n", 1);
665         if (btrace) {
666                 emit_print_buf_str(&pb, "\tBacktrace:\n", -1);
667                 gen_backtrace(bt_print, &pb);
668         }
669         /* snprintf null terminates the buffer, and does not count that as part of
670          * the len.  If we maxed out the buffer, let's make sure it has a \n.
671          */
672         if (pb.ptr == pb.top)
673                 pb.ptr[-1] = '\n';
674         utop = pb.ptr;
675
676         pb.ptr = kpbuf;
677         pb.top = kpbuf + kp_bufsz;
678         for (uptr = usrbuf; uptr < utop;) {
679                 const char *nlptr = memchr(uptr, '\n', utop - uptr);
680
681                 if (nlptr == NULL)
682                         nlptr = utop;
683                 emit_print_buf_str(&pb, hdr, -1);
684                 emit_print_buf_str(&pb, uptr, (nlptr - uptr) + 1);
685                 uptr = nlptr + 1;
686         }
687         kprof_tracedata_write(kpbuf, pb.ptr - kpbuf);
688         atomic_set(&tpb->in_use, 0);
689 }
690
691 void trace_printk(bool btrace, const char *fmt, ...)
692 {
693         va_list args;
694
695         va_start(args, fmt);
696         trace_vprintk(btrace, fmt, args);
697         va_end(args);
698 }
699
700 struct dev kprofdevtab __devtab = {
701         .name = "kprof",
702
703         .reset = devreset,
704         .init = kprof_init,
705         .shutdown = kprof_shutdown,
706         .attach = kprof_attach,
707         .walk = kprof_walk,
708         .stat = kprof_stat,
709         .open = kprof_open,
710         .create = devcreate,
711         .close = kprof_close,
712         .read = kprof_read,
713         .bread = devbread,
714         .write = kprof_write,
715         .bwrite = devbwrite,
716         .remove = devremove,
717         .wstat = devwstat,
718 };