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