1 /*
2  * Copyright (c) 2012-2013 Apple Computer, Inc.  All Rights Reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 #define __STDC_LIMIT_MACROS     // what are the C++ equivalents?
30 #include <stdint.h>
31 
32 #include <IOKit/IOKernelReportStructs.h>
33 #include <IOKit/IOKernelReporters.h>
34 #include "IOReporterDefs.h"
35 
36 
37 #define super IOReporter
38 OSDefineMetaClassAndStructors(IOHistogramReporter, IOReporter);
39 
40 /* static */
41 IOHistogramReporter*
42 IOHistogramReporter::with(IOService *reportingService,
43                           IOReportCategories categories,
44                           uint64_t channelID,
45                           const char *channelName,
46                           IOReportUnits unit,
47                           int nSegments,
48                           IOHistogramSegmentConfig *config)
49 {
50     IOHistogramReporter *reporter = new IOHistogramReporter;
51 
52     const OSSymbol *tmpChannelName = NULL;
53 
54     if (reporter) {
55 
56         if (channelName)
57             tmpChannelName = OSSymbol::withCString(channelName);
58 
59         if(reporter->initWith(reportingService, categories,
60                               channelID, tmpChannelName,
61                               unit, nSegments, config)) {
62             return reporter;
63         }
64     }
65 
66     return 0;
67 }
68 
69 
70 bool
71 IOHistogramReporter::initWith(IOService *reportingService,
72                               IOReportCategories categories,
73                               uint64_t channelID,
74                               const OSSymbol *channelName,
75                               IOReportUnits unit,
76                               int nSegments,
77                               IOHistogramSegmentConfig *config)
78 {
79     bool            result = false;
80     IOReturn        res;        // for PREFL_MEMOP
81     size_t          configSize, elementsSize, eCountsSize, boundsSize;
82     int             cnt, cnt2, cnt3 = 0;
83     int64_t        bucketBound = 0, previousBucketBound = 0;
84 
85     // analyzer appeasement
86     configSize = elementsSize = eCountsSize = boundsSize = 0;
87 
88     IORLOG("IOHistogramReporter::initWith");
89 
90     // For now, this reporter is currently limited to a single channel
91     _nChannels = 1;
92 
93     IOReportChannelType channelType = {
94         .categories = categories,
95         .report_format = kIOReportFormatHistogram,
96         .nelements = 0,  // Initialized when Config is unpacked
97         .element_idx = 0
98     };
99 
100     if (super::init(reportingService, channelType, unit) != true) {
101         IORLOG("%s - ERROR: super::init failed", __func__);
102         result = false;
103         goto finish;
104     }
105 
106     // Make sure to call this after the commit init phase
107     if (channelName) _channelNames->setObject(channelName);
108 
109     _segmentCount = nSegments;
110     if (_segmentCount == 0) {
111         IORLOG("IOReportHistogram init ERROR. No configuration provided!");
112         result = false;
113         goto finish;
114     }
115 
116     IORLOG("%s - %u segment(s)", __func__, _segmentCount);
117 
118     PREFL_MEMOP_FAIL(_segmentCount, IOHistogramSegmentConfig);
119     configSize = (size_t)_segmentCount * sizeof(IOHistogramSegmentConfig);
120     _histogramSegmentsConfig = (IOHistogramSegmentConfig*)IOMalloc(configSize);
121     if (!_histogramSegmentsConfig)      goto finish;
122     memcpy(_histogramSegmentsConfig, config, configSize);
123 
124     // Find out how many elements are need to store the histogram
125     for (cnt = 0; cnt < _segmentCount; cnt++) {
126 
127         _nElements += _histogramSegmentsConfig[cnt].segment_bucket_count;
128         _channelDimension += _histogramSegmentsConfig[cnt].segment_bucket_count;
129 
130         IORLOG("\t\t bucket_base_width: %u | log_scale: %u | buckets: %u",
131                 _histogramSegmentsConfig[cnt].base_bucket_width,
132                 _histogramSegmentsConfig[cnt].scale_flag,
133                 _histogramSegmentsConfig[cnt].segment_bucket_count);
134 
135         if (_histogramSegmentsConfig[cnt].scale_flag > 1
136             || _histogramSegmentsConfig[cnt].base_bucket_width == 0) {
137             result = false;
138             goto finish;
139         }
140 
141     }
142 
143     // Update the channel type with discovered dimension
144     _channelType.nelements = _channelDimension;
145 
146     IORLOG("%s - %u channel(s) of dimension %u",
147            __func__, _nChannels, _channelDimension);
148 
149     IORLOG("%s %d segments for a total dimension of %d elements",
150            __func__, _nChannels, _nElements);
151 
152     // Allocate memory for the array of report elements
153     PREFL_MEMOP_FAIL(_nElements, IOReportElement);
154     elementsSize = (size_t)_nElements * sizeof(IOReportElement);
155     _elements = (IOReportElement *)IOMalloc(elementsSize);
156     if (!_elements)             goto finish;
157     memset(_elements, 0, elementsSize);
158 
159     // Allocate memory for the array of element watch count
160     PREFL_MEMOP_FAIL(_nElements, int);
161     eCountsSize = (size_t)_nChannels * sizeof(int);
162     _enableCounts = (int *)IOMalloc(eCountsSize);
163     if (!_enableCounts)         goto finish;
164     memset(_enableCounts, 0, eCountsSize);
165 
166     lockReporter();
167     for (cnt2 = 0; cnt2 < _channelDimension; cnt2++) {
168         IOHistogramReportValues hist_values;
169         if (copyElementValues(cnt2, (IOReportElementValues*)&hist_values)){
170             goto finish;
171         }
172         hist_values.bucket_min = kIOReportInvalidIntValue;
173         hist_values.bucket_max = kIOReportInvalidIntValue;
174         hist_values.bucket_sum = kIOReportInvalidIntValue;
175         if (setElementValues(cnt2, (IOReportElementValues*)&hist_values)){
176             goto finish;
177         }
178 
179         // Setup IOReporter's channel IDs
180         _elements[cnt2].channel_id = channelID;
181 
182         // Setup IOReporter's reporting provider service
183         _elements[cnt2].provider_id = _driver_id;
184 
185         // Setup IOReporter's channel type
186         _elements[cnt2].channel_type = _channelType;
187         _elements[cnt2].channel_type.element_idx = cnt2;
188 
189         //IOREPORTER_DEBUG_ELEMENT(cnt2);
190     }
191     unlockReporter();
192 
193     // Allocate memory for the bucket upper bounds
194     PREFL_MEMOP_FAIL(_nElements, uint64_t);
195     boundsSize = (size_t)_nElements * sizeof(uint64_t);
196     _bucketBounds = (int64_t*)IOMalloc(boundsSize);
197     if (!_bucketBounds)         goto finish;
198     memset(_bucketBounds, 0, boundsSize);
199     _bucketCount = _nElements;
200 
201     for (cnt = 0; cnt < _segmentCount; cnt++) {
202 
203         if (_histogramSegmentsConfig[cnt].segment_bucket_count > INT_MAX
204             || _histogramSegmentsConfig[cnt].base_bucket_width > INT_MAX) {
205             goto finish;
206         }
207         for (cnt2 = 0; cnt2 < (int)_histogramSegmentsConfig[cnt].segment_bucket_count; cnt2++) {
208 
209             if (cnt3 >= _nElements) {
210                 IORLOG("ERROR: _bucketBounds init");
211                 return false;
212             }
213 
214             if (_histogramSegmentsConfig[cnt].scale_flag) {
215                 // FIXME: Could use pow() but not sure how to include math.h
216                 int64_t power = 1;
217                 int exponent = cnt2 + 1;
218                 while (exponent) {
219                     power *= _histogramSegmentsConfig[cnt].base_bucket_width;
220                     exponent--;
221                 }
222                 bucketBound = power;
223             }
224 
225             else {
226                 bucketBound = _histogramSegmentsConfig[cnt].base_bucket_width *
227                                                     ((unsigned)cnt2 + 1);
228             }
229 
230             if (previousBucketBound >= bucketBound) {
231                 IORLOG("Histogram ERROR: bucket bound does not increase linearly (segment %u / bucket # %u)",
232                        cnt, cnt2);
233                 result = false;
234                 goto finish;
235             }
236 
237             _bucketBounds[cnt3] = bucketBound;
238             // IORLOG("_bucketBounds[%u] = %llu", cnt3, bucketBound);
239             previousBucketBound = _bucketBounds[cnt3];
240             cnt3++;
241         }
242     }
243 
244     // success
245     result = true;
246 
247 finish:
248     if (result != true) {
249 
250         if (_histogramSegmentsConfig)
251             IOFree(_histogramSegmentsConfig, configSize);
252 
253         if (_elements)
254             IOFree(_elements, elementsSize);
255 
256         if (_enableCounts)
257             IOFree(_enableCounts, eCountsSize);
258 
259         if (_bucketBounds)
260             IOFree(_bucketBounds, boundsSize);
261     }
262 
263     return result;
264 }
265 
266 
267 void
268 IOHistogramReporter::free(void)
269 {
270     if (_bucketBounds) {
271         PREFL_MEMOP_PANIC(_nElements, int64_t);
272         IOFree(_bucketBounds, (size_t)_nElements * sizeof(int64_t));
273     }
274     if (_histogramSegmentsConfig) {
275         PREFL_MEMOP_PANIC(_segmentCount, IOHistogramSegmentConfig);
276         IOFree(_histogramSegmentsConfig,
277                (size_t)_segmentCount * sizeof(IOHistogramSegmentConfig));
278     }
279 
280     super::free();
281 }
282 
283 
284 IOReportLegendEntry*
285 IOHistogramReporter::handleCreateLegend(void)
286 {
287     OSData                  *tmpConfigData;
288     OSDictionary            *tmpDict;
289     IOReportLegendEntry     *legendEntry = NULL;
290 
291     legendEntry = super::handleCreateLegend();
292 
293     if (legendEntry) {
294 
295         PREFL_MEMOP_PANIC(_segmentCount, IOHistogramSegmentConfig);
296         tmpConfigData = OSData::withBytes(_histogramSegmentsConfig,
297                                           (unsigned)_segmentCount *
298                                             (unsigned)sizeof(IOHistogramSegmentConfig));
299         if (!tmpConfigData) {
300             legendEntry->release();
301             goto finish;
302         }
303 
304         tmpDict = OSDynamicCast(OSDictionary, legendEntry->getObject(kIOReportLegendInfoKey));
305         if (!tmpDict) {
306             legendEntry->release();
307             goto finish;
308         }
309 
310         tmpDict->setObject(kIOReportLegendConfigKey, tmpConfigData);
311     }
312 
313 finish:
314     return legendEntry;
315 }
316 
317 int
318 IOHistogramReporter::tallyValue(int64_t value)
319 {
320     int result = -1;
321     int cnt = 0, element_index = 0;
322     IOHistogramReportValues hist_values;
323 
324     lockReporter();
325 
326     // Iterate over _bucketCount minus one to make last bucket of infinite width
327     for (cnt = 0; cnt < _bucketCount - 1; cnt++) {
328         if (value <= _bucketBounds[cnt]) break;
329     }
330 
331     element_index = cnt;
332 
333     if (copyElementValues(element_index, (IOReportElementValues *)&hist_values) != kIOReturnSuccess) {
334         goto finish;
335     }
336 
337     // init stats on first hit
338     if (hist_values.bucket_hits == 0) {
339         hist_values.bucket_min = hist_values.bucket_max = value;
340         hist_values.bucket_sum = 0;     // += is below
341     }
342 
343     // update all values
344     if (value < hist_values.bucket_min) {
345         hist_values.bucket_min = value;
346     } else if (value > hist_values.bucket_max) {
347         hist_values.bucket_max = value;
348     }
349     hist_values.bucket_sum += value;
350     hist_values.bucket_hits++;
351 
352     if (setElementValues(element_index, (IOReportElementValues *)&hist_values) == kIOReturnSuccess) {
353         goto finish;
354     }
355 
356     // success!
357     result = element_index;
358 
359 finish:
360     unlockReporter();
361     return result;
362 }
363