Home | History | Annotate | Download | only in pmu
      1 /*
      2  * Copyright 2013, Michael Ellerman, IBM Corp.
      3  * Licensed under GPLv2.
      4  */
      5 
      6 #define _GNU_SOURCE
      7 
      8 #include <stdio.h>
      9 #include <stdbool.h>
     10 #include <string.h>
     11 #include <sys/prctl.h>
     12 
     13 #include "event.h"
     14 #include "utils.h"
     15 #include "lib.h"
     16 
     17 extern void thirty_two_instruction_loop(u64 loops);
     18 
     19 static void setup_event(struct event *e, u64 config, char *name)
     20 {
     21 	event_init_opts(e, config, PERF_TYPE_HARDWARE, name);
     22 
     23 	e->attr.disabled = 1;
     24 	e->attr.exclude_kernel = 1;
     25 	e->attr.exclude_hv = 1;
     26 	e->attr.exclude_idle = 1;
     27 }
     28 
     29 static int do_count_loop(struct event *events, u64 instructions,
     30 			 u64 overhead, bool report)
     31 {
     32 	s64 difference, expected;
     33 	double percentage;
     34 
     35 	prctl(PR_TASK_PERF_EVENTS_ENABLE);
     36 
     37 	/* Run for 1M instructions */
     38 	thirty_two_instruction_loop(instructions >> 5);
     39 
     40 	prctl(PR_TASK_PERF_EVENTS_DISABLE);
     41 
     42 	event_read(&events[0]);
     43 	event_read(&events[1]);
     44 
     45 	expected = instructions + overhead;
     46 	difference = events[0].result.value - expected;
     47 	percentage = (double)difference / events[0].result.value * 100;
     48 
     49 	if (report) {
     50 		event_report(&events[0]);
     51 		event_report(&events[1]);
     52 
     53 		printf("Looped for %llu instructions, overhead %llu\n", instructions, overhead);
     54 		printf("Expected %llu\n", expected);
     55 		printf("Actual   %llu\n", events[0].result.value);
     56 		printf("Delta    %lld, %f%%\n", difference, percentage);
     57 	}
     58 
     59 	event_reset(&events[0]);
     60 	event_reset(&events[1]);
     61 
     62 	if (difference < 0)
     63 		difference = -difference;
     64 
     65 	/* Tolerate a difference below 0.0001 % */
     66 	difference *= 10000 * 100;
     67 	if (difference / events[0].result.value)
     68 		return -1;
     69 
     70 	return 0;
     71 }
     72 
     73 /* Count how many instructions it takes to do a null loop */
     74 static u64 determine_overhead(struct event *events)
     75 {
     76 	u64 current, overhead;
     77 	int i;
     78 
     79 	do_count_loop(events, 0, 0, false);
     80 	overhead = events[0].result.value;
     81 
     82 	for (i = 0; i < 100; i++) {
     83 		do_count_loop(events, 0, 0, false);
     84 		current = events[0].result.value;
     85 		if (current < overhead) {
     86 			printf("Replacing overhead %llu with %llu\n", overhead, current);
     87 			overhead = current;
     88 		}
     89 	}
     90 
     91 	return overhead;
     92 }
     93 
     94 static int test_body(void)
     95 {
     96 	struct event events[2];
     97 	u64 overhead;
     98 
     99 	setup_event(&events[0], PERF_COUNT_HW_INSTRUCTIONS, "instructions");
    100 	setup_event(&events[1], PERF_COUNT_HW_CPU_CYCLES, "cycles");
    101 
    102 	if (event_open(&events[0])) {
    103 		perror("perf_event_open");
    104 		return -1;
    105 	}
    106 
    107 	if (event_open_with_group(&events[1], events[0].fd)) {
    108 		perror("perf_event_open");
    109 		return -1;
    110 	}
    111 
    112 	overhead = determine_overhead(events);
    113 	printf("Overhead of null loop: %llu instructions\n", overhead);
    114 
    115 	/* Run for 1Mi instructions */
    116 	FAIL_IF(do_count_loop(events, 1000000, overhead, true));
    117 
    118 	/* Run for 10Mi instructions */
    119 	FAIL_IF(do_count_loop(events, 10000000, overhead, true));
    120 
    121 	/* Run for 100Mi instructions */
    122 	FAIL_IF(do_count_loop(events, 100000000, overhead, true));
    123 
    124 	/* Run for 1Bi instructions */
    125 	FAIL_IF(do_count_loop(events, 1000000000, overhead, true));
    126 
    127 	/* Run for 16Bi instructions */
    128 	FAIL_IF(do_count_loop(events, 16000000000, overhead, true));
    129 
    130 	/* Run for 64Bi instructions */
    131 	FAIL_IF(do_count_loop(events, 64000000000, overhead, true));
    132 
    133 	event_close(&events[0]);
    134 	event_close(&events[1]);
    135 
    136 	return 0;
    137 }
    138 
    139 static int count_instructions(void)
    140 {
    141 	return eat_cpu(test_body);
    142 }
    143 
    144 int main(void)
    145 {
    146 	return test_harness(count_instructions, "count_instructions");
    147 }
    148