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
41 OSDefineMetaClassAndStructors(IOHistogramReporter, IOReporter);
42 
43 /* static */
44 OSSharedPtr<IOHistogramReporter>
45 IOHistogramReporter::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(channelName);
59 		}
60 
61 		if (reporter->initWith(reportingService, categories,
62 		    channelID, tmpChannelName.get(),
63 		    unit, nSegments, config)) {
64 			return reporter;
65 		}
66 	}
67 
68 	return nullptr;
69 }
70 
71 
72 bool
73 IOHistogramReporter::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(_histogramSegmentsConfig, config, 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(cnt2, (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(cnt2, (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 				// FIXME: Could use pow() but not sure how to include math.h
222 				int64_t power = 1;
223 				int exponent = cnt2 + 1;
224 				while (exponent) {
225 					power *= _histogramSegmentsConfig[cnt].base_bucket_width;
226 					exponent--;
227 				}
228 				bucketBound = power;
229 			} else {
230 				bucketBound = _histogramSegmentsConfig[cnt].base_bucket_width *
231 				    ((unsigned)cnt2 + 1);
232 			}
233 
234 			if (previousBucketBound >= bucketBound) {
235 				IORLOG("Histogram ERROR: bucket bound does not increase linearly (segment %u / bucket # %u)",
236 				    cnt, cnt2);
237 				result = false;
238 				goto finish;
239 			}
240 
241 			_bucketBounds[cnt3] = bucketBound;
242 			// IORLOG("_bucketBounds[%u] = %llu", cnt3, bucketBound);
243 			previousBucketBound = _bucketBounds[cnt3];
244 			cnt3++;
245 		}
246 	}
247 
248 	// success
249 	result = true;
250 
251 finish:
252 	return result;
253 }
254 
255 
256 void
257 IOHistogramReporter::free(void)
258 {
259 	if (_bucketBounds) {
260 		PREFL_MEMOP_PANIC(_nElements, int64_t);
261 		IOFreeData(_bucketBounds, (size_t)_nElements * sizeof(int64_t));
262 	}
263 	if (_histogramSegmentsConfig) {
264 		PREFL_MEMOP_PANIC(_segmentCount, IOHistogramSegmentConfig);
265 		IOFreeData(_histogramSegmentsConfig,
266 		    (size_t)_segmentCount * sizeof(IOHistogramSegmentConfig));
267 	}
268 
269 	super::free();
270 }
271 
272 
273 OSSharedPtr<IOReportLegendEntry>
274 IOHistogramReporter::handleCreateLegend(void)
275 {
276 	OSSharedPtr<IOReportLegendEntry>        legendEntry;
277 	OSSharedPtr<OSData>                     tmpConfigData;
278 	OSDictionary                            *tmpDict;   // no refcount
279 
280 	legendEntry = super::handleCreateLegend();
281 	if (!legendEntry) {
282 		return nullptr;
283 	}
284 
285 	PREFL_MEMOP_PANIC(_segmentCount, IOHistogramSegmentConfig);
286 	tmpConfigData = OSData::withBytes(_histogramSegmentsConfig,
287 	    (unsigned)_segmentCount *
288 	    sizeof(IOHistogramSegmentConfig));
289 	if (!tmpConfigData) {
290 		return nullptr;
291 	}
292 
293 	tmpDict = OSDynamicCast(OSDictionary,
294 	    legendEntry->getObject(kIOReportLegendInfoKey));
295 	if (!tmpDict) {
296 		return nullptr;
297 	}
298 
299 	tmpDict->setObject(kIOReportLegendConfigKey, tmpConfigData.get());
300 
301 	return legendEntry;
302 }
303 
304 IOReturn
305 IOHistogramReporter::overrideBucketValues(unsigned int index,
306     uint64_t bucket_hits,
307     int64_t bucket_min,
308     int64_t bucket_max,
309     int64_t bucket_sum)
310 {
311 	IOReturn result;
312 	IOHistogramReportValues bucket;
313 	lockReporter();
314 
315 	if (index >= (unsigned int)_bucketCount) {
316 		result = kIOReturnBadArgument;
317 		goto finish;
318 	}
319 
320 	bucket.bucket_hits = bucket_hits;
321 	bucket.bucket_min = bucket_min;
322 	bucket.bucket_max = bucket_max;
323 	bucket.bucket_sum = bucket_sum;
324 
325 	result = setElementValues(index, (IOReportElementValues *)&bucket);
326 finish:
327 	unlockReporter();
328 	return result;
329 }
330 
331 int
332 IOHistogramReporter::tallyValue(int64_t value)
333 {
334 	int result = -1;
335 	int cnt = 0, element_index = 0;
336 	int64_t sum = 0;
337 	IOHistogramReportValues hist_values;
338 
339 	lockReporter();
340 
341 	// Iterate over _bucketCount minus one to make last bucket of infinite width
342 	for (cnt = 0; cnt < _bucketCount - 1; cnt++) {
343 		if (value <= _bucketBounds[cnt]) {
344 			break;
345 		}
346 	}
347 
348 	element_index = cnt;
349 
350 	if (copyElementValues(element_index, (IOReportElementValues *)&hist_values) != kIOReturnSuccess) {
351 		goto finish;
352 	}
353 
354 	// init stats on first hit
355 	if (hist_values.bucket_hits == 0) {
356 		hist_values.bucket_min = hist_values.bucket_max = value;
357 		hist_values.bucket_sum = 0; // += is below
358 	}
359 
360 	// update all values
361 	if (value < hist_values.bucket_min) {
362 		hist_values.bucket_min = value;
363 	} else if (value > hist_values.bucket_max) {
364 		hist_values.bucket_max = value;
365 	}
366 	if (os_add_overflow(hist_values.bucket_sum, value, &sum)) {
367 		hist_values.bucket_sum = INT64_MAX;
368 	} else {
369 		hist_values.bucket_sum = sum;
370 	}
371 	hist_values.bucket_hits++;
372 
373 	if (setElementValues(element_index, (IOReportElementValues *)&hist_values)
374 	    != kIOReturnSuccess) {
375 		goto finish;
376 	}
377 
378 	// success!
379 	result = element_index;
380 
381 finish:
382 	unlockReporter();
383 	return result;
384 }
385 
386 /* static */ OSPtr<IOReportLegendEntry>
387 IOHistogramReporter::createLegend(uint64_t channelID,
388     const char *channelName,
389     int segmentCount,
390     IOHistogramSegmentConfig *config,
391     IOReportCategories categories,
392     IOReportUnit unit)
393 {
394 	OSSharedPtr<IOReportLegendEntry>        legendEntry;
395 	OSSharedPtr<OSData>                     tmpConfigData;
396 	OSDictionary                            *tmpDict;   // no refcount
397 	int                                                                     cnt;
398 
399 	IOReportChannelType channelType = {
400 		.categories = categories,
401 		.report_format = kIOReportFormatHistogram,
402 		.nelements = 0,
403 		.element_idx = 0
404 	};
405 
406 	for (cnt = 0; cnt < segmentCount; cnt++) {
407 		channelType.nelements += config[cnt].segment_bucket_count;
408 	}
409 
410 	legendEntry = IOReporter::legendWith(&channelID, &channelName, 1, channelType, unit);
411 	if (!legendEntry) {
412 		return nullptr;
413 	}
414 
415 	PREFL_MEMOP_PANIC(segmentCount, IOHistogramSegmentConfig);
416 	tmpConfigData = OSData::withBytes(config,
417 	    (unsigned)segmentCount *
418 	    sizeof(IOHistogramSegmentConfig));
419 	if (!tmpConfigData) {
420 		return nullptr;
421 	}
422 
423 	tmpDict = OSDynamicCast(OSDictionary,
424 	    legendEntry->getObject(kIOReportLegendInfoKey));
425 	if (!tmpDict) {
426 		return nullptr;
427 	}
428 
429 	tmpDict->setObject(kIOReportLegendConfigKey, tmpConfigData.get());
430 
431 	return legendEntry;
432 }
433