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 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
72bool
73IOHistogramReporter::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
250finish:
251 return result;
252}
253
254
255void
256IOHistogramReporter::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
272IOReportLegendEntry*
273IOHistogramReporter::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
297finish:
298 if (tmpConfigData) tmpConfigData->release();
299 if (!rval && legendEntry) {
300 legendEntry->release();
301 }
302
303 return rval;
304}
305
306IOReturn
307IOHistogramReporter::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);
328finish:
329 unlockReporter();
330 return result;
331}
332
333int
334IOHistogramReporter::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
376finish:
377 unlockReporter();
378 return result;
379}
380