18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2013, Michael Ellerman, IBM Corp. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#define _GNU_SOURCE 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <stdio.h> 98c2ecf20Sopenharmony_ci#include <stdbool.h> 108c2ecf20Sopenharmony_ci#include <string.h> 118c2ecf20Sopenharmony_ci#include <sys/prctl.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "event.h" 148c2ecf20Sopenharmony_ci#include "utils.h" 158c2ecf20Sopenharmony_ci#include "lib.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ciextern void thirty_two_instruction_loop(u64 loops); 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic void setup_event(struct event *e, u64 config, char *name) 208c2ecf20Sopenharmony_ci{ 218c2ecf20Sopenharmony_ci event_init_opts(e, config, PERF_TYPE_HARDWARE, name); 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci e->attr.disabled = 1; 248c2ecf20Sopenharmony_ci e->attr.exclude_kernel = 1; 258c2ecf20Sopenharmony_ci e->attr.exclude_hv = 1; 268c2ecf20Sopenharmony_ci e->attr.exclude_idle = 1; 278c2ecf20Sopenharmony_ci} 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic int do_count_loop(struct event *events, u64 instructions, 308c2ecf20Sopenharmony_ci u64 overhead, bool report) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci s64 difference, expected; 338c2ecf20Sopenharmony_ci double percentage; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci prctl(PR_TASK_PERF_EVENTS_ENABLE); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci /* Run for 1M instructions */ 388c2ecf20Sopenharmony_ci thirty_two_instruction_loop(instructions >> 5); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci prctl(PR_TASK_PERF_EVENTS_DISABLE); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci event_read(&events[0]); 438c2ecf20Sopenharmony_ci event_read(&events[1]); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci expected = instructions + overhead; 468c2ecf20Sopenharmony_ci difference = events[0].result.value - expected; 478c2ecf20Sopenharmony_ci percentage = (double)difference / events[0].result.value * 100; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci if (report) { 508c2ecf20Sopenharmony_ci event_report(&events[0]); 518c2ecf20Sopenharmony_ci event_report(&events[1]); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci printf("Looped for %llu instructions, overhead %llu\n", instructions, overhead); 548c2ecf20Sopenharmony_ci printf("Expected %llu\n", expected); 558c2ecf20Sopenharmony_ci printf("Actual %llu\n", events[0].result.value); 568c2ecf20Sopenharmony_ci printf("Delta %lld, %f%%\n", difference, percentage); 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci event_reset(&events[0]); 608c2ecf20Sopenharmony_ci event_reset(&events[1]); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (difference < 0) 638c2ecf20Sopenharmony_ci difference = -difference; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci /* Tolerate a difference below 0.0001 % */ 668c2ecf20Sopenharmony_ci difference *= 10000 * 100; 678c2ecf20Sopenharmony_ci if (difference / events[0].result.value) 688c2ecf20Sopenharmony_ci return -1; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci return 0; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/* Count how many instructions it takes to do a null loop */ 748c2ecf20Sopenharmony_cistatic u64 determine_overhead(struct event *events) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci u64 current, overhead; 778c2ecf20Sopenharmony_ci int i; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci do_count_loop(events, 0, 0, false); 808c2ecf20Sopenharmony_ci overhead = events[0].result.value; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci for (i = 0; i < 100; i++) { 838c2ecf20Sopenharmony_ci do_count_loop(events, 0, 0, false); 848c2ecf20Sopenharmony_ci current = events[0].result.value; 858c2ecf20Sopenharmony_ci if (current < overhead) { 868c2ecf20Sopenharmony_ci printf("Replacing overhead %llu with %llu\n", overhead, current); 878c2ecf20Sopenharmony_ci overhead = current; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci return overhead; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic int test_body(void) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct event events[2]; 978c2ecf20Sopenharmony_ci u64 overhead; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci setup_event(&events[0], PERF_COUNT_HW_INSTRUCTIONS, "instructions"); 1008c2ecf20Sopenharmony_ci setup_event(&events[1], PERF_COUNT_HW_CPU_CYCLES, "cycles"); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (event_open(&events[0])) { 1038c2ecf20Sopenharmony_ci perror("perf_event_open"); 1048c2ecf20Sopenharmony_ci return -1; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (event_open_with_group(&events[1], events[0].fd)) { 1088c2ecf20Sopenharmony_ci perror("perf_event_open"); 1098c2ecf20Sopenharmony_ci return -1; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci overhead = determine_overhead(events); 1138c2ecf20Sopenharmony_ci printf("Overhead of null loop: %llu instructions\n", overhead); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci /* Run for 1Mi instructions */ 1168c2ecf20Sopenharmony_ci FAIL_IF(do_count_loop(events, 1000000, overhead, true)); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* Run for 10Mi instructions */ 1198c2ecf20Sopenharmony_ci FAIL_IF(do_count_loop(events, 10000000, overhead, true)); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* Run for 100Mi instructions */ 1228c2ecf20Sopenharmony_ci FAIL_IF(do_count_loop(events, 100000000, overhead, true)); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* Run for 1Bi instructions */ 1258c2ecf20Sopenharmony_ci FAIL_IF(do_count_loop(events, 1000000000, overhead, true)); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* Run for 16Bi instructions */ 1288c2ecf20Sopenharmony_ci FAIL_IF(do_count_loop(events, 16000000000, overhead, true)); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci /* Run for 64Bi instructions */ 1318c2ecf20Sopenharmony_ci FAIL_IF(do_count_loop(events, 64000000000, overhead, true)); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci event_close(&events[0]); 1348c2ecf20Sopenharmony_ci event_close(&events[1]); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci return 0; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic int count_instructions(void) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci return eat_cpu(test_body); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ciint main(void) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci return test_harness(count_instructions, "count_instructions"); 1478c2ecf20Sopenharmony_ci} 148