2 * Copyright (c) 2017 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * Collects general statistics on a 10-second interval.
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
40 #include <sys/kinfo.h>
41 #include <sys/queue.h>
42 #include <sys/sysctl.h>
43 #include <sys/kthread.h>
44 #include <machine/cpu.h>
46 #include <sys/spinlock.h>
47 #include <sys/kcollect.h>
49 #include <sys/thread2.h>
50 #include <sys/spinlock2.h>
53 #include <vm/vm_param.h>
54 #include <vm/vm_kern.h>
55 #include <vm/vm_object.h>
56 #include <vm/vm_page.h>
57 #include <vm/vm_map.h>
58 #include <vm/vm_pager.h>
59 #include <vm/vm_extern.h>
61 #include <machine/stdarg.h>
62 #include <machine/smp.h>
63 #include <machine/clock.h>
65 static uint32_t kcollect_samples
= -1; /* 0 to disable */
66 TUNABLE_INT("kern.collect_samples", &kcollect_samples
);
67 SYSCTL_UINT(_kern
, OID_AUTO
, collect_samples
, CTLFLAG_RD
,
68 &kcollect_samples
, 0, "number of 10-second samples");
70 static uint64_t kcollect_index
;
71 static const char *kcollect_slots
[KCOLLECT_ENTRIES
];
72 static kcallback_t kcollect_callback
[KCOLLECT_ENTRIES
];
73 static kcollect_t kcollect_scale
;
74 static kcollect_t
*kcollect_ary
;
75 static struct lock kcollect_lock
= LOCK_INITIALIZER("kcolk", 0, 0);
77 MALLOC_DEFINE(M_KCOLLECT
, "kcollect", "kcollect array");
80 kcollect_register(int which
, const char *id
, kcallback_t func
, uint64_t scale
)
84 lockmgr(&kcollect_lock
, LK_EXCLUSIVE
);
88 for (n
= KCOLLECT_DYNAMIC_START
; n
< KCOLLECT_ENTRIES
; ++n
) {
89 if (kcollect_slots
[n
] == NULL
)
93 if (n
< 0 || n
>= KCOLLECT_ENTRIES
) {
96 kcollect_slots
[n
] = id
;
97 kcollect_callback
[n
] = func
;
98 kcollect_scale
.data
[n
] = scale
;
100 lockmgr(&kcollect_lock
, LK_RELEASE
);
106 kcollect_unregister(int n
)
108 lockmgr(&kcollect_lock
, LK_EXCLUSIVE
);
109 if (n
>= 0 && n
< KCOLLECT_ENTRIES
) {
110 kcollect_slots
[n
] = NULL
;
111 kcollect_callback
[n
] = NULL
;
113 lockmgr(&kcollect_lock
, LK_RELEASE
);
117 * Typically called by a rollup function in the callback from the
118 * collection thread. Not usually called ad-hoc. This allows a
119 * subsystem to register several collection ids but only one callback
120 * which populates all of them.
123 kcollect_setvalue(int n
, uint64_t value
)
127 if (n
>= 0 && n
< KCOLLECT_ENTRIES
) {
128 i
= kcollect_index
% kcollect_samples
;
129 kcollect_ary
[i
].data
[n
] = value
;
134 * Callback to change scale adjustment, if necessary. Certain statistics
135 * have scale info available (such as KCOLLECT_SWAPANO and SWAPCAC).
138 kcollect_setscale(int n
, uint64_t value
)
140 if (n
>= 0 && n
< KCOLLECT_ENTRIES
) {
141 kcollect_scale
.data
[n
] = value
;
147 kcollect_thread(void *dummy
)
153 lockmgr(&kcollect_lock
, LK_EXCLUSIVE
);
154 i
= kcollect_index
% kcollect_samples
;
155 bzero(&kcollect_ary
[i
], sizeof(kcollect_ary
[i
]));
157 kcollect_ary
[i
].ticks
= ticks
;
158 getmicrotime(&kcollect_ary
[i
].realtime
);
160 for (n
= 0; n
< KCOLLECT_ENTRIES
; ++n
) {
161 if (kcollect_callback
[n
]) {
162 kcollect_ary
[i
].data
[n
] =
163 kcollect_callback
[n
](n
);
168 lockmgr(&kcollect_lock
, LK_RELEASE
);
169 tsleep(&dummy
, 0, "sleep", hz
* KCOLLECT_INTERVAL
);
177 sysctl_kcollect_data(SYSCTL_HANDLER_ARGS
)
186 if (kcollect_samples
== (uint32_t)-1 ||
187 kcollect_samples
== 0) {
192 count
= kcollect_index
;
193 start
= count
% kcollect_samples
;
194 if (count
>= kcollect_samples
)
195 count
= kcollect_samples
- 1;
200 if (req
->oldptr
== NULL
) {
201 error
= SYSCTL_OUT(req
, 0, sizeof(kcollect_t
) * (count
+ 2));
206 * Output request. We output a scale record, a string record, and
207 * N collection records. The strings in the string record can be
208 * up to 8 characters long, and if a string is 8 characters long it
209 * will not be zero-terminated.
211 * The low byte of the scale record specifies the format. To get
212 * the scale value shift right by 8.
214 if (kcollect_ary
== NULL
)
217 lockmgr(&kcollect_lock
, LK_EXCLUSIVE
);
218 scale
= kcollect_scale
;
222 bzero(&id
, sizeof(id
));
223 for (i
= 0; i
< KCOLLECT_ENTRIES
; ++i
) {
224 if (kcollect_slots
[i
]) {
225 char *ptr
= (char *)&id
.data
[i
];
226 size_t len
= strlen(kcollect_slots
[i
]);
227 if (len
> sizeof(id
.data
[0]))
228 len
= sizeof(id
.data
[0]);
229 bcopy(kcollect_slots
[i
], ptr
, len
);
232 lockmgr(&kcollect_lock
, LK_RELEASE
);
234 error
= SYSCTL_OUT(req
, &scale
, sizeof(scale
));
236 error
= SYSCTL_OUT(req
, &id
, sizeof(id
));
239 * Start at the current entry (not yet populated) and work
240 * backwards. This allows callers of the sysctl to acquire
241 * a lesser amount of data aligned to the most recent side of
246 if (req
->oldlen
- req
->oldidx
< sizeof(kcollect_t
))
249 i
= kcollect_samples
- 1;
252 error
= SYSCTL_OUT(req
, &kcollect_ary
[i
], sizeof(kcollect_t
));
259 SYSCTL_PROC(_kern
, OID_AUTO
, collect_data
,
260 CTLFLAG_RD
| CTLTYPE_STRUCT
, 0, 0,
261 sysctl_kcollect_data
, "S,kcollect", "Dump collected statistics");
265 kcollect_thread_init(void)
270 * Autosize sample retention (10 second interval)
272 if ((int)kcollect_samples
< 0) {
273 if (kmem_lim_size() < 1024)
274 kcollect_samples
= 1024;
276 kcollect_samples
= 8192;
279 if (kcollect_samples
) {
280 kcollect_ary
= kmalloc(kcollect_samples
* sizeof(kcollect_t
),
281 M_KCOLLECT
, M_WAITOK
| M_ZERO
);
282 lwkt_create(kcollect_thread
, NULL
, &td
, NULL
, 0, 0, "kcollect");
285 SYSINIT(kcol
, SI_SUB_HELPER_THREADS
, SI_ORDER_ANY
, kcollect_thread_init
, 0);