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 <os/overflow.h>
37 #include "IOReporterDefs.h"
38
39
40 #define super IOReporter
41 OSDefineMetaClassAndStructors(IOHistogramReporter, IOReporter);
42
43 /* static */
44 OSSharedPtr<IOHistogramReporter>
with(IOService * reportingService,IOReportCategories categories,uint64_t channelID,const char * channelName,IOReportUnit unit,int nSegments,IOHistogramSegmentConfig * config)45 IOHistogramReporter::with(IOService *reportingService,
46 IOReportCategories categories,
47 uint64_t channelID,
48 const char *channelName,
49 IOReportUnit unit,
50 int nSegments,
51 IOHistogramSegmentConfig *config)
52 {
53 OSSharedPtr<IOHistogramReporter> reporter = OSMakeShared<IOHistogramReporter>();
54 OSSharedPtr<const OSSymbol> tmpChannelName;
55
56 if (reporter) {
57 if (channelName) {
58 tmpChannelName = OSSymbol::withCString(channelName);
59 }
60
61 if (reporter->initWith(reportingService, categories,
62 channelID, tmpChannelName.get(),
63 unit, nSegments, config)) {
64 return reporter;
65 }
66 }
67
68 return nullptr;
69 }
70
71
72 bool
initWith(IOService * reportingService,IOReportCategories categories,uint64_t channelID,const OSSymbol * channelName,IOReportUnit unit,int nSegments,IOHistogramSegmentConfig * config)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) {
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*)IOMallocData(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 *)IOMallocZeroData(elementsSize);
160 if (!_elements) {
161 goto finish;
162 }
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 *)IOMallocZeroData(eCountsSize);
168 if (!_enableCounts) {
169 goto finish;
170 }
171
172 lockReporter();
173 for (cnt2 = 0; cnt2 < _channelDimension; cnt2++) {
174 IOHistogramReportValues hist_values;
175 if (copyElementValues(cnt2, (IOReportElementValues*)&hist_values)) {
176 goto finish;
177 }
178 hist_values.bucket_min = kIOReportInvalidIntValue;
179 hist_values.bucket_max = kIOReportInvalidIntValue;
180 hist_values.bucket_sum = kIOReportInvalidIntValue;
181 if (setElementValues(cnt2, (IOReportElementValues*)&hist_values)) {
182 goto finish;
183 }
184
185 // Setup IOReporter's channel IDs
186 _elements[cnt2].channel_id = channelID;
187
188 // Setup IOReporter's reporting provider service
189 _elements[cnt2].provider_id = _driver_id;
190
191 // Setup IOReporter's channel type
192 _elements[cnt2].channel_type = _channelType;
193 _elements[cnt2].channel_type.element_idx = ((int16_t) cnt2);
194
195 //IOREPORTER_DEBUG_ELEMENT(cnt2);
196 }
197 unlockReporter();
198
199 // Allocate memory for the bucket upper bounds
200 PREFL_MEMOP_FAIL(_nElements, uint64_t);
201 boundsSize = (size_t)_nElements * sizeof(uint64_t);
202 _bucketBounds = (int64_t*)IOMallocZeroData(boundsSize);
203 if (!_bucketBounds) {
204 goto finish;
205 }
206 _bucketCount = _nElements;
207
208 for (cnt = 0; cnt < _segmentCount; cnt++) {
209 if (_histogramSegmentsConfig[cnt].segment_bucket_count > INT_MAX
210 || _histogramSegmentsConfig[cnt].base_bucket_width > INT_MAX) {
211 goto finish;
212 }
213 for (cnt2 = 0; cnt2 < (int)_histogramSegmentsConfig[cnt].segment_bucket_count; cnt2++) {
214 if (cnt3 >= _nElements) {
215 IORLOG("ERROR: _bucketBounds init");
216 result = false;
217 goto finish;
218 }
219
220 if (_histogramSegmentsConfig[cnt].scale_flag) {
221 // FIXME: Could use pow() but not sure how to include math.h
222 int64_t power = 1;
223 int exponent = cnt2 + 1;
224 while (exponent) {
225 power *= _histogramSegmentsConfig[cnt].base_bucket_width;
226 exponent--;
227 }
228 bucketBound = power;
229 } else {
230 bucketBound = _histogramSegmentsConfig[cnt].base_bucket_width *
231 ((unsigned)cnt2 + 1);
232 }
233
234 if (previousBucketBound >= bucketBound) {
235 IORLOG("Histogram ERROR: bucket bound does not increase linearly (segment %u / bucket # %u)",
236 cnt, cnt2);
237 result = false;
238 goto finish;
239 }
240
241 _bucketBounds[cnt3] = bucketBound;
242 // IORLOG("_bucketBounds[%u] = %llu", cnt3, bucketBound);
243 previousBucketBound = _bucketBounds[cnt3];
244 cnt3++;
245 }
246 }
247
248 // success
249 result = true;
250
251 finish:
252 return result;
253 }
254
255
256 void
free(void)257 IOHistogramReporter::free(void)
258 {
259 if (_bucketBounds) {
260 PREFL_MEMOP_PANIC(_nElements, int64_t);
261 IOFreeData(_bucketBounds, (size_t)_nElements * sizeof(int64_t));
262 }
263 if (_histogramSegmentsConfig) {
264 PREFL_MEMOP_PANIC(_segmentCount, IOHistogramSegmentConfig);
265 IOFreeData(_histogramSegmentsConfig,
266 (size_t)_segmentCount * sizeof(IOHistogramSegmentConfig));
267 }
268
269 super::free();
270 }
271
272
273 OSSharedPtr<IOReportLegendEntry>
handleCreateLegend(void)274 IOHistogramReporter::handleCreateLegend(void)
275 {
276 OSSharedPtr<IOReportLegendEntry> legendEntry;
277 OSSharedPtr<OSData> tmpConfigData;
278 OSDictionary *tmpDict; // no refcount
279
280 legendEntry = super::handleCreateLegend();
281 if (!legendEntry) {
282 return nullptr;
283 }
284
285 PREFL_MEMOP_PANIC(_segmentCount, IOHistogramSegmentConfig);
286 tmpConfigData = OSData::withBytes(_histogramSegmentsConfig,
287 (unsigned)_segmentCount *
288 sizeof(IOHistogramSegmentConfig));
289 if (!tmpConfigData) {
290 return nullptr;
291 }
292
293 tmpDict = OSDynamicCast(OSDictionary,
294 legendEntry->getObject(kIOReportLegendInfoKey));
295 if (!tmpDict) {
296 return nullptr;
297 }
298
299 tmpDict->setObject(kIOReportLegendConfigKey, tmpConfigData.get());
300
301 return legendEntry;
302 }
303
304 IOReturn
overrideBucketValues(unsigned int index,uint64_t bucket_hits,int64_t bucket_min,int64_t bucket_max,int64_t bucket_sum)305 IOHistogramReporter::overrideBucketValues(unsigned int index,
306 uint64_t bucket_hits,
307 int64_t bucket_min,
308 int64_t bucket_max,
309 int64_t bucket_sum)
310 {
311 IOReturn result;
312 IOHistogramReportValues bucket;
313 lockReporter();
314
315 if (index >= (unsigned int)_bucketCount) {
316 result = kIOReturnBadArgument;
317 goto finish;
318 }
319
320 bucket.bucket_hits = bucket_hits;
321 bucket.bucket_min = bucket_min;
322 bucket.bucket_max = bucket_max;
323 bucket.bucket_sum = bucket_sum;
324
325 result = setElementValues(index, (IOReportElementValues *)&bucket);
326 finish:
327 unlockReporter();
328 return result;
329 }
330
331 int
tallyValue(int64_t value)332 IOHistogramReporter::tallyValue(int64_t value)
333 {
334 int result = -1;
335 int cnt = 0, element_index = 0;
336 int64_t sum = 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]) {
344 break;
345 }
346 }
347
348 element_index = cnt;
349
350 if (copyElementValues(element_index, (IOReportElementValues *)&hist_values) != kIOReturnSuccess) {
351 goto finish;
352 }
353
354 // init stats on first hit
355 if (hist_values.bucket_hits == 0) {
356 hist_values.bucket_min = hist_values.bucket_max = value;
357 hist_values.bucket_sum = 0; // += is below
358 }
359
360 // update all values
361 if (value < hist_values.bucket_min) {
362 hist_values.bucket_min = value;
363 } else if (value > hist_values.bucket_max) {
364 hist_values.bucket_max = value;
365 }
366 if (os_add_overflow(hist_values.bucket_sum, value, &sum)) {
367 hist_values.bucket_sum = INT64_MAX;
368 } else {
369 hist_values.bucket_sum = sum;
370 }
371 hist_values.bucket_hits++;
372
373 if (setElementValues(element_index, (IOReportElementValues *)&hist_values)
374 != kIOReturnSuccess) {
375 goto finish;
376 }
377
378 // success!
379 result = element_index;
380
381 finish:
382 unlockReporter();
383 return result;
384 }
385
386 /* static */ OSPtr<IOReportLegendEntry>
createLegend(uint64_t channelID,const char * channelName,int segmentCount,IOHistogramSegmentConfig * config,IOReportCategories categories,IOReportUnit unit)387 IOHistogramReporter::createLegend(uint64_t channelID,
388 const char *channelName,
389 int segmentCount,
390 IOHistogramSegmentConfig *config,
391 IOReportCategories categories,
392 IOReportUnit unit)
393 {
394 OSSharedPtr<IOReportLegendEntry> legendEntry;
395 OSSharedPtr<OSData> tmpConfigData;
396 OSDictionary *tmpDict; // no refcount
397 int cnt;
398
399 IOReportChannelType channelType = {
400 .categories = categories,
401 .report_format = kIOReportFormatHistogram,
402 .nelements = 0,
403 .element_idx = 0
404 };
405
406 for (cnt = 0; cnt < segmentCount; cnt++) {
407 channelType.nelements += config[cnt].segment_bucket_count;
408 }
409
410 legendEntry = IOReporter::legendWith(&channelID, &channelName, 1, channelType, unit);
411 if (!legendEntry) {
412 return nullptr;
413 }
414
415 PREFL_MEMOP_PANIC(segmentCount, IOHistogramSegmentConfig);
416 tmpConfigData = OSData::withBytes(config,
417 (unsigned)segmentCount *
418 sizeof(IOHistogramSegmentConfig));
419 if (!tmpConfigData) {
420 return nullptr;
421 }
422
423 tmpDict = OSDynamicCast(OSDictionary,
424 legendEntry->getObject(kIOReportLegendInfoKey));
425 if (!tmpDict) {
426 return nullptr;
427 }
428
429 tmpDict->setObject(kIOReportLegendConfigKey, tmpConfigData.get());
430
431 return legendEntry;
432 }
433