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