1/* $NetBSD: kernhist.h,v 1.12 2016/04/09 17:04:53 riastradh Exp $ */
2
3/*
4 * Copyright (c) 1997 Charles D. Cranor and Washington University.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
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.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 * from: NetBSD: uvm_stat.h,v 1.49 2011/04/23 18:14:13 rmind Exp
28 * from: Id: uvm_stat.h,v 1.1.2.4 1998/02/07 01:16:56 chs Exp
29 */
30
31#ifndef _SYS_KERNHIST_H_
32#define _SYS_KERNHIST_H_
33
34#if defined(_KERNEL_OPT)
35#include "opt_ddb.h"
36#include "opt_kernhist.h"
37#endif
38
39#include <sys/queue.h>
40#ifdef KERNHIST
41#include <sys/cpu.h>
42#endif
43
44/*
45 * kernel history/tracing, was uvm_stat
46 */
47
48struct kern_history_ent {
49 struct timeval tv; /* time stamp */
50 int cpunum;
51 const char *fmt; /* printf format */
52 size_t fmtlen; /* length of printf format */
53 const char *fn; /* function name */
54 size_t fnlen; /* length of function name */
55 u_long call; /* function call number */
56 u_long v[4]; /* values */
57};
58
59struct kern_history {
60 const char *name; /* name of this history */
61 size_t namelen; /* length of name, not including null */
62 LIST_ENTRY(kern_history) list; /* link on list of all histories */
63 unsigned int n; /* number of entries */
64 unsigned int f; /* next free one */
65 struct kern_history_ent *e; /* the allocated entries */
66};
67
68LIST_HEAD(kern_history_head, kern_history);
69
70/*
71 * grovelling lists all at once. we currently do not allow more than
72 * 32 histories to exist, as the way to dump a number of them at once
73 * is by calling kern_hist() with a bitmask.
74 *
75 * XXX extend this to have a registration function? however, there
76 * needs to be static ones as UVM requires this before almost anything
77 * else is setup.
78 */
79
80/* this is used to set the size of some arrays */
81#define MAXHISTS 32
82
83/* and these are the bit values of each history */
84#define KERNHIST_UVMMAPHIST 0x00000001 /* maphist */
85#define KERNHIST_UVMPDHIST 0x00000002 /* pdhist */
86#define KERNHIST_UVMUBCHIST 0x00000004 /* ubchist */
87#define KERNHIST_UVMLOANHIST 0x00000008 /* loanhist */
88#define KERNHIST_USBHIST 0x00000010 /* usbhist */
89#define KERNHIST_SCDEBUGHIST 0x00000020 /* scdebughist */
90
91#ifdef _KERNEL
92
93/*
94 * macros to use the history/tracing code. note that KERNHIST_LOG
95 * must take 4 arguments (even if they are ignored by the format).
96 */
97#ifndef KERNHIST
98#define KERNHIST_DECL(NAME)
99#define KERNHIST_DEFINE(NAME)
100#define KERNHIST_INIT(NAME,N)
101#define KERNHIST_INIT_STATIC(NAME,BUF)
102#define KERNHIST_LOG(NAME,FMT,A,B,C,D)
103#define KERNHIST_CALLARGS(NAME,FMT,A,B,C,D)
104#define KERNHIST_CALLED(NAME)
105#define KERNHIST_FUNC(FNAME)
106#define KERNHIST_DUMP(NAME)
107#else
108#include <sys/kernel.h> /* for "cold" variable */
109#include <sys/atomic.h>
110#include <sys/kmem.h>
111
112extern struct kern_history_head kern_histories;
113
114#define KERNHIST_DECL(NAME) extern struct kern_history NAME
115#define KERNHIST_DEFINE(NAME) struct kern_history NAME
116
117#define KERNHIST_INIT(NAME,N) \
118do { \
119 (NAME).name = __STRING(NAME); \
120 (NAME).namelen = strlen(__STRING(NAME)); \
121 (NAME).n = (N); \
122 (NAME).f = 0; \
123 (NAME).e = (struct kern_history_ent *) \
124 kmem_zalloc(sizeof(struct kern_history_ent) * (N), KM_SLEEP); \
125 LIST_INSERT_HEAD(&kern_histories, &(NAME), list); \
126} while (/*CONSTCOND*/ 0)
127
128#define KERNHIST_INITIALIZER(NAME,BUF) \
129{ \
130 .name = __STRING(NAME), \
131 .namelen = sizeof(__STRING(NAME)) - 1, \
132 .n = sizeof(BUF) / sizeof(struct kern_history_ent), \
133 .f = 0, \
134 .e = (struct kern_history_ent *) (BUF), \
135 /* BUF will inititalized to zeroes by being in .bss */ \
136}
137
138#define KERNHIST_LINK_STATIC(NAME) \
139 LIST_INSERT_HEAD(&kern_histories, &(NAME), list)
140
141#define KERNHIST_INIT_STATIC(NAME,BUF) \
142do { \
143 (NAME).name = __STRING(NAME); \
144 (NAME).namelen = strlen(__STRING(NAME)); \
145 (NAME).n = sizeof(BUF) / sizeof(struct kern_history_ent); \
146 (NAME).f = 0; \
147 (NAME).e = (struct kern_history_ent *) (BUF); \
148 memset((NAME).e, 0, sizeof(struct kern_history_ent) * (NAME).n); \
149 KERNHIST_LINK_STATIC(NAME); \
150} while (/*CONSTCOND*/ 0)
151
152#ifndef KERNHIST_DELAY
153#define KERNHIST_DELAY 100000
154#endif
155
156#if defined(KERNHIST_PRINT)
157extern int kernhist_print_enabled;
158#define KERNHIST_PRINTNOW(E) \
159do { \
160 if (kernhist_print_enabled) { \
161 kernhist_entry_print(E, printf); \
162 if (KERNHIST_DELAY != 0) \
163 DELAY(KERNHIST_DELAY); \
164 } \
165} while (/*CONSTCOND*/ 0)
166#else
167#define KERNHIST_PRINTNOW(E) /* nothing */
168#endif
169
170#define KERNHIST_LOG(NAME,FMT,A,B,C,D) \
171do { \
172 unsigned int _i_, _j_; \
173 do { \
174 _i_ = (NAME).f; \
175 _j_ = (_i_ + 1 < (NAME).n) ? _i_ + 1 : 0; \
176 } while (atomic_cas_uint(&(NAME).f, _i_, _j_) != _i_); \
177 struct kern_history_ent * const _e_ = &(NAME).e[_i_]; \
178 if (__predict_true(!cold)) \
179 microtime(&_e_->tv); \
180 _e_->cpunum = cpu_number(); \
181 _e_->fmt = (FMT); \
182 _e_->fmtlen = strlen(FMT); \
183 _e_->fn = _kernhist_name; \
184 _e_->fnlen = strlen(_kernhist_name); \
185 _e_->call = _kernhist_call; \
186 _e_->v[0] = (u_long)(A); \
187 _e_->v[1] = (u_long)(B); \
188 _e_->v[2] = (u_long)(C); \
189 _e_->v[3] = (u_long)(D); \
190 KERNHIST_PRINTNOW(_e_); \
191} while (/*CONSTCOND*/ 0)
192
193#define KERNHIST_CALLED(NAME) \
194do { \
195 _kernhist_call = atomic_inc_uint_nv(&_kernhist_cnt); \
196 KERNHIST_LOG(NAME, "called!", 0, 0, 0, 0); \
197} while (/*CONSTCOND*/ 0)
198
199/*
200 * This extends kernhist to avoid wasting a separate "called!" entry on every
201 * function.
202 */
203#define KERNHIST_CALLARGS(NAME, FMT, A, B, C, D) \
204do { \
205 _kernhist_call = atomic_inc_uint_nv(&_kernhist_cnt); \
206 KERNHIST_LOG(NAME, "called: "FMT, (A), (B), (C), (D)); \
207} while (/*CONSTCOND*/ 0)
208
209#define KERNHIST_FUNC(FNAME) \
210 static unsigned int _kernhist_cnt = 0; \
211 static const char *const _kernhist_name = FNAME; \
212 unsigned int _kernhist_call = 0;
213
214#ifdef DDB
215#define KERNHIST_DUMP(NAME) kernhist_dump(&NAME, printf)
216#else
217#define KERNHIST_DUMP(NAME)
218#endif
219
220static inline void
221kernhist_entry_print(const struct kern_history_ent *e, void (*pr)(const char *, ...) __printflike(1, 2))
222{
223 pr("%06" PRIu64 ".%06d ", e->tv.tv_sec, e->tv.tv_usec);
224 pr("%s#%ld@%d: ", e->fn, e->call, e->cpunum);
225 pr(e->fmt, e->v[0], e->v[1], e->v[2], e->v[3]);
226 pr("\n");
227}
228
229#if defined(DDB)
230void kernhist_dump(struct kern_history *, void (*)(const char *, ...) __printflike(1, 2));
231void kernhist_print(void *, void (*)(const char *, ...) __printflike(1, 2));
232#endif /* DDB */
233
234#endif /* KERNHIST */
235
236#endif /* _KERNEL */
237
238#endif /* _SYS_KERNHIST_H_ */
239