2 #include <CoreFoundation/CoreFoundation.h>
3 #include <IOKit/IOKitLib.h>
4 #include <IOKit/storage/IOBlockStorageDriver.h>
5 #include <IOKit/IOBSD.h>
6 // NEEDED BY do_space, do_inodes
8 // NEEDED BY: struct ifaddrs, getifaddrs()
12 // NEEDED BY: do_bandwidth
13 #define IFA_DATA(s) (((struct if_data *)ifa->ifa_data)->ifi_ ## s)
15 #define MAXDRIVENAME 31
17 #define KILO_FACTOR 1024
18 #define MEGA_FACTOR 1048576 // 1024 * 1024
19 #define GIGA_FACTOR 1073741824 // 1024 * 1024 * 1024
21 int do_macos_iokit(int update_every, usec_t dt) {
24 static int do_io = -1, do_space = -1, do_inodes = -1, do_bandwidth = -1;
26 if (unlikely(do_io == -1)) {
27 do_io = config_get_boolean("plugin:macos:iokit", "disk i/o", 1);
28 do_space = config_get_boolean("plugin:macos:sysctl", "space usage for all disks", 1);
29 do_inodes = config_get_boolean("plugin:macos:sysctl", "inodes usage for all disks", 1);
30 do_bandwidth = config_get_boolean("plugin:macos:sysctl", "bandwidth", 1);
35 mach_port_t master_port;
36 io_registry_entry_t drive, drive_media;
37 io_iterator_t drive_list;
38 CFDictionaryRef properties, statistics;
42 collected_number total_disk_reads = 0;
43 collected_number total_disk_writes = 0;
45 char name[MAXDRIVENAME];
46 collected_number bytes_read;
47 collected_number bytes_write;
48 collected_number reads;
49 collected_number writes;
50 collected_number time_read;
51 collected_number time_write;
52 collected_number latency_read;
53 collected_number latency_write;
56 collected_number duration_read_ns;
57 collected_number duration_write_ns;
58 collected_number busy_time_ns;
60 struct prev_diskstat {
61 collected_number bytes_read;
62 collected_number bytes_write;
63 collected_number operations_read;
64 collected_number operations_write;
65 collected_number duration_read_ns;
66 collected_number duration_write_ns;
67 collected_number busy_time_ns;
70 // NEEDED BY: do_space, do_inodes
71 struct statfs *mntbuf;
73 char mntonname[MNAMELEN + 1];
76 // NEEDED BY: do_bandwidth
77 struct ifaddrs *ifa, *ifap;
79 /* Get ports and services for drive statistics. */
80 if (unlikely(IOMasterPort(bootstrap_port, &master_port))) {
81 error("MACOS: IOMasterPort() failed");
83 error("DISABLED: system.io");
84 /* Get the list of all drive objects. */
85 } else if (unlikely(IOServiceGetMatchingServices(master_port, IOServiceMatching("IOBlockStorageDriver"), &drive_list))) {
86 error("MACOS: IOServiceGetMatchingServices() failed");
88 error("DISABLED: system.io");
90 while ((drive = IOIteratorNext(drive_list)) != 0) {
94 bzero(&diskstat, sizeof(diskstat));
96 /* Get drive media object. */
97 status = IORegistryEntryGetChildEntry(drive, kIOServicePlane, &drive_media);
98 if (unlikely(status != KERN_SUCCESS)) {
99 IOObjectRelease(drive);
103 /* Get drive media properties. */
104 if (likely(!IORegistryEntryCreateCFProperties(drive_media, (CFMutableDictionaryRef *)&properties, kCFAllocatorDefault, 0))) {
106 if (likely(name = (CFStringRef)CFDictionaryGetValue(properties, CFSTR(kIOBSDNameKey)))) {
107 CFStringGetCString(name, diskstat.name, MAXDRIVENAME, kCFStringEncodingUTF8);
112 CFRelease(properties);
113 IOObjectRelease(drive_media);
115 /* Obtain the properties for this drive object. */
116 if (unlikely(IORegistryEntryCreateCFProperties(drive, (CFMutableDictionaryRef *)&properties, kCFAllocatorDefault, 0))) {
117 error("MACOS: IORegistryEntryCreateCFProperties() failed");
119 error("DISABLED: system.io");
121 } else if (likely(properties)) {
122 /* Obtain the statistics from the drive properties. */
123 if (likely(statistics = (CFDictionaryRef)CFDictionaryGetValue(properties, CFSTR(kIOBlockStorageDriverStatisticsKey)))) {
125 // --------------------------------------------------------------------
127 /* Get bytes read. */
128 if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey)))) {
129 CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.bytes_read);
130 total_disk_reads += diskstat.bytes_read;
133 /* Get bytes written. */
134 if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey)))) {
135 CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.bytes_write);
136 total_disk_writes += diskstat.bytes_write;
139 st = rrdset_find_bytype_localhost("disk", diskstat.name);
141 st = rrdset_create_localhost("disk", diskstat.name, NULL, diskstat.name, "disk.io"
142 , "Disk I/O Bandwidth", "kilobytes/s", 2000, update_every
145 rrddim_add(st, "reads", NULL, 1, 1024, RRD_ALGORITHM_INCREMENTAL);
146 rrddim_add(st, "writes", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL);
148 else rrdset_next(st);
150 prev_diskstat.bytes_read = rrddim_set(st, "reads", diskstat.bytes_read);
151 prev_diskstat.bytes_write = rrddim_set(st, "writes", diskstat.bytes_write);
154 // --------------------------------------------------------------------
156 /* Get number of reads. */
157 if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsReadsKey)))) {
158 CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.reads);
161 /* Get number of writes. */
162 if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsWritesKey)))) {
163 CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.writes);
166 st = rrdset_find_bytype_localhost("disk_ops", diskstat.name);
168 st = rrdset_create_localhost("disk_ops", diskstat.name, NULL, diskstat.name, "disk.ops"
169 , "Disk Completed I/O Operations", "operations/s", 2001
170 , update_every, RRDSET_TYPE_LINE);
171 rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
173 rrddim_add(st, "reads", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
174 rrddim_add(st, "writes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
176 else rrdset_next(st);
178 prev_diskstat.operations_read = rrddim_set(st, "reads", diskstat.reads);
179 prev_diskstat.operations_write = rrddim_set(st, "writes", diskstat.writes);
182 // --------------------------------------------------------------------
184 /* Get reads time. */
185 if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsTotalReadTimeKey)))) {
186 CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.time_read);
189 /* Get writes time. */
190 if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsTotalWriteTimeKey)))) {
191 CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.time_write);
194 st = rrdset_find_bytype_localhost("disk_util", diskstat.name);
196 st = rrdset_create_localhost("disk_util", diskstat.name, NULL, diskstat.name, "disk.util"
197 , "Disk Utilization Time", "% of time working", 2004, update_every
199 rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
201 rrddim_add(st, "utilization", NULL, 1, 10000000, RRD_ALGORITHM_INCREMENTAL);
203 else rrdset_next(st);
205 cur_diskstat.busy_time_ns = (diskstat.time_read + diskstat.time_write);
206 prev_diskstat.busy_time_ns = rrddim_set(st, "utilization", cur_diskstat.busy_time_ns);
209 // --------------------------------------------------------------------
211 /* Get reads latency. */
212 if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsLatentReadTimeKey)))) {
213 CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.latency_read);
216 /* Get writes latency. */
217 if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsLatentWriteTimeKey)))) {
218 CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.latency_write);
221 st = rrdset_find_bytype_localhost("disk_iotime", diskstat.name);
223 st = rrdset_create_localhost("disk_iotime", diskstat.name, NULL, diskstat.name, "disk.iotime"
224 , "Disk Total I/O Time", "milliseconds/s", 2022, update_every
226 rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
228 rrddim_add(st, "reads", NULL, 1, 1000000, RRD_ALGORITHM_INCREMENTAL);
229 rrddim_add(st, "writes", NULL, -1, 1000000, RRD_ALGORITHM_INCREMENTAL);
231 else rrdset_next(st);
233 cur_diskstat.duration_read_ns = diskstat.time_read + diskstat.latency_read;
234 cur_diskstat.duration_write_ns = diskstat.time_write + diskstat.latency_write;
235 prev_diskstat.duration_read_ns = rrddim_set(st, "reads", cur_diskstat.duration_read_ns);
236 prev_diskstat.duration_write_ns = rrddim_set(st, "writes", cur_diskstat.duration_write_ns);
239 // --------------------------------------------------------------------
240 // calculate differential charts
241 // only if this is not the first time we run
245 // --------------------------------------------------------------------
247 st = rrdset_find_bytype_localhost("disk_await", diskstat.name);
249 st = rrdset_create_localhost("disk_await", diskstat.name, NULL, diskstat.name, "disk.await"
250 , "Average Completed I/O Operation Time", "ms per operation"
251 , 2005, update_every, RRDSET_TYPE_LINE);
252 rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
254 rrddim_add(st, "reads", NULL, 1, 1000000, RRD_ALGORITHM_ABSOLUTE);
255 rrddim_add(st, "writes", NULL, -1, 1000000, RRD_ALGORITHM_ABSOLUTE);
257 else rrdset_next(st);
259 rrddim_set(st, "reads", (diskstat.reads - prev_diskstat.operations_read) ?
260 (cur_diskstat.duration_read_ns - prev_diskstat.duration_read_ns) / (diskstat.reads - prev_diskstat.operations_read) : 0);
261 rrddim_set(st, "writes", (diskstat.writes - prev_diskstat.operations_write) ?
262 (cur_diskstat.duration_write_ns - prev_diskstat.duration_write_ns) / (diskstat.writes - prev_diskstat.operations_write) : 0);
265 // --------------------------------------------------------------------
267 st = rrdset_find_bytype_localhost("disk_avgsz", diskstat.name);
269 st = rrdset_create_localhost("disk_avgsz", diskstat.name, NULL, diskstat.name, "disk.avgsz"
270 , "Average Completed I/O Operation Bandwidth"
271 , "kilobytes per operation", 2006, update_every
273 rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
275 rrddim_add(st, "reads", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
276 rrddim_add(st, "writes", NULL, -1, 1024, RRD_ALGORITHM_ABSOLUTE);
278 else rrdset_next(st);
280 rrddim_set(st, "reads", (diskstat.reads - prev_diskstat.operations_read) ?
281 (diskstat.bytes_read - prev_diskstat.bytes_read) / (diskstat.reads - prev_diskstat.operations_read) : 0);
282 rrddim_set(st, "writes", (diskstat.writes - prev_diskstat.operations_write) ?
283 (diskstat.bytes_write - prev_diskstat.bytes_write) / (diskstat.writes - prev_diskstat.operations_write) : 0);
286 // --------------------------------------------------------------------
288 st = rrdset_find_bytype_localhost("disk_svctm", diskstat.name);
290 st = rrdset_create_localhost("disk_svctm", diskstat.name, NULL, diskstat.name, "disk.svctm"
291 , "Average Service Time", "ms per operation", 2007
292 , update_every, RRDSET_TYPE_LINE);
293 rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
295 rrddim_add(st, "svctm", NULL, 1, 1000000, RRD_ALGORITHM_ABSOLUTE);
297 else rrdset_next(st);
299 rrddim_set(st, "svctm", ((diskstat.reads - prev_diskstat.operations_read) + (diskstat.writes - prev_diskstat.operations_write)) ?
300 (cur_diskstat.busy_time_ns - prev_diskstat.busy_time_ns) / ((diskstat.reads - prev_diskstat.operations_read) + (diskstat.writes - prev_diskstat.operations_write)) : 0);
306 CFRelease(properties);
310 IOObjectRelease(drive);
312 IOIteratorReset(drive_list);
315 IOObjectRelease(drive_list);
319 st = rrdset_find_bytype_localhost("system", "io");
321 st = rrdset_create_localhost("system", "io", NULL, "disk", NULL, "Disk I/O", "kilobytes/s", 150
322 , update_every, RRDSET_TYPE_AREA);
323 rrddim_add(st, "in", NULL, 1, 1024, RRD_ALGORITHM_INCREMENTAL);
324 rrddim_add(st, "out", NULL, -1, 1024, RRD_ALGORITHM_INCREMENTAL);
326 else rrdset_next(st);
328 rrddim_set(st, "in", total_disk_reads);
329 rrddim_set(st, "out", total_disk_writes);
333 // Can be merged with FreeBSD plugin
334 // --------------------------------------------------------------------------
336 if (likely(do_space || do_inodes)) {
337 // there is no mount info in sysctl MIBs
338 if (unlikely(!(mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)))) {
339 error("MACOS: getmntinfo() failed");
341 error("DISABLED: disk_space.X");
343 error("DISABLED: disk_inodes.X");
345 for (i = 0; i < mntsize; i++) {
346 if (mntbuf[i].f_flags == MNT_RDONLY ||
347 mntbuf[i].f_blocks == 0 ||
348 // taken from gnulib/mountlist.c and shortened to FreeBSD related fstypes
349 strcmp(mntbuf[i].f_fstypename, "autofs") == 0 ||
350 strcmp(mntbuf[i].f_fstypename, "procfs") == 0 ||
351 strcmp(mntbuf[i].f_fstypename, "subfs") == 0 ||
352 strcmp(mntbuf[i].f_fstypename, "devfs") == 0 ||
353 strcmp(mntbuf[i].f_fstypename, "none") == 0)
356 // --------------------------------------------------------------------------
358 if (likely(do_space)) {
359 st = rrdset_find_bytype_localhost("disk_space", mntbuf[i].f_mntonname);
361 snprintfz(title, 4096, "Disk Space Usage for %s [%s]", mntbuf[i].f_mntonname, mntbuf[i].f_mntfromname);
362 st = rrdset_create_localhost("disk_space", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname
363 , "disk.space", title, "GB", 2023, update_every
364 , RRDSET_TYPE_STACKED);
366 rrddim_add(st, "avail", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRD_ALGORITHM_ABSOLUTE);
367 rrddim_add(st, "used", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRD_ALGORITHM_ABSOLUTE);
368 rrddim_add(st, "reserved_for_root", "reserved for root", mntbuf[i].f_bsize, GIGA_FACTOR,
369 RRD_ALGORITHM_ABSOLUTE);
373 rrddim_set(st, "avail", (collected_number) mntbuf[i].f_bavail);
374 rrddim_set(st, "used", (collected_number) (mntbuf[i].f_blocks - mntbuf[i].f_bfree));
375 rrddim_set(st, "reserved_for_root", (collected_number) (mntbuf[i].f_bfree - mntbuf[i].f_bavail));
379 // --------------------------------------------------------------------------
381 if (likely(do_inodes)) {
382 st = rrdset_find_bytype_localhost("disk_inodes", mntbuf[i].f_mntonname);
384 snprintfz(title, 4096, "Disk Files (inodes) Usage for %s [%s]", mntbuf[i].f_mntonname, mntbuf[i].f_mntfromname);
385 st = rrdset_create_localhost("disk_inodes", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname
386 , "disk.inodes", title, "Inodes", 2024, update_every
387 , RRDSET_TYPE_STACKED);
389 rrddim_add(st, "avail", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
390 rrddim_add(st, "used", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
391 rrddim_add(st, "reserved_for_root", "reserved for root", 1, 1, RRD_ALGORITHM_ABSOLUTE);
395 rrddim_set(st, "avail", (collected_number) mntbuf[i].f_ffree);
396 rrddim_set(st, "used", (collected_number) (mntbuf[i].f_files - mntbuf[i].f_ffree));
403 // Can be merged with FreeBSD plugin
404 // --------------------------------------------------------------------
406 if (likely(do_bandwidth)) {
407 if (unlikely(getifaddrs(&ifap))) {
408 error("MACOS: getifaddrs()");
410 error("DISABLED: system.ipv4");
412 for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
413 if (ifa->ifa_addr->sa_family != AF_LINK)
416 // --------------------------------------------------------------------
418 st = rrdset_find_bytype_localhost("net", ifa->ifa_name);
420 st = rrdset_create_localhost("net", ifa->ifa_name, NULL, ifa->ifa_name, "net.net", "Bandwidth"
421 , "kilobits/s", 7000, update_every, RRDSET_TYPE_AREA);
423 rrddim_add(st, "received", NULL, 8, 1024, RRD_ALGORITHM_INCREMENTAL);
424 rrddim_add(st, "sent", NULL, -8, 1024, RRD_ALGORITHM_INCREMENTAL);
426 else rrdset_next(st);
428 rrddim_set(st, "received", IFA_DATA(ibytes));
429 rrddim_set(st, "sent", IFA_DATA(obytes));
432 // --------------------------------------------------------------------
434 st = rrdset_find_bytype_localhost("net_packets", ifa->ifa_name);
436 st = rrdset_create_localhost("net_packets", ifa->ifa_name, NULL, ifa->ifa_name, "net.packets"
437 , "Packets", "packets/s", 7001, update_every, RRDSET_TYPE_LINE);
438 rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
440 rrddim_add(st, "received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
441 rrddim_add(st, "sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
442 rrddim_add(st, "multicast_received", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
443 rrddim_add(st, "multicast_sent", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
445 else rrdset_next(st);
447 rrddim_set(st, "received", IFA_DATA(ipackets));
448 rrddim_set(st, "sent", IFA_DATA(opackets));
449 rrddim_set(st, "multicast_received", IFA_DATA(imcasts));
450 rrddim_set(st, "multicast_sent", IFA_DATA(omcasts));
453 // --------------------------------------------------------------------
455 st = rrdset_find_bytype_localhost("net_errors", ifa->ifa_name);
457 st = rrdset_create_localhost("net_errors", ifa->ifa_name, NULL, ifa->ifa_name, "net.errors"
458 , "Interface Errors", "errors/s", 7002, update_every, RRDSET_TYPE_LINE);
459 rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
461 rrddim_add(st, "inbound", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
462 rrddim_add(st, "outbound", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
464 else rrdset_next(st);
466 rrddim_set(st, "inbound", IFA_DATA(ierrors));
467 rrddim_set(st, "outbound", IFA_DATA(oerrors));
470 // --------------------------------------------------------------------
472 st = rrdset_find_bytype_localhost("net_drops", ifa->ifa_name);
474 st = rrdset_create_localhost("net_drops", ifa->ifa_name, NULL, ifa->ifa_name, "net.drops"
475 , "Interface Drops", "drops/s", 7003, update_every, RRDSET_TYPE_LINE);
476 rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
478 rrddim_add(st, "inbound", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
480 else rrdset_next(st);
482 rrddim_set(st, "inbound", IFA_DATA(iqdrops));
485 // --------------------------------------------------------------------
487 st = rrdset_find_bytype_localhost("net_events", ifa->ifa_name);
489 st = rrdset_create_localhost("net_events", ifa->ifa_name, NULL, ifa->ifa_name, "net.events"
490 , "Network Interface Events", "events/s", 7006, update_every
492 rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
494 rrddim_add(st, "frames", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
495 rrddim_add(st, "collisions", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
496 rrddim_add(st, "carrier", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
498 else rrdset_next(st);
500 rrddim_set(st, "collisions", IFA_DATA(collisions));