xref: /xnu-11417.140.69/san/tools/ksancov.c (revision 43a90889846e00bfb5cf1d255cdc0a701a1e05a4)
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 	    "  -n | --entries <n>  override max entries in trace log\n"
53 	    "  -x | --exec <path>  instrument execution of binary at <path>\n"
54 	    "  -b | --bundle <b>   bundle for on-demand tracing\n");
55 	exit(1);
56 }
57 
58 /*
59  * Structure holds all data required for coverage collection.
60  */
61 typedef struct ksancov_state {
62 	ksancov_mode_t       ks_mode;
63 	ksancov_edgemap_t    *ks_edgemap;
64 	union {
65 		ksancov_header_t       *ks_header;
66 		ksancov_trace_t        *ks_trace;
67 		ksancov_counters_t     *ks_counters;
68 	};
69 } ksancov_state_t;
70 
71 /*
72  * Configures ksancov device for selected coverage mode.
73  */
74 static int
ksancov_set_mode(int fd,ksancov_mode_t mode,int max_entries)75 ksancov_set_mode(int fd, ksancov_mode_t mode, int max_entries)
76 {
77 	int ret = 0;
78 
79 	switch (mode) {
80 	case KS_MODE_TRACE:
81 		ret = ksancov_mode_trace(fd, max_entries);
82 		break;
83 	case KS_MODE_STKSIZE:
84 		ret = ksancov_mode_stksize(fd, max_entries);
85 		break;
86 	case KS_MODE_COUNTERS:
87 		ret = ksancov_mode_counters(fd);
88 		break;
89 	default:
90 		perror("ksancov unsupported mode\n");
91 		return ENOTSUP;
92 	}
93 
94 	return ret;
95 }
96 
97 /*
98  * Initialize coverage state from provided options. Shared mappings with kernel are established
99  * here.
100  */
101 static int
ksancov_init_state(int fd,ksancov_mode_t mode,int max_entries,ksancov_state_t * state)102 ksancov_init_state(int fd, ksancov_mode_t mode, int max_entries, ksancov_state_t *state)
103 {
104 	uintptr_t addr;
105 	size_t sz;
106 	int ret = 0;
107 
108 	/* Map edge map into process address space. */
109 	ret = ksancov_map_edgemap(fd, &addr, NULL);
110 	if (ret) {
111 		perror("ksancov map counters\n");
112 		return ret;
113 	}
114 	state->ks_edgemap = (void *)addr;
115 	fprintf(stderr, "nedges (edgemap) = %u\n", state->ks_edgemap->ke_nedges);
116 
117 	/* Setup selected tracing mode. */
118 	ret = ksancov_set_mode(fd, mode, max_entries);
119 	if (ret) {
120 		perror("ksancov set mode\n");
121 		return ret;
122 	}
123 
124 	/* Map buffer for selected mode into process address space. */
125 	ret = ksancov_map(fd, &addr, &sz);
126 	if (ret) {
127 		perror("ksancov map");
128 		return ret;
129 	}
130 	fprintf(stderr, "mapped to 0x%lx + %lu\n", addr, sz);
131 
132 	/* Finalize state members. */
133 	state->ks_mode = mode;
134 	state->ks_header = (void *)addr;
135 
136 	if (mode == KS_MODE_COUNTERS) {
137 		fprintf(stderr, "nedges (counters) = %u\n", state->ks_counters->kc_nedges);
138 	} else {
139 		fprintf(stderr, "maxpcs = %lu\n", ksancov_trace_max_ent(state->ks_trace));
140 	}
141 
142 	return ret;
143 }
144 
145 static int
ksancov_print_state(ksancov_state_t * state)146 ksancov_print_state(ksancov_state_t *state)
147 {
148 	if (state->ks_mode == KS_MODE_COUNTERS) {
149 		for (size_t i = 0; i < state->ks_counters->kc_nedges; i++) {
150 			size_t hits = state->ks_counters->kc_hits[i];
151 			if (hits) {
152 				fprintf(stderr, "0x%lx: %lu hits [idx %lu]\n",
153 				    ksancov_edge_addr(state->ks_edgemap, i), hits, i);
154 			}
155 		}
156 	} else {
157 		size_t head = ksancov_trace_head(state->ks_trace);
158 		fprintf(stderr, "head = %lu\n", head);
159 
160 		for (uint32_t i = 0; i < head; i++) {
161 			if (state->ks_mode == KS_MODE_TRACE) {
162 				fprintf(stderr, "0x%lx\n", ksancov_trace_entry(state->ks_trace, i));
163 			} else {
164 				fprintf(stderr, "0x%lx [size %u]\n", ksancov_stksize_pc(state->ks_trace, i),
165 				    ksancov_stksize_size(state->ks_trace, i));
166 			}
167 		}
168 	}
169 
170 	return 0;
171 }
172 
173 static int
ksancov_on_demand_set_enabled(int fd,const char * bundle,bool enabled)174 ksancov_on_demand_set_enabled(int fd, const char *bundle, bool enabled)
175 {
176 	int ret = 0;
177 	const uint64_t gate = enabled ? 1 : 0;
178 	if (bundle) {
179 		fprintf(stderr, "setting on-demand gate for '%s': %llu\n", bundle, gate);
180 		ret = ksancov_on_demand_set_gate(fd, bundle, gate);
181 		if (ret) {
182 			perror("ksancov on demand");
183 		}
184 	}
185 	return ret;
186 }
187 
188 int
main(int argc,char * argv[])189 main(int argc, char *argv[])
190 {
191 	ksancov_mode_t ksan_mode = KS_MODE_NONE;
192 	ksancov_state_t ksan_state;
193 
194 	int ret;
195 	size_t max_entries = 64UL * 1024;
196 	char *path = NULL;
197 	char *od_bundle = NULL;
198 
199 	static struct option opts[] = {
200 		{ "entries", required_argument, NULL, 'n' },
201 		{ "exec", required_argument, NULL, 'x' },
202 
203 		{ "trace", no_argument, NULL, 't' },
204 		{ "counters", no_argument, NULL, 'c' },
205 		{ "stksize", no_argument, NULL, 's' },
206 
207 		{ "bundle", required_argument, NULL, 'b' },
208 
209 		{ NULL, 0, NULL, 0 }
210 	};
211 
212 	int ch;
213 	while ((ch = getopt_long(argc, argv, "tsn:x:cb:", opts, NULL)) != -1) {
214 		switch (ch) {
215 		case 'n':
216 			max_entries = strtoul(optarg, NULL, 0);
217 			break;
218 		case 'x':
219 			path = optarg;
220 			break;
221 		case 't':
222 			ksan_mode = KS_MODE_TRACE;
223 			break;
224 		case 'c':
225 			ksan_mode = KS_MODE_COUNTERS;
226 			break;
227 		case 's':
228 			ksan_mode = KS_MODE_STKSIZE;
229 			break;
230 		case 'b':
231 			od_bundle = optarg;
232 			break;
233 		default:
234 			usage();
235 		}
236 	}
237 
238 	int fd = ksancov_open();
239 	if (fd < 0) {
240 		perror("ksancov_open");
241 		return errno;
242 	}
243 	fprintf(stderr, "opened ksancov on fd %i\n", fd);
244 
245 	/* Initialize ksancov state. */
246 	ret = ksancov_init_state(fd, ksan_mode, max_entries, &ksan_state);
247 	if (ret) {
248 		perror("ksancov init\n");
249 		return ret;
250 	}
251 
252 	/* Execute binary (when provided) with enabled coverage collection. Run getppid() otherwise. */
253 	if (path) {
254 		int pid = fork();
255 		if (pid == 0) {
256 			/* child */
257 
258 			ret = ksancov_thread_self(fd);
259 			if (ret) {
260 				perror("ksancov thread");
261 				return ret;
262 			}
263 
264 			ksancov_on_demand_set_enabled(fd, od_bundle, true);
265 			ksancov_reset(ksan_state.ks_header);
266 			ksancov_start(ksan_state.ks_header);
267 			ret = execl(path, path, 0);
268 			perror("execl");
269 			ksancov_on_demand_set_enabled(fd, od_bundle, false);
270 
271 			exit(1);
272 		} else {
273 			/* parent */
274 			waitpid(pid, NULL, 0);
275 			ksancov_stop(ksan_state.ks_header);
276 			ksancov_on_demand_set_enabled(fd, od_bundle, false);
277 		}
278 	} else {
279 		ret = ksancov_thread_self(fd);
280 		if (ret) {
281 			perror("ksancov thread");
282 			return ret;
283 		}
284 
285 		ksancov_on_demand_set_enabled(fd, od_bundle, true);
286 		ksancov_reset(ksan_state.ks_header);
287 		ksancov_start(ksan_state.ks_header);
288 		int ppid = getppid();
289 		ksancov_stop(ksan_state.ks_header);
290 		ksancov_on_demand_set_enabled(fd, od_bundle, false);
291 		fprintf(stderr, "ppid = %i\n", ppid);
292 	}
293 
294 	/* Print report and cleanup. */
295 	ksancov_print_state(&ksan_state);
296 	ret = close(fd);
297 	fprintf(stderr, "close = %i\n", ret);
298 
299 	return 0;
300 }
301