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