18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * sampleip: sample instruction pointer and frequency count in a BPF map.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2016 Netflix, Inc.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <stdio.h>
88c2ecf20Sopenharmony_ci#include <stdlib.h>
98c2ecf20Sopenharmony_ci#include <unistd.h>
108c2ecf20Sopenharmony_ci#include <errno.h>
118c2ecf20Sopenharmony_ci#include <signal.h>
128c2ecf20Sopenharmony_ci#include <string.h>
138c2ecf20Sopenharmony_ci#include <linux/perf_event.h>
148c2ecf20Sopenharmony_ci#include <linux/ptrace.h>
158c2ecf20Sopenharmony_ci#include <linux/bpf.h>
168c2ecf20Sopenharmony_ci#include <bpf/bpf.h>
178c2ecf20Sopenharmony_ci#include <bpf/libbpf.h>
188c2ecf20Sopenharmony_ci#include "perf-sys.h"
198c2ecf20Sopenharmony_ci#include "trace_helpers.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define DEFAULT_FREQ	99
228c2ecf20Sopenharmony_ci#define DEFAULT_SECS	5
238c2ecf20Sopenharmony_ci#define MAX_IPS		8192
248c2ecf20Sopenharmony_ci#define PAGE_OFFSET	0xffff880000000000
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic int map_fd;
278c2ecf20Sopenharmony_cistatic int nr_cpus;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic void usage(void)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	printf("USAGE: sampleip [-F freq] [duration]\n");
328c2ecf20Sopenharmony_ci	printf("       -F freq    # sample frequency (Hertz), default 99\n");
338c2ecf20Sopenharmony_ci	printf("       duration   # sampling duration (seconds), default 5\n");
348c2ecf20Sopenharmony_ci}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic int sampling_start(int freq, struct bpf_program *prog,
378c2ecf20Sopenharmony_ci			  struct bpf_link *links[])
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	int i, pmu_fd;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	struct perf_event_attr pe_sample_attr = {
428c2ecf20Sopenharmony_ci		.type = PERF_TYPE_SOFTWARE,
438c2ecf20Sopenharmony_ci		.freq = 1,
448c2ecf20Sopenharmony_ci		.sample_period = freq,
458c2ecf20Sopenharmony_ci		.config = PERF_COUNT_SW_CPU_CLOCK,
468c2ecf20Sopenharmony_ci		.inherit = 1,
478c2ecf20Sopenharmony_ci	};
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	for (i = 0; i < nr_cpus; i++) {
508c2ecf20Sopenharmony_ci		pmu_fd = sys_perf_event_open(&pe_sample_attr, -1 /* pid */, i,
518c2ecf20Sopenharmony_ci					    -1 /* group_fd */, 0 /* flags */);
528c2ecf20Sopenharmony_ci		if (pmu_fd < 0) {
538c2ecf20Sopenharmony_ci			fprintf(stderr, "ERROR: Initializing perf sampling\n");
548c2ecf20Sopenharmony_ci			return 1;
558c2ecf20Sopenharmony_ci		}
568c2ecf20Sopenharmony_ci		links[i] = bpf_program__attach_perf_event(prog, pmu_fd);
578c2ecf20Sopenharmony_ci		if (libbpf_get_error(links[i])) {
588c2ecf20Sopenharmony_ci			fprintf(stderr, "ERROR: Attach perf event\n");
598c2ecf20Sopenharmony_ci			links[i] = NULL;
608c2ecf20Sopenharmony_ci			close(pmu_fd);
618c2ecf20Sopenharmony_ci			return 1;
628c2ecf20Sopenharmony_ci		}
638c2ecf20Sopenharmony_ci	}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	return 0;
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic void sampling_end(struct bpf_link *links[])
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	int i;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	for (i = 0; i < nr_cpus; i++)
738c2ecf20Sopenharmony_ci		bpf_link__destroy(links[i]);
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistruct ipcount {
778c2ecf20Sopenharmony_ci	__u64 ip;
788c2ecf20Sopenharmony_ci	__u32 count;
798c2ecf20Sopenharmony_ci};
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci/* used for sorting */
828c2ecf20Sopenharmony_cistruct ipcount counts[MAX_IPS];
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic int count_cmp(const void *p1, const void *p2)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	return ((struct ipcount *)p1)->count - ((struct ipcount *)p2)->count;
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic void print_ip_map(int fd)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	struct ksym *sym;
928c2ecf20Sopenharmony_ci	__u64 key, next_key;
938c2ecf20Sopenharmony_ci	__u32 value;
948c2ecf20Sopenharmony_ci	int i, max;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	printf("%-19s %-32s %s\n", "ADDR", "KSYM", "COUNT");
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	/* fetch IPs and counts */
998c2ecf20Sopenharmony_ci	key = 0, i = 0;
1008c2ecf20Sopenharmony_ci	while (bpf_map_get_next_key(fd, &key, &next_key) == 0) {
1018c2ecf20Sopenharmony_ci		bpf_map_lookup_elem(fd, &next_key, &value);
1028c2ecf20Sopenharmony_ci		counts[i].ip = next_key;
1038c2ecf20Sopenharmony_ci		counts[i++].count = value;
1048c2ecf20Sopenharmony_ci		key = next_key;
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci	max = i;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	/* sort and print */
1098c2ecf20Sopenharmony_ci	qsort(counts, max, sizeof(struct ipcount), count_cmp);
1108c2ecf20Sopenharmony_ci	for (i = 0; i < max; i++) {
1118c2ecf20Sopenharmony_ci		if (counts[i].ip > PAGE_OFFSET) {
1128c2ecf20Sopenharmony_ci			sym = ksym_search(counts[i].ip);
1138c2ecf20Sopenharmony_ci			if (!sym) {
1148c2ecf20Sopenharmony_ci				printf("ksym not found. Is kallsyms loaded?\n");
1158c2ecf20Sopenharmony_ci				continue;
1168c2ecf20Sopenharmony_ci			}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci			printf("0x%-17llx %-32s %u\n", counts[i].ip, sym->name,
1198c2ecf20Sopenharmony_ci			       counts[i].count);
1208c2ecf20Sopenharmony_ci		} else {
1218c2ecf20Sopenharmony_ci			printf("0x%-17llx %-32s %u\n", counts[i].ip, "(user)",
1228c2ecf20Sopenharmony_ci			       counts[i].count);
1238c2ecf20Sopenharmony_ci		}
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	if (max == MAX_IPS) {
1278c2ecf20Sopenharmony_ci		printf("WARNING: IP hash was full (max %d entries); ", max);
1288c2ecf20Sopenharmony_ci		printf("may have dropped samples\n");
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic void int_exit(int sig)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	printf("\n");
1358c2ecf20Sopenharmony_ci	print_ip_map(map_fd);
1368c2ecf20Sopenharmony_ci	exit(0);
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ciint main(int argc, char **argv)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	int opt, freq = DEFAULT_FREQ, secs = DEFAULT_SECS, error = 1;
1428c2ecf20Sopenharmony_ci	struct bpf_object *obj = NULL;
1438c2ecf20Sopenharmony_ci	struct bpf_program *prog;
1448c2ecf20Sopenharmony_ci	struct bpf_link **links;
1458c2ecf20Sopenharmony_ci	char filename[256];
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	/* process arguments */
1488c2ecf20Sopenharmony_ci	while ((opt = getopt(argc, argv, "F:h")) != -1) {
1498c2ecf20Sopenharmony_ci		switch (opt) {
1508c2ecf20Sopenharmony_ci		case 'F':
1518c2ecf20Sopenharmony_ci			freq = atoi(optarg);
1528c2ecf20Sopenharmony_ci			break;
1538c2ecf20Sopenharmony_ci		case 'h':
1548c2ecf20Sopenharmony_ci		default:
1558c2ecf20Sopenharmony_ci			usage();
1568c2ecf20Sopenharmony_ci			return 0;
1578c2ecf20Sopenharmony_ci		}
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci	if (argc - optind == 1)
1608c2ecf20Sopenharmony_ci		secs = atoi(argv[optind]);
1618c2ecf20Sopenharmony_ci	if (freq == 0 || secs == 0) {
1628c2ecf20Sopenharmony_ci		usage();
1638c2ecf20Sopenharmony_ci		return 1;
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	/* initialize kernel symbol translation */
1678c2ecf20Sopenharmony_ci	if (load_kallsyms()) {
1688c2ecf20Sopenharmony_ci		fprintf(stderr, "ERROR: loading /proc/kallsyms\n");
1698c2ecf20Sopenharmony_ci		return 2;
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	/* create perf FDs for each CPU */
1738c2ecf20Sopenharmony_ci	nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
1748c2ecf20Sopenharmony_ci	links = calloc(nr_cpus, sizeof(struct bpf_link *));
1758c2ecf20Sopenharmony_ci	if (!links) {
1768c2ecf20Sopenharmony_ci		fprintf(stderr, "ERROR: malloc of links\n");
1778c2ecf20Sopenharmony_ci		goto cleanup;
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
1818c2ecf20Sopenharmony_ci	obj = bpf_object__open_file(filename, NULL);
1828c2ecf20Sopenharmony_ci	if (libbpf_get_error(obj)) {
1838c2ecf20Sopenharmony_ci		fprintf(stderr, "ERROR: opening BPF object file failed\n");
1848c2ecf20Sopenharmony_ci		obj = NULL;
1858c2ecf20Sopenharmony_ci		goto cleanup;
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	prog = bpf_object__find_program_by_name(obj, "do_sample");
1898c2ecf20Sopenharmony_ci	if (!prog) {
1908c2ecf20Sopenharmony_ci		fprintf(stderr, "ERROR: finding a prog in obj file failed\n");
1918c2ecf20Sopenharmony_ci		goto cleanup;
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	/* load BPF program */
1958c2ecf20Sopenharmony_ci	if (bpf_object__load(obj)) {
1968c2ecf20Sopenharmony_ci		fprintf(stderr, "ERROR: loading BPF object file failed\n");
1978c2ecf20Sopenharmony_ci		goto cleanup;
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	map_fd = bpf_object__find_map_fd_by_name(obj, "ip_map");
2018c2ecf20Sopenharmony_ci	if (map_fd < 0) {
2028c2ecf20Sopenharmony_ci		fprintf(stderr, "ERROR: finding a map in obj file failed\n");
2038c2ecf20Sopenharmony_ci		goto cleanup;
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	signal(SIGINT, int_exit);
2078c2ecf20Sopenharmony_ci	signal(SIGTERM, int_exit);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	/* do sampling */
2108c2ecf20Sopenharmony_ci	printf("Sampling at %d Hertz for %d seconds. Ctrl-C also ends.\n",
2118c2ecf20Sopenharmony_ci	       freq, secs);
2128c2ecf20Sopenharmony_ci	if (sampling_start(freq, prog, links) != 0)
2138c2ecf20Sopenharmony_ci		goto cleanup;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	sleep(secs);
2168c2ecf20Sopenharmony_ci	error = 0;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cicleanup:
2198c2ecf20Sopenharmony_ci	sampling_end(links);
2208c2ecf20Sopenharmony_ci	/* output sample counts */
2218c2ecf20Sopenharmony_ci	if (!error)
2228c2ecf20Sopenharmony_ci		print_ip_map(map_fd);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	free(links);
2258c2ecf20Sopenharmony_ci	bpf_object__close(obj);
2268c2ecf20Sopenharmony_ci	return error;
2278c2ecf20Sopenharmony_ci}
228