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