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 | 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 | |
72 | bool |
73 | IOHistogramReporter::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 | |
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 | 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 | |
297 | finish: |
298 | if (tmpConfigData) tmpConfigData->release(); |
299 | if (!rval && legendEntry) { |
300 | legendEntry->release(); |
301 | } |
302 | |
303 | return rval; |
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]) 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 | |
376 | finish: |
377 | unlockReporter(); |
378 | return result; |
379 | } |
380 | |