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 IOKIT_ENABLE_SHARED_PTR
30
31#define __STDC_LIMIT_MACROS // what are the C++ equivalents?
32#include <stdint.h>
33
34#include <IOKit/IOKernelReportStructs.h>
35#include <IOKit/IOKernelReporters.h>
36#include <os/overflow.h>
37#include "IOReporterDefs.h"
38
39
40#define super IOReporter
41OSDefineMetaClassAndStructors(IOHistogramReporter, IOReporter);
42
43/* static */
44OSSharedPtr<IOHistogramReporter>
45IOHistogramReporter::with(IOService *reportingService,
46 IOReportCategories categories,
47 uint64_t channelID,
48 const char *channelName,
49 IOReportUnit unit,
50 int nSegments,
51 IOHistogramSegmentConfig *config)
52{
53 OSSharedPtr<IOHistogramReporter> reporter = OSMakeShared<IOHistogramReporter>();
54 OSSharedPtr<const OSSymbol> tmpChannelName;
55
56 if (reporter) {
57 if (channelName) {
58 tmpChannelName = OSSymbol::withCString(cString: channelName);
59 }
60
61 if (reporter->initWith(reportingService, categories,
62 channelID, channelName: tmpChannelName.get(),
63 unit, nSegments, config)) {
64 return reporter;
65 }
66 }
67
68 return nullptr;
69}
70
71
72bool
73IOHistogramReporter::initWith(IOService *reportingService,
74 IOReportCategories categories,
75 uint64_t channelID,
76 const OSSymbol *channelName,
77 IOReportUnit unit,
78 int nSegments,
79 IOHistogramSegmentConfig *config)
80{
81 bool result = false;
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;
86
87 // analyzer appeasement
88 configSize = elementsSize = eCountsSize = boundsSize = 0;
89
90 IORLOG("IOHistogramReporter::initWith");
91
92 // For now, this reporter is currently limited to a single channel
93 _nChannels = 1;
94
95 IOReportChannelType channelType = {
96 .categories = categories,
97 .report_format = kIOReportFormatHistogram,
98 .nelements = 0, // Initialized when Config is unpacked
99 .element_idx = 0
100 };
101
102 if (super::init(reportingService, channelType, unit) != true) {
103 IORLOG("%s - ERROR: super::init failed", __func__);
104 result = false;
105 goto finish;
106 }
107
108 // Make sure to call this after the commit init phase
109 if (channelName) {
110 _channelNames->setObject(channelName);
111 }
112
113 _segmentCount = nSegments;
114 if (_segmentCount == 0) {
115 IORLOG("IOReportHistogram init ERROR. No configuration provided!");
116 result = false;
117 goto finish;
118 }
119
120 IORLOG("%s - %u segment(s)", __func__, _segmentCount);
121
122 PREFL_MEMOP_FAIL(_segmentCount, IOHistogramSegmentConfig);
123 configSize = (size_t)_segmentCount * sizeof(IOHistogramSegmentConfig);
124 _histogramSegmentsConfig = (IOHistogramSegmentConfig*)IOMallocData(configSize);
125 if (!_histogramSegmentsConfig) {
126 goto finish;
127 }
128 memcpy(dst: _histogramSegmentsConfig, src: config, n: configSize);
129
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;
134
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);
139
140 if (_histogramSegmentsConfig[cnt].scale_flag > 1
141 || _histogramSegmentsConfig[cnt].base_bucket_width == 0) {
142 result = false;
143 goto finish;
144 }
145 }
146
147 // Update the channel type with discovered dimension
148 _channelType.nelements = _channelDimension;
149
150 IORLOG("%s - %u channel(s) of dimension %u",
151 __func__, _nChannels, _channelDimension);
152
153 IORLOG("%s %d segments for a total dimension of %d elements",
154 __func__, _nChannels, _nElements);
155
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 *)IOMallocZeroData(elementsSize);
160 if (!_elements) {
161 goto finish;
162 }
163
164 // Allocate memory for the array of element watch count
165 PREFL_MEMOP_FAIL(_nElements, int);
166 eCountsSize = (size_t)_nChannels * sizeof(int);
167 _enableCounts = (int *)IOMallocZeroData(eCountsSize);
168 if (!_enableCounts) {
169 goto finish;
170 }
171
172 lockReporter();
173 for (cnt2 = 0; cnt2 < _channelDimension; cnt2++) {
174 IOHistogramReportValues hist_values;
175 if (copyElementValues(element_index: cnt2, elementValues: (IOReportElementValues*)&hist_values)) {
176 goto finish;
177 }
178 hist_values.bucket_min = kIOReportInvalidIntValue;
179 hist_values.bucket_max = kIOReportInvalidIntValue;
180 hist_values.bucket_sum = kIOReportInvalidIntValue;
181 if (setElementValues(element_index: cnt2, values: (IOReportElementValues*)&hist_values)) {
182 goto finish;
183 }
184
185 // Setup IOReporter's channel IDs
186 _elements[cnt2].channel_id = channelID;
187
188 // Setup IOReporter's reporting provider service
189 _elements[cnt2].provider_id = _driver_id;
190
191 // Setup IOReporter's channel type
192 _elements[cnt2].channel_type = _channelType;
193 _elements[cnt2].channel_type.element_idx = ((int16_t) cnt2);
194
195 //IOREPORTER_DEBUG_ELEMENT(cnt2);
196 }
197 unlockReporter();
198
199 // Allocate memory for the bucket upper bounds
200 PREFL_MEMOP_FAIL(_nElements, uint64_t);
201 boundsSize = (size_t)_nElements * sizeof(uint64_t);
202 _bucketBounds = (int64_t*)IOMallocZeroData(boundsSize);
203 if (!_bucketBounds) {
204 goto finish;
205 }
206 _bucketCount = _nElements;
207
208 for (cnt = 0; cnt < _segmentCount; cnt++) {
209 if (_histogramSegmentsConfig[cnt].segment_bucket_count > INT_MAX
210 || _histogramSegmentsConfig[cnt].base_bucket_width > INT_MAX) {
211 goto finish;
212 }
213 for (cnt2 = 0; cnt2 < (int)_histogramSegmentsConfig[cnt].segment_bucket_count; cnt2++) {
214 if (cnt3 >= _nElements) {
215 IORLOG("ERROR: _bucketBounds init");
216 result = false;
217 goto finish;
218 }
219
220 if (_histogramSegmentsConfig[cnt].scale_flag) {
221 int64_t power = 1;
222 int exponent = cnt2 + 1;
223 while (exponent) {
224 power *= _histogramSegmentsConfig[cnt].base_bucket_width;
225 exponent--;
226 }
227 bucketBound = power;
228 } else {
229 bucketBound = _histogramSegmentsConfig[cnt].base_bucket_width *
230 ((unsigned)cnt2 + 1);
231 }
232
233 if (previousBucketBound >= bucketBound) {
234 IORLOG("Histogram ERROR: bucket bound does not increase linearly (segment %u / bucket # %u)",
235 cnt, cnt2);
236 result = false;
237 goto finish;
238 }
239
240 _bucketBounds[cnt3] = bucketBound;
241 // IORLOG("_bucketBounds[%u] = %llu", cnt3, bucketBound);
242 previousBucketBound = _bucketBounds[cnt3];
243 cnt3++;
244 }
245 }
246
247 // success
248 result = true;
249
250finish:
251 return result;
252}
253
254
255void
256IOHistogramReporter::free(void)
257{
258 if (_bucketBounds) {
259 PREFL_MEMOP_PANIC(_nElements, int64_t);
260 IOFreeData(address: _bucketBounds, size: (size_t)_nElements * sizeof(int64_t));
261 }
262 if (_histogramSegmentsConfig) {
263 PREFL_MEMOP_PANIC(_segmentCount, IOHistogramSegmentConfig);
264 IOFreeData(address: _histogramSegmentsConfig,
265 size: (size_t)_segmentCount * sizeof(IOHistogramSegmentConfig));
266 }
267
268 super::free();
269}
270
271
272OSSharedPtr<IOReportLegendEntry>
273IOHistogramReporter::handleCreateLegend(void)
274{
275 OSSharedPtr<IOReportLegendEntry> legendEntry;
276 OSSharedPtr<OSData> tmpConfigData;
277 OSDictionary *tmpDict; // no refcount
278
279 legendEntry = super::handleCreateLegend();
280 if (!legendEntry) {
281 return nullptr;
282 }
283
284 PREFL_MEMOP_PANIC(_segmentCount, IOHistogramSegmentConfig);
285 tmpConfigData = OSData::withBytes(bytes: _histogramSegmentsConfig,
286 numBytes: (unsigned)_segmentCount *
287 sizeof(IOHistogramSegmentConfig));
288 if (!tmpConfigData) {
289 return nullptr;
290 }
291
292 tmpDict = OSDynamicCast(OSDictionary,
293 legendEntry->getObject(kIOReportLegendInfoKey));
294 if (!tmpDict) {
295 return nullptr;
296 }
297
298 tmpDict->setObject(kIOReportLegendConfigKey, anObject: tmpConfigData.get());
299
300 return legendEntry;
301}
302
303IOReturn
304IOHistogramReporter::overrideBucketValues(unsigned int index,
305 uint64_t bucket_hits,
306 int64_t bucket_min,
307 int64_t bucket_max,
308 int64_t bucket_sum)
309{
310 IOReturn result;
311 IOHistogramReportValues bucket;
312 lockReporter();
313
314 if (index >= (unsigned int)_bucketCount) {
315 result = kIOReturnBadArgument;
316 goto finish;
317 }
318
319 bucket.bucket_hits = bucket_hits;
320 bucket.bucket_min = bucket_min;
321 bucket.bucket_max = bucket_max;
322 bucket.bucket_sum = bucket_sum;
323
324 result = setElementValues(element_index: index, values: (IOReportElementValues *)&bucket);
325finish:
326 unlockReporter();
327 return result;
328}
329
330int
331IOHistogramReporter::tallyValue(int64_t value)
332{
333 int result = -1;
334 int cnt = 0, element_index = 0;
335 int64_t sum = 0;
336 IOHistogramReportValues hist_values;
337
338 lockReporter();
339
340 // Iterate over _bucketCount minus one to make last bucket of infinite width
341 for (cnt = 0; cnt < _bucketCount - 1; cnt++) {
342 if (value <= _bucketBounds[cnt]) {
343 break;
344 }
345 }
346
347 element_index = cnt;
348
349 if (copyElementValues(element_index, elementValues: (IOReportElementValues *)&hist_values) != kIOReturnSuccess) {
350 goto finish;
351 }
352
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
357 }
358
359 // update all values
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;
364 }
365 if (os_add_overflow(hist_values.bucket_sum, value, &sum)) {
366 hist_values.bucket_sum = INT64_MAX;
367 } else {
368 hist_values.bucket_sum = sum;
369 }
370 hist_values.bucket_hits++;
371
372 if (setElementValues(element_index, values: (IOReportElementValues *)&hist_values)
373 != kIOReturnSuccess) {
374 goto finish;
375 }
376
377 // success!
378 result = element_index;
379
380finish:
381 unlockReporter();
382 return result;
383}
384
385/* static */ OSPtr<IOReportLegendEntry>
386IOHistogramReporter::createLegend(uint64_t channelID,
387 const char *channelName,
388 int segmentCount,
389 IOHistogramSegmentConfig *config,
390 IOReportCategories categories,
391 IOReportUnit unit)
392{
393 OSSharedPtr<IOReportLegendEntry> legendEntry;
394 OSSharedPtr<OSData> tmpConfigData;
395 OSDictionary *tmpDict; // no refcount
396 int cnt;
397
398 IOReportChannelType channelType = {
399 .categories = categories,
400 .report_format = kIOReportFormatHistogram,
401 .nelements = 0,
402 .element_idx = 0
403 };
404
405 for (cnt = 0; cnt < segmentCount; cnt++) {
406 channelType.nelements += config[cnt].segment_bucket_count;
407 }
408
409 legendEntry = IOReporter::legendWith(channelIDs: &channelID, channelNames: &channelName, channelCount: 1, channelType, unit);
410 if (!legendEntry) {
411 return nullptr;
412 }
413
414 PREFL_MEMOP_PANIC(segmentCount, IOHistogramSegmentConfig);
415 tmpConfigData = OSData::withBytes(bytes: config,
416 numBytes: (unsigned)segmentCount *
417 sizeof(IOHistogramSegmentConfig));
418 if (!tmpConfigData) {
419 return nullptr;
420 }
421
422 tmpDict = OSDynamicCast(OSDictionary,
423 legendEntry->getObject(kIOReportLegendInfoKey));
424 if (!tmpDict) {
425 return nullptr;
426 }
427
428 tmpDict->setObject(kIOReportLegendConfigKey, anObject: tmpConfigData.get());
429
430 return legendEntry;
431}
432