xref: /xnu-12377.1.9/san/tools/ksancov.c (revision f6217f891ac0bb64f3d375211650a4c1ff8ca1ea)
1 /*
2  * Copyright (c) 2019 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <stdint.h>
32 #include <stdbool.h>
33 #include <stdatomic.h>
34 #include <errno.h>
35 
36 #include <fcntl.h>
37 #include <sys/ioctl.h>
38 
39 #include <getopt.h>
40 #include <unistd.h>
41 
42 #include "ksancov.h"
43 
44 static void
usage(void)45 usage(void)
46 {
47 	fprintf(stderr,
48 	    "usage: ./ksancov [OPTIONS]\n\n"
49 	    "  -t | --trace        use trace (PC log) mode [default]\n"
50 	    "  -s | --stksize      use trace (PC log) with stack size mode\n"
51 	    "  -c | --counters     use edge counter mode\n"
52 	    "  -p | --cmptrace     use trace (CMP log) mode\n"
53 	    "  -n | --entries <n>  override max entries in trace log\n"
54 	    "  -x | --exec <path>  instrument execution of binary at <path>\n"
55 	    "  -b | --bundle <b>   bundle for on-demand tracing\n");
56 	exit(1);
57 }
58 
59 /*
60  * Structure holds all data required for coverage collection.
61  */
62 typedef struct ksancov_state {
63 	ksancov_mode_t       ks_mode;
64 	ksancov_edgemap_t    *ks_edgemap;
65 	union {
66 		ksancov_header_t       *ks_header;
67 		ksancov_trace_t        *ks_trace;
68 		ksancov_counters_t     *ks_counters;
69 	};
70 	ksancov_cmps_mode_t  ks_cmps_mode;
71 	union {
72 		ksancov_header_t       *ks_cmps_header;
73 		ksancov_trace_t        *ks_cmps_trace;
74 	};
75 } ksancov_state_t;
76 
77 /*
78  * Configures ksancov device for selected coverage mode.
79  */
80 static int
ksancov_set_mode(int fd,ksancov_mode_t mode,int max_entries)81 ksancov_set_mode(int fd, ksancov_mode_t mode, int max_entries)
82 {
83 	int ret = 0;
84 
85 	switch (mode) {
86 	case KS_MODE_TRACE:
87 		ret = ksancov_mode_trace(fd, max_entries);
88 		break;
89 	case KS_MODE_STKSIZE:
90 		ret = ksancov_mode_stksize(fd, max_entries);
91 		break;
92 	case KS_MODE_COUNTERS:
93 		ret = ksancov_mode_counters(fd);
94 		break;
95 	default:
96 		perror("ksancov unsupported mode\n");
97 		return ENOTSUP;
98 	}
99 
100 	return ret;
101 }
102 
103 /*
104  * Configures ksancov device for selected comparison mode.
105  */
106 static int
ksancov_cmps_set_mode(int fd,ksancov_cmps_mode_t mode,int max_entries)107 ksancov_cmps_set_mode(int fd, ksancov_cmps_mode_t mode, int max_entries)
108 {
109 	int ret = 0;
110 
111 	switch (mode) {
112 	case KS_CMPS_MODE_TRACE:
113 		ret = ksancov_cmps_mode_trace(fd, max_entries, false);
114 		break;
115 	case KS_CMPS_MODE_TRACE_FUNC:
116 		ret = ksancov_cmps_mode_trace(fd, max_entries, true);
117 		break;
118 	default:
119 		perror("ksancov unsupported cmps mode\n");
120 		return ENOTSUP;
121 	}
122 
123 	return ret;
124 }
125 
126 /*
127  * Initialize coverage state from provided options. Shared mappings with kernel are established
128  * here.
129  */
130 static int
ksancov_init_state(int fd,ksancov_mode_t mode,ksancov_cmps_mode_t cmps_mode,int max_entries,ksancov_state_t * state)131 ksancov_init_state(int fd, ksancov_mode_t mode, ksancov_cmps_mode_t cmps_mode, int max_entries, ksancov_state_t *state)
132 {
133 	uintptr_t addr;
134 	size_t sz;
135 	int ret = 0;
136 
137 	/* Map edge map into process address space. */
138 	ret = ksancov_map_edgemap(fd, &addr, NULL);
139 	if (ret) {
140 		perror("ksancov map counters\n");
141 		return ret;
142 	}
143 	state->ks_edgemap = (void *)addr;
144 	fprintf(stderr, "nedges (edgemap) = %u\n", state->ks_edgemap->ke_nedges);
145 
146 	/* Setup selected tracing mode. */
147 	ret = ksancov_set_mode(fd, mode, max_entries);
148 	if (ret) {
149 		perror("ksancov set mode\n");
150 		return ret;
151 	}
152 
153 	/* Map buffer for selected mode into process address space. */
154 	ret = ksancov_map(fd, &addr, &sz);
155 	if (ret) {
156 		perror("ksancov map");
157 		return ret;
158 	}
159 	fprintf(stderr, "mapped to 0x%lx + %lu\n", addr, sz);
160 
161 	/* Finalize state members. */
162 	state->ks_mode = mode;
163 	state->ks_header = (void *)addr;
164 
165 	if (mode == KS_MODE_COUNTERS) {
166 		fprintf(stderr, "nedges (counters) = %u\n", state->ks_counters->kc_nedges);
167 	} else {
168 		fprintf(stderr, "maxpcs = %lu\n", ksancov_trace_max_ent(state->ks_trace));
169 	}
170 
171 	if (cmps_mode == KS_CMPS_MODE_NONE) {
172 		state->ks_cmps_mode = cmps_mode;
173 		state->ks_cmps_header = NULL;
174 		return ret;
175 	}
176 
177 	/* Setup selected comparison tracing mode. */
178 	ret = ksancov_cmps_set_mode(fd, cmps_mode, max_entries);
179 	if (ret) {
180 		perror("ksancov cmps set mode\n");
181 		return ret;
182 	}
183 
184 	/* Map buffer for selected mode into process address space. */
185 	ret = ksancov_cmps_map(fd, &addr, &sz);
186 	if (ret) {
187 		perror("ksancov cmps map");
188 		return ret;
189 	}
190 	fprintf(stderr, "cmps mapped to 0x%lx + %lu\n", addr, sz);
191 
192 	/* Finalize state members. */
193 	state->ks_cmps_mode = cmps_mode;
194 	state->ks_cmps_header = (void *)addr;
195 
196 	fprintf(stderr, "maxcmps = %lu\n", ksancov_trace_max_ent(state->ks_cmps_trace));
197 
198 	return ret;
199 }
200 
201 static int
ksancov_print_state(ksancov_state_t * state)202 ksancov_print_state(ksancov_state_t *state)
203 {
204 	if (state->ks_mode == KS_MODE_COUNTERS) {
205 		for (size_t i = 0; i < state->ks_counters->kc_nedges; i++) {
206 			size_t hits = state->ks_counters->kc_hits[i];
207 			if (hits) {
208 				fprintf(stderr, "0x%lx: %lu hits [idx %lu]\n",
209 				    ksancov_edge_addr(state->ks_edgemap, i), hits, i);
210 			}
211 		}
212 	} else {
213 		size_t head = ksancov_trace_head(state->ks_trace);
214 		fprintf(stderr, "head = %lu\n", head);
215 
216 		for (uint32_t i = 0; i < head; i++) {
217 			if (state->ks_mode == KS_MODE_TRACE) {
218 				fprintf(stderr, "0x%lx\n", ksancov_trace_entry(state->ks_trace, i));
219 			} else {
220 				fprintf(stderr, "0x%lx [size %u]\n", ksancov_stksize_pc(state->ks_trace, i),
221 				    ksancov_stksize_size(state->ks_trace, i));
222 			}
223 		}
224 	}
225 
226 	if (state->ks_cmps_mode == KS_CMPS_MODE_TRACE || state->ks_cmps_mode == KS_CMPS_MODE_TRACE_FUNC) {
227 		static const char *type_map[KCOV_CMP_SIZE8 + 1] = {
228 			"8 bits", NULL, "16 bits", NULL, "32 bits",
229 			NULL, "64 bits"
230 		};
231 
232 		size_t head = ksancov_trace_head(state->ks_cmps_trace);
233 		fprintf(stderr, "cmps head = %lu\n", head);
234 
235 		for (uint32_t i = 0; i < head;) {
236 			ksancov_cmps_trace_ent_t *entry = ksancov_cmps_trace_entry(state->ks_cmps_trace, i);
237 			if (KCOV_CMP_IS_FUNC(entry->type)) {
238 				size_t space = ksancov_cmps_trace_func_space(entry->len1_func, entry->len2_func);
239 				i += space / sizeof(ksancov_cmps_trace_ent_t);
240 				fprintf(stderr, "0x%llx [func %u %u] '%s' '%s'\n", entry->pc, entry->len1_func, entry->len2_func,
241 				    ksancov_cmps_trace_func_arg1(entry),
242 				    ksancov_cmps_trace_func_arg2(entry));
243 			} else {
244 				uint64_t type = entry->type & KCOV_CMP_SIZE_MASK;
245 				fprintf(stderr, "0x%llx [%s] 0x%llx 0x%llx\n", entry->pc, type_map[type], entry->args[0], entry->args[1]);
246 				++i;
247 			}
248 		}
249 	}
250 
251 	return 0;
252 }
253 
254 static int
ksancov_on_demand_set_enabled(int fd,const char * bundle,bool enabled)255 ksancov_on_demand_set_enabled(int fd, const char *bundle, bool enabled)
256 {
257 	int ret = 0;
258 	const uint64_t gate = enabled ? 1 : 0;
259 	if (bundle) {
260 		fprintf(stderr, "setting on-demand gate for '%s': %llu\n", bundle, gate);
261 		ret = ksancov_on_demand_set_gate(fd, bundle, gate);
262 		if (ret) {
263 			perror("ksancov on demand");
264 		}
265 	}
266 	return ret;
267 }
268 
269 int
main(int argc,char * argv[])270 main(int argc, char *argv[])
271 {
272 	ksancov_mode_t ksan_mode = KS_MODE_NONE;
273 	ksancov_cmps_mode_t ksan_cmps_mode = KS_CMPS_MODE_NONE;
274 	ksancov_state_t ksan_state = {0};
275 
276 	int ret;
277 	size_t max_entries = 64UL * 1024;
278 	char *path = NULL;
279 	char *od_bundle = NULL;
280 
281 	static struct option opts[] = {
282 		{ "entries", required_argument, NULL, 'n' },
283 		{ "exec", required_argument, NULL, 'x' },
284 
285 		{ "trace", no_argument, NULL, 't' },
286 		{ "counters", no_argument, NULL, 'c' },
287 		{ "stksize", no_argument, NULL, 's' },
288 		{ "cmptrace", no_argument, NULL, 'p' },
289 
290 		{ "bundle", required_argument, NULL, 'b' },
291 
292 		{ NULL, 0, NULL, 0 }
293 	};
294 
295 	int ch;
296 	while ((ch = getopt_long(argc, argv, "tsn:x:cpb:", opts, NULL)) != -1) {
297 		switch (ch) {
298 		case 'n':
299 			max_entries = strtoul(optarg, NULL, 0);
300 			break;
301 		case 'x':
302 			path = optarg;
303 			break;
304 		case 't':
305 			ksan_mode = KS_MODE_TRACE;
306 			break;
307 		case 'c':
308 			ksan_mode = KS_MODE_COUNTERS;
309 			break;
310 		case 's':
311 			ksan_mode = KS_MODE_STKSIZE;
312 			break;
313 		case 'p':
314 			ksan_cmps_mode = KS_CMPS_MODE_TRACE_FUNC;
315 			break;
316 		case 'b':
317 			od_bundle = optarg;
318 			break;
319 		default:
320 			usage();
321 		}
322 	}
323 
324 	int fd = ksancov_open();
325 	if (fd < 0) {
326 		perror("ksancov_open");
327 		return errno;
328 	}
329 	fprintf(stderr, "opened ksancov on fd %i\n", fd);
330 
331 	/* Initialize ksancov state. */
332 	ret = ksancov_init_state(fd, ksan_mode, ksan_cmps_mode, max_entries, &ksan_state);
333 	if (ret) {
334 		perror("ksancov init\n");
335 		return ret;
336 	}
337 
338 	/* Execute binary (when provided) with enabled coverage collection. Run getppid() otherwise. */
339 	if (path) {
340 		int pid = fork();
341 		if (pid == 0) {
342 			/* child */
343 
344 			ret = ksancov_thread_self(fd);
345 			if (ret) {
346 				perror("ksancov thread");
347 				return ret;
348 			}
349 
350 			ksancov_on_demand_set_enabled(fd, od_bundle, true);
351 			ksancov_reset(ksan_state.ks_header);
352 			ksancov_start(ksan_state.ks_header);
353 			if (ksan_state.ks_cmps_header) {
354 				ksancov_reset(ksan_state.ks_cmps_header);
355 				ksancov_start(ksan_state.ks_cmps_header);
356 			}
357 			ret = execl(path, path, 0);
358 			perror("execl");
359 			ksancov_on_demand_set_enabled(fd, od_bundle, false);
360 
361 			exit(1);
362 		} else {
363 			/* parent */
364 			waitpid(pid, NULL, 0);
365 			ksancov_stop(ksan_state.ks_header);
366 			if (ksan_state.ks_cmps_header) {
367 				ksancov_stop(ksan_state.ks_cmps_header);
368 			}
369 			ksancov_on_demand_set_enabled(fd, od_bundle, false);
370 		}
371 	} else {
372 		ret = ksancov_thread_self(fd);
373 		if (ret) {
374 			perror("ksancov thread");
375 			return ret;
376 		}
377 
378 		ksancov_on_demand_set_enabled(fd, od_bundle, true);
379 		ksancov_reset(ksan_state.ks_header);
380 		ksancov_start(ksan_state.ks_header);
381 		if (ksan_state.ks_cmps_header) {
382 			ksancov_reset(ksan_state.ks_cmps_header);
383 			ksancov_start(ksan_state.ks_cmps_header);
384 		}
385 		int ppid = getppid();
386 		ksancov_stop(ksan_state.ks_header);
387 		if (ksan_state.ks_cmps_header) {
388 			ksancov_stop(ksan_state.ks_cmps_header);
389 		}
390 		ksancov_on_demand_set_enabled(fd, od_bundle, false);
391 		fprintf(stderr, "ppid = %i\n", ppid);
392 	}
393 
394 	/* Print report and cleanup. */
395 	ksancov_print_state(&ksan_state);
396 	ret = close(fd);
397 	fprintf(stderr, "close = %i\n", ret);
398 
399 	return 0;
400 }
401