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