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
)) {
65 OSSafeReleaseNULL(reporter
);
66 OSSafeReleaseNULL(tmpChannelName
);
73 IOHistogramReporter::initWith(IOService
*reportingService
,
74 IOReportCategories categories
,
76 const OSSymbol
*channelName
,
79 IOHistogramSegmentConfig
*config
)
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;
87 // analyzer appeasement
88 configSize
= elementsSize
= eCountsSize
= boundsSize
= 0;
90 IORLOG("IOHistogramReporter::initWith");
92 // For now, this reporter is currently limited to a single channel
95 IOReportChannelType channelType
= {
96 .categories
= categories
,
97 .report_format
= kIOReportFormatHistogram
,
98 .nelements
= 0, // Initialized when Config is unpacked
102 if (super::init(reportingService
, channelType
, unit
) != true) {
103 IORLOG("%s - ERROR: super::init failed", __func__
);
108 // Make sure to call this after the commit init phase
109 if (channelName
) _channelNames
->setObject(channelName
);
111 _segmentCount
= nSegments
;
112 if (_segmentCount
== 0) {
113 IORLOG("IOReportHistogram init ERROR. No configuration provided!");
118 IORLOG("%s - %u segment(s)", __func__
, _segmentCount
);
120 PREFL_MEMOP_FAIL(_segmentCount
, IOHistogramSegmentConfig
);
121 configSize
= (size_t)_segmentCount
* sizeof(IOHistogramSegmentConfig
);
122 _histogramSegmentsConfig
= (IOHistogramSegmentConfig
*)IOMalloc(configSize
);
123 if (!_histogramSegmentsConfig
) goto finish
;
124 memcpy(_histogramSegmentsConfig
, config
, configSize
);
126 // Find out how many elements are need to store the histogram
127 for (cnt
= 0; cnt
< _segmentCount
; cnt
++) {
129 _nElements
+= _histogramSegmentsConfig
[cnt
].segment_bucket_count
;
130 _channelDimension
+= _histogramSegmentsConfig
[cnt
].segment_bucket_count
;
132 IORLOG("\t\t bucket_base_width: %u | log_scale: %u | buckets: %u",
133 _histogramSegmentsConfig
[cnt
].base_bucket_width
,
134 _histogramSegmentsConfig
[cnt
].scale_flag
,
135 _histogramSegmentsConfig
[cnt
].segment_bucket_count
);
137 if (_histogramSegmentsConfig
[cnt
].scale_flag
> 1
138 || _histogramSegmentsConfig
[cnt
].base_bucket_width
== 0) {
145 // Update the channel type with discovered dimension
146 _channelType
.nelements
= _channelDimension
;
148 IORLOG("%s - %u channel(s) of dimension %u",
149 __func__
, _nChannels
, _channelDimension
);
151 IORLOG("%s %d segments for a total dimension of %d elements",
152 __func__
, _nChannels
, _nElements
);
154 // Allocate memory for the array of report elements
155 PREFL_MEMOP_FAIL(_nElements
, IOReportElement
);
156 elementsSize
= (size_t)_nElements
* sizeof(IOReportElement
);
157 _elements
= (IOReportElement
*)IOMalloc(elementsSize
);
158 if (!_elements
) goto finish
;
159 memset(_elements
, 0, elementsSize
);
161 // Allocate memory for the array of element watch count
162 PREFL_MEMOP_FAIL(_nElements
, int);
163 eCountsSize
= (size_t)_nChannels
* sizeof(int);
164 _enableCounts
= (int *)IOMalloc(eCountsSize
);
165 if (!_enableCounts
) goto finish
;
166 memset(_enableCounts
, 0, eCountsSize
);
169 for (cnt2
= 0; cnt2
< _channelDimension
; cnt2
++) {
170 IOHistogramReportValues hist_values
;
171 if (copyElementValues(cnt2
, (IOReportElementValues
*)&hist_values
)){
174 hist_values
.bucket_min
= kIOReportInvalidIntValue
;
175 hist_values
.bucket_max
= kIOReportInvalidIntValue
;
176 hist_values
.bucket_sum
= kIOReportInvalidIntValue
;
177 if (setElementValues(cnt2
, (IOReportElementValues
*)&hist_values
)){
181 // Setup IOReporter's channel IDs
182 _elements
[cnt2
].channel_id
= channelID
;
184 // Setup IOReporter's reporting provider service
185 _elements
[cnt2
].provider_id
= _driver_id
;
187 // Setup IOReporter's channel type
188 _elements
[cnt2
].channel_type
= _channelType
;
189 _elements
[cnt2
].channel_type
.element_idx
= cnt2
;
191 //IOREPORTER_DEBUG_ELEMENT(cnt2);
195 // Allocate memory for the bucket upper bounds
196 PREFL_MEMOP_FAIL(_nElements
, uint64_t);
197 boundsSize
= (size_t)_nElements
* sizeof(uint64_t);
198 _bucketBounds
= (int64_t*)IOMalloc(boundsSize
);
199 if (!_bucketBounds
) goto finish
;
200 memset(_bucketBounds
, 0, boundsSize
);
201 _bucketCount
= _nElements
;
203 for (cnt
= 0; cnt
< _segmentCount
; cnt
++) {
205 if (_histogramSegmentsConfig
[cnt
].segment_bucket_count
> INT_MAX
206 || _histogramSegmentsConfig
[cnt
].base_bucket_width
> INT_MAX
) {
209 for (cnt2
= 0; cnt2
< (int)_histogramSegmentsConfig
[cnt
].segment_bucket_count
; cnt2
++) {
211 if (cnt3
>= _nElements
) {
212 IORLOG("ERROR: _bucketBounds init");
217 if (_histogramSegmentsConfig
[cnt
].scale_flag
) {
218 // FIXME: Could use pow() but not sure how to include math.h
220 int exponent
= cnt2
+ 1;
222 power
*= _histogramSegmentsConfig
[cnt
].base_bucket_width
;
229 bucketBound
= _histogramSegmentsConfig
[cnt
].base_bucket_width
*
230 ((unsigned)cnt2
+ 1);
233 if (previousBucketBound
>= bucketBound
) {
234 IORLOG("Histogram ERROR: bucket bound does not increase linearly (segment %u / bucket # %u)",
240 _bucketBounds
[cnt3
] = bucketBound
;
241 // IORLOG("_bucketBounds[%u] = %llu", cnt3, bucketBound);
242 previousBucketBound
= _bucketBounds
[cnt3
];
256 IOHistogramReporter::free(void)
259 PREFL_MEMOP_PANIC(_nElements
, int64_t);
260 IOFree(_bucketBounds
, (size_t)_nElements
* sizeof(int64_t));
262 if (_histogramSegmentsConfig
) {
263 PREFL_MEMOP_PANIC(_segmentCount
, IOHistogramSegmentConfig
);
264 IOFree(_histogramSegmentsConfig
,
265 (size_t)_segmentCount
* sizeof(IOHistogramSegmentConfig
));
273 IOHistogramReporter::handleCreateLegend(void)
275 IOReportLegendEntry
*rval
= NULL
, *legendEntry
= NULL
;
276 OSData
*tmpConfigData
= NULL
;
277 OSDictionary
*tmpDict
; // no refcount
279 legendEntry
= super::handleCreateLegend();
280 if (!legendEntry
) goto finish
;
282 PREFL_MEMOP_PANIC(_segmentCount
, IOHistogramSegmentConfig
);
283 tmpConfigData
= OSData::withBytes(_histogramSegmentsConfig
,
284 (unsigned)_segmentCount
*
285 sizeof(IOHistogramSegmentConfig
));
286 if (!tmpConfigData
) goto finish
;
288 tmpDict
= OSDynamicCast(OSDictionary
,
289 legendEntry
->getObject(kIOReportLegendInfoKey
));
290 if (!tmpDict
) goto finish
;
292 tmpDict
->setObject(kIOReportLegendConfigKey
, tmpConfigData
);
298 if (tmpConfigData
) tmpConfigData
->release();
299 if (!rval
&& legendEntry
) {
300 legendEntry
->release();
307 IOHistogramReporter::overrideBucketValues(unsigned int index
,
308 uint64_t bucket_hits
,
314 IOHistogramReportValues bucket
;
317 if (index
>= (unsigned int)_bucketCount
) {
318 result
= kIOReturnBadArgument
;
322 bucket
.bucket_hits
= bucket_hits
;
323 bucket
.bucket_min
= bucket_min
;
324 bucket
.bucket_max
= bucket_max
;
325 bucket
.bucket_sum
= bucket_sum
;
327 result
= setElementValues(index
, (IOReportElementValues
*)&bucket
);
334 IOHistogramReporter::tallyValue(int64_t value
)
337 int cnt
= 0, element_index
= 0;
338 IOHistogramReportValues hist_values
;
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
]) break;
349 if (copyElementValues(element_index
, (IOReportElementValues
*)&hist_values
) != kIOReturnSuccess
) {
353 // init stats on first hit
354 if (hist_values
.bucket_hits
== 0) {
355 hist_values
.bucket_min
= hist_values
.bucket_max
= value
;
356 hist_values
.bucket_sum
= 0; // += is below
360 if (value
< hist_values
.bucket_min
) {
361 hist_values
.bucket_min
= value
;
362 } else if (value
> hist_values
.bucket_max
) {
363 hist_values
.bucket_max
= value
;
365 hist_values
.bucket_sum
+= value
;
366 hist_values
.bucket_hits
++;
368 if (setElementValues(element_index
, (IOReportElementValues
*)&hist_values
)
369 != kIOReturnSuccess
) {
374 result
= element_index
;