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     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                               IOReportUnits 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     OSData                  *tmpConfigData;
276     OSDictionary            *tmpDict;
277     IOReportLegendEntry     *legendEntry = NULL;
278 
279     legendEntry = super::handleCreateLegend();
280 
281     if (legendEntry) {
282 
283         PREFL_MEMOP_PANIC(_segmentCount, IOHistogramSegmentConfig);
284         tmpConfigData = OSData::withBytes(_histogramSegmentsConfig,
285                                           (unsigned)_segmentCount *
286                                             (unsigned)sizeof(IOHistogramSegmentConfig));
287         if (!tmpConfigData) {
288             legendEntry->release();
289             goto finish;
290         }
291 
292         tmpDict = OSDynamicCast(OSDictionary, legendEntry->getObject(kIOReportLegendInfoKey));
293         if (!tmpDict) {
294             legendEntry->release();
295             goto finish;
296         }
297 
298         tmpDict->setObject(kIOReportLegendConfigKey, tmpConfigData);
299     }
300 
301 finish:
302     return legendEntry;
303 }
304 
305 IOReturn
306 IOHistogramReporter::overrideBucketValues(unsigned int index,
307                                           uint64_t bucket_hits,
308                                           int64_t bucket_min,
309                                           int64_t bucket_max,
310                                           int64_t bucket_sum)
311 {
312     IOReturn result;
313     IOHistogramReportValues bucket;
314     lockReporter();
315 
316     if (index >= (unsigned int)_bucketCount) {
317         result = kIOReturnBadArgument;
318         goto finish;
319     }
320 
321     bucket.bucket_hits = bucket_hits;
322     bucket.bucket_min = bucket_min;
323     bucket.bucket_max = bucket_max;
324     bucket.bucket_sum = bucket_sum;
325 
326     result = setElementValues(index, (IOReportElementValues *)&bucket);
327 finish:
328     unlockReporter();
329     return result;
330 }
331 
332 int
333 IOHistogramReporter::tallyValue(int64_t value)
334 {
335     int result = -1;
336     int cnt = 0, element_index = 0;
337     IOHistogramReportValues hist_values;
338 
339     lockReporter();
340 
341     // Iterate over _bucketCount minus one to make last bucket of infinite width
342     for (cnt = 0; cnt < _bucketCount - 1; cnt++) {
343         if (value <= _bucketBounds[cnt]) break;
344     }
345 
346     element_index = cnt;
347 
348     if (copyElementValues(element_index, (IOReportElementValues *)&hist_values) != kIOReturnSuccess) {
349         goto finish;
350     }
351 
352     // init stats on first hit
353     if (hist_values.bucket_hits == 0) {
354         hist_values.bucket_min = hist_values.bucket_max = value;
355         hist_values.bucket_sum = 0;     // += is below
356     }
357 
358     // update all values
359     if (value < hist_values.bucket_min) {
360         hist_values.bucket_min = value;
361     } else if (value > hist_values.bucket_max) {
362         hist_values.bucket_max = value;
363     }
364     hist_values.bucket_sum += value;
365     hist_values.bucket_hits++;
366 
367     if (setElementValues(element_index, (IOReportElementValues *)&hist_values)
368                 != kIOReturnSuccess) {
369         goto finish;
370     }
371 
372     // success!
373     result = element_index;
374 
375 finish:
376     unlockReporter();
377     return result;
378 }
379