]> git.saurik.com Git - apple/xnu.git/blobdiff - iokit/Kernel/IOHistogramReporter.cpp
xnu-2782.1.97.tar.gz
[apple/xnu.git] / iokit / Kernel / IOHistogramReporter.cpp
diff --git a/iokit/Kernel/IOHistogramReporter.cpp b/iokit/Kernel/IOHistogramReporter.cpp
new file mode 100644 (file)
index 0000000..f51a1c9
--- /dev/null
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2012-2013 Apple Computer, Inc.  All Rights Reserved.
+ * 
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ * 
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+
+#define __STDC_LIMIT_MACROS     // what are the C++ equivalents?
+#include <stdint.h>
+
+#include <IOKit/IOKernelReportStructs.h>
+#include <IOKit/IOKernelReporters.h>
+#include "IOReporterDefs.h"
+
+
+#define super IOReporter
+OSDefineMetaClassAndStructors(IOHistogramReporter, IOReporter);
+
+/* static */
+IOHistogramReporter*
+IOHistogramReporter::with(IOService *reportingService,
+                          IOReportCategories categories,
+                          uint64_t channelID,
+                          const char *channelName,
+                          IOReportUnits unit,
+                          int nSegments,
+                          IOHistogramSegmentConfig *config)
+{
+    IOHistogramReporter *reporter = new IOHistogramReporter;
+    
+    const OSSymbol *tmpChannelName = NULL;
+    
+    if (reporter) {
+        
+        if (channelName)
+            tmpChannelName = OSSymbol::withCString(channelName);
+                
+        if(reporter->initWith(reportingService, categories,
+                              channelID, tmpChannelName,
+                              unit, nSegments, config)) {
+            return reporter;
+        }
+    }
+    
+    return 0;
+}
+
+
+bool
+IOHistogramReporter::initWith(IOService *reportingService,
+                              IOReportCategories categories,
+                              uint64_t channelID,
+                              const OSSymbol *channelName,
+                              IOReportUnits unit,
+                              int nSegments,
+                              IOHistogramSegmentConfig *config)
+{
+    bool            result = false;
+    IOReturn        res;        // for PREFL_MEMOP
+    size_t          configSize, elementsSize, eCountsSize, boundsSize;
+    int             cnt, cnt2, cnt3 = 0;
+    int64_t        bucketBound = 0, previousBucketBound = 0;
+    
+    // analyzer appeasement
+    configSize = elementsSize = eCountsSize = boundsSize = 0;
+    
+    IORLOG("IOHistogramReporter::initWith");
+
+    // For now, this reporter is currently limited to a single channel
+    _nChannels = 1;
+
+    IOReportChannelType channelType = {
+        .categories = categories,
+        .report_format = kIOReportFormatHistogram,
+        .nelements = 0,  // Initialized when Config is unpacked
+        .element_idx = 0
+    };
+    
+    if (super::init(reportingService, channelType, unit) != true) {
+        IORLOG("%s - ERROR: super::init failed", __func__);
+        result = false;
+        goto finish;
+    }
+    
+    // Make sure to call this after the commit init phase
+    if (channelName) _channelNames->setObject(channelName);
+    
+    _segmentCount = nSegments;
+    if (_segmentCount == 0) {
+        IORLOG("IOReportHistogram init ERROR. No configuration provided!");
+        result = false;
+        goto finish;
+    }
+    
+    IORLOG("%s - %u segment(s)", __func__, _segmentCount);
+    
+    PREFL_MEMOP_FAIL(_segmentCount, IOHistogramSegmentConfig);
+    configSize = (size_t)_segmentCount * sizeof(IOHistogramSegmentConfig);
+    _histogramSegmentsConfig = (IOHistogramSegmentConfig*)IOMalloc(configSize);
+    if (!_histogramSegmentsConfig)      goto finish;
+    memcpy(_histogramSegmentsConfig, config, configSize);
+    
+    // Find out how many elements are need to store the histogram
+    for (cnt = 0; cnt < _segmentCount; cnt++) {
+        
+        _nElements += _histogramSegmentsConfig[cnt].segment_bucket_count;
+        _channelDimension += _histogramSegmentsConfig[cnt].segment_bucket_count;
+        
+        IORLOG("\t\t bucket_base_width: %u | log_scale: %u | buckets: %u",
+                _histogramSegmentsConfig[cnt].base_bucket_width,
+                _histogramSegmentsConfig[cnt].scale_flag,
+                _histogramSegmentsConfig[cnt].segment_bucket_count);
+        
+        if (_histogramSegmentsConfig[cnt].scale_flag > 1
+            || _histogramSegmentsConfig[cnt].base_bucket_width == 0) {
+            result = false;
+            goto finish;
+        }
+
+    }
+    
+    // Update the channel type with discovered dimension
+    _channelType.nelements = _channelDimension;
+
+    IORLOG("%s - %u channel(s) of dimension %u",
+           __func__, _nChannels, _channelDimension);
+
+    IORLOG("%s %d segments for a total dimension of %d elements",
+           __func__, _nChannels, _nElements);
+    
+    // Allocate memory for the array of report elements
+    PREFL_MEMOP_FAIL(_nElements, IOReportElement);
+    elementsSize = (size_t)_nElements * sizeof(IOReportElement);
+    _elements = (IOReportElement *)IOMalloc(elementsSize);
+    if (!_elements)             goto finish;
+    memset(_elements, 0, elementsSize);
+    
+    // Allocate memory for the array of element watch count
+    PREFL_MEMOP_FAIL(_nElements, int);
+    eCountsSize = (size_t)_nChannels * sizeof(int);
+    _enableCounts = (int *)IOMalloc(eCountsSize);
+    if (!_enableCounts)         goto finish;
+    memset(_enableCounts, 0, eCountsSize);
+    
+    lockReporter();
+    for (cnt2 = 0; cnt2 < _channelDimension; cnt2++) {
+        IOHistogramReportValues hist_values;
+        if (copyElementValues(cnt2, (IOReportElementValues*)&hist_values)){
+            goto finish;
+        }
+        hist_values.bucket_min = kIOReportInvalidIntValue;
+        hist_values.bucket_max = kIOReportInvalidIntValue;
+        hist_values.bucket_sum = kIOReportInvalidIntValue;
+        if (setElementValues(cnt2, (IOReportElementValues*)&hist_values)){
+            goto finish;
+        }
+
+        // Setup IOReporter's channel IDs
+        _elements[cnt2].channel_id = channelID;
+        
+        // Setup IOReporter's reporting provider service
+        _elements[cnt2].provider_id = _driver_id;
+        
+        // Setup IOReporter's channel type
+        _elements[cnt2].channel_type = _channelType;
+        _elements[cnt2].channel_type.element_idx = cnt2;
+        
+        //IOREPORTER_DEBUG_ELEMENT(cnt2);
+    }
+    unlockReporter();
+    
+    // Allocate memory for the bucket upper bounds
+    PREFL_MEMOP_FAIL(_nElements, uint64_t);
+    boundsSize = (size_t)_nElements * sizeof(uint64_t);
+    _bucketBounds = (int64_t*)IOMalloc(boundsSize);
+    if (!_bucketBounds)         goto finish;
+    memset(_bucketBounds, 0, boundsSize);
+    _bucketCount = _nElements;
+
+    for (cnt = 0; cnt < _segmentCount; cnt++) {
+        
+        if (_histogramSegmentsConfig[cnt].segment_bucket_count > INT_MAX
+            || _histogramSegmentsConfig[cnt].base_bucket_width > INT_MAX) {
+            goto finish;
+        }
+        for (cnt2 = 0; cnt2 < (int)_histogramSegmentsConfig[cnt].segment_bucket_count; cnt2++) {
+            
+            if (cnt3 >= _nElements) {
+                IORLOG("ERROR: _bucketBounds init");
+                return false;
+            }
+            
+            if (_histogramSegmentsConfig[cnt].scale_flag) {
+                // FIXME: Could use pow() but not sure how to include math.h
+                int64_t power = 1;
+                int exponent = cnt2 + 1;
+                while (exponent) {
+                    power *= _histogramSegmentsConfig[cnt].base_bucket_width;
+                    exponent--;
+                }
+                bucketBound = power;
+            }
+            
+            else {
+                bucketBound = _histogramSegmentsConfig[cnt].base_bucket_width *
+                                                    ((unsigned)cnt2 + 1);
+            }
+            
+            if (previousBucketBound >= bucketBound) {
+                IORLOG("Histogram ERROR: bucket bound does not increase linearly (segment %u / bucket # %u)",
+                       cnt, cnt2);
+                result = false;
+                goto finish;
+            }
+            
+            _bucketBounds[cnt3] = bucketBound;
+            // IORLOG("_bucketBounds[%u] = %llu", cnt3, bucketBound);
+            previousBucketBound = _bucketBounds[cnt3];
+            cnt3++;
+        }
+    }
+    
+    // success
+    result = true;
+    
+finish:
+    if (result != true) {
+        
+        if (_histogramSegmentsConfig)
+            IOFree(_histogramSegmentsConfig, configSize);
+        
+        if (_elements)
+            IOFree(_elements, elementsSize);
+        
+        if (_enableCounts)
+            IOFree(_enableCounts, eCountsSize);
+        
+        if (_bucketBounds)
+            IOFree(_bucketBounds, boundsSize);
+    }
+    
+    return result;
+}
+
+
+void
+IOHistogramReporter::free(void)
+{
+    if (_bucketBounds) {
+        PREFL_MEMOP_PANIC(_nElements, int64_t);
+        IOFree(_bucketBounds, (size_t)_nElements * sizeof(int64_t));
+    }
+    if (_histogramSegmentsConfig) {
+        PREFL_MEMOP_PANIC(_segmentCount, IOHistogramSegmentConfig);
+        IOFree(_histogramSegmentsConfig,
+               (size_t)_segmentCount * sizeof(IOHistogramSegmentConfig));
+    }
+    
+    super::free();
+}
+
+
+IOReportLegendEntry*
+IOHistogramReporter::handleCreateLegend(void)
+{
+    OSData                  *tmpConfigData;
+    OSDictionary            *tmpDict;
+    IOReportLegendEntry     *legendEntry = NULL;
+        
+    legendEntry = super::handleCreateLegend();
+    
+    if (legendEntry) {
+        
+        PREFL_MEMOP_PANIC(_segmentCount, IOHistogramSegmentConfig);
+        tmpConfigData = OSData::withBytes(_histogramSegmentsConfig,
+                                          (unsigned)_segmentCount *
+                                            (unsigned)sizeof(IOHistogramSegmentConfig));
+        if (!tmpConfigData) {
+            legendEntry->release();
+            goto finish;
+        }
+        
+        tmpDict = OSDynamicCast(OSDictionary, legendEntry->getObject(kIOReportLegendInfoKey));
+        if (!tmpDict) {
+            legendEntry->release();
+            goto finish;
+        }
+        
+        tmpDict->setObject(kIOReportLegendConfigKey, tmpConfigData);
+    }
+    
+finish:
+    return legendEntry;
+}
+
+int
+IOHistogramReporter::tallyValue(int64_t value)
+{
+    int result = -1;
+    int cnt = 0, element_index = 0;
+    IOHistogramReportValues hist_values;
+    
+    lockReporter();
+    
+    // Iterate over _bucketCount minus one to make last bucket of infinite width
+    for (cnt = 0; cnt < _bucketCount - 1; cnt++) {
+        if (value <= _bucketBounds[cnt]) break;
+    }
+    
+    element_index = cnt;
+    
+    if (copyElementValues(element_index, (IOReportElementValues *)&hist_values) != kIOReturnSuccess) {
+        goto finish;
+    }
+    
+    // init stats on first hit
+    if (hist_values.bucket_hits == 0) {
+        hist_values.bucket_min = hist_values.bucket_max = value;
+        hist_values.bucket_sum = 0;     // += is below
+    }
+
+    // update all values
+    if (value < hist_values.bucket_min) {
+        hist_values.bucket_min = value;
+    } else if (value > hist_values.bucket_max) {
+        hist_values.bucket_max = value;
+    }
+    hist_values.bucket_sum += value;
+    hist_values.bucket_hits++;
+    
+    if (setElementValues(element_index, (IOReportElementValues *)&hist_values) == kIOReturnSuccess) {
+        goto finish;
+    }
+
+    // success!
+    result = element_index;
+    
+finish:
+    unlockReporter();
+    return result;
+}