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
38OSDefineMetaClassAndStructors(IOHistogramReporter, IOReporter);
39
40/* static */
41IOHistogramReporter*
42IOHistogramReporter::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
70bool
71IOHistogramReporter::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
247finish:
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
267void
268IOHistogramReporter::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
284IOReportLegendEntry*
285IOHistogramReporter::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
313finish:
314    return legendEntry;
315}
316
317int
318IOHistogramReporter::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
359finish:
360    unlockReporter();
361    return result;
362}
363