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("disk", diskstat.name);
141 st = rrdset_create("disk", diskstat.name, NULL, diskstat.name, "disk.io", "Disk I/O Bandwidth", "kilobytes/s", 2000, update_every, RRDSET_TYPE_AREA);
143 rrddim_add(st, "reads", NULL, 1, 1024, RRDDIM_INCREMENTAL);
144 rrddim_add(st, "writes", NULL, -1, 1024, RRDDIM_INCREMENTAL);
146 else rrdset_next(st);
148 prev_diskstat.bytes_read = rrddim_set(st, "reads", diskstat.bytes_read);
149 prev_diskstat.bytes_write = rrddim_set(st, "writes", diskstat.bytes_write);
152 // --------------------------------------------------------------------
154 /* Get number of reads. */
155 if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsReadsKey)))) {
156 CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.reads);
159 /* Get number of writes. */
160 if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsWritesKey)))) {
161 CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.writes);
164 st = rrdset_find_bytype("disk_ops", diskstat.name);
166 st = rrdset_create("disk_ops", diskstat.name, NULL, diskstat.name, "disk.ops", "Disk Completed I/O Operations", "operations/s", 2001, update_every, RRDSET_TYPE_LINE);
169 rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
170 rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
172 else rrdset_next(st);
174 prev_diskstat.operations_read = rrddim_set(st, "reads", diskstat.reads);
175 prev_diskstat.operations_write = rrddim_set(st, "writes", diskstat.writes);
178 // --------------------------------------------------------------------
180 /* Get reads time. */
181 if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsTotalReadTimeKey)))) {
182 CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.time_read);
185 /* Get writes time. */
186 if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsTotalWriteTimeKey)))) {
187 CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.time_write);
190 st = rrdset_find_bytype("disk_util", diskstat.name);
192 st = rrdset_create("disk_util", diskstat.name, NULL, diskstat.name, "disk.util", "Disk Utilization Time", "% of time working", 2004, update_every, RRDSET_TYPE_AREA);
195 rrddim_add(st, "utilization", NULL, 1, 10000000, RRDDIM_INCREMENTAL);
197 else rrdset_next(st);
199 cur_diskstat.busy_time_ns = (diskstat.time_read + diskstat.time_write);
200 prev_diskstat.busy_time_ns = rrddim_set(st, "utilization", cur_diskstat.busy_time_ns);
203 // --------------------------------------------------------------------
205 /* Get reads latency. */
206 if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsLatentReadTimeKey)))) {
207 CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.latency_read);
210 /* Get writes latency. */
211 if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsLatentWriteTimeKey)))) {
212 CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.latency_write);
215 st = rrdset_find_bytype("disk_iotime", diskstat.name);
217 st = rrdset_create("disk_iotime", diskstat.name, NULL, diskstat.name, "disk.iotime", "Disk Total I/O Time", "milliseconds/s", 2022, update_every, RRDSET_TYPE_LINE);
220 rrddim_add(st, "reads", NULL, 1, 1000000, RRDDIM_INCREMENTAL);
221 rrddim_add(st, "writes", NULL, -1, 1000000, RRDDIM_INCREMENTAL);
223 else rrdset_next(st);
225 cur_diskstat.duration_read_ns = diskstat.time_read + diskstat.latency_read;
226 cur_diskstat.duration_write_ns = diskstat.time_write + diskstat.latency_write;
227 prev_diskstat.duration_read_ns = rrddim_set(st, "reads", cur_diskstat.duration_read_ns);
228 prev_diskstat.duration_write_ns = rrddim_set(st, "writes", cur_diskstat.duration_write_ns);
231 // --------------------------------------------------------------------
232 // calculate differential charts
233 // only if this is not the first time we run
237 // --------------------------------------------------------------------
239 st = rrdset_find_bytype("disk_await", diskstat.name);
241 st = rrdset_create("disk_await", diskstat.name, NULL, diskstat.name, "disk.await", "Average Completed I/O Operation Time", "ms per operation", 2005, update_every, RRDSET_TYPE_LINE);
244 rrddim_add(st, "reads", NULL, 1, 1000000, RRDDIM_ABSOLUTE);
245 rrddim_add(st, "writes", NULL, -1, 1000000, RRDDIM_ABSOLUTE);
247 else rrdset_next(st);
249 rrddim_set(st, "reads", (diskstat.reads - prev_diskstat.operations_read) ?
250 (cur_diskstat.duration_read_ns - prev_diskstat.duration_read_ns) / (diskstat.reads - prev_diskstat.operations_read) : 0);
251 rrddim_set(st, "writes", (diskstat.writes - prev_diskstat.operations_write) ?
252 (cur_diskstat.duration_write_ns - prev_diskstat.duration_write_ns) / (diskstat.writes - prev_diskstat.operations_write) : 0);
255 // --------------------------------------------------------------------
257 st = rrdset_find_bytype("disk_avgsz", diskstat.name);
259 st = rrdset_create("disk_avgsz", diskstat.name, NULL, diskstat.name, "disk.avgsz", "Average Completed I/O Operation Bandwidth", "kilobytes per operation", 2006, update_every, RRDSET_TYPE_AREA);
262 rrddim_add(st, "reads", NULL, 1, 1024, RRDDIM_ABSOLUTE);
263 rrddim_add(st, "writes", NULL, -1, 1024, RRDDIM_ABSOLUTE);
265 else rrdset_next(st);
267 rrddim_set(st, "reads", (diskstat.reads - prev_diskstat.operations_read) ?
268 (diskstat.bytes_read - prev_diskstat.bytes_read) / (diskstat.reads - prev_diskstat.operations_read) : 0);
269 rrddim_set(st, "writes", (diskstat.writes - prev_diskstat.operations_write) ?
270 (diskstat.bytes_write - prev_diskstat.bytes_write) / (diskstat.writes - prev_diskstat.operations_write) : 0);
273 // --------------------------------------------------------------------
275 st = rrdset_find_bytype("disk_svctm", diskstat.name);
277 st = rrdset_create("disk_svctm", diskstat.name, NULL, diskstat.name, "disk.svctm", "Average Service Time", "ms per operation", 2007, update_every, RRDSET_TYPE_LINE);
280 rrddim_add(st, "svctm", NULL, 1, 1000000, RRDDIM_ABSOLUTE);
282 else rrdset_next(st);
284 rrddim_set(st, "svctm", ((diskstat.reads - prev_diskstat.operations_read) + (diskstat.writes - prev_diskstat.operations_write)) ?
285 (cur_diskstat.busy_time_ns - prev_diskstat.busy_time_ns) / ((diskstat.reads - prev_diskstat.operations_read) + (diskstat.writes - prev_diskstat.operations_write)) : 0);
291 CFRelease(properties);
295 IOObjectRelease(drive);
297 IOIteratorReset(drive_list);
300 IOObjectRelease(drive_list);
304 st = rrdset_find_bytype("system", "io");
306 st = rrdset_create("system", "io", NULL, "disk", NULL, "Disk I/O", "kilobytes/s", 150, update_every, RRDSET_TYPE_AREA);
307 rrddim_add(st, "in", NULL, 1, 1024, RRDDIM_INCREMENTAL);
308 rrddim_add(st, "out", NULL, -1, 1024, RRDDIM_INCREMENTAL);
310 else rrdset_next(st);
312 rrddim_set(st, "in", total_disk_reads);
313 rrddim_set(st, "out", total_disk_writes);
317 // Can be merged with FreeBSD plugin
318 // --------------------------------------------------------------------------
320 if (likely(do_space || do_inodes)) {
321 // there is no mount info in sysctl MIBs
322 if (unlikely(!(mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)))) {
323 error("MACOS: getmntinfo() failed");
325 error("DISABLED: disk_space.X");
327 error("DISABLED: disk_inodes.X");
329 for (i = 0; i < mntsize; i++) {
330 if (mntbuf[i].f_flags == MNT_RDONLY ||
331 mntbuf[i].f_blocks == 0 ||
332 // taken from gnulib/mountlist.c and shortened to FreeBSD related fstypes
333 strcmp(mntbuf[i].f_fstypename, "autofs") == 0 ||
334 strcmp(mntbuf[i].f_fstypename, "procfs") == 0 ||
335 strcmp(mntbuf[i].f_fstypename, "subfs") == 0 ||
336 strcmp(mntbuf[i].f_fstypename, "devfs") == 0 ||
337 strcmp(mntbuf[i].f_fstypename, "none") == 0)
340 // --------------------------------------------------------------------------
342 if (likely(do_space)) {
343 st = rrdset_find_bytype("disk_space", mntbuf[i].f_mntonname);
345 snprintfz(title, 4096, "Disk Space Usage for %s [%s]", mntbuf[i].f_mntonname, mntbuf[i].f_mntfromname);
346 st = rrdset_create("disk_space", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname, "disk.space", title, "GB", 2023,
348 RRDSET_TYPE_STACKED);
350 rrddim_add(st, "avail", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRDDIM_ABSOLUTE);
351 rrddim_add(st, "used", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRDDIM_ABSOLUTE);
352 rrddim_add(st, "reserved_for_root", "reserved for root", mntbuf[i].f_bsize, GIGA_FACTOR,
357 rrddim_set(st, "avail", (collected_number) mntbuf[i].f_bavail);
358 rrddim_set(st, "used", (collected_number) (mntbuf[i].f_blocks - mntbuf[i].f_bfree));
359 rrddim_set(st, "reserved_for_root", (collected_number) (mntbuf[i].f_bfree - mntbuf[i].f_bavail));
363 // --------------------------------------------------------------------------
365 if (likely(do_inodes)) {
366 st = rrdset_find_bytype("disk_inodes", mntbuf[i].f_mntonname);
368 snprintfz(title, 4096, "Disk Files (inodes) Usage for %s [%s]", mntbuf[i].f_mntonname, mntbuf[i].f_mntfromname);
369 st = rrdset_create("disk_inodes", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname, "disk.inodes", title, "Inodes", 2024,
370 update_every, RRDSET_TYPE_STACKED);
372 rrddim_add(st, "avail", NULL, 1, 1, RRDDIM_ABSOLUTE);
373 rrddim_add(st, "used", NULL, 1, 1, RRDDIM_ABSOLUTE);
374 rrddim_add(st, "reserved_for_root", "reserved for root", 1, 1, RRDDIM_ABSOLUTE);
378 rrddim_set(st, "avail", (collected_number) mntbuf[i].f_ffree);
379 rrddim_set(st, "used", (collected_number) (mntbuf[i].f_files - mntbuf[i].f_ffree));
386 // Can be merged with FreeBSD plugin
387 // --------------------------------------------------------------------
389 if (likely(do_bandwidth)) {
390 if (unlikely(getifaddrs(&ifap))) {
391 error("MACOS: getifaddrs()");
393 error("DISABLED: system.ipv4");
395 for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
396 if (ifa->ifa_addr->sa_family != AF_LINK)
399 // --------------------------------------------------------------------
401 st = rrdset_find_bytype("net", ifa->ifa_name);
403 st = rrdset_create("net", ifa->ifa_name, NULL, ifa->ifa_name, "net.net", "Bandwidth", "kilobits/s", 7000, update_every, RRDSET_TYPE_AREA);
405 rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
406 rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
408 else rrdset_next(st);
410 rrddim_set(st, "received", IFA_DATA(ibytes));
411 rrddim_set(st, "sent", IFA_DATA(obytes));
414 // --------------------------------------------------------------------
416 st = rrdset_find_bytype("net_packets", ifa->ifa_name);
418 st = rrdset_create("net_packets", ifa->ifa_name, NULL, ifa->ifa_name, "net.packets", "Packets", "packets/s", 7001, update_every, RRDSET_TYPE_LINE);
421 rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
422 rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
423 rrddim_add(st, "multicast_received", NULL, 1, 1, RRDDIM_INCREMENTAL);
424 rrddim_add(st, "multicast_sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
426 else rrdset_next(st);
428 rrddim_set(st, "received", IFA_DATA(ipackets));
429 rrddim_set(st, "sent", IFA_DATA(opackets));
430 rrddim_set(st, "multicast_received", IFA_DATA(imcasts));
431 rrddim_set(st, "multicast_sent", IFA_DATA(omcasts));
434 // --------------------------------------------------------------------
436 st = rrdset_find_bytype("net_errors", ifa->ifa_name);
438 st = rrdset_create("net_errors", ifa->ifa_name, NULL, ifa->ifa_name, "net.errors", "Interface Errors", "errors/s", 7002, update_every, RRDSET_TYPE_LINE);
441 rrddim_add(st, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL);
442 rrddim_add(st, "outbound", NULL, -1, 1, RRDDIM_INCREMENTAL);
444 else rrdset_next(st);
446 rrddim_set(st, "inbound", IFA_DATA(ierrors));
447 rrddim_set(st, "outbound", IFA_DATA(oerrors));
450 // --------------------------------------------------------------------
452 st = rrdset_find_bytype("net_drops", ifa->ifa_name);
454 st = rrdset_create("net_drops", ifa->ifa_name, NULL, ifa->ifa_name, "net.drops", "Interface Drops", "drops/s", 7003, update_every, RRDSET_TYPE_LINE);
457 rrddim_add(st, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL);
459 else rrdset_next(st);
461 rrddim_set(st, "inbound", IFA_DATA(iqdrops));
464 // --------------------------------------------------------------------
466 st = rrdset_find_bytype("net_events", ifa->ifa_name);
468 st = rrdset_create("net_events", ifa->ifa_name, NULL, ifa->ifa_name, "net.events", "Network Interface Events", "events/s", 7006, update_every, RRDSET_TYPE_LINE);
471 rrddim_add(st, "frames", NULL, 1, 1, RRDDIM_INCREMENTAL);
472 rrddim_add(st, "collisions", NULL, -1, 1, RRDDIM_INCREMENTAL);
473 rrddim_add(st, "carrier", NULL, -1, 1, RRDDIM_INCREMENTAL);
475 else rrdset_next(st);
477 rrddim_set(st, "collisions", IFA_DATA(collisions));