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
;
56 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
110 _channelNames
->setObject(channelName
);
113 _segmentCount
= nSegments
;
114 if (_segmentCount
== 0) {
115 IORLOG("IOReportHistogram init ERROR. No configuration provided!");
120 IORLOG("%s - %u segment(s)", __func__
, _segmentCount
);
122 PREFL_MEMOP_FAIL(_segmentCount
, IOHistogramSegmentConfig
);
123 configSize
= (size_t)_segmentCount
* sizeof(IOHistogramSegmentConfig
);
124 _histogramSegmentsConfig
= (IOHistogramSegmentConfig
*)IOMalloc(configSize
);
125 if (!_histogramSegmentsConfig
) {
128 memcpy(_histogramSegmentsConfig
, config
, configSize
);
130 // Find out how many elements are need to store the histogram
131 for (cnt
= 0; cnt
< _segmentCount
; cnt
++) {
132 _nElements
+= _histogramSegmentsConfig
[cnt
].segment_bucket_count
;
133 _channelDimension
+= _histogramSegmentsConfig
[cnt
].segment_bucket_count
;
135 IORLOG("\t\t bucket_base_width: %u | log_scale: %u | buckets: %u",
136 _histogramSegmentsConfig
[cnt
].base_bucket_width
,
137 _histogramSegmentsConfig
[cnt
].scale_flag
,
138 _histogramSegmentsConfig
[cnt
].segment_bucket_count
);
140 if (_histogramSegmentsConfig
[cnt
].scale_flag
> 1
141 || _histogramSegmentsConfig
[cnt
].base_bucket_width
== 0) {
147 // Update the channel type with discovered dimension
148 _channelType
.nelements
= _channelDimension
;
150 IORLOG("%s - %u channel(s) of dimension %u",
151 __func__
, _nChannels
, _channelDimension
);
153 IORLOG("%s %d segments for a total dimension of %d elements",
154 __func__
, _nChannels
, _nElements
);
156 // Allocate memory for the array of report elements
157 PREFL_MEMOP_FAIL(_nElements
, IOReportElement
);
158 elementsSize
= (size_t)_nElements
* sizeof(IOReportElement
);
159 _elements
= (IOReportElement
*)IOMalloc(elementsSize
);
163 memset(_elements
, 0, elementsSize
);
165 // Allocate memory for the array of element watch count
166 PREFL_MEMOP_FAIL(_nElements
, int);
167 eCountsSize
= (size_t)_nChannels
* sizeof(int);
168 _enableCounts
= (int *)IOMalloc(eCountsSize
);
169 if (!_enableCounts
) {
172 memset(_enableCounts
, 0, eCountsSize
);
175 for (cnt2
= 0; cnt2
< _channelDimension
; cnt2
++) {
176 IOHistogramReportValues hist_values
;
177 if (copyElementValues(cnt2
, (IOReportElementValues
*)&hist_values
)) {
180 hist_values
.bucket_min
= kIOReportInvalidIntValue
;
181 hist_values
.bucket_max
= kIOReportInvalidIntValue
;
182 hist_values
.bucket_sum
= kIOReportInvalidIntValue
;
183 if (setElementValues(cnt2
, (IOReportElementValues
*)&hist_values
)) {
187 // Setup IOReporter's channel IDs
188 _elements
[cnt2
].channel_id
= channelID
;
190 // Setup IOReporter's reporting provider service
191 _elements
[cnt2
].provider_id
= _driver_id
;
193 // Setup IOReporter's channel type
194 _elements
[cnt2
].channel_type
= _channelType
;
195 _elements
[cnt2
].channel_type
.element_idx
= cnt2
;
197 //IOREPORTER_DEBUG_ELEMENT(cnt2);
201 // Allocate memory for the bucket upper bounds
202 PREFL_MEMOP_FAIL(_nElements
, uint64_t);
203 boundsSize
= (size_t)_nElements
* sizeof(uint64_t);
204 _bucketBounds
= (int64_t*)IOMalloc(boundsSize
);
205 if (!_bucketBounds
) {
208 memset(_bucketBounds
, 0, boundsSize
);
209 _bucketCount
= _nElements
;
211 for (cnt
= 0; cnt
< _segmentCount
; cnt
++) {
212 if (_histogramSegmentsConfig
[cnt
].segment_bucket_count
> INT_MAX
213 || _histogramSegmentsConfig
[cnt
].base_bucket_width
> INT_MAX
) {
216 for (cnt2
= 0; cnt2
< (int)_histogramSegmentsConfig
[cnt
].segment_bucket_count
; cnt2
++) {
217 if (cnt3
>= _nElements
) {
218 IORLOG("ERROR: _bucketBounds init");
223 if (_histogramSegmentsConfig
[cnt
].scale_flag
) {
224 // FIXME: Could use pow() but not sure how to include math.h
226 int exponent
= cnt2
+ 1;
228 power
*= _histogramSegmentsConfig
[cnt
].base_bucket_width
;
233 bucketBound
= _histogramSegmentsConfig
[cnt
].base_bucket_width
*
234 ((unsigned)cnt2
+ 1);
237 if (previousBucketBound
>= bucketBound
) {
238 IORLOG("Histogram ERROR: bucket bound does not increase linearly (segment %u / bucket # %u)",
244 _bucketBounds
[cnt3
] = bucketBound
;
245 // IORLOG("_bucketBounds[%u] = %llu", cnt3, bucketBound);
246 previousBucketBound
= _bucketBounds
[cnt3
];
260 IOHistogramReporter::free(void)
263 PREFL_MEMOP_PANIC(_nElements
, int64_t);
264 IOFree(_bucketBounds
, (size_t)_nElements
* sizeof(int64_t));
266 if (_histogramSegmentsConfig
) {
267 PREFL_MEMOP_PANIC(_segmentCount
, IOHistogramSegmentConfig
);
268 IOFree(_histogramSegmentsConfig
,
269 (size_t)_segmentCount
* sizeof(IOHistogramSegmentConfig
));
277 IOHistogramReporter::handleCreateLegend(void)
279 IOReportLegendEntry
*rval
= NULL
, *legendEntry
= NULL
;
280 OSData
*tmpConfigData
= NULL
;
281 OSDictionary
*tmpDict
; // no refcount
283 legendEntry
= super::handleCreateLegend();
288 PREFL_MEMOP_PANIC(_segmentCount
, IOHistogramSegmentConfig
);
289 tmpConfigData
= OSData::withBytes(_histogramSegmentsConfig
,
290 (unsigned)_segmentCount
*
291 sizeof(IOHistogramSegmentConfig
));
292 if (!tmpConfigData
) {
296 tmpDict
= OSDynamicCast(OSDictionary
,
297 legendEntry
->getObject(kIOReportLegendInfoKey
));
302 tmpDict
->setObject(kIOReportLegendConfigKey
, tmpConfigData
);
309 tmpConfigData
->release();
311 if (!rval
&& legendEntry
) {
312 legendEntry
->release();
319 IOHistogramReporter::overrideBucketValues(unsigned int index
,
320 uint64_t bucket_hits
,
326 IOHistogramReportValues bucket
;
329 if (index
>= (unsigned int)_bucketCount
) {
330 result
= kIOReturnBadArgument
;
334 bucket
.bucket_hits
= bucket_hits
;
335 bucket
.bucket_min
= bucket_min
;
336 bucket
.bucket_max
= bucket_max
;
337 bucket
.bucket_sum
= bucket_sum
;
339 result
= setElementValues(index
, (IOReportElementValues
*)&bucket
);
346 IOHistogramReporter::tallyValue(int64_t value
)
349 int cnt
= 0, element_index
= 0;
350 IOHistogramReportValues hist_values
;
354 // Iterate over _bucketCount minus one to make last bucket of infinite width
355 for (cnt
= 0; cnt
< _bucketCount
- 1; cnt
++) {
356 if (value
<= _bucketBounds
[cnt
]) {
363 if (copyElementValues(element_index
, (IOReportElementValues
*)&hist_values
) != kIOReturnSuccess
) {
367 // init stats on first hit
368 if (hist_values
.bucket_hits
== 0) {
369 hist_values
.bucket_min
= hist_values
.bucket_max
= value
;
370 hist_values
.bucket_sum
= 0; // += is below
374 if (value
< hist_values
.bucket_min
) {
375 hist_values
.bucket_min
= value
;
376 } else if (value
> hist_values
.bucket_max
) {
377 hist_values
.bucket_max
= value
;
379 hist_values
.bucket_sum
+= value
;
380 hist_values
.bucket_hits
++;
382 if (setElementValues(element_index
, (IOReportElementValues
*)&hist_values
)
383 != kIOReturnSuccess
) {
388 result
= element_index
;