]> git.saurik.com Git - apple/xnu.git/blame - iokit/Kernel/IOHistogramReporter.cpp
xnu-3248.60.10.tar.gz
[apple/xnu.git] / iokit / Kernel / IOHistogramReporter.cpp
CommitLineData
fe8ab488
A
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
3e170ce0
A
352 if (setElementValues(element_index, (IOReportElementValues *)&hist_values)
353 != kIOReturnSuccess) {
fe8ab488
A
354 goto finish;
355 }
356
357 // success!
358 result = element_index;
359
360finish:
361 unlockReporter();
362 return result;
363}