xref: /xnu-8792.81.2/san/tools/ksancov.c (revision 19c3b8c28c31cb8130e034cfb5df6bf9ba342d90)
1*19c3b8c2SApple OSS Distributions /*
2*19c3b8c2SApple OSS Distributions  * Copyright (c) 2019 Apple Inc. All rights reserved.
3*19c3b8c2SApple OSS Distributions  *
4*19c3b8c2SApple OSS Distributions  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5*19c3b8c2SApple OSS Distributions  *
6*19c3b8c2SApple OSS Distributions  * This file contains Original Code and/or Modifications of Original Code
7*19c3b8c2SApple OSS Distributions  * as defined in and that are subject to the Apple Public Source License
8*19c3b8c2SApple OSS Distributions  * Version 2.0 (the 'License'). You may not use this file except in
9*19c3b8c2SApple OSS Distributions  * compliance with the License. The rights granted to you under the License
10*19c3b8c2SApple OSS Distributions  * may not be used to create, or enable the creation or redistribution of,
11*19c3b8c2SApple OSS Distributions  * unlawful or unlicensed copies of an Apple operating system, or to
12*19c3b8c2SApple OSS Distributions  * circumvent, violate, or enable the circumvention or violation of, any
13*19c3b8c2SApple OSS Distributions  * terms of an Apple operating system software license agreement.
14*19c3b8c2SApple OSS Distributions  *
15*19c3b8c2SApple OSS Distributions  * Please obtain a copy of the License at
16*19c3b8c2SApple OSS Distributions  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17*19c3b8c2SApple OSS Distributions  *
18*19c3b8c2SApple OSS Distributions  * The Original Code and all software distributed under the License are
19*19c3b8c2SApple OSS Distributions  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20*19c3b8c2SApple OSS Distributions  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21*19c3b8c2SApple OSS Distributions  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22*19c3b8c2SApple OSS Distributions  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23*19c3b8c2SApple OSS Distributions  * Please see the License for the specific language governing rights and
24*19c3b8c2SApple OSS Distributions  * limitations under the License.
25*19c3b8c2SApple OSS Distributions  *
26*19c3b8c2SApple OSS Distributions  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27*19c3b8c2SApple OSS Distributions  */
28*19c3b8c2SApple OSS Distributions 
29*19c3b8c2SApple OSS Distributions #include <stdlib.h>
30*19c3b8c2SApple OSS Distributions #include <stdio.h>
31*19c3b8c2SApple OSS Distributions #include <stdint.h>
32*19c3b8c2SApple OSS Distributions #include <stdbool.h>
33*19c3b8c2SApple OSS Distributions #include <stdatomic.h>
34*19c3b8c2SApple OSS Distributions #include <errno.h>
35*19c3b8c2SApple OSS Distributions 
36*19c3b8c2SApple OSS Distributions #include <fcntl.h>
37*19c3b8c2SApple OSS Distributions #include <sys/ioctl.h>
38*19c3b8c2SApple OSS Distributions 
39*19c3b8c2SApple OSS Distributions #include <getopt.h>
40*19c3b8c2SApple OSS Distributions 
41*19c3b8c2SApple OSS Distributions #include "ksancov.h"
42*19c3b8c2SApple OSS Distributions 
43*19c3b8c2SApple OSS Distributions static void
usage(void)44*19c3b8c2SApple OSS Distributions usage(void)
45*19c3b8c2SApple OSS Distributions {
46*19c3b8c2SApple OSS Distributions 	fprintf(stderr,
47*19c3b8c2SApple OSS Distributions 	    "usage: ./ksancov [OPTIONS]\n\n"
48*19c3b8c2SApple OSS Distributions 	    "  -t | --trace        use trace (PC log) mode [default]\n"
49*19c3b8c2SApple OSS Distributions 	    "  -s | --stksize      use trace (PC log) with stack size mode\n"
50*19c3b8c2SApple OSS Distributions 	    "  -c | --counters     use edge counter mode\n"
51*19c3b8c2SApple OSS Distributions 	    "  -n | --entries <n>  override max entries in trace log\n"
52*19c3b8c2SApple OSS Distributions 	    "  -x | --exec <path>  instrument execution of binary at <path>\n");
53*19c3b8c2SApple OSS Distributions 	exit(1);
54*19c3b8c2SApple OSS Distributions }
55*19c3b8c2SApple OSS Distributions 
56*19c3b8c2SApple OSS Distributions /*
57*19c3b8c2SApple OSS Distributions  * Structure holds all data required for coverage collection.
58*19c3b8c2SApple OSS Distributions  */
59*19c3b8c2SApple OSS Distributions typedef struct ksancov_state {
60*19c3b8c2SApple OSS Distributions 	ksancov_mode_t       ks_mode;
61*19c3b8c2SApple OSS Distributions 	ksancov_edgemap_t    *ks_edgemap;
62*19c3b8c2SApple OSS Distributions 	union {
63*19c3b8c2SApple OSS Distributions 		ksancov_header_t       *ks_header;
64*19c3b8c2SApple OSS Distributions 		ksancov_trace_t        *ks_trace;
65*19c3b8c2SApple OSS Distributions 		ksancov_counters_t     *ks_counters;
66*19c3b8c2SApple OSS Distributions 	};
67*19c3b8c2SApple OSS Distributions } ksancov_state_t;
68*19c3b8c2SApple OSS Distributions 
69*19c3b8c2SApple OSS Distributions /*
70*19c3b8c2SApple OSS Distributions  * Configures ksancov device for selected coverage mode.
71*19c3b8c2SApple OSS Distributions  */
72*19c3b8c2SApple OSS Distributions static int
ksancov_set_mode(int fd,ksancov_mode_t mode,int max_entries)73*19c3b8c2SApple OSS Distributions ksancov_set_mode(int fd, ksancov_mode_t mode, int max_entries)
74*19c3b8c2SApple OSS Distributions {
75*19c3b8c2SApple OSS Distributions 	int ret = 0;
76*19c3b8c2SApple OSS Distributions 
77*19c3b8c2SApple OSS Distributions 	switch (mode) {
78*19c3b8c2SApple OSS Distributions 	case KS_MODE_TRACE:
79*19c3b8c2SApple OSS Distributions 		ret = ksancov_mode_trace(fd, max_entries);
80*19c3b8c2SApple OSS Distributions 		break;
81*19c3b8c2SApple OSS Distributions 	case KS_MODE_STKSIZE:
82*19c3b8c2SApple OSS Distributions 		ret = ksancov_mode_stksize(fd, max_entries);
83*19c3b8c2SApple OSS Distributions 		break;
84*19c3b8c2SApple OSS Distributions 	case KS_MODE_COUNTERS:
85*19c3b8c2SApple OSS Distributions 		ret = ksancov_mode_counters(fd);
86*19c3b8c2SApple OSS Distributions 		break;
87*19c3b8c2SApple OSS Distributions 	default:
88*19c3b8c2SApple OSS Distributions 		perror("ksancov unsupported mode\n");
89*19c3b8c2SApple OSS Distributions 		return ENOTSUP;
90*19c3b8c2SApple OSS Distributions 	}
91*19c3b8c2SApple OSS Distributions 
92*19c3b8c2SApple OSS Distributions 	return ret;
93*19c3b8c2SApple OSS Distributions }
94*19c3b8c2SApple OSS Distributions 
95*19c3b8c2SApple OSS Distributions /*
96*19c3b8c2SApple OSS Distributions  * Initialize coverage state from provided options. Shared mappings with kernel are established
97*19c3b8c2SApple OSS Distributions  * here.
98*19c3b8c2SApple OSS Distributions  */
99*19c3b8c2SApple OSS Distributions static int
ksancov_init_state(int fd,ksancov_mode_t mode,int max_entries,ksancov_state_t * state)100*19c3b8c2SApple OSS Distributions ksancov_init_state(int fd, ksancov_mode_t mode, int max_entries, ksancov_state_t *state)
101*19c3b8c2SApple OSS Distributions {
102*19c3b8c2SApple OSS Distributions 	uintptr_t addr;
103*19c3b8c2SApple OSS Distributions 	size_t sz;
104*19c3b8c2SApple OSS Distributions 	int ret = 0;
105*19c3b8c2SApple OSS Distributions 
106*19c3b8c2SApple OSS Distributions 	/* Map edge map into process address space. */
107*19c3b8c2SApple OSS Distributions 	ret = ksancov_map_edgemap(fd, &addr, NULL);
108*19c3b8c2SApple OSS Distributions 	if (ret) {
109*19c3b8c2SApple OSS Distributions 		perror("ksancov map counters\n");
110*19c3b8c2SApple OSS Distributions 		return ret;
111*19c3b8c2SApple OSS Distributions 	}
112*19c3b8c2SApple OSS Distributions 	state->ks_edgemap = (void *)addr;
113*19c3b8c2SApple OSS Distributions 	fprintf(stderr, "nedges (edgemap) = %u\n", state->ks_edgemap->ke_nedges);
114*19c3b8c2SApple OSS Distributions 
115*19c3b8c2SApple OSS Distributions 	/* Setup selected tracing mode. */
116*19c3b8c2SApple OSS Distributions 	ret = ksancov_set_mode(fd, mode, max_entries);
117*19c3b8c2SApple OSS Distributions 	if (ret) {
118*19c3b8c2SApple OSS Distributions 		perror("ksancov set mode\n");
119*19c3b8c2SApple OSS Distributions 		return ret;
120*19c3b8c2SApple OSS Distributions 	}
121*19c3b8c2SApple OSS Distributions 
122*19c3b8c2SApple OSS Distributions 	/* Map buffer for selected mode into process address space. */
123*19c3b8c2SApple OSS Distributions 	ret = ksancov_map(fd, &addr, &sz);
124*19c3b8c2SApple OSS Distributions 	if (ret) {
125*19c3b8c2SApple OSS Distributions 		perror("ksancov map");
126*19c3b8c2SApple OSS Distributions 		return ret;
127*19c3b8c2SApple OSS Distributions 	}
128*19c3b8c2SApple OSS Distributions 	fprintf(stderr, "mapped to 0x%lx + %lu\n", addr, sz);
129*19c3b8c2SApple OSS Distributions 
130*19c3b8c2SApple OSS Distributions 	/* Finalize state members. */
131*19c3b8c2SApple OSS Distributions 	state->ks_mode = mode;
132*19c3b8c2SApple OSS Distributions 	state->ks_header = (void *)addr;
133*19c3b8c2SApple OSS Distributions 
134*19c3b8c2SApple OSS Distributions 	if (mode == KS_MODE_COUNTERS) {
135*19c3b8c2SApple OSS Distributions 		fprintf(stderr, "nedges (counters) = %u\n", state->ks_counters->kc_nedges);
136*19c3b8c2SApple OSS Distributions 	} else {
137*19c3b8c2SApple OSS Distributions 		fprintf(stderr, "maxpcs = %lu\n", ksancov_trace_max_ent(state->ks_trace));
138*19c3b8c2SApple OSS Distributions 	}
139*19c3b8c2SApple OSS Distributions 
140*19c3b8c2SApple OSS Distributions 	return ret;
141*19c3b8c2SApple OSS Distributions }
142*19c3b8c2SApple OSS Distributions 
143*19c3b8c2SApple OSS Distributions static int
ksancov_print_state(ksancov_state_t * state)144*19c3b8c2SApple OSS Distributions ksancov_print_state(ksancov_state_t *state)
145*19c3b8c2SApple OSS Distributions {
146*19c3b8c2SApple OSS Distributions 	if (state->ks_mode == KS_MODE_COUNTERS) {
147*19c3b8c2SApple OSS Distributions 		for (size_t i = 0; i < state->ks_counters->kc_nedges; i++) {
148*19c3b8c2SApple OSS Distributions 			size_t hits = state->ks_counters->kc_hits[i];
149*19c3b8c2SApple OSS Distributions 			if (hits) {
150*19c3b8c2SApple OSS Distributions 				fprintf(stderr, "0x%lx: %lu hits [idx %lu]\n",
151*19c3b8c2SApple OSS Distributions 				    ksancov_edge_addr(state->ks_edgemap, i), hits, i);
152*19c3b8c2SApple OSS Distributions 			}
153*19c3b8c2SApple OSS Distributions 		}
154*19c3b8c2SApple OSS Distributions 	} else {
155*19c3b8c2SApple OSS Distributions 		size_t head = ksancov_trace_head(state->ks_trace);
156*19c3b8c2SApple OSS Distributions 		fprintf(stderr, "head = %lu\n", head);
157*19c3b8c2SApple OSS Distributions 
158*19c3b8c2SApple OSS Distributions 		for (uint32_t i = 0; i < head; i++) {
159*19c3b8c2SApple OSS Distributions 			if (state->ks_mode == KS_MODE_TRACE) {
160*19c3b8c2SApple OSS Distributions 				fprintf(stderr, "0x%lx\n", ksancov_trace_entry(state->ks_trace, i));
161*19c3b8c2SApple OSS Distributions 			} else {
162*19c3b8c2SApple OSS Distributions 				fprintf(stderr, "0x%lx [size %u]\n", ksancov_stksize_pc(state->ks_trace, i),
163*19c3b8c2SApple OSS Distributions 				    ksancov_stksize_size(state->ks_trace, i));
164*19c3b8c2SApple OSS Distributions 			}
165*19c3b8c2SApple OSS Distributions 		}
166*19c3b8c2SApple OSS Distributions 	}
167*19c3b8c2SApple OSS Distributions 
168*19c3b8c2SApple OSS Distributions 	return 0;
169*19c3b8c2SApple OSS Distributions }
170*19c3b8c2SApple OSS Distributions 
171*19c3b8c2SApple OSS Distributions int
main(int argc,char * argv[])172*19c3b8c2SApple OSS Distributions main(int argc, char *argv[])
173*19c3b8c2SApple OSS Distributions {
174*19c3b8c2SApple OSS Distributions 	ksancov_mode_t ksan_mode = KS_MODE_NONE;
175*19c3b8c2SApple OSS Distributions 	ksancov_state_t ksan_state;
176*19c3b8c2SApple OSS Distributions 
177*19c3b8c2SApple OSS Distributions 	int ret;
178*19c3b8c2SApple OSS Distributions 	size_t max_entries = 64UL * 1024;
179*19c3b8c2SApple OSS Distributions 	char *path = NULL;
180*19c3b8c2SApple OSS Distributions 
181*19c3b8c2SApple OSS Distributions 	static struct option opts[] = {
182*19c3b8c2SApple OSS Distributions 		{ "entries", required_argument, NULL, 'n' },
183*19c3b8c2SApple OSS Distributions 		{ "exec", required_argument, NULL, 'x' },
184*19c3b8c2SApple OSS Distributions 
185*19c3b8c2SApple OSS Distributions 		{ "trace", no_argument, NULL, 't' },
186*19c3b8c2SApple OSS Distributions 		{ "counters", no_argument, NULL, 'c' },
187*19c3b8c2SApple OSS Distributions 		{ "stksize", no_argument, NULL, 's' },
188*19c3b8c2SApple OSS Distributions 
189*19c3b8c2SApple OSS Distributions 		{ NULL, 0, NULL, 0 }
190*19c3b8c2SApple OSS Distributions 	};
191*19c3b8c2SApple OSS Distributions 
192*19c3b8c2SApple OSS Distributions 	int ch;
193*19c3b8c2SApple OSS Distributions 	while ((ch = getopt_long(argc, argv, "tsn:x:c", opts, NULL)) != -1) {
194*19c3b8c2SApple OSS Distributions 		switch (ch) {
195*19c3b8c2SApple OSS Distributions 		case 'n':
196*19c3b8c2SApple OSS Distributions 			max_entries = strtoul(optarg, NULL, 0);
197*19c3b8c2SApple OSS Distributions 			break;
198*19c3b8c2SApple OSS Distributions 		case 'x':
199*19c3b8c2SApple OSS Distributions 			path = optarg;
200*19c3b8c2SApple OSS Distributions 			break;
201*19c3b8c2SApple OSS Distributions 		case 't':
202*19c3b8c2SApple OSS Distributions 			ksan_mode = KS_MODE_TRACE;
203*19c3b8c2SApple OSS Distributions 			break;
204*19c3b8c2SApple OSS Distributions 		case 'c':
205*19c3b8c2SApple OSS Distributions 			ksan_mode = KS_MODE_COUNTERS;
206*19c3b8c2SApple OSS Distributions 			break;
207*19c3b8c2SApple OSS Distributions 		case 's':
208*19c3b8c2SApple OSS Distributions 			ksan_mode = KS_MODE_STKSIZE;
209*19c3b8c2SApple OSS Distributions 			break;
210*19c3b8c2SApple OSS Distributions 		default:
211*19c3b8c2SApple OSS Distributions 			usage();
212*19c3b8c2SApple OSS Distributions 		}
213*19c3b8c2SApple OSS Distributions 	}
214*19c3b8c2SApple OSS Distributions 
215*19c3b8c2SApple OSS Distributions 	int fd = ksancov_open();
216*19c3b8c2SApple OSS Distributions 	if (fd < 0) {
217*19c3b8c2SApple OSS Distributions 		perror("ksancov_open");
218*19c3b8c2SApple OSS Distributions 		return errno;
219*19c3b8c2SApple OSS Distributions 	}
220*19c3b8c2SApple OSS Distributions 	fprintf(stderr, "opened ksancov on fd %i\n", fd);
221*19c3b8c2SApple OSS Distributions 
222*19c3b8c2SApple OSS Distributions 	/* Initialize ksancov state. */
223*19c3b8c2SApple OSS Distributions 	ret = ksancov_init_state(fd, ksan_mode, max_entries, &ksan_state);
224*19c3b8c2SApple OSS Distributions 	if (ret) {
225*19c3b8c2SApple OSS Distributions 		perror("ksancov init\n");
226*19c3b8c2SApple OSS Distributions 		return ret;
227*19c3b8c2SApple OSS Distributions 	}
228*19c3b8c2SApple OSS Distributions 
229*19c3b8c2SApple OSS Distributions 	/* Execute binary (when provided) with enabled coverage collection. Run getppid() otherwise. */
230*19c3b8c2SApple OSS Distributions 	if (path) {
231*19c3b8c2SApple OSS Distributions 		int pid = fork();
232*19c3b8c2SApple OSS Distributions 		if (pid == 0) {
233*19c3b8c2SApple OSS Distributions 			/* child */
234*19c3b8c2SApple OSS Distributions 
235*19c3b8c2SApple OSS Distributions 			ret = ksancov_thread_self(fd);
236*19c3b8c2SApple OSS Distributions 			if (ret) {
237*19c3b8c2SApple OSS Distributions 				perror("ksancov thread");
238*19c3b8c2SApple OSS Distributions 				return ret;
239*19c3b8c2SApple OSS Distributions 			}
240*19c3b8c2SApple OSS Distributions 
241*19c3b8c2SApple OSS Distributions 			ksancov_reset(ksan_state.ks_header);
242*19c3b8c2SApple OSS Distributions 			ksancov_start(ksan_state.ks_header);
243*19c3b8c2SApple OSS Distributions 			ret = execl(path, path, 0);
244*19c3b8c2SApple OSS Distributions 			perror("execl");
245*19c3b8c2SApple OSS Distributions 
246*19c3b8c2SApple OSS Distributions 			exit(1);
247*19c3b8c2SApple OSS Distributions 		} else {
248*19c3b8c2SApple OSS Distributions 			/* parent */
249*19c3b8c2SApple OSS Distributions 			waitpid(pid, NULL, 0);
250*19c3b8c2SApple OSS Distributions 			ksancov_stop(ksan_state.ks_header);
251*19c3b8c2SApple OSS Distributions 		}
252*19c3b8c2SApple OSS Distributions 	} else {
253*19c3b8c2SApple OSS Distributions 		ret = ksancov_thread_self(fd);
254*19c3b8c2SApple OSS Distributions 		if (ret) {
255*19c3b8c2SApple OSS Distributions 			perror("ksancov thread");
256*19c3b8c2SApple OSS Distributions 			return ret;
257*19c3b8c2SApple OSS Distributions 		}
258*19c3b8c2SApple OSS Distributions 
259*19c3b8c2SApple OSS Distributions 		ksancov_reset(ksan_state.ks_header);
260*19c3b8c2SApple OSS Distributions 		ksancov_start(ksan_state.ks_header);
261*19c3b8c2SApple OSS Distributions 		int ppid = getppid();
262*19c3b8c2SApple OSS Distributions 		ksancov_stop(ksan_state.ks_header);
263*19c3b8c2SApple OSS Distributions 		fprintf(stderr, "ppid = %i\n", ppid);
264*19c3b8c2SApple OSS Distributions 	}
265*19c3b8c2SApple OSS Distributions 
266*19c3b8c2SApple OSS Distributions 	/* Print report and cleanup. */
267*19c3b8c2SApple OSS Distributions 	ksancov_print_state(&ksan_state);
268*19c3b8c2SApple OSS Distributions 	ret = close(fd);
269*19c3b8c2SApple OSS Distributions 	fprintf(stderr, "close = %i\n", ret);
270*19c3b8c2SApple OSS Distributions 
271*19c3b8c2SApple OSS Distributions 	return 0;
272*19c3b8c2SApple OSS Distributions }
273