]>
Commit | Line | Data |
---|---|---|
c3a08f59 | 1 | /* |
83f6dbe8 A |
2 | * Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights |
3 | * Reserved. | |
c3a08f59 | 4 | * |
83f6dbe8 A |
5 | * This file contains Original Code and/or Modifications of Original Code |
6 | * as defined in and that are subject to the Apple Public Source License | |
7 | * Version 2.0 (the 'License'). You may not use this file except in | |
8 | * compliance with the License. Please obtain a copy of the License at | |
9 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
10 | * file. | |
c3a08f59 A |
11 | * |
12 | * The Original Code and all software distributed under the License are | |
13 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
83f6dbe8 A |
16 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
17 | * Please see the License for the specific language governing rights and | |
18 | * limitations under the License. | |
19 | */ | |
c3a08f59 A |
20 | |
21 | #define IOKIT 1 /* to get io_name_t in device_types.h */ | |
22 | ||
23 | #include <stdio.h> | |
24 | #include <stdlib.h> | |
25 | #include <unistd.h> | |
26 | #include <ctype.h> | |
27 | #include <time.h> | |
28 | #include <err.h> | |
29 | #include <fcntl.h> | |
30 | #include <errno.h> | |
c3a08f59 A |
31 | #include <mach/mach.h> |
32 | #include <mach/mach_error.h> | |
33 | #include <sys/param.h> | |
34 | ||
35 | #include <CoreFoundation/CoreFoundation.h> | |
36 | #include <IOKit/IOKitLib.h> | |
37 | #include <IOKit/storage/IOBlockStorageDriver.h> | |
38 | #include <IOKit/storage/IOMedia.h> | |
39 | #include <IOKit/IOBSD.h> | |
40 | ||
41 | #include <sys/socket.h> | |
42 | #include <net/if.h> | |
43 | #include <net/if_var.h> | |
2fc1e207 | 44 | #include <ifaddrs.h> |
c3a08f59 A |
45 | |
46 | #include <sadc.h> | |
47 | ||
48 | extern int errno; | |
49 | ||
50 | FILE *data_fp = (FILE *)0; /* raw data output file pointer */ | |
51 | ||
52 | ||
53 | #define REVISION_HISTORY_DATE 20030718 | |
54 | ||
55 | struct record_hdr restart_record = { SAR_RESTART, REVISION_HISTORY_DATE, 0, 0 }; | |
56 | struct record_hdr timestamp_record = { SAR_TIMESTAMP, 1, 0, 0 }; | |
57 | struct record_hdr vmstat_record = {SAR_VMSTAT, 1, 1, 0 }; | |
58 | struct record_hdr cpu_record = {SAR_CPU, 1, 1, 0 }; | |
59 | struct record_hdr drivestats_record = {SAR_DRIVESTATS, 1, 0, 0 }; | |
60 | struct record_hdr drivepath_record = {SAR_DRIVEPATH, 1, 1, 0 }; | |
61 | struct record_hdr netstats_record = {SAR_NETSTATS, 1, 0, 0}; | |
62 | ||
63 | /* Compile for verbose output */ | |
64 | ||
65 | int t_interval = 0; /* in seconds */ | |
66 | int n_samples = 1; /* number of sample loops */ | |
67 | char *ofile = NULL; /* output file */ | |
68 | int ofd; /* output file descriptor */ | |
69 | static mach_port_t myHost; | |
70 | static mach_port_t masterPort; | |
71 | ||
72 | /* internal table of drive path mappings */ | |
73 | struct drivepath *dp_table = NULL; | |
74 | ||
75 | /* number of entries in the dp_table */ | |
76 | int dp_count = 0; | |
77 | ||
78 | /* internal table of network interface statistics */ | |
79 | struct netstats *ns_table = NULL; | |
80 | int ns_count = 0; | |
81 | ||
c3a08f59 A |
82 | static uid_t realuid; |
83 | ||
84 | int network_mode = 0; | |
85 | ||
86 | /* Forward fuction declarations */ | |
87 | static void exit_usage(); | |
88 | static void open_datafile(char *); | |
89 | static void write_record_hdr(struct record_hdr *); | |
90 | static void write_record_data(char *, int); | |
91 | static void get_all_stats(); | |
92 | static void get_vmstat_sample(); | |
93 | static void get_drivestat_sample(); | |
94 | static int get_ndrives(); | |
95 | static int record_device(io_registry_entry_t, struct drivestats *, int ndrives); | |
96 | static int check_device_path (char *name, char *path, int ndrives); | |
97 | static void get_netstat_sample(int pppflag); | |
c3a08f59 A |
98 | |
99 | int | |
100 | main(argc, argv) | |
101 | int argc; | |
102 | char *argv[]; | |
103 | { | |
104 | ||
105 | char *p; | |
106 | char ch; | |
107 | ||
108 | /* | |
109 | * Stop being root ASAP. | |
110 | */ | |
111 | if (geteuid() != 0) | |
112 | { | |
113 | fprintf(stderr, "sadc: must be setuid root or root"); | |
114 | exit(1); | |
115 | } | |
116 | ||
117 | realuid = getuid(); | |
118 | seteuid(realuid); | |
119 | ||
120 | setvbuf(stdout, (char *)NULL, _IONBF, 0); | |
121 | ||
122 | while ((ch=getopt(argc, argv, "m:")) != EOF) { | |
123 | switch(ch) { | |
124 | case 'm': | |
125 | /* Only the PPP mode matters on this collector side */ | |
126 | /* The reporter side deals with the DEV or EDEV modes */ | |
127 | if (!strncmp(optarg, "PPP", 3)) | |
128 | network_mode |= NET_PPP_MODE; | |
129 | break; | |
130 | default: | |
131 | exit_usage(); | |
132 | break; | |
133 | } | |
134 | } | |
135 | ||
136 | argc -= optind; | |
137 | if (argc > 0) | |
138 | { | |
139 | if (isdigit(*argv[optind])) | |
140 | { | |
141 | /* we expect to have both an interval and a sample count */ | |
142 | errno=0; | |
143 | t_interval = strtol(argv[optind], &p, 0); | |
144 | if (errno || (*p !='\0') || t_interval <= 0) | |
145 | { | |
146 | exit_usage(); | |
147 | } | |
148 | ||
149 | optind++; | |
150 | if ((argc < 2) || (!isdigit(*argv[optind]))) { | |
151 | exit_usage(); | |
152 | } | |
153 | ||
154 | errno=0; | |
155 | n_samples = strtol(argv[optind], &p, 0); | |
156 | if (errno || (*p != '\0') || n_samples <= 0) | |
157 | { | |
158 | exit_usage(); | |
159 | } | |
160 | ||
161 | optind++; | |
162 | if (argc == 3) | |
163 | { | |
164 | /* we have an output file */ | |
165 | ofile = argv[optind]; | |
166 | } | |
167 | } | |
168 | else | |
169 | { | |
170 | /* all we have is an output file */ | |
171 | ofile = argv[optind]; | |
172 | } | |
173 | } | |
174 | ||
175 | ||
176 | /* open the output file */ | |
177 | (void)open_datafile(ofile); | |
178 | ||
179 | /* | |
180 | * Get the Mach private port. | |
181 | */ | |
182 | myHost = mach_host_self(); | |
183 | ||
184 | /* | |
185 | * Get the I/O Kit communication handle. | |
186 | */ | |
187 | IOMasterPort(bootstrap_port, &masterPort); | |
188 | ||
189 | ||
190 | restart_record.rec_timestamp = time((time_t *)0); | |
191 | write_record_hdr(&restart_record); | |
192 | get_all_stats(); /* this is the initial stat collection */ | |
193 | sleep(t_interval); | |
194 | ||
195 | if (n_samples > 0) | |
196 | { | |
197 | /* this init sample is not counted */ | |
198 | timestamp_record.rec_data = time((time_t *)0); /* returns time in | |
199 | * seconds */ | |
200 | #if 0 | |
201 | struct tm *tm; | |
202 | tm = gmtime(&(timestamp_record.rec_data)); | |
203 | fprintf(stderr, "timestamp=%ld\n", timestamp_record.rec_data); | |
204 | fprintf(stderr, "GMTIME offset from UTC in seconds = %ld\n", tm->tm_gmtoff); | |
205 | fprintf(stderr, "GMTIME secnds=%d, min=%d, hour=%d\n", tm->tm_sec, tm->tm_min, tm->tm_hour); | |
206 | fprintf(stderr, "asctime = %s\n", asctime(tm)); | |
207 | ||
208 | tm=localtime(&(timestamp_record.rec_data)); | |
209 | fprintf(stderr, "LOCTIME offset from UTC in seconds = %ld\n",tm->tm_gmtoff); | |
210 | fprintf(stderr, "LOCTIME secnds=%d, min=%d, hour=%d\n", tm->tm_sec, tm->tm_min, tm->tm_hour); | |
211 | fprintf(stderr, "asctime = %s\n", asctime(tm)); | |
212 | #endif | |
213 | ||
214 | write_record_hdr(×tamp_record); | |
215 | get_all_stats(); | |
216 | } | |
217 | ||
218 | while (n_samples) | |
219 | { | |
220 | sleep(t_interval); | |
221 | timestamp_record.rec_timestamp = time((time_t *)0); /* returns time in | |
222 | * seconds */ | |
223 | write_record_hdr(×tamp_record); | |
224 | get_all_stats(); | |
225 | n_samples--; | |
226 | } | |
227 | exit(EXIT_SUCCESS); | |
228 | } | |
229 | ||
230 | static void | |
231 | exit_usage() | |
232 | { | |
233 | fprintf(stderr, "/usr/lib/sa/sadc [-m {PPP}] [t n] [ofile]\n"); | |
234 | exit(EXIT_FAILURE); | |
235 | } | |
236 | ||
237 | static void | |
238 | open_datafile(char *path) | |
239 | { | |
240 | if (path == NULL) | |
241 | { | |
242 | data_fp = stdout; | |
243 | return; | |
244 | } | |
245 | else | |
246 | data_fp = fopen(path, "w+"); | |
247 | ||
248 | if (!data_fp) | |
249 | { | |
250 | /* failed to open path */ | |
251 | fprintf(stderr, "sadc: failed to open data file [%s]\n", path?path:"stdout"); | |
252 | exit_usage(); | |
253 | } | |
254 | } | |
255 | ||
256 | static void | |
257 | write_record_hdr(hdr) | |
258 | struct record_hdr *hdr; | |
259 | { | |
260 | errno = 0; | |
261 | ||
262 | if (fwrite(hdr, sizeof(struct record_hdr), 1, data_fp) != 1) | |
263 | { | |
264 | fprintf(stderr, "sadc: write_record_hdr failed, errno=%d\n", errno); | |
265 | exit(EXIT_FAILURE); | |
266 | } | |
267 | ||
268 | fflush(data_fp); | |
269 | return; | |
270 | } | |
271 | ||
272 | static void | |
273 | write_record_data(data, size) | |
274 | char *data; | |
275 | int size; | |
276 | { | |
277 | errno = 0; | |
278 | ||
279 | if (fwrite(data, size, 1, data_fp) != 1) | |
280 | { | |
281 | fprintf(stderr, "sadc: write_record_data failed, errno=%d\n", errno); | |
282 | exit(EXIT_FAILURE); | |
283 | } | |
284 | ||
285 | fflush(data_fp); | |
286 | return; | |
287 | } | |
288 | ||
289 | ||
290 | static void | |
291 | get_vmstat_sample() | |
292 | { | |
293 | struct vm_statistics stat; | |
294 | kern_return_t error; | |
295 | mach_msg_type_number_t count; | |
296 | ||
297 | count = HOST_VM_INFO_COUNT; | |
298 | error = host_statistics(myHost, HOST_VM_INFO, (host_info_t)&stat, &count); | |
299 | if (error != KERN_SUCCESS) { | |
300 | fprintf(stderr, "sadc: Error in vm host_statistics(): %s\n", | |
301 | mach_error_string(error)); | |
302 | exit(2); | |
303 | } | |
304 | ||
305 | vmstat_record.rec_count = 1; | |
306 | vmstat_record.rec_size = sizeof(vm_statistics_data_t); | |
307 | write_record_hdr(&vmstat_record); | |
308 | write_record_data((char *)&stat, sizeof(vm_statistics_data_t)); | |
309 | } | |
310 | ||
311 | static void | |
312 | get_cpu_sample() | |
313 | { | |
314 | host_cpu_load_info_data_t cpuload; | |
315 | kern_return_t error; | |
316 | mach_msg_type_number_t count; | |
317 | ||
318 | count = HOST_CPU_LOAD_INFO_COUNT; | |
319 | error = host_statistics(myHost, HOST_CPU_LOAD_INFO,(host_info_t)&cpuload, &count); | |
320 | if (error != KERN_SUCCESS) { | |
321 | fprintf(stderr, "sadc: Error in cpu host_statistics(): %s", | |
322 | mach_error_string(error)); | |
323 | exit(2); | |
324 | } | |
325 | ||
326 | cpu_record.rec_count = 1; | |
327 | cpu_record.rec_size = sizeof(host_cpu_load_info_data_t); | |
328 | write_record_hdr(&cpu_record); | |
329 | write_record_data((char *)&cpuload, sizeof(host_cpu_load_info_data_t)); | |
330 | } | |
331 | ||
332 | static void | |
333 | get_drivestat_sample() | |
334 | { | |
335 | io_registry_entry_t drive; | |
336 | io_iterator_t drivelist; | |
337 | CFMutableDictionaryRef match; | |
338 | int ndrives; | |
339 | int i = 0; | |
340 | long bufsize = 0; | |
341 | char *buf; | |
342 | struct drivestats *dbuf; | |
343 | kern_return_t status; | |
344 | int error; | |
345 | ||
346 | if ((ndrives = get_ndrives()) <= 0) | |
347 | return; | |
348 | ||
349 | /* allocate space to collect stats for all the drives */ | |
350 | bufsize = ndrives * sizeof(struct drivestats); | |
351 | buf = (char *) malloc (bufsize); | |
352 | dbuf = (struct drivestats *)buf; | |
353 | if (buf) | |
354 | bzero((char *)buf, bufsize); | |
355 | else | |
356 | return; | |
357 | ||
358 | /* | |
359 | * Get an iterator for IOMedia objects. | |
360 | */ | |
361 | match = IOServiceMatching("IOMedia"); | |
362 | ||
363 | /* Get whole disk info */ | |
364 | CFDictionaryAddValue(match, CFSTR(kIOMediaWholeKey), kCFBooleanTrue); | |
365 | ||
366 | status = IOServiceGetMatchingServices(masterPort, match, &drivelist); | |
367 | if (status != KERN_SUCCESS) | |
368 | goto RETURN; | |
369 | ||
370 | /* | |
371 | * Scan all of the IOMedia objects, and for each | |
372 | * object that has a parent IOBlockStorageDriver, | |
373 | * record the statistics | |
374 | * | |
375 | * XXX What about RAID devices? | |
376 | */ | |
377 | error = 1; | |
378 | i = 0; | |
379 | while ((drive = IOIteratorNext(drivelist))) | |
380 | { | |
381 | if (i < ndrives) | |
382 | { | |
383 | if (record_device(drive, &dbuf[i], ndrives)) | |
384 | { | |
385 | error = 0; | |
386 | i++; | |
387 | } | |
388 | } | |
389 | else | |
390 | { | |
391 | IOObjectRelease(drive); | |
392 | break; | |
393 | } | |
394 | IOObjectRelease(drive); | |
395 | } | |
396 | IOObjectRelease(drivelist); | |
397 | ||
398 | if (! error) | |
399 | { | |
400 | drivestats_record.rec_count = i; | |
401 | drivestats_record.rec_size = sizeof (struct drivestats); | |
402 | write_record_hdr(&drivestats_record); | |
403 | write_record_data((char *)buf, (i * sizeof(struct drivestats))); | |
404 | } | |
405 | ||
406 | RETURN: | |
407 | if (buf) | |
408 | free(buf); | |
409 | return; | |
410 | } | |
411 | ||
412 | /* | |
413 | * Determine whether an IORegistryEntry refers to a valid | |
414 | * I/O device, and if so, record it. | |
415 | * Return zero: no device recorded | |
416 | * Return non-zero: device stats recorded | |
417 | */ | |
418 | static int | |
419 | record_device(io_registry_entry_t drive, struct drivestats* drivestat, int ndrives) | |
420 | { | |
421 | io_registry_entry_t parent; | |
422 | CFDictionaryRef properties, statistics; | |
423 | CFStringRef name; | |
424 | CFNumberRef number; | |
425 | UInt64 value; | |
426 | kern_return_t status; | |
427 | int retval = 0; | |
428 | int drive_id; | |
429 | io_string_t path; | |
430 | char BSDName[MAXDRIVENAME + 1]; | |
431 | ||
432 | status = IORegistryEntryGetParentEntry(drive, kIOServicePlane, &parent); | |
433 | if (status != KERN_SUCCESS) | |
434 | { | |
435 | /* device has no parent */ | |
436 | return(retval); | |
437 | } | |
438 | ||
439 | if (IOObjectConformsTo(parent, "IOBlockStorageDriver")) | |
440 | { | |
441 | /* | |
442 | * Get a unique device path identifier. | |
443 | * Devices available at boot have an Open Firmware Device Tree path. | |
444 | * The OF path is short and concise and should be first choice. | |
445 | * Devices that show up after boot, are guaranteed to have | |
446 | * a Service Plane, hardware unique path. | |
447 | */ | |
448 | ||
449 | bzero(path, sizeof(io_string_t)); | |
450 | if (IORegistryEntryGetPath(drive, kIODeviceTreePlane, path) != KERN_SUCCESS) | |
451 | { | |
452 | if(IORegistryEntryGetPath(drive, kIOServicePlane, path) != KERN_SUCCESS) | |
453 | /* device has no unique path identifier */ | |
454 | goto RETURN; | |
455 | } | |
456 | retval++; | |
457 | ||
458 | /* get drive properties */ | |
459 | status = IORegistryEntryCreateCFProperties(drive, | |
460 | (CFMutableDictionaryRef *)&properties, | |
461 | kCFAllocatorDefault, | |
462 | kNilOptions); | |
463 | if (status != KERN_SUCCESS) | |
464 | { | |
465 | /* device has no properties */ | |
466 | goto RETURN; | |
467 | } | |
468 | ||
469 | bzero(BSDName, MAXDRIVENAME+1); | |
470 | /* get name from properties */ | |
471 | name = (CFStringRef)CFDictionaryGetValue(properties, | |
472 | CFSTR(kIOBSDNameKey)); | |
473 | if (name) { | |
474 | CFStringGetCString(name, BSDName, | |
8459d725 | 475 | MAXDRIVENAME, kCFStringEncodingUTF8); |
c3a08f59 A |
476 | retval++; |
477 | } | |
478 | ||
479 | /* get blocksize from properties */ | |
480 | number = (CFNumberRef)CFDictionaryGetValue(properties, | |
481 | CFSTR(kIOMediaPreferredBlockSizeKey)); | |
482 | if (number != 0) { | |
483 | CFNumberGetValue(number, | |
484 | kCFNumberSInt64Type, &value); | |
485 | drivestat->blocksize = value; | |
486 | retval++; | |
487 | } | |
488 | CFRelease(properties); | |
489 | } | |
490 | else | |
491 | goto RETURN; | |
492 | ||
493 | /* we should have a name and blocksize at a minimum */ | |
494 | if (retval != 3) | |
495 | { | |
496 | retval = FALSE; | |
497 | goto RETURN; | |
498 | } | |
499 | ||
500 | drive_id = check_device_path (BSDName, path, ndrives); | |
501 | if (drive_id == -1) | |
502 | { | |
503 | retval = FALSE; | |
504 | goto RETURN; | |
505 | } | |
506 | else | |
507 | drivestat->drivepath_id = drive_id; | |
508 | ||
509 | ||
510 | /* get parent drive properties */ | |
511 | status = IORegistryEntryCreateCFProperties(parent, | |
512 | (CFMutableDictionaryRef *)&properties, | |
513 | kCFAllocatorDefault, | |
514 | kNilOptions); | |
515 | if (status != KERN_SUCCESS) | |
516 | { | |
517 | /* device has no properties */ | |
518 | goto RETURN; | |
519 | } | |
520 | ||
521 | /* Obtain the statistics from the parent drive properties. */ | |
522 | ||
523 | statistics | |
524 | = (CFDictionaryRef)CFDictionaryGetValue(properties, | |
525 | CFSTR(kIOBlockStorageDriverStatisticsKey)); | |
526 | ||
527 | if (statistics != 0) | |
528 | { | |
529 | /* Get number of reads. */ | |
530 | number = | |
531 | (CFNumberRef)CFDictionaryGetValue(statistics, | |
532 | CFSTR(kIOBlockStorageDriverStatisticsReadsKey)); | |
533 | if (number != 0) { | |
534 | CFNumberGetValue(number, | |
535 | kCFNumberSInt64Type, &value); | |
536 | drivestat->Reads = value; | |
537 | } | |
538 | ||
539 | /* Get bytes read. */ | |
540 | number = | |
541 | (CFNumberRef)CFDictionaryGetValue(statistics, | |
542 | CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey)); | |
543 | if (number != 0) { | |
544 | CFNumberGetValue(number, kCFNumberSInt64Type, &value); | |
545 | drivestat->BytesRead = value; | |
546 | } | |
547 | ||
548 | /* Get number of writes. */ | |
549 | number = | |
550 | (CFNumberRef)CFDictionaryGetValue(statistics, | |
551 | CFSTR(kIOBlockStorageDriverStatisticsWritesKey)); | |
552 | if (number != 0) { | |
553 | CFNumberGetValue(number, kCFNumberSInt64Type, &value); | |
554 | drivestat->Writes = value; | |
555 | } | |
556 | ||
557 | /* Get bytes written. */ | |
558 | number = | |
559 | (CFNumberRef)CFDictionaryGetValue(statistics, | |
560 | CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey)); | |
561 | if (number != 0) { | |
562 | CFNumberGetValue(number, kCFNumberSInt64Type, &value); | |
563 | drivestat->BytesWritten = value; | |
564 | } | |
565 | ||
566 | /* Get LatentReadTime. */ | |
567 | number = | |
568 | (CFNumberRef)CFDictionaryGetValue(statistics, | |
569 | CFSTR(kIOBlockStorageDriverStatisticsLatentReadTimeKey)); | |
570 | if (number != 0) { | |
571 | CFNumberGetValue(number, kCFNumberSInt64Type, &value); | |
572 | drivestat->LatentReadTime = value; | |
573 | } | |
574 | ||
575 | /* Get LatentWriteTime. */ | |
576 | number = | |
577 | (CFNumberRef)CFDictionaryGetValue(statistics, | |
578 | CFSTR(kIOBlockStorageDriverStatisticsLatentWriteTimeKey)); | |
579 | if (number != 0) { | |
580 | CFNumberGetValue(number, kCFNumberSInt64Type, &value); | |
581 | drivestat->LatentWriteTime = value; | |
582 | } | |
583 | ||
584 | /* Get ReadErrors. */ | |
585 | number = | |
586 | (CFNumberRef)CFDictionaryGetValue(statistics, | |
587 | CFSTR(kIOBlockStorageDriverStatisticsReadErrorsKey)); | |
588 | if (number != 0) { | |
589 | CFNumberGetValue(number, kCFNumberSInt64Type, &value); | |
590 | drivestat->ReadErrors = value; | |
591 | } | |
592 | ||
593 | /* Get WriteErrors. */ | |
594 | number = | |
595 | (CFNumberRef)CFDictionaryGetValue(statistics, | |
596 | CFSTR(kIOBlockStorageDriverStatisticsWriteErrorsKey)); | |
597 | if (number != 0) { | |
598 | CFNumberGetValue(number, kCFNumberSInt64Type, &value); | |
599 | drivestat->WriteErrors = value; | |
600 | } | |
601 | ||
602 | /* Get ReadRetries. */ | |
603 | number = | |
604 | (CFNumberRef)CFDictionaryGetValue(statistics, | |
605 | CFSTR(kIOBlockStorageDriverStatisticsReadRetriesKey)); | |
606 | if (number != 0) { | |
607 | CFNumberGetValue(number, kCFNumberSInt64Type, &value); | |
608 | drivestat->ReadRetries = value; | |
609 | } | |
610 | ||
611 | /* Get WriteRetries. */ | |
612 | number = | |
613 | (CFNumberRef)CFDictionaryGetValue(statistics, | |
614 | CFSTR(kIOBlockStorageDriverStatisticsWriteRetriesKey)); | |
615 | if (number != 0) { | |
616 | CFNumberGetValue(number, kCFNumberSInt64Type, &value); | |
617 | drivestat->WriteRetries = value; | |
618 | } | |
619 | ||
620 | /* Get TotalReadTime. */ | |
621 | number = | |
622 | (CFNumberRef)CFDictionaryGetValue(statistics, | |
623 | CFSTR(kIOBlockStorageDriverStatisticsTotalReadTimeKey)); | |
624 | if (number != 0) { | |
625 | CFNumberGetValue(number, kCFNumberSInt64Type, &value); | |
626 | drivestat->TotalReadTime = value; | |
627 | } | |
628 | ||
629 | /* Get WriteRetries. */ | |
630 | number = | |
631 | (CFNumberRef)CFDictionaryGetValue(statistics, | |
632 | CFSTR(kIOBlockStorageDriverStatisticsTotalWriteTimeKey)); | |
633 | if (number != 0) { | |
634 | CFNumberGetValue(number, kCFNumberSInt64Type, &value); | |
635 | drivestat->TotalWriteTime = value; | |
636 | } | |
637 | ||
638 | CFRelease(properties); | |
639 | } /* end if statistics != 0 */ | |
640 | ||
641 | RETURN: | |
642 | IOObjectRelease(parent); | |
643 | return(retval); | |
644 | } | |
645 | ||
646 | ||
647 | /* | |
648 | * find IOMedia objects | |
649 | * This routine always gives me a lower count on the number | |
650 | * of disks. I don't know which one to use. | |
651 | */ | |
652 | static int | |
653 | get_ndrives(void) | |
654 | { | |
655 | io_iterator_t drivelist; | |
656 | io_registry_entry_t drive; | |
657 | io_registry_entry_t parent; | |
658 | CFMutableDictionaryRef match; | |
659 | int error, ndrives; | |
660 | kern_return_t status; | |
661 | ||
662 | /* | |
663 | * Get an iterator for IOMedia objects. | |
664 | */ | |
665 | match = IOServiceMatching("IOMedia"); | |
666 | CFDictionaryAddValue(match, CFSTR(kIOMediaWholeKey), kCFBooleanTrue); | |
667 | status = IOServiceGetMatchingServices(masterPort, match, &drivelist); | |
668 | if (status != KERN_SUCCESS) | |
669 | return(0); | |
670 | ||
671 | /* | |
672 | * Scan all of the IOMedia objects, and count each | |
673 | * object that has a parent IOBlockStorageDriver | |
674 | * | |
675 | * XXX What about RAID devices? | |
676 | */ | |
677 | error = 1; | |
678 | ndrives = 0; | |
679 | while ((drive = IOIteratorNext(drivelist))) | |
680 | { | |
681 | /* get drive's parent */ | |
682 | status = IORegistryEntryGetParentEntry(drive, | |
683 | kIOServicePlane, &parent); | |
684 | if (status != KERN_SUCCESS) | |
685 | { | |
686 | IOObjectRelease(drive); | |
687 | continue; | |
688 | } | |
689 | ||
690 | if (IOObjectConformsTo(parent, "IOBlockStorageDriver")) | |
691 | { | |
692 | error = 0; | |
693 | ndrives++; | |
694 | } | |
695 | IOObjectRelease(parent); | |
696 | IOObjectRelease(drive); | |
697 | } | |
698 | ||
699 | IOObjectRelease(drivelist); | |
700 | ||
701 | return(ndrives); | |
702 | } | |
703 | ||
704 | ||
705 | /* | |
706 | * When getting the stats, do it in the order | |
707 | * of their type. The types that have the most | |
708 | * data come first in the list if possible. | |
709 | * This makes the sar reporter tool more efficient, | |
710 | * because in some cases, it will allocate a buffer | |
711 | * and keep reusing it as long as the sample data fits. | |
712 | * When a sample data doesn't fit, it reallocates the buffer | |
713 | * to a bigger size etc. | |
714 | */ | |
715 | void | |
716 | get_all_stats() | |
717 | { | |
718 | ||
719 | get_drivestat_sample(); | |
720 | get_netstat_sample(network_mode); | |
721 | get_vmstat_sample(); | |
722 | get_cpu_sample(); | |
723 | } | |
724 | ||
725 | ||
726 | /* | |
727 | * An internal table maps the BSDName to a unique ioregistry path. | |
728 | * The table's index is then used as a unique compressed path, and | |
729 | * helps track disks that come and go during the sampling intervals. | |
730 | * This routine finds an entry that maps both the BSDName and the | |
731 | * IOKit registry path. If no mapping is discovered, a new entry | |
732 | * is created. An entry is never removed, this maintaining the | |
733 | * unique index throughout the data collection. | |
734 | * Success returns the map index. Failure returns -1. | |
735 | */ | |
736 | static int | |
737 | check_device_path (char *name, char *path, int ndrives) | |
738 | { | |
739 | int i; | |
740 | int index; | |
741 | int n; | |
742 | ||
743 | if (dp_table == NULL) | |
744 | { | |
745 | /* First setup of internal drivepath table */ | |
746 | dp_table = (struct drivepath *)malloc (ndrives * sizeof(struct drivepath)); | |
747 | if (dp_table == NULL) | |
748 | return(-1); | |
749 | else | |
750 | { | |
751 | bzero(dp_table, (ndrives * sizeof(struct drivepath))); | |
752 | dp_count = ndrives; | |
753 | drivepath_record.rec_size = sizeof(struct drivepath); | |
754 | } | |
755 | } | |
756 | ||
757 | for (i=0; i < dp_count; i++) | |
758 | { | |
759 | if (dp_table[i].state == DPSTATE_UNINITIALIZED) | |
760 | { | |
761 | /* This is a new drive entry that should be recorded */ | |
762 | index = i; | |
763 | goto NEW_ENTRY; | |
764 | } | |
765 | else if (!strcmp (dp_table[i].ioreg_path, path)) | |
766 | { | |
767 | /* Found a matching hardware path */ | |
768 | if (!strcmp(dp_table[i].BSDName, name)) | |
769 | { | |
770 | /* The BSDName matches the entry in the table | |
771 | * so there is no need to record this data. | |
772 | */ | |
773 | return(i); | |
774 | } | |
775 | else | |
776 | { | |
777 | /* The BSDName is different ... implies a change, | |
778 | * like the drive was removed and now is back | |
779 | */ | |
780 | bzero((char *)dp_table[i].BSDName, MAXDRIVENAME+1); | |
781 | dp_table[i].drivepath_id = i; | |
782 | dp_table[i].state = DPSTATE_CHANGED; | |
783 | strcpy(dp_table[i].BSDName, name); | |
784 | write_record_hdr(&drivepath_record); | |
785 | write_record_data((char *)&dp_table[i], sizeof(struct drivepath)); | |
786 | return(i); | |
787 | } | |
788 | } | |
789 | } /* end for loop */ | |
790 | ||
791 | /* | |
792 | * If we reach this point, then we've run out of | |
793 | * table entries. Double the size of the table. | |
794 | */ | |
795 | n = dp_count * 2; | |
796 | dp_table = (struct drivepath *)realloc(dp_table, n * sizeof(struct drivepath)); | |
797 | bzero(&dp_table[dp_count], dp_count * sizeof(struct drivepath)); | |
798 | index = dp_count; | |
799 | dp_count = n; | |
800 | ||
801 | /* This is a new drive entry that should be recorded */ | |
802 | NEW_ENTRY: | |
803 | dp_table[index].drivepath_id = index; | |
804 | dp_table[index].state = DPSTATE_NEW; | |
805 | strcpy(dp_table[index].BSDName, name); | |
806 | strcpy(dp_table[index].ioreg_path, path); | |
807 | write_record_hdr(&drivepath_record); | |
808 | write_record_data((char *)&dp_table[index], sizeof(struct drivepath)); | |
809 | return(index); | |
810 | } | |
811 | ||
812 | ||
c3a08f59 A |
813 | |
814 | /* | |
815 | * Thus far, only the networking stats take an optional flag | |
816 | * to modify the collection of data. The number of ppp | |
817 | * interfaces can be very high, causing the raw data file to | |
818 | * grow very large. We want this option to include ppp | |
819 | * statistics to be off by default. When we see the -m PPP | |
820 | * mode passed in, ppp collection will be turned on. | |
821 | */ | |
822 | static void | |
823 | get_netstat_sample(int mode) | |
824 | { | |
825 | ||
826 | int n; | |
827 | int ns_index = 0; | |
c3a08f59 A |
828 | char tname[MAX_TNAME_SIZE + 1]; |
829 | char name[MAX_TNAME_UNIT_SIZE + 1]; | |
2fc1e207 | 830 | struct ifaddrs *ifa_list, *ifa; |
c3a08f59 | 831 | |
c3a08f59 A |
832 | |
833 | /* | |
834 | * Set the starting table size to 100 entries | |
835 | * That should be big enough for most cases, | |
836 | * even with a lot of ppp connections. | |
837 | */ | |
838 | ns_count = 100; | |
839 | ns_table = (struct netstats *) malloc(ns_count * sizeof (struct netstats)); | |
840 | if (ns_table == NULL) | |
841 | { | |
842 | fprintf(stderr, "sadc: malloc netstat table failed\n"); | |
843 | return; | |
844 | } | |
c3a08f59 A |
845 | |
846 | bzero(ns_table, ns_count * sizeof(struct netstats)); | |
2fc1e207 A |
847 | if (getifaddrs(&ifa_list) == -1) |
848 | return; | |
849 | ||
850 | for (ifa = ifa_list; ifa; ifa = ifa->ifa_next) | |
c3a08f59 | 851 | { |
2fc1e207 A |
852 | struct if_data *if_data = (struct if_data *)ifa->ifa_data; |
853 | ||
854 | if (AF_LINK != ifa->ifa_addr->sa_family) | |
855 | continue; | |
856 | if (ifa->ifa_data == 0) | |
857 | continue; | |
c3a08f59 A |
858 | tname[MAX_TNAME_SIZE] = '\0'; |
859 | if (!(network_mode & NET_PPP_MODE)) | |
860 | { | |
861 | /* | |
862 | * If the flag is set, include PPP connections. | |
863 | * By default this collection is turned off | |
864 | */ | |
2fc1e207 | 865 | if(!strncmp(ifa->ifa_name, "ppp", 3)) |
c3a08f59 A |
866 | continue; |
867 | } | |
2fc1e207 | 868 | snprintf(name, MAX_TNAME_UNIT_SIZE, "%s", ifa->ifa_name); |
c3a08f59 A |
869 | name[MAX_TNAME_UNIT_SIZE] = '\0'; |
870 | ||
871 | if (ns_index == ns_count) | |
872 | { | |
873 | /* the stat table needs to grow */ | |
874 | n = ns_count * 2; | |
875 | ns_table = (struct netstats *)realloc(ns_table, n * sizeof(struct netstats)); | |
876 | bzero(&ns_table[ns_count], ns_count * sizeof(struct netstats)); | |
877 | ns_count = n; | |
878 | } | |
879 | ||
c3a08f59 A |
880 | /* |
881 | * As a means of helping to identify when interface unit numbers | |
882 | * are reused, a generation counter may eventually be implemented. | |
883 | * This will be especially helpful with ppp-x connections. | |
884 | * In anticipation, we will reserve a space for it, but always | |
885 | * set it to zero for now. | |
886 | */ | |
887 | ns_table[ns_index].gen_counter = 0; | |
888 | ||
889 | strncpy(ns_table[ns_index].tname_unit, name, MAX_TNAME_UNIT_SIZE); | |
890 | ns_table[ns_index].tname_unit[MAX_TNAME_UNIT_SIZE] = '\0'; | |
2fc1e207 A |
891 | ns_table[ns_index].net_ipackets = if_data->ifi_ipackets; |
892 | ns_table[ns_index].net_ierrors = if_data->ifi_ierrors; | |
893 | ns_table[ns_index].net_opackets = if_data->ifi_opackets; | |
894 | ns_table[ns_index].net_oerrors = if_data->ifi_oerrors; | |
895 | ns_table[ns_index].net_collisions = if_data->ifi_collisions; | |
896 | ns_table[ns_index].net_ibytes = if_data->ifi_ibytes; | |
897 | ns_table[ns_index].net_obytes = if_data->ifi_obytes; | |
898 | ns_table[ns_index].net_imcasts = if_data->ifi_imcasts; | |
899 | ns_table[ns_index].net_omcasts = if_data->ifi_omcasts; | |
900 | ns_table[ns_index].net_drops = if_data->ifi_iqdrops; | |
c3a08f59 A |
901 | ns_index++; |
902 | } /* end for */ | |
903 | ||
904 | netstats_record.rec_count = ns_index; | |
905 | netstats_record.rec_size = sizeof(struct netstats); | |
906 | write_record_hdr(&netstats_record); | |
907 | write_record_data((char *)ns_table, (ns_index * sizeof(struct netstats))); | |
c3a08f59 A |
908 | return; |
909 | } |