Deleted Added
full compact
devstat.c (50476) devstat.c (81133)
1/*
2 * Copyright (c) 1997, 1998 Kenneth D. Merry.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright

--- 11 unchanged lines hidden (view full) ---

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
26 * SUCH DAMAGE.
27 *
1/*
2 * Copyright (c) 1997, 1998 Kenneth D. Merry.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright

--- 11 unchanged lines hidden (view full) ---

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
26 * SUCH DAMAGE.
27 *
28 * $FreeBSD: head/lib/libdevstat/devstat.c 50476 1999-08-28 00:22:10Z peter $
28 * $FreeBSD: head/lib/libdevstat/devstat.c 81133 2001-08-04 18:25:48Z tmm $
29 */
30
31#include <sys/types.h>
32#include <sys/sysctl.h>
33#include <sys/errno.h>
34#include <sys/dkstat.h>
29 */
30
31#include <sys/types.h>
32#include <sys/sysctl.h>
33#include <sys/errno.h>
34#include <sys/dkstat.h>
35#include <sys/queue.h>
35
36#include <ctype.h>
37#include <err.h>
36
37#include <ctype.h>
38#include <err.h>
39#include <fcntl.h>
40#include <limits.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <stdarg.h>
45#include <kvm.h>
41
42#include "devstat.h"
43
46
47#include "devstat.h"
48
49typedef enum {
50 DEVSTAT_ARG_NOTYPE,
51 DEVSTAT_ARG_UINT64,
52 DEVSTAT_ARG_LD
53} devstat_arg_type;
54
44char devstat_errbuf[DEVSTAT_ERRBUF_SIZE];
45
46/*
47 * Table to match descriptive strings with device types. These are in
48 * order from most common to least common to speed search time.
49 */
50struct devstat_match_table match_table[] = {
51 {"da", DEVSTAT_TYPE_DIRECT, DEVSTAT_MATCH_TYPE},

--- 11 unchanged lines hidden (view full) ---

63 {"printer", DEVSTAT_TYPE_PRINTER, DEVSTAT_MATCH_TYPE},
64 {"floppy", DEVSTAT_TYPE_FLOPPY, DEVSTAT_MATCH_TYPE},
65 {"proc", DEVSTAT_TYPE_PROCESSOR, DEVSTAT_MATCH_TYPE},
66 {"comm", DEVSTAT_TYPE_COMM, DEVSTAT_MATCH_TYPE},
67 {"enclosure", DEVSTAT_TYPE_ENCLOSURE, DEVSTAT_MATCH_TYPE},
68 {NULL, 0, 0}
69};
70
55char devstat_errbuf[DEVSTAT_ERRBUF_SIZE];
56
57/*
58 * Table to match descriptive strings with device types. These are in
59 * order from most common to least common to speed search time.
60 */
61struct devstat_match_table match_table[] = {
62 {"da", DEVSTAT_TYPE_DIRECT, DEVSTAT_MATCH_TYPE},

--- 11 unchanged lines hidden (view full) ---

74 {"printer", DEVSTAT_TYPE_PRINTER, DEVSTAT_MATCH_TYPE},
75 {"floppy", DEVSTAT_TYPE_FLOPPY, DEVSTAT_MATCH_TYPE},
76 {"proc", DEVSTAT_TYPE_PROCESSOR, DEVSTAT_MATCH_TYPE},
77 {"comm", DEVSTAT_TYPE_COMM, DEVSTAT_MATCH_TYPE},
78 {"enclosure", DEVSTAT_TYPE_ENCLOSURE, DEVSTAT_MATCH_TYPE},
79 {NULL, 0, 0}
80};
81
82struct devstat_args {
83 devstat_metric metric;
84 devstat_arg_type argtype;
85} devstat_arg_list[] = {
86 { DSM_NONE, DEVSTAT_ARG_NOTYPE },
87 { DSM_TOTAL_BYTES, DEVSTAT_ARG_UINT64 },
88 { DSM_TOTAL_BYTES_READ, DEVSTAT_ARG_UINT64 },
89 { DSM_TOTAL_BYTES_WRITE, DEVSTAT_ARG_UINT64 },
90 { DSM_TOTAL_TRANSFERS, DEVSTAT_ARG_UINT64 },
91 { DSM_TOTAL_TRANSFERS_READ, DEVSTAT_ARG_UINT64 },
92 { DSM_TOTAL_TRANSFERS_WRITE, DEVSTAT_ARG_UINT64 },
93 { DSM_TOTAL_TRANSFERS_OTHER, DEVSTAT_ARG_UINT64 },
94 { DSM_TOTAL_BLOCKS, DEVSTAT_ARG_UINT64 },
95 { DSM_TOTAL_BLOCKS_READ, DEVSTAT_ARG_UINT64 },
96 { DSM_TOTAL_BLOCKS_WRITE, DEVSTAT_ARG_UINT64 },
97 { DSM_KB_PER_TRANSFER, DEVSTAT_ARG_LD },
98 { DSM_KB_PER_TRANSFER_READ, DEVSTAT_ARG_LD },
99 { DSM_KB_PER_TRANSFER_WRITE, DEVSTAT_ARG_LD },
100 { DSM_TRANSFERS_PER_SECOND, DEVSTAT_ARG_LD },
101 { DSM_TRANSFERS_PER_SECOND_READ, DEVSTAT_ARG_LD },
102 { DSM_TRANSFERS_PER_SECOND_WRITE, DEVSTAT_ARG_LD },
103 { DSM_TRANSFERS_PER_SECOND_OTHER, DEVSTAT_ARG_LD },
104 { DSM_MB_PER_SECOND, DEVSTAT_ARG_LD },
105 { DSM_MB_PER_SECOND_READ, DEVSTAT_ARG_LD },
106 { DSM_MB_PER_SECOND_WRITE, DEVSTAT_ARG_LD },
107 { DSM_BLOCKS_PER_SECOND, DEVSTAT_ARG_LD },
108 { DSM_BLOCKS_PER_SECOND_READ, DEVSTAT_ARG_LD },
109 { DSM_BLOCKS_PER_SECOND_WRITE, DEVSTAT_ARG_LD },
110 { DSM_MS_PER_TRANSACTION, DEVSTAT_ARG_LD },
111 { DSM_MS_PER_TRANSACTION_READ, DEVSTAT_ARG_LD },
112 { DSM_MS_PER_TRANSACTION_WRITE, DEVSTAT_ARG_LD }
113};
114
115static char *namelist[] = {
116#define X_NUMDEVS 0
117 "_devstat_num_devs",
118#define X_GENERATION 1
119 "_devstat_generation",
120#define X_VERSION 2
121 "_devstat_version",
122#define X_DEVICE_STATQ 3
123 "_device_statq",
124#define X_END 4
125};
126
71/*
72 * Local function declarations.
73 */
74static int compare_select(const void *arg1, const void *arg2);
127/*
128 * Local function declarations.
129 */
130static int compare_select(const void *arg1, const void *arg2);
131static int readkmem(kvm_t *kd, unsigned long addr, void *buf, size_t nbytes);
132static int readkmem_nl(kvm_t *kd, char *name, void *buf, size_t nbytes);
133static char *get_devstat_kvm(kvm_t *kd);
75
134
135#define KREADNL(kd, var, val) \
136 readkmem_nl(kd, namelist[var], &val, sizeof(val))
137
76int
138int
77getnumdevs(void)
139devstat_getnumdevs(kvm_t *kd)
78{
79 size_t numdevsize;
80 int numdevs;
140{
141 size_t numdevsize;
142 int numdevs;
81 char *func_name = "getnumdevs";
143 char *func_name = "devstat_getnumdevs";
82
83 numdevsize = sizeof(int);
84
85 /*
86 * Find out how many devices we have in the system.
87 */
144
145 numdevsize = sizeof(int);
146
147 /*
148 * Find out how many devices we have in the system.
149 */
88 if (sysctlbyname("kern.devstat.numdevs", &numdevs,
89 &numdevsize, NULL, 0) == -1) {
90 sprintf(devstat_errbuf, "%s: error getting number of devices\n"
91 "%s: %s", func_name, func_name, strerror(errno));
92 return(-1);
93 } else
94 return(numdevs);
150 if (kd == NULL) {
151 if (sysctlbyname("kern.devstat.numdevs", &numdevs,
152 &numdevsize, NULL, 0) == -1) {
153 snprintf(devstat_errbuf, sizeof(devstat_errbuf),
154 "%s: error getting number of devices\n"
155 "%s: %s", func_name, func_name,
156 strerror(errno));
157 return(-1);
158 } else
159 return(numdevs);
160 } else {
161 if (KREADNL(kd, X_NUMDEVS, numdevs) == -1)
162 return(-1);
163 else
164 return(numdevs);
165 }
95}
96
97/*
98 * This is an easy way to get the generation number, but the generation is
99 * supplied in a more atmoic manner by the kern.devstat.all sysctl.
100 * Because this generation sysctl is separate from the statistics sysctl,
101 * the device list and the generation could change between the time that
102 * this function is called and the device list is retreived.
103 */
104long
166}
167
168/*
169 * This is an easy way to get the generation number, but the generation is
170 * supplied in a more atmoic manner by the kern.devstat.all sysctl.
171 * Because this generation sysctl is separate from the statistics sysctl,
172 * the device list and the generation could change between the time that
173 * this function is called and the device list is retreived.
174 */
175long
105getgeneration(void)
176devstat_getgeneration(kvm_t *kd)
106{
107 size_t gensize;
108 long generation;
177{
178 size_t gensize;
179 long generation;
109 char *func_name = "getgeneration";
180 char *func_name = "devstat_getgeneration";
110
111 gensize = sizeof(long);
112
113 /*
114 * Get the current generation number.
115 */
181
182 gensize = sizeof(long);
183
184 /*
185 * Get the current generation number.
186 */
116 if (sysctlbyname("kern.devstat.generation", &generation,
117 &gensize, NULL, 0) == -1) {
118 sprintf(devstat_errbuf,"%s: error getting devstat generation\n"
119 "%s: %s", func_name, func_name, strerror(errno));
120 return(-1);
121 } else
122 return(generation);
187 if (kd == NULL) {
188 if (sysctlbyname("kern.devstat.generation", &generation,
189 &gensize, NULL, 0) == -1) {
190 snprintf(devstat_errbuf, sizeof(devstat_errbuf),
191 "%s: error getting devstat generation\n%s: %s",
192 func_name, func_name, strerror(errno));
193 return(-1);
194 } else
195 return(generation);
196 } else {
197 if (KREADNL(kd, X_GENERATION, generation) == -1)
198 return(-1);
199 else
200 return(generation);
201 }
123}
124
125/*
126 * Get the current devstat version. The return value of this function
127 * should be compared with DEVSTAT_VERSION, which is defined in
128 * sys/devicestat.h. This will enable userland programs to determine
129 * whether they are out of sync with the kernel.
130 */
131int
202}
203
204/*
205 * Get the current devstat version. The return value of this function
206 * should be compared with DEVSTAT_VERSION, which is defined in
207 * sys/devicestat.h. This will enable userland programs to determine
208 * whether they are out of sync with the kernel.
209 */
210int
132getversion(void)
211devstat_getversion(kvm_t *kd)
133{
134 size_t versize;
135 int version;
212{
213 size_t versize;
214 int version;
136 char *func_name = "getversion";
215 char *func_name = "devstat_getversion";
137
138 versize = sizeof(int);
139
140 /*
141 * Get the current devstat version.
142 */
216
217 versize = sizeof(int);
218
219 /*
220 * Get the current devstat version.
221 */
143 if (sysctlbyname("kern.devstat.version", &version, &versize,
144 NULL, 0) == -1) {
145 sprintf(devstat_errbuf, "%s: error getting devstat version\n"
146 "%s: %s", func_name, func_name, strerror(errno));
147 return(-1);
148 } else
149 return(version);
222 if (kd == NULL) {
223 if (sysctlbyname("kern.devstat.version", &version, &versize,
224 NULL, 0) == -1) {
225 snprintf(devstat_errbuf, sizeof(devstat_errbuf),
226 "%s: error getting devstat version\n%s: %s",
227 func_name, func_name, strerror(errno));
228 return(-1);
229 } else
230 return(version);
231 } else {
232 if (KREADNL(kd, X_VERSION, version) == -1)
233 return(-1);
234 else
235 return(version);
236 }
150}
151
152/*
153 * Check the devstat version we know about against the devstat version the
154 * kernel knows about. If they don't match, print an error into the
155 * devstat error buffer, and return -1. If they match, return 0.
156 */
157int
237}
238
239/*
240 * Check the devstat version we know about against the devstat version the
241 * kernel knows about. If they don't match, print an error into the
242 * devstat error buffer, and return -1. If they match, return 0.
243 */
244int
158checkversion(void)
245devstat_checkversion(kvm_t *kd)
159{
160 int retval = 0;
161 int errlen = 0;
246{
247 int retval = 0;
248 int errlen = 0;
162 char *func_name = "checkversion";
249 char *func_name = "devstat_checkversion";
163 int version;
164
250 int version;
251
165 version = getversion();
252 version = devstat_getversion(kd);
166
167 if (version != DEVSTAT_VERSION) {
168 int buflen = 0;
169 char tmpstr[256];
170
171 /*
172 * This is really pretty silly, but basically the idea is
173 * that if getversion() returns an error (i.e. -1), then it

--- 47 unchanged lines hidden (view full) ---

221 * generation number.
222 *
223 * Return values:
224 * -1 -- error
225 * 0 -- device list is unchanged
226 * 1 -- device list has changed
227 */
228int
253
254 if (version != DEVSTAT_VERSION) {
255 int buflen = 0;
256 char tmpstr[256];
257
258 /*
259 * This is really pretty silly, but basically the idea is
260 * that if getversion() returns an error (i.e. -1), then it

--- 47 unchanged lines hidden (view full) ---

308 * generation number.
309 *
310 * Return values:
311 * -1 -- error
312 * 0 -- device list is unchanged
313 * 1 -- device list has changed
314 */
315int
229getdevs(struct statinfo *stats)
316devstat_getdevs(kvm_t *kd, struct statinfo *stats)
230{
231 int error;
232 size_t dssize;
233 int oldnumdevs;
234 long oldgeneration;
235 int retval = 0;
236 struct devinfo *dinfo;
317{
318 int error;
319 size_t dssize;
320 int oldnumdevs;
321 long oldgeneration;
322 int retval = 0;
323 struct devinfo *dinfo;
237 char *func_name = "getdevs";
324 char *func_name = "devstat_getdevs";
238
239 dinfo = stats->dinfo;
240
241 if (dinfo == NULL) {
325
326 dinfo = stats->dinfo;
327
328 if (dinfo == NULL) {
242 sprintf(devstat_errbuf, "%s: stats->dinfo was NULL", func_name);
329 snprintf(devstat_errbuf, sizeof(devstat_errbuf),
330 "%s: stats->dinfo was NULL", func_name);
243 return(-1);
244 }
245
246 oldnumdevs = dinfo->numdevs;
247 oldgeneration = dinfo->generation;
248
331 return(-1);
332 }
333
334 oldnumdevs = dinfo->numdevs;
335 oldgeneration = dinfo->generation;
336
249 /*
250 * If this is our first time through, mem_ptr will be null.
251 */
252 if (dinfo->mem_ptr == NULL) {
253 /*
254 * Get the number of devices. If it's negative, it's an
255 * error. Don't bother setting the error string, since
256 * getnumdevs() has already done that for us.
257 */
258 if ((dinfo->numdevs = getnumdevs()) < 0)
259 return(-1);
260
261 /*
262 * The kern.devstat.all sysctl returns the current generation
263 * number, as well as all the devices. So we need four
264 * bytes more.
265 */
266 dssize =(dinfo->numdevs * sizeof(struct devstat)) +sizeof(long);
267 dinfo->mem_ptr = (u_int8_t *)malloc(dssize);
268 } else
269 dssize =(dinfo->numdevs * sizeof(struct devstat)) +sizeof(long);
270
271 /* Get the current time when we get the stats */
272 gettimeofday(&stats->busy_time, NULL);
273
337 /* Get the current time when we get the stats */
338 gettimeofday(&stats->busy_time, NULL);
339
274 /*
275 * Request all of the devices. We only really allow for one
276 * ENOMEM failure. It would, of course, be possible to just go in
277 * a loop and keep reallocing the device structure until we don't
278 * get ENOMEM back. I'm not sure it's worth it, though. If
279 * devices are being added to the system that quickly, maybe the
280 * user can just wait until all devices are added.
281 */
282 if ((error = sysctlbyname("kern.devstat.all", dinfo->mem_ptr,
283 &dssize, NULL, 0)) == -1) {
284 /*
285 * If we get ENOMEM back, that means that there are
286 * more devices now, so we need to allocate more
287 * space for the device array.
288 */
289 if (errno == ENOMEM) {
340 if (kd == NULL) {
341 /* If this is our first time through, mem_ptr will be null. */
342 if (dinfo->mem_ptr == NULL) {
290 /*
343 /*
291 * No need to set the error string here, getnumdevs()
292 * will do that if it fails.
344 * Get the number of devices. If it's negative, it's an
345 * error. Don't bother setting the error string, since
346 * getnumdevs() has already done that for us.
293 */
294 if ((dinfo->numdevs = getnumdevs()) < 0)
295 return(-1);
347 */
348 if ((dinfo->numdevs = getnumdevs()) < 0)
349 return(-1);
296
350
351 /*
352 * The kern.devstat.all sysctl returns the current
353 * generation number, as well as all the devices.
354 * So we need four bytes more.
355 */
297 dssize = (dinfo->numdevs * sizeof(struct devstat)) +
356 dssize = (dinfo->numdevs * sizeof(struct devstat)) +
298 sizeof(long);
299 dinfo->mem_ptr = (u_int8_t *)realloc(dinfo->mem_ptr,
300 dssize);
301 if ((error = sysctlbyname("kern.devstat.all",
302 dinfo->mem_ptr, &dssize, NULL, 0)) == -1) {
303 sprintf(devstat_errbuf,
357 sizeof(long);
358 dinfo->mem_ptr = (u_int8_t *)malloc(dssize);
359 } else
360 dssize = (dinfo->numdevs * sizeof(struct devstat)) +
361 sizeof(long);
362
363 /*
364 * Request all of the devices. We only really allow for one
365 * ENOMEM failure. It would, of course, be possible to just go
366 * in a loop and keep reallocing the device structure until we
367 * don't get ENOMEM back. I'm not sure it's worth it, though.
368 * If devices are being added to the system that quickly, maybe
369 * the user can just wait until all devices are added.
370 */
371 if ((error = sysctlbyname("kern.devstat.all", dinfo->mem_ptr,
372 &dssize, NULL, 0)) == -1) {
373 /*
374 * If we get ENOMEM back, that means that there are
375 * more devices now, so we need to allocate more
376 * space for the device array.
377 */
378 if (errno == ENOMEM) {
379 /*
380 * No need to set the error string here,
381 * getnumdevs() will do that if it fails.
382 */
383 if ((dinfo->numdevs = getnumdevs()) < 0)
384 return(-1);
385
386 dssize = (dinfo->numdevs *
387 sizeof(struct devstat)) + sizeof(long);
388 dinfo->mem_ptr = (u_int8_t *)
389 realloc(dinfo->mem_ptr, dssize);
390 if ((error = sysctlbyname("kern.devstat.all",
391 dinfo->mem_ptr, &dssize, NULL, 0)) == -1) {
392 snprintf(devstat_errbuf,
393 sizeof(devstat_errbuf),
394 "%s: error getting device "
395 "stats\n%s: %s", func_name,
396 func_name, strerror(errno));
397 return(-1);
398 }
399 } else {
400 snprintf(devstat_errbuf, sizeof(devstat_errbuf),
304 "%s: error getting device stats\n"
305 "%s: %s", func_name, func_name,
306 strerror(errno));
307 return(-1);
308 }
401 "%s: error getting device stats\n"
402 "%s: %s", func_name, func_name,
403 strerror(errno));
404 return(-1);
405 }
309 } else {
310 sprintf(devstat_errbuf,
311 "%s: error getting device stats\n"
312 "%s: %s", func_name, func_name,
313 strerror(errno));
314 return(-1);
315 }
316 }
406 }
317
407
408 } else {
409 /*
410 * This is of course non-atomic, but since we are working
411 * on a core dump, the generation is unlikely to change
412 */
413 if ((dinfo->numdevs = getnumdevs()) == -1)
414 return(-1);
415 if ((dinfo->mem_ptr = get_devstat_kvm(kd)) == NULL)
416 return(-1);
417 }
318 /*
319 * The sysctl spits out the generation as the first four bytes,
320 * then all of the device statistics structures.
321 */
322 dinfo->generation = *(long *)dinfo->mem_ptr;
323
324 /*
325 * If the generation has changed, and if the current number of

--- 84 unchanged lines hidden (view full) ---

410 * - the selection generation may be changed to match the current generation
411 *
412 * Return values:
413 * -1 -- error
414 * 0 -- selected devices are unchanged
415 * 1 -- selected devices changed
416 */
417int
418 /*
419 * The sysctl spits out the generation as the first four bytes,
420 * then all of the device statistics structures.
421 */
422 dinfo->generation = *(long *)dinfo->mem_ptr;
423
424 /*
425 * If the generation has changed, and if the current number of

--- 84 unchanged lines hidden (view full) ---

510 * - the selection generation may be changed to match the current generation
511 *
512 * Return values:
513 * -1 -- error
514 * 0 -- selected devices are unchanged
515 * 1 -- selected devices changed
516 */
517int
418selectdevs(struct device_selection **dev_select, int *num_selected,
419 int *num_selections, long *select_generation,
420 long current_generation, struct devstat *devices, int numdevs,
421 struct devstat_match *matches, int num_matches,
422 char **dev_selections, int num_dev_selections,
423 devstat_select_mode select_mode, int maxshowdevs, int perf_select)
518devstat_selectdevs(struct device_selection **dev_select, int *num_selected,
519 int *num_selections, long *select_generation,
520 long current_generation, struct devstat *devices,
521 int numdevs, struct devstat_match *matches, int num_matches,
522 char **dev_selections, int num_dev_selections,
523 devstat_select_mode select_mode, int maxshowdevs,
524 int perf_select)
424{
425 register int i, j, k;
426 int init_selections = 0, init_selected_var = 0;
427 struct device_selection *old_dev_select = NULL;
428 int old_num_selections = 0, old_num_selected;
429 int selection_number = 0;
430 int changed = 0, found = 0;
431

--- 442 unchanged lines hidden (view full) ---

874 return(0);
875}
876
877/*
878 * Take a string with the general format "arg1,arg2,arg3", and build a
879 * device matching expression from it.
880 */
881int
525{
526 register int i, j, k;
527 int init_selections = 0, init_selected_var = 0;
528 struct device_selection *old_dev_select = NULL;
529 int old_num_selections = 0, old_num_selected;
530 int selection_number = 0;
531 int changed = 0, found = 0;
532

--- 442 unchanged lines hidden (view full) ---

975 return(0);
976}
977
978/*
979 * Take a string with the general format "arg1,arg2,arg3", and build a
980 * device matching expression from it.
981 */
982int
882buildmatch(char *match_str, struct devstat_match **matches, int *num_matches)
983devstat_buildmatch(char *match_str, struct devstat_match **matches,
984 int *num_matches)
883{
884 char *tstr[5];
885 char **tempstr;
886 int num_args;
887 register int i, j;
985{
986 char *tstr[5];
987 char **tempstr;
988 int num_args;
989 register int i, j;
888 char *func_name = "buildmatch";
990 char *func_name = "devstat_buildmatch";
889
890 /* We can't do much without a string to parse */
891 if (match_str == NULL) {
991
992 /* We can't do much without a string to parse */
993 if (match_str == NULL) {
892 sprintf(devstat_errbuf, "%s: no match expression", func_name);
994 snprintf(devstat_errbuf, sizeof(devstat_errbuf),
995 "%s: no match expression", func_name);
893 return(-1);
894 }
895
896 /*
897 * Break the (comma delimited) input string out into separate strings.
898 */
899 for (tempstr = tstr, num_args = 0;
900 (*tempstr = strsep(&match_str, ",")) != NULL && (num_args < 5);
901 num_args++)
902 if (**tempstr != '\0')
903 if (++tempstr >= &tstr[5])
904 break;
905
906 /* The user gave us too many type arguments */
907 if (num_args > 3) {
996 return(-1);
997 }
998
999 /*
1000 * Break the (comma delimited) input string out into separate strings.
1001 */
1002 for (tempstr = tstr, num_args = 0;
1003 (*tempstr = strsep(&match_str, ",")) != NULL && (num_args < 5);
1004 num_args++)
1005 if (**tempstr != '\0')
1006 if (++tempstr >= &tstr[5])
1007 break;
1008
1009 /* The user gave us too many type arguments */
1010 if (num_args > 3) {
908 sprintf(devstat_errbuf, "%s: too many type arguments",
909 func_name);
1011 snprintf(devstat_errbuf, sizeof(devstat_errbuf),
1012 "%s: too many type arguments", func_name);
910 return(-1);
911 }
912
913 /*
914 * Since you can't realloc a pointer that hasn't been malloced
915 * first, we malloc first and then realloc.
916 */
917 if (*num_matches == 0)

--- 48 unchanged lines hidden (view full) ---

966 strlen(match_table[j].match_str)) == 0) {
967 /*
968 * Make sure the user hasn't specified two
969 * items of the same type, like "da" and
970 * "cd". One device cannot be both.
971 */
972 if (((*matches)[*num_matches].match_fields &
973 match_table[j].match_field) != 0) {
1013 return(-1);
1014 }
1015
1016 /*
1017 * Since you can't realloc a pointer that hasn't been malloced
1018 * first, we malloc first and then realloc.
1019 */
1020 if (*num_matches == 0)

--- 48 unchanged lines hidden (view full) ---

1069 strlen(match_table[j].match_str)) == 0) {
1070 /*
1071 * Make sure the user hasn't specified two
1072 * items of the same type, like "da" and
1073 * "cd". One device cannot be both.
1074 */
1075 if (((*matches)[*num_matches].match_fields &
1076 match_table[j].match_field) != 0) {
974 sprintf(devstat_errbuf,
1077 snprintf(devstat_errbuf,
1078 sizeof(devstat_errbuf),
975 "%s: cannot have more than "
976 "one match item in a single "
977 "category", func_name);
978 return(-1);
979 }
980 /*
981 * If we've gotten this far, we have a
982 * winner. Set the appropriate fields in

--- 44 unchanged lines hidden (view full) ---

1027{
1028 u_int64_t totalbytes, totaltransfers, totalblocks;
1029 char *func_name = "compute_stats";
1030
1031 /*
1032 * current is the only mandatory field.
1033 */
1034 if (current == NULL) {
1079 "%s: cannot have more than "
1080 "one match item in a single "
1081 "category", func_name);
1082 return(-1);
1083 }
1084 /*
1085 * If we've gotten this far, we have a
1086 * winner. Set the appropriate fields in

--- 44 unchanged lines hidden (view full) ---

1131{
1132 u_int64_t totalbytes, totaltransfers, totalblocks;
1133 char *func_name = "compute_stats";
1134
1135 /*
1136 * current is the only mandatory field.
1137 */
1138 if (current == NULL) {
1035 sprintf(devstat_errbuf, "%s: current stats structure was NULL",
1139 snprintf(devstat_errbuf, sizeof(devstat_errbuf),
1140 "%s: current stats structure was NULL",
1036 func_name);
1037 return(-1);
1038 }
1039
1040 totalbytes = (current->bytes_written + current->bytes_read) -
1041 ((previous) ? (previous->bytes_written +
1042 previous->bytes_read) : 0);
1043

--- 61 unchanged lines hidden (view full) ---

1105 } else
1106 *ms_per_transaction = 0.0;
1107 }
1108
1109 return(0);
1110}
1111
1112long double
1141 func_name);
1142 return(-1);
1143 }
1144
1145 totalbytes = (current->bytes_written + current->bytes_read) -
1146 ((previous) ? (previous->bytes_written +
1147 previous->bytes_read) : 0);
1148

--- 61 unchanged lines hidden (view full) ---

1210 } else
1211 *ms_per_transaction = 0.0;
1212 }
1213
1214 return(0);
1215}
1216
1217long double
1113compute_etime(struct timeval cur_time, struct timeval prev_time)
1218devstat_compute_etime(struct timeval cur_time, struct timeval prev_time)
1114{
1115 struct timeval busy_time;
1116 u_int64_t busy_usec;
1117 long double etime;
1118
1119 timersub(&cur_time, &prev_time, &busy_time);
1120
1121 busy_usec = busy_time.tv_sec;
1122 busy_usec *= 1000000;
1123 busy_usec += busy_time.tv_usec;
1124 etime = busy_usec;
1125 etime /= 1000000;
1126
1127 return(etime);
1128}
1219{
1220 struct timeval busy_time;
1221 u_int64_t busy_usec;
1222 long double etime;
1223
1224 timersub(&cur_time, &prev_time, &busy_time);
1225
1226 busy_usec = busy_time.tv_sec;
1227 busy_usec *= 1000000;
1228 busy_usec += busy_time.tv_usec;
1229 etime = busy_usec;
1230 etime /= 1000000;
1231
1232 return(etime);
1233}
1234
1235int
1236devstat_compute_statistics(struct devstat *current, struct devstat *previous,
1237 long double etime, ...)
1238{
1239 char *func_name = "devstat_compute_statistics";
1240 u_int64_t totalbytes, totalbytesread, totalbyteswrite;
1241 u_int64_t totaltransfers, totaltransfersread, totaltransferswrite;
1242 u_int64_t totaltransfersother, totalblocks, totalblocksread;
1243 u_int64_t totalblockswrite;
1244 va_list ap;
1245 devstat_metric metric;
1246 u_int64_t *destu64;
1247 long double *destld;
1248 int retval;
1249
1250 retval = 0;
1251
1252 /*
1253 * current is the only mandatory field.
1254 */
1255 if (current == NULL) {
1256 snprintf(devstat_errbuf, sizeof(devstat_errbuf),
1257 "%s: current stats structure was NULL", func_name);
1258 return(-1);
1259 }
1260
1261 totalbytesread = current->bytes_read -
1262 ((previous) ? previous->bytes_read : 0);
1263 totalbyteswrite = current->bytes_written -
1264 ((previous) ? previous->bytes_written : 0);
1265
1266 totalbytes = totalbytesread + totalbyteswrite;
1267
1268 totaltransfersread = current->num_reads -
1269 ((previous) ? previous->num_reads : 0);
1270
1271 totaltransferswrite = current->num_writes -
1272 ((previous) ? previous->num_writes : 0);
1273
1274 totaltransfersother = current->num_other -
1275 ((previous) ? previous->num_other : 0);
1276
1277 totaltransfers = totaltransfersread + totaltransferswrite +
1278 totaltransfersother;
1279
1280 totalblocks = totalbytes;
1281 totalblocksread = totalbytesread;
1282 totalblockswrite = totalbyteswrite;
1283
1284 if (current->block_size > 0) {
1285 totalblocks /= current->block_size;
1286 totalblocksread /= current->block_size;
1287 totalblockswrite /= current->block_size;
1288 } else {
1289 totalblocks /= 512;
1290 totalblocksread /= 512;
1291 totalblockswrite /= 512;
1292 }
1293
1294 va_start(ap, etime);
1295
1296 while ((metric = (devstat_metric)va_arg(ap, devstat_metric)) != 0) {
1297
1298 if (metric == DSM_NONE)
1299 break;
1300
1301 if (metric >= DSM_MAX) {
1302 snprintf(devstat_errbuf, sizeof(devstat_errbuf),
1303 "%s: metric %d is out of range", func_name,
1304 metric);
1305 retval = -1;
1306 goto bailout;
1307 }
1308
1309 switch (devstat_arg_list[metric].argtype) {
1310 case DEVSTAT_ARG_UINT64:
1311 destu64 = (u_int64_t *)va_arg(ap, u_int64_t *);
1312 if (destu64 == NULL) {
1313 snprintf(devstat_errbuf, sizeof(devstat_errbuf),
1314 "%s: argument type not u_int64_t * or "
1315 "argument type missing", func_name);
1316 retval = -1;
1317 goto bailout;
1318 break; /* NOTREACHED */
1319 }
1320 break;
1321 case DEVSTAT_ARG_LD:
1322 destld = (long double *)va_arg(ap, long double *);
1323 if (destld == NULL) {
1324 snprintf(devstat_errbuf, sizeof(devstat_errbuf),
1325 "%s: argument type not long double * "
1326 "or argument type missing", func_name);
1327 retval = -1;
1328 goto bailout;
1329 break; /* NOTREACHED */
1330 }
1331 break;
1332 default:
1333 snprintf(devstat_errbuf, sizeof(devstat_errbuf),
1334 "%s: unknown argument type %d", func_name,
1335 devstat_arg_list[metric].argtype);
1336 retval = -1;
1337 goto bailout;
1338 break; /* NOTREACHED */
1339 }
1340
1341 switch (metric) {
1342 case DSM_TOTAL_BYTES:
1343 *destu64 = totalbytes;
1344 break;
1345 case DSM_TOTAL_BYTES_READ:
1346 *destu64 = totalbytesread;
1347 break;
1348 case DSM_TOTAL_BYTES_WRITE:
1349 *destu64 = totalbyteswrite;
1350 break;
1351 case DSM_TOTAL_TRANSFERS:
1352 *destu64 = totaltransfers;
1353 break;
1354 case DSM_TOTAL_TRANSFERS_READ:
1355 *destu64 = totaltransfersread;
1356 break;
1357 case DSM_TOTAL_TRANSFERS_WRITE:
1358 *destu64 = totaltransferswrite;
1359 break;
1360 case DSM_TOTAL_TRANSFERS_OTHER:
1361 *destu64 = totaltransfersother;
1362 break;
1363 case DSM_TOTAL_BLOCKS:
1364 *destu64 = totalblocks;
1365 break;
1366 case DSM_TOTAL_BLOCKS_READ:
1367 *destu64 = totalblocksread;
1368 break;
1369 case DSM_TOTAL_BLOCKS_WRITE:
1370 *destu64 = totalblockswrite;
1371 break;
1372 case DSM_KB_PER_TRANSFER:
1373 *destld = totalbytes;
1374 *destld /= 1024;
1375 if (totaltransfers > 0)
1376 *destld /= totaltransfers;
1377 else
1378 *destld = 0.0;
1379 break;
1380 case DSM_KB_PER_TRANSFER_READ:
1381 *destld = totalbytesread;
1382 *destld /= 1024;
1383 if (totaltransfersread > 0)
1384 *destld /= totaltransfersread;
1385 else
1386 *destld = 0.0;
1387 break;
1388 case DSM_KB_PER_TRANSFER_WRITE:
1389 *destld = totalbyteswrite;
1390 *destld /= 1024;
1391 if (totaltransferswrite > 0)
1392 *destld /= totaltransferswrite;
1393 else
1394 *destld = 0.0;
1395 break;
1396 case DSM_TRANSFERS_PER_SECOND:
1397 if (etime > 0.0) {
1398 *destld = totaltransfers;
1399 *destld /= etime;
1400 } else
1401 *destld = 0.0;
1402 break;
1403 case DSM_TRANSFERS_PER_SECOND_READ:
1404 if (etime > 0.0) {
1405 *destld = totaltransfersread;
1406 *destld /= etime;
1407 } else
1408 *destld = 0.0;
1409 break;
1410 case DSM_TRANSFERS_PER_SECOND_WRITE:
1411 if (etime > 0.0) {
1412 *destld = totaltransferswrite;
1413 *destld /= etime;
1414 } else
1415 *destld = 0.0;
1416 break;
1417 case DSM_TRANSFERS_PER_SECOND_OTHER:
1418 if (etime > 0.0) {
1419 *destld = totaltransfersother;
1420 *destld /= etime;
1421 } else
1422 *destld = 0.0;
1423 break;
1424 case DSM_MB_PER_SECOND:
1425 *destld = totalbytes;
1426 *destld /= 1024 * 1024;
1427 if (etime > 0.0)
1428 *destld /= etime;
1429 else
1430 *destld = 0.0;
1431 break;
1432 case DSM_MB_PER_SECOND_READ:
1433 *destld = totalbytesread;
1434 *destld /= 1024 * 1024;
1435 if (etime > 0.0)
1436 *destld /= etime;
1437 else
1438 *destld = 0.0;
1439 break;
1440 case DSM_MB_PER_SECOND_WRITE:
1441 *destld = totalbyteswrite;
1442 *destld /= 1024 * 1024;
1443 if (etime > 0.0)
1444 *destld /= etime;
1445 else
1446 *destld = 0.0;
1447 break;
1448 case DSM_BLOCKS_PER_SECOND:
1449 *destld = totalblocks;
1450 if (etime > 0.0)
1451 *destld /= etime;
1452 else
1453 *destld = 0.0;
1454 break;
1455 case DSM_BLOCKS_PER_SECOND_READ:
1456 *destld = totalblocksread;
1457 if (etime > 0.0)
1458 *destld /= etime;
1459 else
1460 *destld = 0.0;
1461 break;
1462 case DSM_BLOCKS_PER_SECOND_WRITE:
1463 *destld = totalblockswrite;
1464 if (etime > 0.0)
1465 *destld /= etime;
1466 else
1467 *destld = 0.0;
1468 break;
1469 /*
1470 * This calculation is somewhat bogus. It simply divides
1471 * the elapsed time by the total number of transactions
1472 * completed. While that does give the caller a good
1473 * picture of the average rate of transaction completion,
1474 * it doesn't necessarily give the caller a good view of
1475 * how long transactions took to complete on average.
1476 * Those two numbers will be different for a device that
1477 * can handle more than one transaction at a time. e.g.
1478 * SCSI disks doing tagged queueing.
1479 *
1480 * The only way to accurately determine the real average
1481 * time per transaction would be to compute and store the
1482 * time on a per-transaction basis. That currently isn't
1483 * done in the kernel, and would only be desireable if it
1484 * could be implemented in a somewhat non-intrusive and high
1485 * performance way.
1486 */
1487 case DSM_MS_PER_TRANSACTION:
1488 if (totaltransfers > 0) {
1489 *destld = etime;
1490 *destld /= totaltransfers;
1491 *destld *= 1000;
1492 } else
1493 *destld = 0.0;
1494 break;
1495 /*
1496 * As above, these next two really only give the average
1497 * rate of completion for read and write transactions, not
1498 * the average time the transaction took to complete.
1499 */
1500 case DSM_MS_PER_TRANSACTION_READ:
1501 if (totaltransfersread > 0) {
1502 *destld = etime;
1503 *destld /= totaltransfersread;
1504 *destld *= 1000;
1505 } else
1506 *destld = 0.0;
1507 break;
1508 case DSM_MS_PER_TRANSACTION_WRITE:
1509 if (totaltransferswrite > 0) {
1510 *destld = etime;
1511 *destld /= totaltransferswrite;
1512 *destld *= 1000;
1513 } else
1514 *destld = 0.0;
1515 break;
1516 default:
1517 /*
1518 * This shouldn't happen, since we should have
1519 * caught any out of range metrics at the top of
1520 * the loop.
1521 */
1522 snprintf(devstat_errbuf, sizeof(devstat_errbuf),
1523 "%s: unknown metric %d", func_name, metric);
1524 retval = -1;
1525 goto bailout;
1526 break; /* NOTREACHED */
1527 }
1528 }
1529
1530bailout:
1531
1532 va_end(ap);
1533 return(retval);
1534}
1535
1536static int
1537readkmem(kvm_t *kd, unsigned long addr, void *buf, size_t nbytes)
1538{
1539 char *func_name = "readkmem";
1540
1541 if (kvm_read(kd, addr, buf, nbytes) == -1) {
1542 snprintf(devstat_errbuf, sizeof(devstat_errbuf),
1543 "%s: error reading value (kvm_read): %s", func_name,
1544 kvm_geterr(kd));
1545 return(-1);
1546 }
1547 return(0);
1548}
1549
1550static int
1551readkmem_nl(kvm_t *kd, char *name, void *buf, size_t nbytes)
1552{
1553 char *func_name = "readkmem_nl";
1554 struct nlist nl[2] = { { name }, { NULL } };
1555
1556 if (kvm_nlist(kd, nl) == -1) {
1557 snprintf(devstat_errbuf, sizeof(devstat_errbuf),
1558 "%s: error getting name list (kvm_nlist): %s",
1559 func_name, kvm_geterr(kd));
1560 return(-1);
1561 }
1562 return(readkmem(kd, nl[0].n_value, buf, nbytes));
1563}
1564
1565/*
1566 * This duplicates the functionality of the kernel sysctl handler for poking
1567 * through crash dumps.
1568 */
1569static char *
1570get_devstat_kvm(kvm_t *kd)
1571{
1572 int error, i, wp;
1573 long gen;
1574 struct devstat *nds;
1575 struct devstat ds;
1576 struct devstatlist dhead;
1577 int num_devs;
1578 char *rv = NULL;
1579 char *func_name = "get_devstat_kvm";
1580
1581 if ((num_devs = getnumdevs()) <= 0)
1582 return(NULL);
1583 error = 0;
1584 if (KREADNL(kd, X_DEVICE_STATQ, dhead) == -1)
1585 return(NULL);
1586
1587 nds = STAILQ_FIRST(&dhead);
1588
1589 if ((rv = malloc(sizeof(gen))) == NULL) {
1590 snprintf(devstat_errbuf, sizeof(devstat_errbuf),
1591 "%s: out of memory (initial malloc failed)",
1592 func_name);
1593 return(NULL);
1594 }
1595 gen = getgeneration();
1596 memcpy(rv, &gen, sizeof(gen));
1597 wp = sizeof(gen);
1598 /*
1599 * Now push out all the devices.
1600 */
1601 for (i = 0; (nds != NULL) && (i < num_devs);
1602 nds = STAILQ_NEXT(nds, dev_links), i++) {
1603 if (readkmem(kd, (long)nds, &ds, sizeof(ds)) == -1) {
1604 free(rv);
1605 return(NULL);
1606 }
1607 nds = &ds;
1608 rv = (char *)reallocf(rv, sizeof(gen) +
1609 sizeof(ds) * (i + 1));
1610 if (rv == NULL) {
1611 snprintf(devstat_errbuf, sizeof(devstat_errbuf),
1612 "%s: out of memory (malloc failed)",
1613 func_name);
1614 return(NULL);
1615 }
1616 memcpy(rv + wp, &ds, sizeof(ds));
1617 wp += sizeof(ds);
1618 }
1619 return(rv);
1620}
1621
1622/*
1623 * Compatability functions for libdevstat 2. These are deprecated and may
1624 * eventually be removed.
1625 */
1626int
1627getnumdevs(void)
1628{
1629 return(devstat_getnumdevs(NULL));
1630}
1631
1632long
1633getgeneration(void)
1634{
1635 return(devstat_getgeneration(NULL));
1636}
1637
1638int
1639getversion(void)
1640{
1641 return(devstat_getversion(NULL));
1642}
1643
1644int
1645checkversion(void)
1646{
1647 return(devstat_checkversion(NULL));
1648}
1649
1650int
1651getdevs(struct statinfo *stats)
1652{
1653 return(devstat_getdevs(NULL, stats));
1654}
1655
1656int
1657selectdevs(struct device_selection **dev_select, int *num_selected,
1658 int *num_selections, long *select_generation,
1659 long current_generation, struct devstat *devices, int numdevs,
1660 struct devstat_match *matches, int num_matches,
1661 char **dev_selections, int num_dev_selections,
1662 devstat_select_mode select_mode, int maxshowdevs,
1663 int perf_select)
1664{
1665
1666 return(devstat_selectdevs(dev_select, num_selected, num_selections,
1667 select_generation, current_generation, devices, numdevs,
1668 matches, num_matches, dev_selections, num_dev_selections,
1669 select_mode, maxshowdevs, perf_select));
1670}
1671
1672int
1673buildmatch(char *match_str, struct devstat_match **matches,
1674 int *num_matches)
1675{
1676 return(devstat_buildmatch(match_str, matches, num_matches));
1677}
1678
1679long double
1680compute_etime(struct timeval cur_time, struct timeval prev_time)
1681{
1682 return(devstat_compute_etime(cur_time, prev_time));
1683}