| 
									
										
										
										
											2013-08-06 17:42:37 +10:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright 2013, Michael Ellerman, IBM Corp. | 
					
						
							|  |  |  |  * Licensed under GPLv2. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define _GNU_SOURCE
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <stdio.h>
 | 
					
						
							|  |  |  | #include <stdbool.h>
 | 
					
						
							|  |  |  | #include <string.h>
 | 
					
						
							|  |  |  | #include <sys/prctl.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "event.h"
 | 
					
						
							|  |  |  | #include "utils.h"
 | 
					
						
							| 
									
										
										
										
											2014-07-23 17:31:35 +10:00
										 |  |  | #include "lib.h"
 | 
					
						
							| 
									
										
										
										
											2013-08-06 17:42:37 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | extern void thirty_two_instruction_loop(u64 loops); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void setup_event(struct event *e, u64 config, char *name) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	event_init_opts(e, config, PERF_TYPE_HARDWARE, name); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	e->attr.disabled = 1; | 
					
						
							|  |  |  | 	e->attr.exclude_kernel = 1; | 
					
						
							|  |  |  | 	e->attr.exclude_hv = 1; | 
					
						
							|  |  |  | 	e->attr.exclude_idle = 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int do_count_loop(struct event *events, u64 instructions, | 
					
						
							|  |  |  | 			 u64 overhead, bool report) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	s64 difference, expected; | 
					
						
							|  |  |  | 	double percentage; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	prctl(PR_TASK_PERF_EVENTS_ENABLE); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Run for 1M instructions */ | 
					
						
							|  |  |  | 	thirty_two_instruction_loop(instructions >> 5); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	prctl(PR_TASK_PERF_EVENTS_DISABLE); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	event_read(&events[0]); | 
					
						
							|  |  |  | 	event_read(&events[1]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	expected = instructions + overhead; | 
					
						
							|  |  |  | 	difference = events[0].result.value - expected; | 
					
						
							|  |  |  | 	percentage = (double)difference / events[0].result.value * 100; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (report) { | 
					
						
							|  |  |  | 		event_report(&events[0]); | 
					
						
							|  |  |  | 		event_report(&events[1]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		printf("Looped for %llu instructions, overhead %llu\n", instructions, overhead); | 
					
						
							|  |  |  | 		printf("Expected %llu\n", expected); | 
					
						
							|  |  |  | 		printf("Actual   %llu\n", events[0].result.value); | 
					
						
							|  |  |  | 		printf("Delta    %lld, %f%%\n", difference, percentage); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	event_reset(&events[0]); | 
					
						
							|  |  |  | 	event_reset(&events[1]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (difference < 0) | 
					
						
							|  |  |  | 		difference = -difference; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Tolerate a difference below 0.0001 % */ | 
					
						
							|  |  |  | 	difference *= 10000 * 100; | 
					
						
							|  |  |  | 	if (difference / events[0].result.value) | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Count how many instructions it takes to do a null loop */ | 
					
						
							|  |  |  | static u64 determine_overhead(struct event *events) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u64 current, overhead; | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	do_count_loop(events, 0, 0, false); | 
					
						
							|  |  |  | 	overhead = events[0].result.value; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < 100; i++) { | 
					
						
							|  |  |  | 		do_count_loop(events, 0, 0, false); | 
					
						
							|  |  |  | 		current = events[0].result.value; | 
					
						
							|  |  |  | 		if (current < overhead) { | 
					
						
							|  |  |  | 			printf("Replacing overhead %llu with %llu\n", overhead, current); | 
					
						
							|  |  |  | 			overhead = current; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return overhead; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-23 17:31:35 +10:00
										 |  |  | static int test_body(void) | 
					
						
							| 
									
										
										
										
											2013-08-06 17:42:37 +10:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct event events[2]; | 
					
						
							|  |  |  | 	u64 overhead; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	setup_event(&events[0], PERF_COUNT_HW_INSTRUCTIONS, "instructions"); | 
					
						
							|  |  |  | 	setup_event(&events[1], PERF_COUNT_HW_CPU_CYCLES, "cycles"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (event_open(&events[0])) { | 
					
						
							|  |  |  | 		perror("perf_event_open"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (event_open_with_group(&events[1], events[0].fd)) { | 
					
						
							|  |  |  | 		perror("perf_event_open"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	overhead = determine_overhead(events); | 
					
						
							|  |  |  | 	printf("Overhead of null loop: %llu instructions\n", overhead); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-23 17:31:36 +10:00
										 |  |  | 	/* Run for 1Mi instructions */ | 
					
						
							|  |  |  | 	FAIL_IF(do_count_loop(events, 1000000, overhead, true)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Run for 10Mi instructions */ | 
					
						
							|  |  |  | 	FAIL_IF(do_count_loop(events, 10000000, overhead, true)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Run for 100Mi instructions */ | 
					
						
							|  |  |  | 	FAIL_IF(do_count_loop(events, 100000000, overhead, true)); | 
					
						
							| 
									
										
										
										
											2013-08-06 17:42:37 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-23 17:31:36 +10:00
										 |  |  | 	/* Run for 1Bi instructions */ | 
					
						
							|  |  |  | 	FAIL_IF(do_count_loop(events, 1000000000, overhead, true)); | 
					
						
							| 
									
										
										
										
											2013-08-06 17:42:37 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-23 17:31:36 +10:00
										 |  |  | 	/* Run for 16Bi instructions */ | 
					
						
							|  |  |  | 	FAIL_IF(do_count_loop(events, 16000000000, overhead, true)); | 
					
						
							| 
									
										
										
										
											2013-08-06 17:42:37 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-23 17:31:36 +10:00
										 |  |  | 	/* Run for 64Bi instructions */ | 
					
						
							|  |  |  | 	FAIL_IF(do_count_loop(events, 64000000000, overhead, true)); | 
					
						
							| 
									
										
										
										
											2013-08-06 17:42:37 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	event_close(&events[0]); | 
					
						
							|  |  |  | 	event_close(&events[1]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-23 17:31:35 +10:00
										 |  |  | static int count_instructions(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return eat_cpu(test_body); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-06 17:42:37 +10:00
										 |  |  | int main(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return test_harness(count_instructions, "count_instructions"); | 
					
						
							|  |  |  | } |