]> git.saurik.com Git - apple/xnu.git/blame - iokit/Kernel/IOHistogramReporter.cpp
xnu-6153.41.3.tar.gz
[apple/xnu.git] / iokit / Kernel / IOHistogramReporter.cpp
CommitLineData
fe8ab488
A
1/*
2 * Copyright (c) 2012-2013 Apple Computer, Inc. All Rights Reserved.
0a7de745 3 *
fe8ab488 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
0a7de745 5 *
fe8ab488
A
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.
0a7de745 14 *
fe8ab488
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
0a7de745 17 *
fe8ab488
A
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.
0a7de745 25 *
fe8ab488
A
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,
0a7de745
A
43 IOReportCategories categories,
44 uint64_t channelID,
45 const char *channelName,
46 IOReportUnit unit,
47 int nSegments,
48 IOHistogramSegmentConfig *config)
fe8ab488 49{
0a7de745
A
50 IOHistogramReporter *reporter = new IOHistogramReporter;
51
52 const OSSymbol *tmpChannelName = NULL;
53
54 if (reporter) {
55 if (channelName) {
56 tmpChannelName = OSSymbol::withCString(channelName);
57 }
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
cb323159 68 return NULL;
fe8ab488
A
69}
70
71
72bool
73IOHistogramReporter::initWith(IOService *reportingService,
0a7de745
A
74 IOReportCategories categories,
75 uint64_t channelID,
76 const OSSymbol *channelName,
77 IOReportUnit unit,
78 int nSegments,
79 IOHistogramSegmentConfig *config)
fe8ab488 80{
0a7de745
A
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) {
110 _channelNames->setObject(channelName);
111 }
112
113 _segmentCount = nSegments;
114 if (_segmentCount == 0) {
115 IORLOG("IOReportHistogram init ERROR. No configuration provided!");
116 result = false;
117 goto finish;
118 }
119
120 IORLOG("%s - %u segment(s)", __func__, _segmentCount);
121
122 PREFL_MEMOP_FAIL(_segmentCount, IOHistogramSegmentConfig);
123 configSize = (size_t)_segmentCount * sizeof(IOHistogramSegmentConfig);
124 _histogramSegmentsConfig = (IOHistogramSegmentConfig*)IOMalloc(configSize);
125 if (!_histogramSegmentsConfig) {
126 goto finish;
127 }
128 memcpy(_histogramSegmentsConfig, config, configSize);
129
130 // Find out how many elements are need to store the histogram
131 for (cnt = 0; cnt < _segmentCount; cnt++) {
132 _nElements += _histogramSegmentsConfig[cnt].segment_bucket_count;
133 _channelDimension += _histogramSegmentsConfig[cnt].segment_bucket_count;
134
135 IORLOG("\t\t bucket_base_width: %u | log_scale: %u | buckets: %u",
136 _histogramSegmentsConfig[cnt].base_bucket_width,
137 _histogramSegmentsConfig[cnt].scale_flag,
138 _histogramSegmentsConfig[cnt].segment_bucket_count);
139
140 if (_histogramSegmentsConfig[cnt].scale_flag > 1
141 || _histogramSegmentsConfig[cnt].base_bucket_width == 0) {
142 result = false;
143 goto finish;
144 }
145 }
146
147 // Update the channel type with discovered dimension
148 _channelType.nelements = _channelDimension;
149
150 IORLOG("%s - %u channel(s) of dimension %u",
151 __func__, _nChannels, _channelDimension);
152
153 IORLOG("%s %d segments for a total dimension of %d elements",
154 __func__, _nChannels, _nElements);
155
156 // Allocate memory for the array of report elements
157 PREFL_MEMOP_FAIL(_nElements, IOReportElement);
158 elementsSize = (size_t)_nElements * sizeof(IOReportElement);
159 _elements = (IOReportElement *)IOMalloc(elementsSize);
160 if (!_elements) {
161 goto finish;
162 }
163 memset(_elements, 0, elementsSize);
164
165 // Allocate memory for the array of element watch count
166 PREFL_MEMOP_FAIL(_nElements, int);
167 eCountsSize = (size_t)_nChannels * sizeof(int);
168 _enableCounts = (int *)IOMalloc(eCountsSize);
169 if (!_enableCounts) {
170 goto finish;
171 }
172 memset(_enableCounts, 0, eCountsSize);
173
174 lockReporter();
175 for (cnt2 = 0; cnt2 < _channelDimension; cnt2++) {
176 IOHistogramReportValues hist_values;
177 if (copyElementValues(cnt2, (IOReportElementValues*)&hist_values)) {
178 goto finish;
179 }
180 hist_values.bucket_min = kIOReportInvalidIntValue;
181 hist_values.bucket_max = kIOReportInvalidIntValue;
182 hist_values.bucket_sum = kIOReportInvalidIntValue;
183 if (setElementValues(cnt2, (IOReportElementValues*)&hist_values)) {
184 goto finish;
185 }
186
187 // Setup IOReporter's channel IDs
188 _elements[cnt2].channel_id = channelID;
189
190 // Setup IOReporter's reporting provider service
191 _elements[cnt2].provider_id = _driver_id;
192
193 // Setup IOReporter's channel type
194 _elements[cnt2].channel_type = _channelType;
195 _elements[cnt2].channel_type.element_idx = cnt2;
196
197 //IOREPORTER_DEBUG_ELEMENT(cnt2);
198 }
199 unlockReporter();
200
201 // Allocate memory for the bucket upper bounds
202 PREFL_MEMOP_FAIL(_nElements, uint64_t);
203 boundsSize = (size_t)_nElements * sizeof(uint64_t);
204 _bucketBounds = (int64_t*)IOMalloc(boundsSize);
205 if (!_bucketBounds) {
206 goto finish;
207 }
208 memset(_bucketBounds, 0, boundsSize);
209 _bucketCount = _nElements;
210
211 for (cnt = 0; cnt < _segmentCount; cnt++) {
212 if (_histogramSegmentsConfig[cnt].segment_bucket_count > INT_MAX
213 || _histogramSegmentsConfig[cnt].base_bucket_width > INT_MAX) {
214 goto finish;
215 }
216 for (cnt2 = 0; cnt2 < (int)_histogramSegmentsConfig[cnt].segment_bucket_count; cnt2++) {
217 if (cnt3 >= _nElements) {
218 IORLOG("ERROR: _bucketBounds init");
219 result = false;
220 goto finish;
221 }
222
223 if (_histogramSegmentsConfig[cnt].scale_flag) {
224 // FIXME: Could use pow() but not sure how to include math.h
225 int64_t power = 1;
226 int exponent = cnt2 + 1;
227 while (exponent) {
228 power *= _histogramSegmentsConfig[cnt].base_bucket_width;
229 exponent--;
230 }
231 bucketBound = power;
232 } else {
233 bucketBound = _histogramSegmentsConfig[cnt].base_bucket_width *
234 ((unsigned)cnt2 + 1);
235 }
236
237 if (previousBucketBound >= bucketBound) {
238 IORLOG("Histogram ERROR: bucket bound does not increase linearly (segment %u / bucket # %u)",
239 cnt, cnt2);
240 result = false;
241 goto finish;
242 }
243
244 _bucketBounds[cnt3] = bucketBound;
245 // IORLOG("_bucketBounds[%u] = %llu", cnt3, bucketBound);
246 previousBucketBound = _bucketBounds[cnt3];
247 cnt3++;
248 }
249 }
250
251 // success
252 result = true;
253
fe8ab488 254finish:
0a7de745 255 return result;
fe8ab488
A
256}
257
258
259void
260IOHistogramReporter::free(void)
261{
0a7de745
A
262 if (_bucketBounds) {
263 PREFL_MEMOP_PANIC(_nElements, int64_t);
264 IOFree(_bucketBounds, (size_t)_nElements * sizeof(int64_t));
265 }
266 if (_histogramSegmentsConfig) {
267 PREFL_MEMOP_PANIC(_segmentCount, IOHistogramSegmentConfig);
268 IOFree(_histogramSegmentsConfig,
269 (size_t)_segmentCount * sizeof(IOHistogramSegmentConfig));
270 }
271
272 super::free();
fe8ab488
A
273}
274
275
276IOReportLegendEntry*
277IOHistogramReporter::handleCreateLegend(void)
278{
0a7de745
A
279 IOReportLegendEntry *rval = NULL, *legendEntry = NULL;
280 OSData *tmpConfigData = NULL;
281 OSDictionary *tmpDict; // no refcount
282
283 legendEntry = super::handleCreateLegend();
284 if (!legendEntry) {
285 goto finish;
286 }
287
288 PREFL_MEMOP_PANIC(_segmentCount, IOHistogramSegmentConfig);
289 tmpConfigData = OSData::withBytes(_histogramSegmentsConfig,
290 (unsigned)_segmentCount *
291 sizeof(IOHistogramSegmentConfig));
292 if (!tmpConfigData) {
293 goto finish;
294 }
295
296 tmpDict = OSDynamicCast(OSDictionary,
297 legendEntry->getObject(kIOReportLegendInfoKey));
298 if (!tmpDict) {
299 goto finish;
300 }
301
302 tmpDict->setObject(kIOReportLegendConfigKey, tmpConfigData);
303
304 // success
305 rval = legendEntry;
5ba3f43e 306
fe8ab488 307finish:
0a7de745
A
308 if (tmpConfigData) {
309 tmpConfigData->release();
310 }
311 if (!rval && legendEntry) {
312 legendEntry->release();
313 }
5ba3f43e 314
0a7de745 315 return rval;
fe8ab488
A
316}
317
39037602 318IOReturn
0a7de745
A
319IOHistogramReporter::overrideBucketValues(unsigned int index,
320 uint64_t bucket_hits,
321 int64_t bucket_min,
322 int64_t bucket_max,
323 int64_t bucket_sum)
39037602 324{
0a7de745
A
325 IOReturn result;
326 IOHistogramReportValues bucket;
327 lockReporter();
39037602 328
0a7de745
A
329 if (index >= (unsigned int)_bucketCount) {
330 result = kIOReturnBadArgument;
331 goto finish;
332 }
39037602 333
0a7de745
A
334 bucket.bucket_hits = bucket_hits;
335 bucket.bucket_min = bucket_min;
336 bucket.bucket_max = bucket_max;
337 bucket.bucket_sum = bucket_sum;
39037602 338
0a7de745 339 result = setElementValues(index, (IOReportElementValues *)&bucket);
39037602 340finish:
0a7de745
A
341 unlockReporter();
342 return result;
39037602
A
343}
344
fe8ab488
A
345int
346IOHistogramReporter::tallyValue(int64_t value)
347{
0a7de745
A
348 int result = -1;
349 int cnt = 0, element_index = 0;
350 IOHistogramReportValues hist_values;
351
352 lockReporter();
353
354 // Iterate over _bucketCount minus one to make last bucket of infinite width
355 for (cnt = 0; cnt < _bucketCount - 1; cnt++) {
356 if (value <= _bucketBounds[cnt]) {
357 break;
358 }
359 }
360
361 element_index = cnt;
362
363 if (copyElementValues(element_index, (IOReportElementValues *)&hist_values) != kIOReturnSuccess) {
364 goto finish;
365 }
366
367 // init stats on first hit
368 if (hist_values.bucket_hits == 0) {
369 hist_values.bucket_min = hist_values.bucket_max = value;
370 hist_values.bucket_sum = 0; // += is below
371 }
372
373 // update all values
374 if (value < hist_values.bucket_min) {
375 hist_values.bucket_min = value;
376 } else if (value > hist_values.bucket_max) {
377 hist_values.bucket_max = value;
378 }
379 hist_values.bucket_sum += value;
380 hist_values.bucket_hits++;
381
382 if (setElementValues(element_index, (IOReportElementValues *)&hist_values)
383 != kIOReturnSuccess) {
384 goto finish;
385 }
386
387 // success!
388 result = element_index;
389
fe8ab488 390finish:
0a7de745
A
391 unlockReporter();
392 return result;
fe8ab488 393}