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 __STDC_LIMIT_MACROS     // what are the C++ equivalents?
30 #include <stdint.h>
31 
32 #include <IOKit/IOKernelReportStructs.h>
33 #include <IOKit/IOKernelReporters.h>
34 #include "IOReporterDefs.h"
35 
36 
37 #define super IOReporter
38 OSDefineMetaClassAndStructors(IOHistogramReporter, IOReporter);
39 
40 /* static */
41 IOHistogramReporter*
42 IOHistogramReporter::with(IOService *reportingService,
43     IOReportCategories categories,
44     uint64_t channelID,
45     const char *channelName,
46     IOReportUnit unit,
47     int nSegments,
48     IOHistogramSegmentConfig *config)
49 {
50 	IOHistogramReporter *reporter = new IOHistogramReporter;
51 
52 	const OSSymbol *tmpChannelName = NULL;
53 
54 	if (reporter) {
55 		if (channelName) {
56 			tmpChannelName = OSSymbol::withCString(channelName);
57 		}
58 
59 		if (reporter->initWith(reportingService, categories,
60 		    channelID, tmpChannelName,
61 		    unit, nSegments, config)) {
62 			return reporter;
63 		}
64 	}
65 	OSSafeReleaseNULL(reporter);
66 	OSSafeReleaseNULL(tmpChannelName);
67 
68 	return NULL;
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*)IOMalloc(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 *)IOMalloc(elementsSize);
160 	if (!_elements) {
161 		goto finish;
162 	}
163 	memset(_elements, 0, elementsSize);
164 
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) {
170 		goto finish;
171 	}
172 	memset(_enableCounts, 0, eCountsSize);
173 
174 	lockReporter();
175 	for (cnt2 = 0; cnt2 < _channelDimension; cnt2++) {
176 		IOHistogramReportValues hist_values;
177 		if (copyElementValues(cnt2, (IOReportElementValues*)&hist_values)) {
178 			goto finish;
179 		}
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)) {
184 			goto finish;
185 		}
186 
187 		// Setup IOReporter's channel IDs
188 		_elements[cnt2].channel_id = channelID;
189 
190 		// Setup IOReporter's reporting provider service
191 		_elements[cnt2].provider_id = _driver_id;
192 
193 		// Setup IOReporter's channel type
194 		_elements[cnt2].channel_type = _channelType;
195 		_elements[cnt2].channel_type.element_idx = cnt2;
196 
197 		//IOREPORTER_DEBUG_ELEMENT(cnt2);
198 	}
199 	unlockReporter();
200 
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) {
206 		goto finish;
207 	}
208 	memset(_bucketBounds, 0, boundsSize);
209 	_bucketCount = _nElements;
210 
211 	for (cnt = 0; cnt < _segmentCount; cnt++) {
212 		if (_histogramSegmentsConfig[cnt].segment_bucket_count > INT_MAX
213 		    || _histogramSegmentsConfig[cnt].base_bucket_width > INT_MAX) {
214 			goto finish;
215 		}
216 		for (cnt2 = 0; cnt2 < (int)_histogramSegmentsConfig[cnt].segment_bucket_count; cnt2++) {
217 			if (cnt3 >= _nElements) {
218 				IORLOG("ERROR: _bucketBounds init");
219 				result = false;
220 				goto finish;
221 			}
222 
223 			if (_histogramSegmentsConfig[cnt].scale_flag) {
224 				// FIXME: Could use pow() but not sure how to include math.h
225 				int64_t power = 1;
226 				int exponent = cnt2 + 1;
227 				while (exponent) {
228 					power *= _histogramSegmentsConfig[cnt].base_bucket_width;
229 					exponent--;
230 				}
231 				bucketBound = power;
232 			} else {
233 				bucketBound = _histogramSegmentsConfig[cnt].base_bucket_width *
234 				    ((unsigned)cnt2 + 1);
235 			}
236 
237 			if (previousBucketBound >= bucketBound) {
238 				IORLOG("Histogram ERROR: bucket bound does not increase linearly (segment %u / bucket # %u)",
239 				    cnt, cnt2);
240 				result = false;
241 				goto finish;
242 			}
243 
244 			_bucketBounds[cnt3] = bucketBound;
245 			// IORLOG("_bucketBounds[%u] = %llu", cnt3, bucketBound);
246 			previousBucketBound = _bucketBounds[cnt3];
247 			cnt3++;
248 		}
249 	}
250 
251 	// success
252 	result = true;
253 
254 finish:
255 	return result;
256 }
257 
258 
259 void
260 IOHistogramReporter::free(void)
261 {
262 	if (_bucketBounds) {
263 		PREFL_MEMOP_PANIC(_nElements, int64_t);
264 		IOFree(_bucketBounds, (size_t)_nElements * sizeof(int64_t));
265 	}
266 	if (_histogramSegmentsConfig) {
267 		PREFL_MEMOP_PANIC(_segmentCount, IOHistogramSegmentConfig);
268 		IOFree(_histogramSegmentsConfig,
269 		    (size_t)_segmentCount * sizeof(IOHistogramSegmentConfig));
270 	}
271 
272 	super::free();
273 }
274 
275 
276 IOReportLegendEntry*
277 IOHistogramReporter::handleCreateLegend(void)
278 {
279 	IOReportLegendEntry     *rval = NULL, *legendEntry = NULL;
280 	OSData                  *tmpConfigData = NULL;
281 	OSDictionary            *tmpDict;   // no refcount
282 
283 	legendEntry = super::handleCreateLegend();
284 	if (!legendEntry) {
285 		goto finish;
286 	}
287 
288 	PREFL_MEMOP_PANIC(_segmentCount, IOHistogramSegmentConfig);
289 	tmpConfigData = OSData::withBytes(_histogramSegmentsConfig,
290 	    (unsigned)_segmentCount *
291 	    sizeof(IOHistogramSegmentConfig));
292 	if (!tmpConfigData) {
293 		goto finish;
294 	}
295 
296 	tmpDict = OSDynamicCast(OSDictionary,
297 	    legendEntry->getObject(kIOReportLegendInfoKey));
298 	if (!tmpDict) {
299 		goto finish;
300 	}
301 
302 	tmpDict->setObject(kIOReportLegendConfigKey, tmpConfigData);
303 
304 	// success
305 	rval = legendEntry;
306 
307 finish:
308 	if (tmpConfigData) {
309 		tmpConfigData->release();
310 	}
311 	if (!rval && legendEntry) {
312 		legendEntry->release();
313 	}
314 
315 	return rval;
316 }
317 
318 IOReturn
319 IOHistogramReporter::overrideBucketValues(unsigned int index,
320     uint64_t bucket_hits,
321     int64_t bucket_min,
322     int64_t bucket_max,
323     int64_t bucket_sum)
324 {
325 	IOReturn result;
326 	IOHistogramReportValues bucket;
327 	lockReporter();
328 
329 	if (index >= (unsigned int)_bucketCount) {
330 		result = kIOReturnBadArgument;
331 		goto finish;
332 	}
333 
334 	bucket.bucket_hits = bucket_hits;
335 	bucket.bucket_min = bucket_min;
336 	bucket.bucket_max = bucket_max;
337 	bucket.bucket_sum = bucket_sum;
338 
339 	result = setElementValues(index, (IOReportElementValues *)&bucket);
340 finish:
341 	unlockReporter();
342 	return result;
343 }
344 
345 int
346 IOHistogramReporter::tallyValue(int64_t value)
347 {
348 	int result = -1;
349 	int cnt = 0, element_index = 0;
350 	IOHistogramReportValues hist_values;
351 
352 	lockReporter();
353 
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]) {
357 			break;
358 		}
359 	}
360 
361 	element_index = cnt;
362 
363 	if (copyElementValues(element_index, (IOReportElementValues *)&hist_values) != kIOReturnSuccess) {
364 		goto finish;
365 	}
366 
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
371 	}
372 
373 	// update all values
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;
378 	}
379 	hist_values.bucket_sum += value;
380 	hist_values.bucket_hits++;
381 
382 	if (setElementValues(element_index, (IOReportElementValues *)&hist_values)
383 	    != kIOReturnSuccess) {
384 		goto finish;
385 	}
386 
387 	// success!
388 	result = element_index;
389 
390 finish:
391 	unlockReporter();
392 	return result;
393 }
394