2 * Copyright (c) 2012-2013 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #define __STDC_LIMIT_MACROS // what are the C++ equivalents?
32 #include <IOKit/IOKernelReportStructs.h>
33 #include <IOKit/IOKernelReporters.h>
34 #include "IOReporterDefs.h"
37 #define super IOReporter
38 OSDefineMetaClassAndStructors(IOHistogramReporter
, IOReporter
);
42 IOHistogramReporter::with(IOService
*reportingService
,
43 IOReportCategories categories
,
45 const char *channelName
,
48 IOHistogramSegmentConfig
*config
)
50 IOHistogramReporter
*reporter
= new IOHistogramReporter
;
52 const OSSymbol
*tmpChannelName
= NULL
;
57 tmpChannelName
= OSSymbol::withCString(channelName
);
59 if(reporter
->initWith(reportingService
, categories
,
60 channelID
, tmpChannelName
,
61 unit
, nSegments
, config
)) {
71 IOHistogramReporter::initWith(IOService
*reportingService
,
72 IOReportCategories categories
,
74 const OSSymbol
*channelName
,
77 IOHistogramSegmentConfig
*config
)
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;
85 // analyzer appeasement
86 configSize
= elementsSize
= eCountsSize
= boundsSize
= 0;
88 IORLOG("IOHistogramReporter::initWith");
90 // For now, this reporter is currently limited to a single channel
93 IOReportChannelType channelType
= {
94 .categories
= categories
,
95 .report_format
= kIOReportFormatHistogram
,
96 .nelements
= 0, // Initialized when Config is unpacked
100 if (super::init(reportingService
, channelType
, unit
) != true) {
101 IORLOG("%s - ERROR: super::init failed", __func__
);
106 // Make sure to call this after the commit init phase
107 if (channelName
) _channelNames
->setObject(channelName
);
109 _segmentCount
= nSegments
;
110 if (_segmentCount
== 0) {
111 IORLOG("IOReportHistogram init ERROR. No configuration provided!");
116 IORLOG("%s - %u segment(s)", __func__
, _segmentCount
);
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
);
124 // Find out how many elements are need to store the histogram
125 for (cnt
= 0; cnt
< _segmentCount
; cnt
++) {
127 _nElements
+= _histogramSegmentsConfig
[cnt
].segment_bucket_count
;
128 _channelDimension
+= _histogramSegmentsConfig
[cnt
].segment_bucket_count
;
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
);
135 if (_histogramSegmentsConfig
[cnt
].scale_flag
> 1
136 || _histogramSegmentsConfig
[cnt
].base_bucket_width
== 0) {
143 // Update the channel type with discovered dimension
144 _channelType
.nelements
= _channelDimension
;
146 IORLOG("%s - %u channel(s) of dimension %u",
147 __func__
, _nChannels
, _channelDimension
);
149 IORLOG("%s %d segments for a total dimension of %d elements",
150 __func__
, _nChannels
, _nElements
);
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
);
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
);
167 for (cnt2
= 0; cnt2
< _channelDimension
; cnt2
++) {
168 IOHistogramReportValues hist_values
;
169 if (copyElementValues(cnt2
, (IOReportElementValues
*)&hist_values
)){
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
)){
179 // Setup IOReporter's channel IDs
180 _elements
[cnt2
].channel_id
= channelID
;
182 // Setup IOReporter's reporting provider service
183 _elements
[cnt2
].provider_id
= _driver_id
;
185 // Setup IOReporter's channel type
186 _elements
[cnt2
].channel_type
= _channelType
;
187 _elements
[cnt2
].channel_type
.element_idx
= cnt2
;
189 //IOREPORTER_DEBUG_ELEMENT(cnt2);
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
;
201 for (cnt
= 0; cnt
< _segmentCount
; cnt
++) {
203 if (_histogramSegmentsConfig
[cnt
].segment_bucket_count
> INT_MAX
204 || _histogramSegmentsConfig
[cnt
].base_bucket_width
> INT_MAX
) {
207 for (cnt2
= 0; cnt2
< (int)_histogramSegmentsConfig
[cnt
].segment_bucket_count
; cnt2
++) {
209 if (cnt3
>= _nElements
) {
210 IORLOG("ERROR: _bucketBounds init");
214 if (_histogramSegmentsConfig
[cnt
].scale_flag
) {
215 // FIXME: Could use pow() but not sure how to include math.h
217 int exponent
= cnt2
+ 1;
219 power
*= _histogramSegmentsConfig
[cnt
].base_bucket_width
;
226 bucketBound
= _histogramSegmentsConfig
[cnt
].base_bucket_width
*
227 ((unsigned)cnt2
+ 1);
230 if (previousBucketBound
>= bucketBound
) {
231 IORLOG("Histogram ERROR: bucket bound does not increase linearly (segment %u / bucket # %u)",
237 _bucketBounds
[cnt3
] = bucketBound
;
238 // IORLOG("_bucketBounds[%u] = %llu", cnt3, bucketBound);
239 previousBucketBound
= _bucketBounds
[cnt3
];
248 if (result
!= true) {
250 if (_histogramSegmentsConfig
)
251 IOFree(_histogramSegmentsConfig
, configSize
);
254 IOFree(_elements
, elementsSize
);
257 IOFree(_enableCounts
, eCountsSize
);
260 IOFree(_bucketBounds
, boundsSize
);
268 IOHistogramReporter::free(void)
271 PREFL_MEMOP_PANIC(_nElements
, int64_t);
272 IOFree(_bucketBounds
, (size_t)_nElements
* sizeof(int64_t));
274 if (_histogramSegmentsConfig
) {
275 PREFL_MEMOP_PANIC(_segmentCount
, IOHistogramSegmentConfig
);
276 IOFree(_histogramSegmentsConfig
,
277 (size_t)_segmentCount
* sizeof(IOHistogramSegmentConfig
));
285 IOHistogramReporter::handleCreateLegend(void)
287 OSData
*tmpConfigData
;
288 OSDictionary
*tmpDict
;
289 IOReportLegendEntry
*legendEntry
= NULL
;
291 legendEntry
= super::handleCreateLegend();
295 PREFL_MEMOP_PANIC(_segmentCount
, IOHistogramSegmentConfig
);
296 tmpConfigData
= OSData::withBytes(_histogramSegmentsConfig
,
297 (unsigned)_segmentCount
*
298 (unsigned)sizeof(IOHistogramSegmentConfig
));
299 if (!tmpConfigData
) {
300 legendEntry
->release();
304 tmpDict
= OSDynamicCast(OSDictionary
, legendEntry
->getObject(kIOReportLegendInfoKey
));
306 legendEntry
->release();
310 tmpDict
->setObject(kIOReportLegendConfigKey
, tmpConfigData
);
318 IOHistogramReporter::tallyValue(int64_t value
)
321 int cnt
= 0, element_index
= 0;
322 IOHistogramReportValues hist_values
;
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;
333 if (copyElementValues(element_index
, (IOReportElementValues
*)&hist_values
) != kIOReturnSuccess
) {
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
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
;
349 hist_values
.bucket_sum
+= value
;
350 hist_values
.bucket_hits
++;
352 if (setElementValues(element_index
, (IOReportElementValues
*)&hist_values
) == kIOReturnSuccess
) {
357 result
= element_index
;