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
9 #define MAXDRIVENAME 31
11 #define KILO_FACTOR 1024
12 #define MEGA_FACTOR 1048576 // 1024 * 1024
13 #define GIGA_FACTOR 1073741824 // 1024 * 1024 * 1024
15 int do_macos_iokit(int update_every, usec_t dt) {
18 static int do_io = -1, do_space = -1, do_inodes = -1;
20 if (unlikely(do_io == -1)) {
21 do_io = config_get_boolean("plugin:macos:iokit", "disk i/o", 1);
22 do_space = config_get_boolean("plugin:macos:sysctl", "space usage for all disks", 1);
23 do_inodes = config_get_boolean("plugin:macos:sysctl", "inodes usage for all disks", 1);
28 mach_port_t master_port;
29 io_registry_entry_t drive, drive_media;
30 io_iterator_t drive_list;
31 CFDictionaryRef properties, statistics;
35 collected_number total_disk_reads = 0;
36 collected_number total_disk_writes = 0;
38 char name[MAXDRIVENAME];
39 collected_number bytes_read;
40 collected_number bytes_write;
41 collected_number reads;
42 collected_number writes;
43 collected_number time_read;
44 collected_number time_write;
45 collected_number latency_read;
46 collected_number latency_write;
49 collected_number duration_read_ns;
50 collected_number duration_write_ns;
51 collected_number busy_time_ns;
53 struct prev_diskstat {
54 collected_number bytes_read;
55 collected_number bytes_write;
56 collected_number operations_read;
57 collected_number operations_write;
58 collected_number duration_read_ns;
59 collected_number duration_write_ns;
60 collected_number busy_time_ns;
63 // NEEDED BY: do_space, do_inodes
64 struct statfs *mntbuf;
66 char mntonname[MNAMELEN + 1];
69 /* Get ports and services for drive statistics. */
70 if (unlikely(IOMasterPort(bootstrap_port, &master_port))) {
71 error("MACOS: IOMasterPort() failed");
73 error("DISABLED: system.io");
74 /* Get the list of all drive objects. */
75 } else if (unlikely(IOServiceGetMatchingServices(master_port, IOServiceMatching("IOBlockStorageDriver"), &drive_list))) {
76 error("MACOS: IOServiceGetMatchingServices() failed");
78 error("DISABLED: system.io");
80 while ((drive = IOIteratorNext(drive_list)) != 0) {
84 bzero(&diskstat, sizeof(diskstat));
86 /* Get drive media object. */
87 status = IORegistryEntryGetChildEntry(drive, kIOServicePlane, &drive_media);
88 if (unlikely(status != KERN_SUCCESS)) {
89 IOObjectRelease(drive);
93 /* Get drive media properties. */
94 if (likely(!IORegistryEntryCreateCFProperties(drive_media, (CFMutableDictionaryRef *)&properties, kCFAllocatorDefault, 0))) {
96 if (likely(name = (CFStringRef)CFDictionaryGetValue(properties, CFSTR(kIOBSDNameKey)))) {
97 CFStringGetCString(name, diskstat.name, MAXDRIVENAME, kCFStringEncodingUTF8);
102 CFRelease(properties);
103 IOObjectRelease(drive_media);
105 /* Obtain the properties for this drive object. */
106 if (unlikely(IORegistryEntryCreateCFProperties(drive, (CFMutableDictionaryRef *)&properties, kCFAllocatorDefault, 0))) {
107 error("MACOS: IORegistryEntryCreateCFProperties() failed");
109 error("DISABLED: system.io");
111 } else if (likely(properties)) {
112 /* Obtain the statistics from the drive properties. */
113 if (likely(statistics = (CFDictionaryRef)CFDictionaryGetValue(properties, CFSTR(kIOBlockStorageDriverStatisticsKey)))) {
115 // --------------------------------------------------------------------
117 /* Get bytes read. */
118 if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey)))) {
119 CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.bytes_read);
120 total_disk_reads += diskstat.bytes_read;
123 /* Get bytes written. */
124 if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey)))) {
125 CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.bytes_write);
126 total_disk_writes += diskstat.bytes_write;
129 st = rrdset_find_bytype("disk", diskstat.name);
131 st = rrdset_create("disk", diskstat.name, NULL, diskstat.name, "disk.io", "Disk I/O Bandwidth", "kilobytes/s", 2000, update_every, RRDSET_TYPE_AREA);
133 rrddim_add(st, "reads", NULL, 1, 1024, RRDDIM_INCREMENTAL);
134 rrddim_add(st, "writes", NULL, -1, 1024, RRDDIM_INCREMENTAL);
136 else rrdset_next(st);
138 prev_diskstat.bytes_read = rrddim_set(st, "reads", diskstat.bytes_read);
139 prev_diskstat.bytes_write = rrddim_set(st, "writes", diskstat.bytes_write);
142 // --------------------------------------------------------------------
144 /* Get number of reads. */
145 if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsReadsKey)))) {
146 CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.reads);
149 /* Get number of writes. */
150 if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsWritesKey)))) {
151 CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.writes);
154 st = rrdset_find_bytype("disk_ops", diskstat.name);
156 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);
159 rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
160 rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
162 else rrdset_next(st);
164 prev_diskstat.operations_read = rrddim_set(st, "reads", diskstat.reads);
165 prev_diskstat.operations_write = rrddim_set(st, "writes", diskstat.writes);
168 // --------------------------------------------------------------------
170 /* Get reads time. */
171 if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsTotalReadTimeKey)))) {
172 CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.time_read);
175 /* Get writes time. */
176 if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsTotalWriteTimeKey)))) {
177 CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.time_write);
180 st = rrdset_find_bytype("disk_util", diskstat.name);
182 st = rrdset_create("disk_util", diskstat.name, NULL, diskstat.name, "disk.util", "Disk Utilization Time", "% of time working", 2004, update_every, RRDSET_TYPE_AREA);
185 rrddim_add(st, "utilization", NULL, 1, 10000000, RRDDIM_INCREMENTAL);
187 else rrdset_next(st);
189 cur_diskstat.busy_time_ns = (diskstat.time_read + diskstat.time_write);
190 prev_diskstat.busy_time_ns = rrddim_set(st, "utilization", cur_diskstat.busy_time_ns);
193 // --------------------------------------------------------------------
195 /* Get reads latency. */
196 if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsLatentReadTimeKey)))) {
197 CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.latency_read);
200 /* Get writes latency. */
201 if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsLatentWriteTimeKey)))) {
202 CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.latency_write);
205 st = rrdset_find_bytype("disk_iotime", diskstat.name);
207 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);
210 rrddim_add(st, "reads", NULL, 1, 1000000, RRDDIM_INCREMENTAL);
211 rrddim_add(st, "writes", NULL, -1, 1000000, RRDDIM_INCREMENTAL);
213 else rrdset_next(st);
215 cur_diskstat.duration_read_ns = diskstat.time_read + diskstat.latency_read;
216 cur_diskstat.duration_write_ns = diskstat.time_write + diskstat.latency_write;
217 prev_diskstat.duration_read_ns = rrddim_set(st, "reads", cur_diskstat.duration_read_ns);
218 prev_diskstat.duration_write_ns = rrddim_set(st, "writes", cur_diskstat.duration_write_ns);
221 // --------------------------------------------------------------------
222 // calculate differential charts
223 // only if this is not the first time we run
227 // --------------------------------------------------------------------
229 st = rrdset_find_bytype("disk_await", diskstat.name);
231 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);
234 rrddim_add(st, "reads", NULL, 1, 1000000, RRDDIM_ABSOLUTE);
235 rrddim_add(st, "writes", NULL, -1, 1000000, RRDDIM_ABSOLUTE);
237 else rrdset_next(st);
239 rrddim_set(st, "reads", (diskstat.reads - prev_diskstat.operations_read) ?
240 (cur_diskstat.duration_read_ns - prev_diskstat.duration_read_ns) / (diskstat.reads - prev_diskstat.operations_read) : 0);
241 rrddim_set(st, "writes", (diskstat.writes - prev_diskstat.operations_write) ?
242 (cur_diskstat.duration_write_ns - prev_diskstat.duration_write_ns) / (diskstat.writes - prev_diskstat.operations_write) : 0);
245 // --------------------------------------------------------------------
247 st = rrdset_find_bytype("disk_avgsz", diskstat.name);
249 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);
252 rrddim_add(st, "reads", NULL, 1, 1024, RRDDIM_ABSOLUTE);
253 rrddim_add(st, "writes", NULL, -1, 1024, RRDDIM_ABSOLUTE);
255 else rrdset_next(st);
257 rrddim_set(st, "reads", (diskstat.reads - prev_diskstat.operations_read) ?
258 (diskstat.bytes_read - prev_diskstat.bytes_read) / (diskstat.reads - prev_diskstat.operations_read) : 0);
259 rrddim_set(st, "writes", (diskstat.writes - prev_diskstat.operations_write) ?
260 (diskstat.bytes_write - prev_diskstat.bytes_write) / (diskstat.writes - prev_diskstat.operations_write) : 0);
263 // --------------------------------------------------------------------
265 st = rrdset_find_bytype("disk_svctm", diskstat.name);
267 st = rrdset_create("disk_svctm", diskstat.name, NULL, diskstat.name, "disk.svctm", "Average Service Time", "ms per operation", 2007, update_every, RRDSET_TYPE_LINE);
270 rrddim_add(st, "svctm", NULL, 1, 1000000, RRDDIM_ABSOLUTE);
272 else rrdset_next(st);
274 rrddim_set(st, "svctm", ((diskstat.reads - prev_diskstat.operations_read) + (diskstat.writes - prev_diskstat.operations_write)) ?
275 (cur_diskstat.busy_time_ns - prev_diskstat.busy_time_ns) / ((diskstat.reads - prev_diskstat.operations_read) + (diskstat.writes - prev_diskstat.operations_write)) : 0);
281 CFRelease(properties);
285 IOObjectRelease(drive);
287 IOIteratorReset(drive_list);
290 IOObjectRelease(drive_list);
294 st = rrdset_find_bytype("system", "io");
296 st = rrdset_create("system", "io", NULL, "disk", NULL, "Disk I/O", "kilobytes/s", 150, update_every, RRDSET_TYPE_AREA);
297 rrddim_add(st, "in", NULL, 1, 1024, RRDDIM_INCREMENTAL);
298 rrddim_add(st, "out", NULL, -1, 1024, RRDDIM_INCREMENTAL);
300 else rrdset_next(st);
302 rrddim_set(st, "in", total_disk_reads);
303 rrddim_set(st, "out", total_disk_writes);
307 // --------------------------------------------------------------------------
309 if (likely(do_space || do_inodes)) {
310 // there is no mount info in sysctl MIBs
311 if (unlikely(!(mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)))) {
312 error("FREEBSD: getmntinfo() failed");
314 error("DISABLED: disk_space.X");
316 error("DISABLED: disk_inodes.X");
318 for (i = 0; i < mntsize; i++) {
319 if (mntbuf[i].f_flags == MNT_RDONLY ||
320 mntbuf[i].f_blocks == 0 ||
321 // taken from gnulib/mountlist.c and shortened to FreeBSD related fstypes
322 strcmp(mntbuf[i].f_fstypename, "autofs") == 0 ||
323 strcmp(mntbuf[i].f_fstypename, "procfs") == 0 ||
324 strcmp(mntbuf[i].f_fstypename, "subfs") == 0 ||
325 strcmp(mntbuf[i].f_fstypename, "devfs") == 0 ||
326 strcmp(mntbuf[i].f_fstypename, "none") == 0)
329 // --------------------------------------------------------------------------
331 if (likely(do_space)) {
332 st = rrdset_find_bytype("disk_space", mntbuf[i].f_mntonname);
334 snprintfz(title, 4096, "Disk Space Usage for %s [%s]", mntbuf[i].f_mntonname, mntbuf[i].f_mntfromname);
335 st = rrdset_create("disk_space", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname, "disk.space", title, "GB", 2023,
337 RRDSET_TYPE_STACKED);
339 rrddim_add(st, "avail", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRDDIM_ABSOLUTE);
340 rrddim_add(st, "used", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRDDIM_ABSOLUTE);
341 rrddim_add(st, "reserved_for_root", "reserved for root", mntbuf[i].f_bsize, GIGA_FACTOR,
346 rrddim_set(st, "avail", (collected_number) mntbuf[i].f_bavail);
347 rrddim_set(st, "used", (collected_number) (mntbuf[i].f_blocks - mntbuf[i].f_bfree));
348 rrddim_set(st, "reserved_for_root", (collected_number) (mntbuf[i].f_bfree - mntbuf[i].f_bavail));
352 // --------------------------------------------------------------------------
354 if (likely(do_inodes)) {
355 st = rrdset_find_bytype("disk_inodes", mntbuf[i].f_mntonname);
357 snprintfz(title, 4096, "Disk Files (inodes) Usage for %s [%s]", mntbuf[i].f_mntonname, mntbuf[i].f_mntfromname);
358 st = rrdset_create("disk_inodes", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname, "disk.inodes", title, "Inodes", 2024,
359 update_every, RRDSET_TYPE_STACKED);
361 rrddim_add(st, "avail", NULL, 1, 1, RRDDIM_ABSOLUTE);
362 rrddim_add(st, "used", NULL, 1, 1, RRDDIM_ABSOLUTE);
363 rrddim_add(st, "reserved_for_root", "reserved for root", 1, 1, RRDDIM_ABSOLUTE);
367 rrddim_set(st, "avail", (collected_number) mntbuf[i].f_ffree);
368 rrddim_set(st, "used", (collected_number) (mntbuf[i].f_files - mntbuf[i].f_ffree));