1 /* Copyright (C) 2007-2008 The Android Open Source Project
2 **
3 ** This software is licensed under the terms of the GNU General Public
4 ** License version 2, as published by the Free Software Foundation, and
5 ** may be copied, distributed, and modified under those terms.
6 **
7 ** This program is distributed in the hope that it will be useful,
8 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
9 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 ** GNU General Public License for more details.
11 */
12 /*
13 * Virtual hardware for bridging the FUSE kernel module
14 * in the emulated OS and outside file system
15 */
16 #include "qemu_file.h"
17 #include "goldfish_trace.h"
18 #include "goldfish_vmem.h"
19 #include "sysemu.h"
20 #include "android-trace.h"
21 #ifdef CONFIG_MEMCHECK
22 #include "memcheck/memcheck.h"
23 #include "memcheck/memcheck_util.h"
24 #endif // CONFIG_MEMCHECK
25
26 /* Set to 1 to debug tracing */
27 #define DEBUG 0
28
29 #if DEBUG
30 # define D(...) printf(__VA_ARGS__), fflush(stdout)
31 #else
32 # define D(...) ((void)0)
33 #endif
34
35 /* Set to 1 to debug PID tracking */
36 #define DEBUG_PID 0
37
38 #if DEBUG_PID
39 # define DPID(...) printf(__VA_ARGS__), fflush(stdout)
40 #else
41 # define DPID(...) ((void)0)
42 #endif
43
44 extern void cpu_loop_exit(void);
45
46 extern int tracing;
47 extern const char *trace_filename;
48
49 /* for execve */
50 static char exec_path[CLIENT_PAGE_SIZE];
51 static char exec_arg[CLIENT_PAGE_SIZE];
52 static unsigned long vstart; // VM start
53 static unsigned long vend; // VM end
54 static unsigned long eoff; // offset in EXE file
55 static unsigned cmdlen; // cmdline length
56 static unsigned pid; // PID (really thread id)
57 static unsigned tgid; // thread group id (really process id)
58 static unsigned tid; // current thread id (same as pid, most of the time)
59 static unsigned long dsaddr; // dynamic symbol address
60 static unsigned long unmap_start; // start address to unmap
61
62 /* for context switch */
63 //static unsigned long cs_pid; // context switch PID
64
65 /* I/O write */
trace_dev_write(void * opaque,target_phys_addr_t offset,uint32_t value)66 static void trace_dev_write(void *opaque, target_phys_addr_t offset, uint32_t value)
67 {
68 trace_dev_state *s = (trace_dev_state *)opaque;
69
70 (void)s;
71
72 switch (offset >> 2) {
73 case TRACE_DEV_REG_SWITCH: // context switch, switch to pid
74 DPID("QEMU.trace: context switch tid=%u\n", value);
75 if (trace_filename != NULL) {
76 trace_switch(value);
77 D("QEMU.trace: kernel, context switch %u\n", value);
78 }
79 #ifdef CONFIG_MEMCHECK
80 if (memcheck_enabled) {
81 memcheck_switch(value);
82 }
83 #endif // CONFIG_MEMCHECK
84 tid = (unsigned) value;
85 break;
86 case TRACE_DEV_REG_TGID: // save the tgid for the following fork/clone
87 DPID("QEMU.trace: tgid=%u\n", value);
88 tgid = value;
89 if (trace_filename != NULL) {
90 D("QEMU.trace: kernel, tgid %u\n", value);
91 }
92 break;
93 case TRACE_DEV_REG_FORK: // fork, fork new pid
94 DPID("QEMU.trace: fork (pid=%d tgid=%d value=%d)\n", pid, tgid, value);
95 if (trace_filename != NULL) {
96 trace_fork(tgid, value);
97 D("QEMU.trace: kernel, fork %u\n", value);
98 }
99 #ifdef CONFIG_MEMCHECK
100 if (memcheck_enabled) {
101 memcheck_fork(tgid, value);
102 }
103 #endif // CONFIG_MEMCHECK
104 break;
105 case TRACE_DEV_REG_CLONE: // fork, clone new pid (i.e. thread)
106 DPID("QEMU.trace: clone (pid=%d tgid=%d value=%d)\n", pid, tgid, value);
107 if (trace_filename != NULL) {
108 trace_clone(tgid, value);
109 D("QEMU.trace: kernel, clone %u\n", value);
110 }
111 #ifdef CONFIG_MEMCHECK
112 if (memcheck_enabled) {
113 memcheck_clone(tgid, value);
114 }
115 #endif // CONFIG_MEMCHECK
116 break;
117 case TRACE_DEV_REG_EXECVE_VMSTART: // execve, vstart
118 vstart = value;
119 break;
120 case TRACE_DEV_REG_EXECVE_VMEND: // execve, vend
121 vend = value;
122 break;
123 case TRACE_DEV_REG_EXECVE_OFFSET: // execve, offset in EXE
124 eoff = value;
125 break;
126 case TRACE_DEV_REG_EXECVE_EXEPATH: // init exec, path of EXE
127 vstrcpy(value, exec_path, CLIENT_PAGE_SIZE);
128 if (trace_filename != NULL) {
129 trace_init_exec(vstart, vend, eoff, exec_path);
130 D("QEMU.trace: kernel, init exec [%lx,%lx]@%lx [%s]\n",
131 vstart, vend, eoff, exec_path);
132 }
133 #ifdef CONFIG_MEMCHECK
134 if (memcheck_enabled) {
135 if (exec_path[0] == '\0') {
136 // vstrcpy may fail to copy path. In this case lets do it
137 // differently.
138 memcheck_get_guest_kernel_string(exec_path, value, CLIENT_PAGE_SIZE);
139 }
140 memcheck_mmap_exepath(vstart, vend, eoff, exec_path);
141 }
142 #endif // CONFIG_MEMCHECK
143 exec_path[0] = 0;
144 break;
145 case TRACE_DEV_REG_CMDLINE_LEN: // execve, process cmdline length
146 cmdlen = value;
147 break;
148 case TRACE_DEV_REG_CMDLINE: // execve, process cmdline
149 safe_memory_rw_debug(cpu_single_env, value, (uint8_t*)exec_arg, cmdlen, 0);
150 if (trace_filename != NULL) {
151 trace_execve(exec_arg, cmdlen);
152 }
153 #ifdef CONFIG_MEMCHECK
154 if (memcheck_enabled) {
155 memcheck_set_cmd_line(exec_arg, cmdlen);
156 }
157 #endif // CONFIG_MEMCHECK
158 #if DEBUG || DEBUG_PID
159 if (trace_filename != NULL) {
160 int i;
161 for (i = 0; i < cmdlen; i ++)
162 if (i != cmdlen - 1 && exec_arg[i] == 0)
163 exec_arg[i] = ' ';
164 printf("QEMU.trace: kernel, execve %s[%d]\n", exec_arg, cmdlen);
165 exec_arg[0] = 0;
166 }
167 #endif
168 break;
169 case TRACE_DEV_REG_EXIT: // exit, exit current process with exit code
170 DPID("QEMU.trace: exit tid=%u\n", value);
171 if (trace_filename != NULL) {
172 trace_exit(value);
173 D("QEMU.trace: kernel, exit %x\n", value);
174 }
175 #ifdef CONFIG_MEMCHECK
176 if (memcheck_enabled) {
177 memcheck_exit(value);
178 }
179 #endif // CONFIG_MEMCHECK
180 break;
181 case TRACE_DEV_REG_NAME: // record thread name
182 vstrcpy(value, exec_path, CLIENT_PAGE_SIZE);
183 DPID("QEMU.trace: thread name=%s\n", exec_path);
184
185 // Remove the trailing newline if it exists
186 int len = strlen(exec_path);
187 if (exec_path[len - 1] == '\n') {
188 exec_path[len - 1] = 0;
189 }
190 if (trace_filename != NULL) {
191 trace_name(exec_path);
192 D("QEMU.trace: kernel, name %s\n", exec_path);
193 }
194 break;
195 case TRACE_DEV_REG_MMAP_EXEPATH: // mmap, path of EXE, the others are same as execve
196 vstrcpy(value, exec_path, CLIENT_PAGE_SIZE);
197 DPID("QEMU.trace: mmap exe=%s\n", exec_path);
198 if (trace_filename != NULL) {
199 trace_mmap(vstart, vend, eoff, exec_path);
200 D("QEMU.trace: kernel, mmap [%lx,%lx]@%lx [%s]\n", vstart, vend, eoff, exec_path);
201 }
202 #ifdef CONFIG_MEMCHECK
203 if (memcheck_enabled) {
204 if (exec_path[0] == '\0') {
205 // vstrcpy may fail to copy path. In this case lets do it
206 // differently.
207 memcheck_get_guest_kernel_string(exec_path, value, CLIENT_PAGE_SIZE);
208 }
209 memcheck_mmap_exepath(vstart, vend, eoff, exec_path);
210 }
211 #endif // CONFIG_MEMCHECK
212 exec_path[0] = 0;
213 break;
214 case TRACE_DEV_REG_INIT_PID: // init, name the pid that starts before device registered
215 pid = value;
216 DPID("QEMU.trace: pid=%d\n", value);
217 #ifdef CONFIG_MEMCHECK
218 if (memcheck_enabled) {
219 memcheck_init_pid(value);
220 }
221 #endif // CONFIG_MEMCHECK
222 break;
223 case TRACE_DEV_REG_INIT_NAME: // init, the comm of the init pid
224 vstrcpy(value, exec_path, CLIENT_PAGE_SIZE);
225 DPID("QEMU.trace: tgid=%d pid=%d name=%s\n", tgid, pid, exec_path);
226 if (trace_filename != NULL) {
227 trace_init_name(tgid, pid, exec_path);
228 D("QEMU.trace: kernel, init name %u [%s]\n", pid, exec_path);
229 }
230 exec_path[0] = 0;
231 break;
232
233 case TRACE_DEV_REG_DYN_SYM_ADDR: // dynamic symbol address
234 dsaddr = value;
235 break;
236 case TRACE_DEV_REG_DYN_SYM: // add dynamic symbol
237 vstrcpy(value, exec_arg, CLIENT_PAGE_SIZE);
238 if (trace_filename != NULL) {
239 trace_dynamic_symbol_add(dsaddr, exec_arg);
240 D("QEMU.trace: dynamic symbol %lx:%s\n", dsaddr, exec_arg);
241 }
242 exec_arg[0] = 0;
243 break;
244 case TRACE_DEV_REG_REMOVE_ADDR: // remove dynamic symbol addr
245 if (trace_filename != NULL) {
246 trace_dynamic_symbol_remove(value);
247 D("QEMU.trace: dynamic symbol remove %lx\n", dsaddr);
248 }
249 break;
250
251 case TRACE_DEV_REG_PRINT_STR: // print string
252 vstrcpy(value, exec_arg, CLIENT_PAGE_SIZE);
253 printf("%s", exec_arg);
254 exec_arg[0] = 0;
255 break;
256 case TRACE_DEV_REG_PRINT_NUM_DEC: // print number in decimal
257 printf("%d", value);
258 break;
259 case TRACE_DEV_REG_PRINT_NUM_HEX: // print number in hexical
260 printf("%x", value);
261 break;
262
263 case TRACE_DEV_REG_STOP_EMU: // stop the VM execution
264 if (trace_filename != NULL) {
265 // To ensure that the number of instructions executed in this
266 // block is correct, we pretend that there was an exception.
267 trace_exception(0);
268 }
269 cpu_single_env->exception_index = EXCP_HLT;
270 cpu_single_env->halted = 1;
271 qemu_system_shutdown_request();
272 cpu_loop_exit();
273 break;
274
275 case TRACE_DEV_REG_ENABLE: // tracing enable: 0 = stop, 1 = start
276 if (value == 1) {
277 if (trace_filename != NULL) {
278 start_tracing();
279 }
280 }
281 else if (value == 0) {
282 if (trace_filename != NULL) {
283 stop_tracing();
284
285 // To ensure that the number of instructions executed in this
286 // block is correct, we pretend that there was an exception.
287 trace_exception(0);
288 }
289 }
290 break;
291
292 case TRACE_DEV_REG_UNMAP_START:
293 unmap_start = value;
294 break;
295 case TRACE_DEV_REG_UNMAP_END:
296 if (trace_filename != NULL) {
297 trace_munmap(unmap_start, value);
298 }
299 #ifdef CONFIG_MEMCHECK
300 if (memcheck_enabled) {
301 memcheck_unmap(unmap_start, value);
302 }
303 #endif // CONFIG_MEMCHECK
304 break;
305
306 case TRACE_DEV_REG_METHOD_ENTRY:
307 case TRACE_DEV_REG_METHOD_EXIT:
308 case TRACE_DEV_REG_METHOD_EXCEPTION:
309 case TRACE_DEV_REG_NATIVE_ENTRY:
310 case TRACE_DEV_REG_NATIVE_EXIT:
311 case TRACE_DEV_REG_NATIVE_EXCEPTION:
312 if (trace_filename != NULL) {
313 if (tracing) {
314 int call_type = (offset - 4096) >> 2;
315 trace_interpreted_method(value, call_type);
316 }
317 }
318 break;
319
320 #ifdef CONFIG_MEMCHECK
321 case TRACE_DEV_REG_MALLOC:
322 if (memcheck_enabled) {
323 memcheck_guest_alloc(value);
324 }
325 break;
326
327 case TRACE_DEV_REG_FREE_PTR:
328 if (memcheck_enabled) {
329 memcheck_guest_free(value);
330 }
331 break;
332
333 case TRACE_DEV_REG_QUERY_MALLOC:
334 if (memcheck_enabled) {
335 memcheck_guest_query_malloc(value);
336 }
337 break;
338
339 case TRACE_DEV_REG_LIBC_INIT:
340 if (memcheck_enabled) {
341 memcheck_guest_libc_initialized(value);
342 }
343 break;
344
345 case TRACE_DEV_REG_PRINT_USER_STR:
346 if (memcheck_enabled) {
347 memcheck_guest_print_str(value);
348 }
349 break;
350 #endif // CONFIG_MEMCHECK
351
352 default:
353 if (offset < 4096) {
354 cpu_abort(cpu_single_env, "trace_dev_write: Bad offset %x\n", offset);
355 } else {
356 D("%s: offset=%d (0x%x) value=%d (0x%x)\n", __FUNCTION__, offset,
357 offset, value, value);
358 }
359 break;
360 }
361 }
362
363 /* I/O read */
trace_dev_read(void * opaque,target_phys_addr_t offset)364 static uint32_t trace_dev_read(void *opaque, target_phys_addr_t offset)
365 {
366 trace_dev_state *s = (trace_dev_state *)opaque;
367
368 (void)s;
369
370 switch (offset >> 2) {
371 case TRACE_DEV_REG_ENABLE: // tracing enable
372 return tracing;
373
374 default:
375 if (offset < 4096) {
376 cpu_abort(cpu_single_env, "trace_dev_read: Bad offset %x\n", offset);
377 } else {
378 D("%s: offset=%d (0x%x)\n", __FUNCTION__, offset, offset);
379 }
380 return 0;
381 }
382 return 0;
383 }
384
385 static CPUReadMemoryFunc *trace_dev_readfn[] = {
386 trace_dev_read,
387 trace_dev_read,
388 trace_dev_read
389 };
390
391 static CPUWriteMemoryFunc *trace_dev_writefn[] = {
392 trace_dev_write,
393 trace_dev_write,
394 trace_dev_write
395 };
396
397 /* initialize the trace device */
trace_dev_init()398 void trace_dev_init()
399 {
400 trace_dev_state *s;
401
402 s = (trace_dev_state *)qemu_mallocz(sizeof(trace_dev_state));
403 s->dev.name = "qemu_trace";
404 s->dev.id = -1;
405 s->dev.base = 0; // will be allocated dynamically
406 s->dev.size = 0x2000;
407 s->dev.irq = 0;
408 s->dev.irq_count = 0;
409
410 goldfish_device_add(&s->dev, trace_dev_readfn, trace_dev_writefn, s);
411
412 exec_path[0] = exec_arg[0] = '\0';
413 }
414