]> git.saurik.com Git - apple/xnu.git/blob - iokit/Kernel/IOHistogramReporter.cpp
xnu-7195.101.1.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 IOKIT_ENABLE_SHARED_PTR
30
31 #define __STDC_LIMIT_MACROS // what are the C++ equivalents?
32 #include <stdint.h>
33
34 #include <IOKit/IOKernelReportStructs.h>
35 #include <IOKit/IOKernelReporters.h>
36 #include "IOReporterDefs.h"
37
38
39 #define super IOReporter
40 OSDefineMetaClassAndStructors(IOHistogramReporter, IOReporter);
41
42 /* static */
43 OSSharedPtr<IOHistogramReporter>
44 IOHistogramReporter::with(IOService *reportingService,
45 IOReportCategories categories,
46 uint64_t channelID,
47 const char *channelName,
48 IOReportUnit unit,
49 int nSegments,
50 IOHistogramSegmentConfig *config)
51 {
52 OSSharedPtr<IOHistogramReporter> reporter = OSMakeShared<IOHistogramReporter>();
53 OSSharedPtr<const OSSymbol> tmpChannelName;
54
55 if (reporter) {
56 if (channelName) {
57 tmpChannelName = OSSymbol::withCString(channelName);
58 }
59
60 if (reporter->initWith(reportingService, categories,
61 channelID, tmpChannelName.get(),
62 unit, nSegments, config)) {
63 return reporter;
64 }
65 }
66
67 return nullptr;
68 }
69
70
71 bool
72 IOHistogramReporter::initWith(IOService *reportingService,
73 IOReportCategories categories,
74 uint64_t channelID,
75 const OSSymbol *channelName,
76 IOReportUnit unit,
77 int nSegments,
78 IOHistogramSegmentConfig *config)
79 {
80 bool result = false;
81 IOReturn res; // for PREFL_MEMOP
82 size_t configSize, elementsSize, eCountsSize, boundsSize;
83 int cnt, cnt2, cnt3 = 0;
84 int64_t bucketBound = 0, previousBucketBound = 0;
85
86 // analyzer appeasement
87 configSize = elementsSize = eCountsSize = boundsSize = 0;
88
89 IORLOG("IOHistogramReporter::initWith");
90
91 // For now, this reporter is currently limited to a single channel
92 _nChannels = 1;
93
94 IOReportChannelType channelType = {
95 .categories = categories,
96 .report_format = kIOReportFormatHistogram,
97 .nelements = 0, // Initialized when Config is unpacked
98 .element_idx = 0
99 };
100
101 if (super::init(reportingService, channelType, unit) != true) {
102 IORLOG("%s - ERROR: super::init failed", __func__);
103 result = false;
104 goto finish;
105 }
106
107 // Make sure to call this after the commit init phase
108 if (channelName) {
109 _channelNames->setObject(channelName);
110 }
111
112 _segmentCount = nSegments;
113 if (_segmentCount == 0) {
114 IORLOG("IOReportHistogram init ERROR. No configuration provided!");
115 result = false;
116 goto finish;
117 }
118
119 IORLOG("%s - %u segment(s)", __func__, _segmentCount);
120
121 PREFL_MEMOP_FAIL(_segmentCount, IOHistogramSegmentConfig);
122 configSize = (size_t)_segmentCount * sizeof(IOHistogramSegmentConfig);
123 _histogramSegmentsConfig = (IOHistogramSegmentConfig*)IOMalloc(configSize);
124 if (!_histogramSegmentsConfig) {
125 goto finish;
126 }
127 memcpy(_histogramSegmentsConfig, config, configSize);
128
129 // Find out how many elements are need to store the histogram
130 for (cnt = 0; cnt < _segmentCount; cnt++) {
131 _nElements += _histogramSegmentsConfig[cnt].segment_bucket_count;
132 _channelDimension += _histogramSegmentsConfig[cnt].segment_bucket_count;
133
134 IORLOG("\t\t bucket_base_width: %u | log_scale: %u | buckets: %u",
135 _histogramSegmentsConfig[cnt].base_bucket_width,
136 _histogramSegmentsConfig[cnt].scale_flag,
137 _histogramSegmentsConfig[cnt].segment_bucket_count);
138
139 if (_histogramSegmentsConfig[cnt].scale_flag > 1
140 || _histogramSegmentsConfig[cnt].base_bucket_width == 0) {
141 result = false;
142 goto finish;
143 }
144 }
145
146 // Update the channel type with discovered dimension
147 _channelType.nelements = _channelDimension;
148
149 IORLOG("%s - %u channel(s) of dimension %u",
150 __func__, _nChannels, _channelDimension);
151
152 IORLOG("%s %d segments for a total dimension of %d elements",
153 __func__, _nChannels, _nElements);
154
155 // Allocate memory for the array of report elements
156 PREFL_MEMOP_FAIL(_nElements, IOReportElement);
157 elementsSize = (size_t)_nElements * sizeof(IOReportElement);
158 _elements = (IOReportElement *)IOMalloc(elementsSize);
159 if (!_elements) {
160 goto finish;
161 }
162 memset(_elements, 0, elementsSize);
163
164 // Allocate memory for the array of element watch count
165 PREFL_MEMOP_FAIL(_nElements, int);
166 eCountsSize = (size_t)_nChannels * sizeof(int);
167 _enableCounts = (int *)IOMalloc(eCountsSize);
168 if (!_enableCounts) {
169 goto finish;
170 }
171 memset(_enableCounts, 0, eCountsSize);
172
173 lockReporter();
174 for (cnt2 = 0; cnt2 < _channelDimension; cnt2++) {
175 IOHistogramReportValues hist_values;
176 if (copyElementValues(cnt2, (IOReportElementValues*)&hist_values)) {
177 goto finish;
178 }
179 hist_values.bucket_min = kIOReportInvalidIntValue;
180 hist_values.bucket_max = kIOReportInvalidIntValue;
181 hist_values.bucket_sum = kIOReportInvalidIntValue;
182 if (setElementValues(cnt2, (IOReportElementValues*)&hist_values)) {
183 goto finish;
184 }
185
186 // Setup IOReporter's channel IDs
187 _elements[cnt2].channel_id = channelID;
188
189 // Setup IOReporter's reporting provider service
190 _elements[cnt2].provider_id = _driver_id;
191
192 // Setup IOReporter's channel type
193 _elements[cnt2].channel_type = _channelType;
194 _elements[cnt2].channel_type.element_idx = ((int16_t) cnt2);
195
196 //IOREPORTER_DEBUG_ELEMENT(cnt2);
197 }
198 unlockReporter();
199
200 // Allocate memory for the bucket upper bounds
201 PREFL_MEMOP_FAIL(_nElements, uint64_t);
202 boundsSize = (size_t)_nElements * sizeof(uint64_t);
203 _bucketBounds = (int64_t*)IOMalloc(boundsSize);
204 if (!_bucketBounds) {
205 goto finish;
206 }
207 memset(_bucketBounds, 0, boundsSize);
208 _bucketCount = _nElements;
209
210 for (cnt = 0; cnt < _segmentCount; cnt++) {
211 if (_histogramSegmentsConfig[cnt].segment_bucket_count > INT_MAX
212 || _histogramSegmentsConfig[cnt].base_bucket_width > INT_MAX) {
213 goto finish;
214 }
215 for (cnt2 = 0; cnt2 < (int)_histogramSegmentsConfig[cnt].segment_bucket_count; cnt2++) {
216 if (cnt3 >= _nElements) {
217 IORLOG("ERROR: _bucketBounds init");
218 result = false;
219 goto finish;
220 }
221
222 if (_histogramSegmentsConfig[cnt].scale_flag) {
223 // FIXME: Could use pow() but not sure how to include math.h
224 int64_t power = 1;
225 int exponent = cnt2 + 1;
226 while (exponent) {
227 power *= _histogramSegmentsConfig[cnt].base_bucket_width;
228 exponent--;
229 }
230 bucketBound = power;
231 } else {
232 bucketBound = _histogramSegmentsConfig[cnt].base_bucket_width *
233 ((unsigned)cnt2 + 1);
234 }
235
236 if (previousBucketBound >= bucketBound) {
237 IORLOG("Histogram ERROR: bucket bound does not increase linearly (segment %u / bucket # %u)",
238 cnt, cnt2);
239 result = false;
240 goto finish;
241 }
242
243 _bucketBounds[cnt3] = bucketBound;
244 // IORLOG("_bucketBounds[%u] = %llu", cnt3, bucketBound);
245 previousBucketBound = _bucketBounds[cnt3];
246 cnt3++;
247 }
248 }
249
250 // success
251 result = true;
252
253 finish:
254 return result;
255 }
256
257
258 void
259 IOHistogramReporter::free(void)
260 {
261 if (_bucketBounds) {
262 PREFL_MEMOP_PANIC(_nElements, int64_t);
263 IOFree(_bucketBounds, (size_t)_nElements * sizeof(int64_t));
264 }
265 if (_histogramSegmentsConfig) {
266 PREFL_MEMOP_PANIC(_segmentCount, IOHistogramSegmentConfig);
267 IOFree(_histogramSegmentsConfig,
268 (size_t)_segmentCount * sizeof(IOHistogramSegmentConfig));
269 }
270
271 super::free();
272 }
273
274
275 OSSharedPtr<IOReportLegendEntry>
276 IOHistogramReporter::handleCreateLegend(void)
277 {
278 OSSharedPtr<IOReportLegendEntry> legendEntry;
279 OSSharedPtr<OSData> tmpConfigData;
280 OSDictionary *tmpDict; // no refcount
281
282 legendEntry = super::handleCreateLegend();
283 if (!legendEntry) {
284 return nullptr;
285 }
286
287 PREFL_MEMOP_PANIC(_segmentCount, IOHistogramSegmentConfig);
288 tmpConfigData = OSData::withBytes(_histogramSegmentsConfig,
289 (unsigned)_segmentCount *
290 sizeof(IOHistogramSegmentConfig));
291 if (!tmpConfigData) {
292 return nullptr;
293 }
294
295 tmpDict = OSDynamicCast(OSDictionary,
296 legendEntry->getObject(kIOReportLegendInfoKey));
297 if (!tmpDict) {
298 return nullptr;
299 }
300
301 tmpDict->setObject(kIOReportLegendConfigKey, tmpConfigData.get());
302
303 return legendEntry;
304 }
305
306 IOReturn
307 IOHistogramReporter::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);
328 finish:
329 unlockReporter();
330 return result;
331 }
332
333 int
334 IOHistogramReporter::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]) {
345 break;
346 }
347 }
348
349 element_index = cnt;
350
351 if (copyElementValues(element_index, (IOReportElementValues *)&hist_values) != kIOReturnSuccess) {
352 goto finish;
353 }
354
355 // init stats on first hit
356 if (hist_values.bucket_hits == 0) {
357 hist_values.bucket_min = hist_values.bucket_max = value;
358 hist_values.bucket_sum = 0; // += is below
359 }
360
361 // update all values
362 if (value < hist_values.bucket_min) {
363 hist_values.bucket_min = value;
364 } else if (value > hist_values.bucket_max) {
365 hist_values.bucket_max = value;
366 }
367 hist_values.bucket_sum += value;
368 hist_values.bucket_hits++;
369
370 if (setElementValues(element_index, (IOReportElementValues *)&hist_values)
371 != kIOReturnSuccess) {
372 goto finish;
373 }
374
375 // success!
376 result = element_index;
377
378 finish:
379 unlockReporter();
380 return result;
381 }