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