• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2 // Copyright (c) 2020 Wenbo Zhang
3 //
4 // Based on llcstat(8) from BCC by Teng Qin.
5 // 29-Sep-2020   Wenbo Zhang   Created this.
6 #include <argp.h>
7 #include <signal.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <linux/perf_event.h>
12 #include <asm/unistd.h>
13 #include <bpf/libbpf.h>
14 #include <bpf/bpf.h>
15 #include "llcstat.h"
16 #include "llcstat.skel.h"
17 #include "trace_helpers.h"
18 
19 struct env {
20 	int sample_period;
21 	time_t duration;
22 	bool verbose;
23 } env = {
24 	.sample_period = 100,
25 	.duration = 10,
26 };
27 
28 static volatile bool exiting;
29 
30 const char *argp_program_version = "llcstat 0.1";
31 const char *argp_program_bug_address =
32 	"https://github.com/iovisor/bcc/tree/master/libbpf-tools";
33 const char argp_program_doc[] =
34 "Summarize cache references and misses by PID.\n"
35 "\n"
36 "USAGE: llcstat [--help] [-c SAMPLE_PERIOD] [duration]\n";
37 
38 static const struct argp_option opts[] = {
39 	{ "sample_period", 'c', "SAMPLE_PERIOD", 0, "Sample one in this many "
40 	  "number of cache reference / miss events" },
41 	{ "verbose", 'v', NULL, 0, "Verbose debug output" },
42 	{ NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" },
43 	{},
44 };
45 
parse_arg(int key,char * arg,struct argp_state * state)46 static error_t parse_arg(int key, char *arg, struct argp_state *state)
47 {
48 	static int pos_args;
49 
50 	switch (key) {
51 	case 'h':
52 		argp_state_help(state, stderr, ARGP_HELP_STD_HELP);
53 		break;
54 	case 'v':
55 		env.verbose = true;
56 		break;
57 	case 'c':
58 		errno = 0;
59 		env.sample_period = strtol(arg, NULL, 10);
60 		if (errno) {
61 			fprintf(stderr, "invalid sample period\n");
62 			argp_usage(state);
63 		}
64 		break;
65 	case ARGP_KEY_ARG:
66 		if (pos_args++) {
67 			fprintf(stderr,
68 				"unrecognized positional argument: %s\n", arg);
69 			argp_usage(state);
70 		}
71 		errno = 0;
72 		env.duration = strtol(arg, NULL, 10);
73 		if (errno) {
74 			fprintf(stderr, "invalid duration\n");
75 			argp_usage(state);
76 		}
77 		break;
78 	default:
79 		return ARGP_ERR_UNKNOWN;
80 	}
81 	return 0;
82 }
83 
84 static int nr_cpus;
85 
open_and_attach_perf_event(__u64 config,int period,struct bpf_program * prog,struct bpf_link * links[])86 static int open_and_attach_perf_event(__u64 config, int period,
87 				struct bpf_program *prog,
88 				struct bpf_link *links[])
89 {
90 	struct perf_event_attr attr = {
91 		.type = PERF_TYPE_HARDWARE,
92 		.freq = 0,
93 		.sample_period = period,
94 		.config = config,
95 	};
96 	int i, fd;
97 
98 	for (i = 0; i < nr_cpus; i++) {
99 		fd = syscall(__NR_perf_event_open, &attr, -1, i, -1, 0);
100 		if (fd < 0) {
101 			/* Ignore CPU that is offline */
102 			if (errno == ENODEV)
103 				continue;
104 			fprintf(stderr, "failed to init perf sampling: %s\n",
105 				strerror(errno));
106 			return -1;
107 		}
108 		links[i] = bpf_program__attach_perf_event(prog, fd);
109 		if (!links[i]) {
110 			fprintf(stderr, "failed to attach perf event on cpu: %d\n", i);
111 			close(fd);
112 			return -1;
113 		}
114 	}
115 	return 0;
116 }
117 
libbpf_print_fn(enum libbpf_print_level level,const char * format,va_list args)118 static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
119 {
120 	if (level == LIBBPF_DEBUG && !env.verbose)
121 		return 0;
122 	return vfprintf(stderr, format, args);
123 }
124 
sig_handler(int sig)125 static void sig_handler(int sig)
126 {
127 	exiting = true;
128 }
129 
print_map(struct bpf_map * map)130 static void print_map(struct bpf_map *map)
131 {
132 	__u64 total_ref = 0, total_miss = 0, total_hit, hit;
133 	__u64 lookup_key = -1, next_key;
134 	int err, fd = bpf_map__fd(map);
135 	struct info info;
136 	__u32 pid, cpu;
137 
138 	while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) {
139 		err = bpf_map_lookup_elem(fd, &next_key, &info);
140 		if (err < 0) {
141 			fprintf(stderr, "failed to lookup infos: %d\n", err);
142 			return;
143 		}
144 		hit = info.ref > info.miss ? info.ref - info.miss : 0;
145 		pid = next_key >> 32;
146 		cpu = next_key;
147 		printf("%-8u %-16s %-4u %12llu %12llu %6.2f%%\n", pid, info.comm,
148 			cpu, info.ref, info.miss, info.ref > 0 ?
149 			hit * 1.0 / info.ref * 100 : 0);
150 		total_miss += info.miss;
151 		total_ref += info.ref;
152 		lookup_key = next_key;
153 	}
154 	total_hit = total_ref > total_miss ? total_ref - total_miss : 0;
155 	printf("Total References: %llu Total Misses: %llu Hit Rate: %.2f%%\n",
156 		total_ref, total_miss, total_ref > 0 ?
157 		total_hit * 1.0 / total_ref * 100 : 0);
158 
159 	lookup_key = -1;
160 	while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) {
161 		err = bpf_map_delete_elem(fd, &next_key);
162 		if (err < 0) {
163 			fprintf(stderr, "failed to cleanup infos: %d\n", err);
164 			return;
165 		}
166 		lookup_key = next_key;
167 	}
168 }
169 
main(int argc,char ** argv)170 int main(int argc, char **argv)
171 {
172 	struct bpf_link **rlinks = NULL, **mlinks = NULL;
173 	static const struct argp argp = {
174 		.options = opts,
175 		.parser = parse_arg,
176 		.doc = argp_program_doc,
177 	};
178 	struct llcstat_bpf *obj;
179 	int err, i;
180 
181 	err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
182 	if (err)
183 		return err;
184 
185 	libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
186 	libbpf_set_print(libbpf_print_fn);
187 
188 	nr_cpus = libbpf_num_possible_cpus();
189 	if (nr_cpus < 0) {
190 		fprintf(stderr, "failed to get # of possible cpus: '%s'!\n",
191 			strerror(-nr_cpus));
192 		return 1;
193 	}
194 	mlinks = calloc(nr_cpus, sizeof(*mlinks));
195 	rlinks = calloc(nr_cpus, sizeof(*rlinks));
196 	if (!mlinks || !rlinks) {
197 		fprintf(stderr, "failed to alloc mlinks or rlinks\n");
198 		return 1;
199 	}
200 
201 	obj = llcstat_bpf__open_and_load();
202 	if (!obj) {
203 		fprintf(stderr, "failed to open and/or load BPF object\n");
204 		goto cleanup;
205 	}
206 
207 	if (open_and_attach_perf_event(PERF_COUNT_HW_CACHE_MISSES,
208 					env.sample_period,
209 					obj->progs.on_cache_miss, mlinks))
210 		goto cleanup;
211 	if (open_and_attach_perf_event(PERF_COUNT_HW_CACHE_REFERENCES,
212 					env.sample_period,
213 					obj->progs.on_cache_ref, rlinks))
214 		goto cleanup;
215 
216 	printf("Running for %ld seconds or Hit Ctrl-C to end.\n", env.duration);
217 
218 	signal(SIGINT, sig_handler);
219 
220 	sleep(env.duration);
221 
222 	printf("%-8s %-16s %-4s %12s %12s %7s\n",
223 		"PID", "NAME", "CPU", "REFERENCE", "MISS", "HIT%");
224 
225 	print_map(obj->maps.infos);
226 
227 cleanup:
228 	for (i = 0; i < nr_cpus; i++) {
229 		bpf_link__destroy(mlinks[i]);
230 		bpf_link__destroy(rlinks[i]);
231 	}
232 	free(mlinks);
233 	free(rlinks);
234 	llcstat_bpf__destroy(obj);
235 
236 	return err != 0;
237 }
238