• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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