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