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