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>
48 #include <sys/malloc.h>
50 #include <sys/thread2.h>
51 #include <sys/spinlock2.h>
54 #include <vm/vm_param.h>
55 #include <vm/vm_kern.h>
56 #include <vm/vm_object.h>
57 #include <vm/vm_page.h>
58 #include <vm/vm_map.h>
59 #include <vm/vm_pager.h>
60 #include <vm/vm_extern.h>
62 #include <machine/stdarg.h>
63 #include <machine/smp.h>
64 #include <machine/clock.h>
66 static uint32_t kcollect_samples
= -1; /* 0 to disable */
67 TUNABLE_INT("kern.collect_samples", &kcollect_samples
);
68 SYSCTL_UINT(_kern
, OID_AUTO
, collect_samples
, CTLFLAG_RD
,
69 &kcollect_samples
, 0, "number of 10-second samples");
71 static uint64_t kcollect_index
;
72 static const char *kcollect_slots
[KCOLLECT_ENTRIES
];
73 static kcallback_t kcollect_callback
[KCOLLECT_ENTRIES
];
74 static kcollect_t kcollect_scale
;
75 static kcollect_t
*kcollect_ary
;
76 static struct lock kcollect_lock
= LOCK_INITIALIZER("kcolk", 0, 0);
78 MALLOC_DEFINE(M_KCOLLECT
, "kcollect", "kcollect array");
81 kcollect_register(int which
, const char *id
, kcallback_t func
, uint64_t scale
)
85 lockmgr(&kcollect_lock
, LK_EXCLUSIVE
);
89 for (n
= KCOLLECT_DYNAMIC_START
; n
< KCOLLECT_ENTRIES
; ++n
) {
90 if (kcollect_slots
[n
] == NULL
)
94 if (n
< 0 || n
>= KCOLLECT_ENTRIES
) {
97 kcollect_slots
[n
] = id
;
98 kcollect_callback
[n
] = func
;
99 kcollect_scale
.data
[n
] = scale
;
101 lockmgr(&kcollect_lock
, LK_RELEASE
);
107 kcollect_unregister(int n
)
109 lockmgr(&kcollect_lock
, LK_EXCLUSIVE
);
110 if (n
>= 0 && n
< KCOLLECT_ENTRIES
) {
111 kcollect_slots
[n
] = NULL
;
112 kcollect_callback
[n
] = NULL
;
114 lockmgr(&kcollect_lock
, LK_RELEASE
);
118 * Typically called by a rollup function in the callback from the
119 * collection thread. Not usually called ad-hoc. This allows a
120 * subsystem to register several collection ids but only one callback
121 * which populates all of them.
124 kcollect_setvalue(int n
, uint64_t value
)
128 if (n
>= 0 && n
< KCOLLECT_ENTRIES
) {
129 i
= kcollect_index
% kcollect_samples
;
130 kcollect_ary
[i
].data
[n
] = value
;
135 * Callback to change scale adjustment, if necessary. Certain statistics
136 * have scale info available (such as KCOLLECT_SWAPANO and SWAPCAC).
139 kcollect_setscale(int n
, uint64_t value
)
141 if (n
>= 0 && n
< KCOLLECT_ENTRIES
) {
142 kcollect_scale
.data
[n
] = value
;
148 kcollect_thread(void *dummy
)
154 lockmgr(&kcollect_lock
, LK_EXCLUSIVE
);
155 i
= kcollect_index
% kcollect_samples
;
156 bzero(&kcollect_ary
[i
], sizeof(kcollect_ary
[i
]));
158 kcollect_ary
[i
].ticks
= ticks
;
159 getmicrotime(&kcollect_ary
[i
].realtime
);
161 for (n
= 0; n
< KCOLLECT_ENTRIES
; ++n
) {
162 if (kcollect_callback
[n
]) {
163 kcollect_ary
[i
].data
[n
] =
164 kcollect_callback
[n
](n
);
169 lockmgr(&kcollect_lock
, LK_RELEASE
);
170 tsleep(&dummy
, 0, "sleep", hz
* KCOLLECT_INTERVAL
);
178 sysctl_kcollect_data(SYSCTL_HANDLER_ARGS
)
187 if (kcollect_samples
== (uint32_t)-1 ||
188 kcollect_samples
== 0) {
193 count
= kcollect_index
;
194 start
= count
% kcollect_samples
;
195 if (count
>= kcollect_samples
)
196 count
= kcollect_samples
- 1;
201 if (req
->oldptr
== NULL
) {
202 error
= SYSCTL_OUT(req
, 0, sizeof(kcollect_t
) * (count
+ 2));
207 * Output request. We output a scale record, a string record, and
208 * N collection records. The strings in the string record can be
209 * up to 8 characters long, and if a string is 8 characters long it
210 * will not be zero-terminated.
212 * The low byte of the scale record specifies the format. To get
213 * the scale value shift right by 8.
215 if (kcollect_ary
== NULL
)
218 lockmgr(&kcollect_lock
, LK_EXCLUSIVE
);
219 scale
= kcollect_scale
;
223 bzero(&id
, sizeof(id
));
224 for (i
= 0; i
< KCOLLECT_ENTRIES
; ++i
) {
225 if (kcollect_slots
[i
]) {
226 char *ptr
= (char *)&id
.data
[i
];
227 size_t len
= strlen(kcollect_slots
[i
]);
228 if (len
> sizeof(id
.data
[0]))
229 len
= sizeof(id
.data
[0]);
230 bcopy(kcollect_slots
[i
], ptr
, len
);
233 lockmgr(&kcollect_lock
, LK_RELEASE
);
235 error
= SYSCTL_OUT(req
, &scale
, sizeof(scale
));
237 error
= SYSCTL_OUT(req
, &id
, sizeof(id
));
240 * Start at the current entry (not yet populated) and work
241 * backwards. This allows callers of the sysctl to acquire
242 * a lesser amount of data aligned to the most recent side of
247 if (req
->oldlen
- req
->oldidx
< sizeof(kcollect_t
))
250 i
= kcollect_samples
- 1;
253 error
= SYSCTL_OUT(req
, &kcollect_ary
[i
], sizeof(kcollect_t
));
260 SYSCTL_PROC(_kern
, OID_AUTO
, collect_data
,
261 CTLFLAG_RD
| CTLTYPE_STRUCT
, 0, 0,
262 sysctl_kcollect_data
, "S,kcollect", "Dump collected statistics");
266 kcollect_thread_init(void)
271 * Autosize sample retention (10 second interval)
273 if ((int)kcollect_samples
< 0) {
274 if (kmem_lim_size() < 1024)
275 kcollect_samples
= 1024;
277 kcollect_samples
= 8192;
280 if (kcollect_samples
) {
281 kcollect_ary
= kmalloc(kcollect_samples
* sizeof(kcollect_t
),
282 M_KCOLLECT
, M_WAITOK
| M_ZERO
);
283 lwkt_create(kcollect_thread
, NULL
, &td
, NULL
, 0, 0, "kcollect");
286 SYSINIT(kcol
, SI_SUB_HELPER_THREADS
, SI_ORDER_ANY
, kcollect_thread_init
, 0);