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 38OSDefineMetaClassAndStructors(IOHistogramReporter, IOReporter); 39 40/* static */ 41IOHistogramReporter* 42IOHistogramReporter::with(IOService *reportingService, 43 IOReportCategories categories, 44 uint64_t channelID, 45 const char *channelName, 46 IOReportUnits 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 66 return 0; 67} 68 69 70bool 71IOHistogramReporter::initWith(IOService *reportingService, 72 IOReportCategories categories, 73 uint64_t channelID, 74 const OSSymbol *channelName, 75 IOReportUnits unit, 76 int nSegments, 77 IOHistogramSegmentConfig *config) 78{ 79 bool result = false; 80 IOReturn res; // for PREFL_MEMOP 81 size_t configSize, elementsSize, eCountsSize, boundsSize; 82 int cnt, cnt2, cnt3 = 0; 83 int64_t bucketBound = 0, previousBucketBound = 0; 84 85 // analyzer appeasement 86 configSize = elementsSize = eCountsSize = boundsSize = 0; 87 88 IORLOG("IOHistogramReporter::initWith"); 89 90 // For now, this reporter is currently limited to a single channel 91 _nChannels = 1; 92 93 IOReportChannelType channelType = { 94 .categories = categories, 95 .report_format = kIOReportFormatHistogram, 96 .nelements = 0, // Initialized when Config is unpacked 97 .element_idx = 0 98 }; 99 100 if (super::init(reportingService, channelType, unit) != true) { 101 IORLOG("%s - ERROR: super::init failed", __func__); 102 result = false; 103 goto finish; 104 } 105 106 // Make sure to call this after the commit init phase 107 if (channelName) _channelNames->setObject(channelName); 108 109 _segmentCount = nSegments; 110 if (_segmentCount == 0) { 111 IORLOG("IOReportHistogram init ERROR. No configuration provided!"); 112 result = false; 113 goto finish; 114 } 115 116 IORLOG("%s - %u segment(s)", __func__, _segmentCount); 117 118 PREFL_MEMOP_FAIL(_segmentCount, IOHistogramSegmentConfig); 119 configSize = (size_t)_segmentCount * sizeof(IOHistogramSegmentConfig); 120 _histogramSegmentsConfig = (IOHistogramSegmentConfig*)IOMalloc(configSize); 121 if (!_histogramSegmentsConfig) goto finish; 122 memcpy(_histogramSegmentsConfig, config, configSize); 123 124 // Find out how many elements are need to store the histogram 125 for (cnt = 0; cnt < _segmentCount; cnt++) { 126 127 _nElements += _histogramSegmentsConfig[cnt].segment_bucket_count; 128 _channelDimension += _histogramSegmentsConfig[cnt].segment_bucket_count; 129 130 IORLOG("\t\t bucket_base_width: %u | log_scale: %u | buckets: %u", 131 _histogramSegmentsConfig[cnt].base_bucket_width, 132 _histogramSegmentsConfig[cnt].scale_flag, 133 _histogramSegmentsConfig[cnt].segment_bucket_count); 134 135 if (_histogramSegmentsConfig[cnt].scale_flag > 1 136 || _histogramSegmentsConfig[cnt].base_bucket_width == 0) { 137 result = false; 138 goto finish; 139 } 140 141 } 142 143 // Update the channel type with discovered dimension 144 _channelType.nelements = _channelDimension; 145 146 IORLOG("%s - %u channel(s) of dimension %u", 147 __func__, _nChannels, _channelDimension); 148 149 IORLOG("%s %d segments for a total dimension of %d elements", 150 __func__, _nChannels, _nElements); 151 152 // Allocate memory for the array of report elements 153 PREFL_MEMOP_FAIL(_nElements, IOReportElement); 154 elementsSize = (size_t)_nElements * sizeof(IOReportElement); 155 _elements = (IOReportElement *)IOMalloc(elementsSize); 156 if (!_elements) goto finish; 157 memset(_elements, 0, elementsSize); 158 159 // Allocate memory for the array of element watch count 160 PREFL_MEMOP_FAIL(_nElements, int); 161 eCountsSize = (size_t)_nChannels * sizeof(int); 162 _enableCounts = (int *)IOMalloc(eCountsSize); 163 if (!_enableCounts) goto finish; 164 memset(_enableCounts, 0, eCountsSize); 165 166 lockReporter(); 167 for (cnt2 = 0; cnt2 < _channelDimension; cnt2++) { 168 IOHistogramReportValues hist_values; 169 if (copyElementValues(cnt2, (IOReportElementValues*)&hist_values)){ 170 goto finish; 171 } 172 hist_values.bucket_min = kIOReportInvalidIntValue; 173 hist_values.bucket_max = kIOReportInvalidIntValue; 174 hist_values.bucket_sum = kIOReportInvalidIntValue; 175 if (setElementValues(cnt2, (IOReportElementValues*)&hist_values)){ 176 goto finish; 177 } 178 179 // Setup IOReporter's channel IDs 180 _elements[cnt2].channel_id = channelID; 181 182 // Setup IOReporter's reporting provider service 183 _elements[cnt2].provider_id = _driver_id; 184 185 // Setup IOReporter's channel type 186 _elements[cnt2].channel_type = _channelType; 187 _elements[cnt2].channel_type.element_idx = cnt2; 188 189 //IOREPORTER_DEBUG_ELEMENT(cnt2); 190 } 191 unlockReporter(); 192 193 // Allocate memory for the bucket upper bounds 194 PREFL_MEMOP_FAIL(_nElements, uint64_t); 195 boundsSize = (size_t)_nElements * sizeof(uint64_t); 196 _bucketBounds = (int64_t*)IOMalloc(boundsSize); 197 if (!_bucketBounds) goto finish; 198 memset(_bucketBounds, 0, boundsSize); 199 _bucketCount = _nElements; 200 201 for (cnt = 0; cnt < _segmentCount; cnt++) { 202 203 if (_histogramSegmentsConfig[cnt].segment_bucket_count > INT_MAX 204 || _histogramSegmentsConfig[cnt].base_bucket_width > INT_MAX) { 205 goto finish; 206 } 207 for (cnt2 = 0; cnt2 < (int)_histogramSegmentsConfig[cnt].segment_bucket_count; cnt2++) { 208 209 if (cnt3 >= _nElements) { 210 IORLOG("ERROR: _bucketBounds init"); 211 return false; 212 } 213 214 if (_histogramSegmentsConfig[cnt].scale_flag) { 215 // FIXME: Could use pow() but not sure how to include math.h 216 int64_t power = 1; 217 int exponent = cnt2 + 1; 218 while (exponent) { 219 power *= _histogramSegmentsConfig[cnt].base_bucket_width; 220 exponent--; 221 } 222 bucketBound = power; 223 } 224 225 else { 226 bucketBound = _histogramSegmentsConfig[cnt].base_bucket_width * 227 ((unsigned)cnt2 + 1); 228 } 229 230 if (previousBucketBound >= bucketBound) { 231 IORLOG("Histogram ERROR: bucket bound does not increase linearly (segment %u / bucket # %u)", 232 cnt, cnt2); 233 result = false; 234 goto finish; 235 } 236 237 _bucketBounds[cnt3] = bucketBound; 238 // IORLOG("_bucketBounds[%u] = %llu", cnt3, bucketBound); 239 previousBucketBound = _bucketBounds[cnt3]; 240 cnt3++; 241 } 242 } 243 244 // success 245 result = true; 246 247finish: 248 if (result != true) { 249 250 if (_histogramSegmentsConfig) 251 IOFree(_histogramSegmentsConfig, configSize); 252 253 if (_elements) 254 IOFree(_elements, elementsSize); 255 256 if (_enableCounts) 257 IOFree(_enableCounts, eCountsSize); 258 259 if (_bucketBounds) 260 IOFree(_bucketBounds, boundsSize); 261 } 262 263 return result; 264} 265 266 267void 268IOHistogramReporter::free(void) 269{ 270 if (_bucketBounds) { 271 PREFL_MEMOP_PANIC(_nElements, int64_t); 272 IOFree(_bucketBounds, (size_t)_nElements * sizeof(int64_t)); 273 } 274 if (_histogramSegmentsConfig) { 275 PREFL_MEMOP_PANIC(_segmentCount, IOHistogramSegmentConfig); 276 IOFree(_histogramSegmentsConfig, 277 (size_t)_segmentCount * sizeof(IOHistogramSegmentConfig)); 278 } 279 280 super::free(); 281} 282 283 284IOReportLegendEntry* 285IOHistogramReporter::handleCreateLegend(void) 286{ 287 OSData *tmpConfigData; 288 OSDictionary *tmpDict; 289 IOReportLegendEntry *legendEntry = NULL; 290 291 legendEntry = super::handleCreateLegend(); 292 293 if (legendEntry) { 294 295 PREFL_MEMOP_PANIC(_segmentCount, IOHistogramSegmentConfig); 296 tmpConfigData = OSData::withBytes(_histogramSegmentsConfig, 297 (unsigned)_segmentCount * 298 (unsigned)sizeof(IOHistogramSegmentConfig)); 299 if (!tmpConfigData) { 300 legendEntry->release(); 301 goto finish; 302 } 303 304 tmpDict = OSDynamicCast(OSDictionary, legendEntry->getObject(kIOReportLegendInfoKey)); 305 if (!tmpDict) { 306 legendEntry->release(); 307 goto finish; 308 } 309 310 tmpDict->setObject(kIOReportLegendConfigKey, tmpConfigData); 311 } 312 313finish: 314 return legendEntry; 315} 316 317int 318IOHistogramReporter::tallyValue(int64_t value) 319{ 320 int result = -1; 321 int cnt = 0, element_index = 0; 322 IOHistogramReportValues hist_values; 323 324 lockReporter(); 325 326 // Iterate over _bucketCount minus one to make last bucket of infinite width 327 for (cnt = 0; cnt < _bucketCount - 1; cnt++) { 328 if (value <= _bucketBounds[cnt]) break; 329 } 330 331 element_index = cnt; 332 333 if (copyElementValues(element_index, (IOReportElementValues *)&hist_values) != kIOReturnSuccess) { 334 goto finish; 335 } 336 337 // init stats on first hit 338 if (hist_values.bucket_hits == 0) { 339 hist_values.bucket_min = hist_values.bucket_max = value; 340 hist_values.bucket_sum = 0; // += is below 341 } 342 343 // update all values 344 if (value < hist_values.bucket_min) { 345 hist_values.bucket_min = value; 346 } else if (value > hist_values.bucket_max) { 347 hist_values.bucket_max = value; 348 } 349 hist_values.bucket_sum += value; 350 hist_values.bucket_hits++; 351 352 if (setElementValues(element_index, (IOReportElementValues *)&hist_values) == kIOReturnSuccess) { 353 goto finish; 354 } 355 356 // success! 357 result = element_index; 358 359finish: 360 unlockReporter(); 361 return result; 362} 363