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
43extern void cpu_loop_exit(void);
44
45extern int tracing;
46extern const char *trace_filename;
47
48/* for execve */
49static char exec_path[CLIENT_PAGE_SIZE];
50static char exec_arg[CLIENT_PAGE_SIZE];
51static unsigned long vstart;    // VM start
52static unsigned long vend;      // VM end
53static unsigned long eoff;      // offset in EXE file
54static unsigned cmdlen;         // cmdline length
55static unsigned pid;            // PID (really thread id)
56static unsigned tgid;           // thread group id (really process id)
57static unsigned tid;            // current thread id (same as pid, most of the time)
58static unsigned long dsaddr;    // dynamic symbol address
59static 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 */
65static 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 */
363static 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
384static CPUReadMemoryFunc *trace_dev_readfn[] = {
385   trace_dev_read,
386   trace_dev_read,
387   trace_dev_read
388};
389
390static CPUWriteMemoryFunc *trace_dev_writefn[] = {
391   trace_dev_write,
392   trace_dev_write,
393   trace_dev_write
394};
395
396/* initialize the trace device */
397void 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