]> git.saurik.com Git - apple/xnu.git/blob - iokit/Kernel/IOHistogramReporter.cpp
xnu-3789.60.24.tar.gz
[apple/xnu.git] / iokit / Kernel / IOHistogramReporter.cpp
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 }