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