]> arthur.barton.de Git - netdata.git/blob - src/macos_fw.c
Merge pull request #1560 from vlvkobal/master
[netdata.git] / src / macos_fw.c
1 #include "common.h"
2 #include <CoreFoundation/CoreFoundation.h>
3 #include <IOKit/IOKitLib.h>
4 #include <IOKit/storage/IOBlockStorageDriver.h>
5 #include <IOKit/IOBSD.h>
6
7 #define MAXDRIVENAME 31
8
9 int do_macos_iokit(int update_every, usec_t dt) {
10     (void)dt;
11
12     static int do_io = -1;
13
14     if (unlikely(do_io == -1)) {
15         do_io                  = config_get_boolean("plugin:macos:iokit", "disk i/o", 1);
16     }
17
18     RRDSET *st;
19
20     mach_port_t         master_port;
21     io_registry_entry_t drive, drive_media;
22     io_iterator_t       drive_list;
23     CFDictionaryRef     properties, statistics;
24     CFStringRef         name;
25     CFNumberRef         number;
26     kern_return_t       status;
27     collected_number    total_disk_reads = 0;
28     collected_number    total_disk_writes = 0;
29     struct diskstat {
30         char name[MAXDRIVENAME];
31         collected_number bytes_read;
32         collected_number bytes_write;
33         collected_number reads;
34         collected_number writes;
35         collected_number time_read;
36         collected_number time_write;
37         collected_number latency_read;
38         collected_number latency_write;
39     } diskstat;
40     struct cur_diskstat {
41         collected_number duration_read_ns;
42         collected_number duration_write_ns;
43         collected_number busy_time_ns;
44     } cur_diskstat;
45     struct prev_diskstat {
46         collected_number bytes_read;
47         collected_number bytes_write;
48         collected_number operations_read;
49         collected_number operations_write;
50         collected_number duration_read_ns;
51         collected_number duration_write_ns;
52         collected_number busy_time_ns;
53     } prev_diskstat;
54
55     /* Get ports and services for drive statistics. */
56     if (unlikely(IOMasterPort(bootstrap_port, &master_port))) {
57         error("MACOS: IOMasterPort() failed");
58         do_io = 0;
59         error("DISABLED: system.io");
60     /* Get the list of all drive objects. */
61     } else if (unlikely(IOServiceGetMatchingServices(master_port, IOServiceMatching("IOBlockStorageDriver"), &drive_list))) {
62         error("MACOS: IOServiceGetMatchingServices() failed");
63         do_io = 0;
64         error("DISABLED: system.io");
65     } else {
66         while ((drive = IOIteratorNext(drive_list)) != 0) {
67             properties = 0;
68             statistics = 0;
69             number = 0;
70             bzero(&diskstat, sizeof(diskstat));
71
72             /* Get drive media object. */
73             status = IORegistryEntryGetChildEntry(drive, kIOServicePlane, &drive_media);
74             if (unlikely(status != KERN_SUCCESS)) {
75                 IOObjectRelease(drive);
76                 continue;
77             }
78
79             /* Get drive media properties. */
80             if (likely(!IORegistryEntryCreateCFProperties(drive_media, (CFMutableDictionaryRef *)&properties, kCFAllocatorDefault, 0))) {
81                 /* Get disk name. */
82                 if (likely(name = (CFStringRef)CFDictionaryGetValue(properties, CFSTR(kIOBSDNameKey)))) {
83                     CFStringGetCString(name, diskstat.name, MAXDRIVENAME, kCFStringEncodingUTF8);
84                 }
85             }
86
87             /* Release. */
88             CFRelease(properties);
89             IOObjectRelease(drive_media);
90
91             /* Obtain the properties for this drive object. */
92             if (unlikely(IORegistryEntryCreateCFProperties(drive, (CFMutableDictionaryRef *)&properties, kCFAllocatorDefault, 0))) {
93                 error("MACOS: IORegistryEntryCreateCFProperties() failed");
94                 do_io = 0;
95                 error("DISABLED: system.io");
96                 break;
97             } else if (likely(properties)) {
98                 /* Obtain the statistics from the drive properties. */
99                 if (likely(statistics = (CFDictionaryRef)CFDictionaryGetValue(properties, CFSTR(kIOBlockStorageDriverStatisticsKey)))) {
100
101                     // --------------------------------------------------------------------
102
103                     /* Get bytes read. */
104                     if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey)))) {
105                         CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.bytes_read);
106                         total_disk_reads += diskstat.bytes_read;
107                     }
108
109                     /* Get bytes written. */
110                     if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey)))) {
111                         CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.bytes_write);
112                         total_disk_writes += diskstat.bytes_write;
113                     }
114
115                     st = rrdset_find_bytype("disk", diskstat.name);
116                     if (unlikely(!st)) {
117                         st = rrdset_create("disk", diskstat.name, NULL, diskstat.name, "disk.io", "Disk I/O Bandwidth", "kilobytes/s", 2000, update_every, RRDSET_TYPE_AREA);
118
119                         rrddim_add(st, "reads", NULL, 1, 1024, RRDDIM_INCREMENTAL);
120                         rrddim_add(st, "writes", NULL, -1, 1024, RRDDIM_INCREMENTAL);
121                     }
122                     else rrdset_next(st);
123
124                     prev_diskstat.bytes_read = rrddim_set(st, "reads", diskstat.bytes_read);
125                     prev_diskstat.bytes_write = rrddim_set(st, "writes", diskstat.bytes_write);
126                     rrdset_done(st);
127
128                     // --------------------------------------------------------------------
129
130                     /* Get number of reads. */
131                     if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsReadsKey)))) {
132                         CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.reads);
133                     }
134
135                     /* Get number of writes. */
136                     if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsWritesKey)))) {
137                         CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.writes);
138                     }
139
140                     st = rrdset_find_bytype("disk_ops", diskstat.name);
141                     if (unlikely(!st)) {
142                         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);
143                         st->isdetail = 1;
144
145                         rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
146                         rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
147                     }
148                     else rrdset_next(st);
149
150                     prev_diskstat.operations_read = rrddim_set(st, "reads", diskstat.reads);
151                     prev_diskstat.operations_write = rrddim_set(st, "writes", diskstat.writes);
152                     rrdset_done(st);
153
154                     // --------------------------------------------------------------------
155
156                     /* Get reads time. */
157                     if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsTotalReadTimeKey)))) {
158                         CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.time_read);
159                     }
160
161                     /* Get writes time. */
162                     if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsTotalWriteTimeKey)))) {
163                         CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.time_write);
164                     }
165
166                     st = rrdset_find_bytype("disk_util", diskstat.name);
167                     if (unlikely(!st)) {
168                         st = rrdset_create("disk_util", diskstat.name, NULL, diskstat.name, "disk.util", "Disk Utilization Time", "% of time working", 2004, update_every, RRDSET_TYPE_AREA);
169                         st->isdetail = 1;
170
171                         rrddim_add(st, "utilization", NULL, 1, 10000000, RRDDIM_INCREMENTAL);
172                     }
173                     else rrdset_next(st);
174
175                     cur_diskstat.busy_time_ns = (diskstat.time_read + diskstat.time_write);
176                     prev_diskstat.busy_time_ns = rrddim_set(st, "utilization", cur_diskstat.busy_time_ns);
177                     rrdset_done(st);
178
179                     // --------------------------------------------------------------------
180
181                     /* Get reads latency. */
182                     if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsLatentReadTimeKey)))) {
183                         CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.latency_read);
184                     }
185
186                     /* Get writes latency. */
187                     if (likely(number = (CFNumberRef)CFDictionaryGetValue(statistics, CFSTR(kIOBlockStorageDriverStatisticsLatentWriteTimeKey)))) {
188                         CFNumberGetValue(number, kCFNumberSInt64Type, &diskstat.latency_write);
189                     }
190
191                     st = rrdset_find_bytype("disk_iotime", diskstat.name);
192                     if (unlikely(!st)) {
193                         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);
194                         st->isdetail = 1;
195
196                         rrddim_add(st, "reads", NULL, 1, 1000000, RRDDIM_INCREMENTAL);
197                         rrddim_add(st, "writes", NULL, -1, 1000000, RRDDIM_INCREMENTAL);
198                     }
199                     else rrdset_next(st);
200
201                     cur_diskstat.duration_read_ns = diskstat.time_read + diskstat.latency_read;
202                     cur_diskstat.duration_write_ns = diskstat.time_write + diskstat.latency_write;
203                     prev_diskstat.duration_read_ns = rrddim_set(st, "reads", cur_diskstat.duration_read_ns);
204                     prev_diskstat.duration_write_ns = rrddim_set(st, "writes", cur_diskstat.duration_write_ns);
205                     rrdset_done(st);
206
207                     // --------------------------------------------------------------------
208                     // calculate differential charts
209                     // only if this is not the first time we run
210
211                     if (likely(dt)) {
212
213                         // --------------------------------------------------------------------
214
215                         st = rrdset_find_bytype("disk_await", diskstat.name);
216                         if (unlikely(!st)) {
217                             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);
218                             st->isdetail = 1;
219
220                             rrddim_add(st, "reads", NULL, 1, 1000000, RRDDIM_ABSOLUTE);
221                             rrddim_add(st, "writes", NULL, -1, 1000000, RRDDIM_ABSOLUTE);
222                         }
223                         else rrdset_next(st);
224
225                         rrddim_set(st, "reads", (diskstat.reads - prev_diskstat.operations_read) ?
226                             (cur_diskstat.duration_read_ns - prev_diskstat.duration_read_ns) / (diskstat.reads - prev_diskstat.operations_read) : 0);
227                         rrddim_set(st, "writes", (diskstat.writes - prev_diskstat.operations_write) ?
228                             (cur_diskstat.duration_write_ns - prev_diskstat.duration_write_ns) / (diskstat.writes - prev_diskstat.operations_write) : 0);
229                         rrdset_done(st);
230
231                         // --------------------------------------------------------------------
232
233                         st = rrdset_find_bytype("disk_avgsz", diskstat.name);
234                         if (unlikely(!st)) {
235                             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);
236                             st->isdetail = 1;
237
238                             rrddim_add(st, "reads", NULL, 1, 1024, RRDDIM_ABSOLUTE);
239                             rrddim_add(st, "writes", NULL, -1, 1024, RRDDIM_ABSOLUTE);
240                         }
241                         else rrdset_next(st);
242
243                         rrddim_set(st, "reads", (diskstat.reads - prev_diskstat.operations_read) ?
244                             (diskstat.bytes_read - prev_diskstat.bytes_read) / (diskstat.reads - prev_diskstat.operations_read) : 0);
245                         rrddim_set(st, "writes", (diskstat.writes - prev_diskstat.operations_write) ?
246                             (diskstat.bytes_write - prev_diskstat.bytes_write) / (diskstat.writes - prev_diskstat.operations_write) : 0);
247                         rrdset_done(st);
248
249                         // --------------------------------------------------------------------
250
251                         st = rrdset_find_bytype("disk_svctm", diskstat.name);
252                         if (unlikely(!st)) {
253                             st = rrdset_create("disk_svctm", diskstat.name, NULL, diskstat.name, "disk.svctm", "Average Service Time", "ms per operation", 2007, update_every, RRDSET_TYPE_LINE);
254                             st->isdetail = 1;
255
256                             rrddim_add(st, "svctm", NULL, 1, 1000000, RRDDIM_ABSOLUTE);
257                         }
258                         else rrdset_next(st);
259
260                         rrddim_set(st, "svctm", ((diskstat.reads - prev_diskstat.operations_read) + (diskstat.writes - prev_diskstat.operations_write)) ?
261                             (cur_diskstat.busy_time_ns - prev_diskstat.busy_time_ns) / ((diskstat.reads - prev_diskstat.operations_read) + (diskstat.writes - prev_diskstat.operations_write)) : 0);
262                         rrdset_done(st);
263                     }
264                 }
265
266                 /* Release. */
267                 CFRelease(properties);
268             }
269
270             /* Release. */
271             IOObjectRelease(drive);
272         }
273         IOIteratorReset(drive_list);
274
275         /* Release. */
276         IOObjectRelease(drive_list);
277     }
278
279     if (likely(do_io)) {
280         st = rrdset_find_bytype("system", "io");
281         if (unlikely(!st)) {
282             st = rrdset_create("system", "io", NULL, "disk", NULL, "Disk I/O", "kilobytes/s", 150, update_every, RRDSET_TYPE_AREA);
283             rrddim_add(st, "in",  NULL,  1, 1024, RRDDIM_INCREMENTAL);
284             rrddim_add(st, "out", NULL, -1, 1024, RRDDIM_INCREMENTAL);
285         }
286         else rrdset_next(st);
287
288         rrddim_set(st, "in", total_disk_reads);
289         rrddim_set(st, "out", total_disk_writes);
290         rrdset_done(st);
291     }
292
293     return 0;
294 }