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                           IOReportUnit 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     OSSafeReleaseNULL(reporter);
66     OSSafeReleaseNULL(tmpChannelName);
67 
68     return 0;
69 }
70 
71 
72 bool
73 IOHistogramReporter::initWith(IOService *reportingService,
74                               IOReportCategories categories,
75                               uint64_t channelID,
76                               const OSSymbol *channelName,
77                               IOReportUnit unit,
78                               int nSegments,
79                               IOHistogramSegmentConfig *config)
80 {
81     bool            result = false;
82     IOReturn        res;        // for PREFL_MEMOP
83     size_t          configSize, elementsSize, eCountsSize, boundsSize;
84     int             cnt, cnt2, cnt3 = 0;
85     int64_t        bucketBound = 0, previousBucketBound = 0;
86 
87     // analyzer appeasement
88     configSize = elementsSize = eCountsSize = boundsSize = 0;
89 
90     IORLOG("IOHistogramReporter::initWith");
91 
92     // For now, this reporter is currently limited to a single channel
93     _nChannels = 1;
94 
95     IOReportChannelType channelType = {
96         .categories = categories,
97         .report_format = kIOReportFormatHistogram,
98         .nelements = 0,  // Initialized when Config is unpacked
99         .element_idx = 0
100     };
101 
102     if (super::init(reportingService, channelType, unit) != true) {
103         IORLOG("%s - ERROR: super::init failed", __func__);
104         result = false;
105         goto finish;
106     }
107 
108     // Make sure to call this after the commit init phase
109     if (channelName) _channelNames->setObject(channelName);
110 
111     _segmentCount = nSegments;
112     if (_segmentCount == 0) {
113         IORLOG("IOReportHistogram init ERROR. No configuration provided!");
114         result = false;
115         goto finish;
116     }
117 
118     IORLOG("%s - %u segment(s)", __func__, _segmentCount);
119 
120     PREFL_MEMOP_FAIL(_segmentCount, IOHistogramSegmentConfig);
121     configSize = (size_t)_segmentCount * sizeof(IOHistogramSegmentConfig);
122     _histogramSegmentsConfig = (IOHistogramSegmentConfig*)IOMalloc(configSize);
123     if (!_histogramSegmentsConfig)      goto finish;
124     memcpy(_histogramSegmentsConfig, config, configSize);
125 
126     // Find out how many elements are need to store the histogram
127     for (cnt = 0; cnt < _segmentCount; cnt++) {
128 
129         _nElements += _histogramSegmentsConfig[cnt].segment_bucket_count;
130         _channelDimension += _histogramSegmentsConfig[cnt].segment_bucket_count;
131 
132         IORLOG("\t\t bucket_base_width: %u | log_scale: %u | buckets: %u",
133                 _histogramSegmentsConfig[cnt].base_bucket_width,
134                 _histogramSegmentsConfig[cnt].scale_flag,
135                 _histogramSegmentsConfig[cnt].segment_bucket_count);
136 
137         if (_histogramSegmentsConfig[cnt].scale_flag > 1
138             || _histogramSegmentsConfig[cnt].base_bucket_width == 0) {
139             result = false;
140             goto finish;
141         }
142 
143     }
144 
145     // Update the channel type with discovered dimension
146     _channelType.nelements = _channelDimension;
147 
148     IORLOG("%s - %u channel(s) of dimension %u",
149            __func__, _nChannels, _channelDimension);
150 
151     IORLOG("%s %d segments for a total dimension of %d elements",
152            __func__, _nChannels, _nElements);
153 
154     // Allocate memory for the array of report elements
155     PREFL_MEMOP_FAIL(_nElements, IOReportElement);
156     elementsSize = (size_t)_nElements * sizeof(IOReportElement);
157     _elements = (IOReportElement *)IOMalloc(elementsSize);
158     if (!_elements)             goto finish;
159     memset(_elements, 0, elementsSize);
160 
161     // Allocate memory for the array of element watch count
162     PREFL_MEMOP_FAIL(_nElements, int);
163     eCountsSize = (size_t)_nChannels * sizeof(int);
164     _enableCounts = (int *)IOMalloc(eCountsSize);
165     if (!_enableCounts)         goto finish;
166     memset(_enableCounts, 0, eCountsSize);
167 
168     lockReporter();
169     for (cnt2 = 0; cnt2 < _channelDimension; cnt2++) {
170         IOHistogramReportValues hist_values;
171         if (copyElementValues(cnt2, (IOReportElementValues*)&hist_values)){
172             goto finish;
173         }
174         hist_values.bucket_min = kIOReportInvalidIntValue;
175         hist_values.bucket_max = kIOReportInvalidIntValue;
176         hist_values.bucket_sum = kIOReportInvalidIntValue;
177         if (setElementValues(cnt2, (IOReportElementValues*)&hist_values)){
178             goto finish;
179         }
180 
181         // Setup IOReporter's channel IDs
182         _elements[cnt2].channel_id = channelID;
183 
184         // Setup IOReporter's reporting provider service
185         _elements[cnt2].provider_id = _driver_id;
186 
187         // Setup IOReporter's channel type
188         _elements[cnt2].channel_type = _channelType;
189         _elements[cnt2].channel_type.element_idx = cnt2;
190 
191         //IOREPORTER_DEBUG_ELEMENT(cnt2);
192     }
193     unlockReporter();
194 
195     // Allocate memory for the bucket upper bounds
196     PREFL_MEMOP_FAIL(_nElements, uint64_t);
197     boundsSize = (size_t)_nElements * sizeof(uint64_t);
198     _bucketBounds = (int64_t*)IOMalloc(boundsSize);
199     if (!_bucketBounds)         goto finish;
200     memset(_bucketBounds, 0, boundsSize);
201     _bucketCount = _nElements;
202 
203     for (cnt = 0; cnt < _segmentCount; cnt++) {
204 
205         if (_histogramSegmentsConfig[cnt].segment_bucket_count > INT_MAX
206             || _histogramSegmentsConfig[cnt].base_bucket_width > INT_MAX) {
207             goto finish;
208         }
209         for (cnt2 = 0; cnt2 < (int)_histogramSegmentsConfig[cnt].segment_bucket_count; cnt2++) {
210 
211             if (cnt3 >= _nElements) {
212                 IORLOG("ERROR: _bucketBounds init");
213                 result = false;
214                 goto finish;
215             }
216 
217             if (_histogramSegmentsConfig[cnt].scale_flag) {
218                 // FIXME: Could use pow() but not sure how to include math.h
219                 int64_t power = 1;
220                 int exponent = cnt2 + 1;
221                 while (exponent) {
222                     power *= _histogramSegmentsConfig[cnt].base_bucket_width;
223                     exponent--;
224                 }
225                 bucketBound = power;
226             }
227 
228             else {
229                 bucketBound = _histogramSegmentsConfig[cnt].base_bucket_width *
230                                                     ((unsigned)cnt2 + 1);
231             }
232 
233             if (previousBucketBound >= bucketBound) {
234                 IORLOG("Histogram ERROR: bucket bound does not increase linearly (segment %u / bucket # %u)",
235                        cnt, cnt2);
236                 result = false;
237                 goto finish;
238             }
239 
240             _bucketBounds[cnt3] = bucketBound;
241             // IORLOG("_bucketBounds[%u] = %llu", cnt3, bucketBound);
242             previousBucketBound = _bucketBounds[cnt3];
243             cnt3++;
244         }
245     }
246 
247     // success
248     result = true;
249 
250 finish:
251     return result;
252 }
253 
254 
255 void
256 IOHistogramReporter::free(void)
257 {
258     if (_bucketBounds) {
259         PREFL_MEMOP_PANIC(_nElements, int64_t);
260         IOFree(_bucketBounds, (size_t)_nElements * sizeof(int64_t));
261     }
262     if (_histogramSegmentsConfig) {
263         PREFL_MEMOP_PANIC(_segmentCount, IOHistogramSegmentConfig);
264         IOFree(_histogramSegmentsConfig,
265                (size_t)_segmentCount * sizeof(IOHistogramSegmentConfig));
266     }
267 
268     super::free();
269 }
270 
271 
272 IOReportLegendEntry*
273 IOHistogramReporter::handleCreateLegend(void)
274 {
275     IOReportLegendEntry     *rval = NULL, *legendEntry = NULL;
276     OSData                  *tmpConfigData = NULL;
277     OSDictionary            *tmpDict;       // no refcount
278 
279     legendEntry = super::handleCreateLegend();
280     if (!legendEntry)       goto finish;
281 
282     PREFL_MEMOP_PANIC(_segmentCount, IOHistogramSegmentConfig);
283     tmpConfigData = OSData::withBytes(_histogramSegmentsConfig,
284                          (unsigned)_segmentCount *
285                              sizeof(IOHistogramSegmentConfig));
286     if (!tmpConfigData)         goto finish;
287 
288     tmpDict = OSDynamicCast(OSDictionary,
289                     legendEntry->getObject(kIOReportLegendInfoKey));
290     if (!tmpDict)               goto finish;
291 
292     tmpDict->setObject(kIOReportLegendConfigKey, tmpConfigData);
293 
294     // success
295     rval = legendEntry;
296 
297 finish:
298     if (tmpConfigData)  tmpConfigData->release();
299     if (!rval && legendEntry) {
300         legendEntry->release();
301     }
302 
303     return rval;
304 }
305 
306 IOReturn
307 IOHistogramReporter::overrideBucketValues(unsigned int index,
308                                           uint64_t bucket_hits,
309                                           int64_t bucket_min,
310                                           int64_t bucket_max,
311                                           int64_t bucket_sum)
312 {
313     IOReturn result;
314     IOHistogramReportValues bucket;
315     lockReporter();
316 
317     if (index >= (unsigned int)_bucketCount) {
318         result = kIOReturnBadArgument;
319         goto finish;
320     }
321 
322     bucket.bucket_hits = bucket_hits;
323     bucket.bucket_min = bucket_min;
324     bucket.bucket_max = bucket_max;
325     bucket.bucket_sum = bucket_sum;
326 
327     result = setElementValues(index, (IOReportElementValues *)&bucket);
328 finish:
329     unlockReporter();
330     return result;
331 }
332 
333 int
334 IOHistogramReporter::tallyValue(int64_t value)
335 {
336     int result = -1;
337     int cnt = 0, element_index = 0;
338     IOHistogramReportValues hist_values;
339 
340     lockReporter();
341 
342     // Iterate over _bucketCount minus one to make last bucket of infinite width
343     for (cnt = 0; cnt < _bucketCount - 1; cnt++) {
344         if (value <= _bucketBounds[cnt]) break;
345     }
346 
347     element_index = cnt;
348 
349     if (copyElementValues(element_index, (IOReportElementValues *)&hist_values) != kIOReturnSuccess) {
350         goto finish;
351     }
352 
353     // init stats on first hit
354     if (hist_values.bucket_hits == 0) {
355         hist_values.bucket_min = hist_values.bucket_max = value;
356         hist_values.bucket_sum = 0;     // += is below
357     }
358 
359     // update all values
360     if (value < hist_values.bucket_min) {
361         hist_values.bucket_min = value;
362     } else if (value > hist_values.bucket_max) {
363         hist_values.bucket_max = value;
364     }
365     hist_values.bucket_sum += value;
366     hist_values.bucket_hits++;
367 
368     if (setElementValues(element_index, (IOReportElementValues *)&hist_values)
369                 != kIOReturnSuccess) {
370         goto finish;
371     }
372 
373     // success!
374     result = element_index;
375 
376 finish:
377     unlockReporter();
378     return result;
379 }
380