165 lines · c
1/*2 * Copyright 2013, Michael Ellerman, IBM Corp.3 * Licensed under GPLv2.4 */5 6#define _GNU_SOURCE7 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 17extern void thirty_two_instruction_loop_with_ll_sc(u64 loops, u64 *ll_sc_target);18 19static void setup_event(struct event *e, u64 config, int type, char *name)20{21 event_init_opts(e, config, type, 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 29static int do_count_loop(struct event *events, u64 instructions,30 u64 overhead, bool report)31{32 s64 difference, expected;33 double percentage;34 u64 dummy;35 36 prctl(PR_TASK_PERF_EVENTS_ENABLE);37 38 /* Run for 1M instructions */39 thirty_two_instruction_loop_with_ll_sc(instructions >> 5, &dummy);40 41 prctl(PR_TASK_PERF_EVENTS_DISABLE);42 43 event_read(&events[0]);44 event_read(&events[1]);45 event_read(&events[2]);46 47 expected = instructions + overhead + (events[2].result.value * 10);48 difference = events[0].result.value - expected;49 percentage = (double)difference / events[0].result.value * 100;50 51 if (report) {52 printf("-----\n");53 event_report(&events[0]);54 event_report(&events[1]);55 event_report(&events[2]);56 57 printf("Looped for %llu instructions, overhead %llu\n", instructions, overhead);58 printf("Expected %llu\n", expected);59 printf("Actual %llu\n", events[0].result.value);60 printf("Delta %lld, %f%%\n", difference, percentage);61 }62 63 event_reset(&events[0]);64 event_reset(&events[1]);65 event_reset(&events[2]);66 67 if (difference < 0)68 difference = -difference;69 70 /* Tolerate a difference below 0.0001 % */71 difference *= 10000 * 100;72 if (difference / events[0].result.value)73 return -1;74 75 return 0;76}77 78/* Count how many instructions it takes to do a null loop */79static u64 determine_overhead(struct event *events)80{81 u64 current, overhead;82 int i;83 84 do_count_loop(events, 0, 0, false);85 overhead = events[0].result.value;86 87 for (i = 0; i < 100; i++) {88 do_count_loop(events, 0, 0, false);89 current = events[0].result.value;90 if (current < overhead) {91 printf("Replacing overhead %llu with %llu\n", overhead, current);92 overhead = current;93 }94 }95 96 return overhead;97}98 99#define PM_MRK_STCX_FAIL 0x03e158100#define PM_STCX_FAIL 0x01e058101 102static int test_body(void)103{104 struct event events[3];105 u64 overhead;106 107 // The STCX_FAIL event we use works on Power8 or later108 SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07));109 110 setup_event(&events[0], PERF_COUNT_HW_INSTRUCTIONS, PERF_TYPE_HARDWARE, "instructions");111 setup_event(&events[1], PERF_COUNT_HW_CPU_CYCLES, PERF_TYPE_HARDWARE, "cycles");112 setup_event(&events[2], PM_STCX_FAIL, PERF_TYPE_RAW, "stcx_fail");113 114 if (event_open(&events[0])) {115 perror("perf_event_open");116 return -1;117 }118 119 if (event_open_with_group(&events[1], events[0].fd)) {120 perror("perf_event_open");121 return -1;122 }123 124 if (event_open_with_group(&events[2], events[0].fd)) {125 perror("perf_event_open");126 return -1;127 }128 129 overhead = determine_overhead(events);130 printf("Overhead of null loop: %llu instructions\n", overhead);131 132 /* Run for 1Mi instructions */133 FAIL_IF(do_count_loop(events, 1000000, overhead, true));134 135 /* Run for 10Mi instructions */136 FAIL_IF(do_count_loop(events, 10000000, overhead, true));137 138 /* Run for 100Mi instructions */139 FAIL_IF(do_count_loop(events, 100000000, overhead, true));140 141 /* Run for 1Bi instructions */142 FAIL_IF(do_count_loop(events, 1000000000, overhead, true));143 144 /* Run for 16Bi instructions */145 FAIL_IF(do_count_loop(events, 16000000000, overhead, true));146 147 /* Run for 64Bi instructions */148 FAIL_IF(do_count_loop(events, 64000000000, overhead, true));149 150 event_close(&events[0]);151 event_close(&events[1]);152 153 return 0;154}155 156static int count_ll_sc(void)157{158 return eat_cpu(test_body);159}160 161int main(void)162{163 return test_harness(count_ll_sc, "count_ll_sc");164}165