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