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> 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 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 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> 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 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 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> 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