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 "migration/qemu-file.h"
17#include "hw/android/goldfish/trace.h"
18#include "hw/android/goldfish/vmem.h"
19#include "sysemu/sysemu.h"
20
21/* Set to 1 to debug tracing */
22#define DEBUG   0
23
24#if DEBUG
25#  define D(...)  printf(__VA_ARGS__), fflush(stdout)
26#else
27#  define D(...)  ((void)0)
28#endif
29
30/* Set to 1 to debug PID tracking */
31#define  DEBUG_PID  0
32
33#if DEBUG_PID
34#  define  DPID(...)  printf(__VA_ARGS__), fflush(stdout)
35#else
36#  define  DPID(...)  ((void)0)
37#endif
38
39// TODO(digit): Re-enable tracing some day?
40#define tracing 0
41
42extern void cpu_loop_exit(CPUArchState* env);
43
44extern const char *trace_filename;
45
46/* for execve */
47static char exec_path[CLIENT_PAGE_SIZE];
48static char exec_arg[CLIENT_PAGE_SIZE];
49static unsigned long vstart;    // VM start
50static unsigned long vend;      // VM end
51static unsigned long eoff;      // offset in EXE file
52static unsigned cmdlen;         // cmdline length
53static unsigned pid;            // PID (really thread id)
54static unsigned tgid;           // thread group id (really process id)
55static unsigned tid;            // current thread id (same as pid, most of the time)
56static unsigned long dsaddr;    // dynamic symbol address
57static unsigned long unmap_start; // start address to unmap
58
59/* for context switch */
60//static unsigned long cs_pid;    // context switch PID
61
62/* I/O write */
63static void trace_dev_write(void *opaque, hwaddr offset, uint32_t value)
64{
65    trace_dev_state *s = (trace_dev_state *)opaque;
66
67    (void)s;
68
69    switch (offset >> 2) {
70    case TRACE_DEV_REG_SWITCH:  // context switch, switch to pid
71        DPID("QEMU.trace: context switch tid=%u\n", value);
72        if (trace_filename != NULL) {
73            D("QEMU.trace: kernel, context switch %u\n", value);
74        }
75        tid = (unsigned) value;
76        break;
77    case TRACE_DEV_REG_TGID:    // save the tgid for the following fork/clone
78        DPID("QEMU.trace: tgid=%u\n", value);
79        tgid = value;
80        if (trace_filename != NULL) {
81            D("QEMU.trace: kernel, tgid %u\n", value);
82        }
83        break;
84    case TRACE_DEV_REG_FORK:    // fork, fork new pid
85        DPID("QEMU.trace: fork (pid=%d tgid=%d value=%d)\n", pid, tgid, value);
86        if (trace_filename != NULL) {
87            D("QEMU.trace: kernel, fork %u\n", value);
88        }
89        break;
90    case TRACE_DEV_REG_CLONE:    // fork, clone new pid (i.e. thread)
91        DPID("QEMU.trace: clone (pid=%d tgid=%d value=%d)\n", pid, tgid, value);
92        if (trace_filename != NULL) {
93            D("QEMU.trace: kernel, clone %u\n", value);
94        }
95        break;
96    case TRACE_DEV_REG_EXECVE_VMSTART:  // execve, vstart
97        vstart = value;
98        break;
99    case TRACE_DEV_REG_EXECVE_VMEND:    // execve, vend
100        vend = value;
101        break;
102    case TRACE_DEV_REG_EXECVE_OFFSET:   // execve, offset in EXE
103        eoff = value;
104        break;
105    case TRACE_DEV_REG_EXECVE_EXEPATH:  // init exec, path of EXE
106        vstrcpy(value, exec_path, CLIENT_PAGE_SIZE);
107        if (trace_filename != NULL) {
108            D("QEMU.trace: kernel, init exec [%lx,%lx]@%lx [%s]\n",
109              vstart, vend, eoff, exec_path);
110        }
111        exec_path[0] = 0;
112        break;
113    case TRACE_DEV_REG_CMDLINE_LEN:     // execve, process cmdline length
114        cmdlen = value;
115        break;
116    case TRACE_DEV_REG_CMDLINE:         // execve, process cmdline
117        safe_memory_rw_debug(current_cpu, value, (uint8_t*)exec_arg, cmdlen, 0);
118        if (trace_filename != NULL) {
119            D("QEMU.trace: kernel, execve [%.*s]\n", cmdlen, exec_arg);
120        }
121#if DEBUG || DEBUG_PID
122        if (trace_filename != NULL) {
123            int i;
124            for (i = 0; i < cmdlen; i ++)
125                if (i != cmdlen - 1 && exec_arg[i] == 0)
126                    exec_arg[i] = ' ';
127            printf("QEMU.trace: kernel, execve %s[%d]\n", exec_arg, cmdlen);
128            exec_arg[0] = 0;
129        }
130#endif
131        break;
132    case TRACE_DEV_REG_EXIT:            // exit, exit current process with exit code
133        DPID("QEMU.trace: exit tid=%u\n", value);
134        if (trace_filename != NULL) {
135            D("QEMU.trace: kernel, exit %x\n", value);
136        }
137        break;
138    case TRACE_DEV_REG_NAME:            // record thread name
139        vstrcpy(value, exec_path, CLIENT_PAGE_SIZE);
140        DPID("QEMU.trace: thread name=%s\n", exec_path);
141
142        // Remove the trailing newline if it exists
143        int len = strlen(exec_path);
144        if (exec_path[len - 1] == '\n') {
145            exec_path[len - 1] = 0;
146        }
147        if (trace_filename != NULL) {
148            D("QEMU.trace: kernel, name %s\n", exec_path);
149        }
150        break;
151    case TRACE_DEV_REG_MMAP_EXEPATH:    // mmap, path of EXE, the others are same as execve
152        vstrcpy(value, exec_path, CLIENT_PAGE_SIZE);
153        DPID("QEMU.trace: mmap exe=%s\n", exec_path);
154        if (trace_filename != NULL) {
155            D("QEMU.trace: kernel, mmap [%lx,%lx]@%lx [%s]\n", vstart, vend, eoff, exec_path);
156        }
157        exec_path[0] = 0;
158        break;
159    case TRACE_DEV_REG_INIT_PID:        // init, name the pid that starts before device registered
160        pid = value;
161        DPID("QEMU.trace: pid=%d\n", value);
162        break;
163    case TRACE_DEV_REG_INIT_NAME:       // init, the comm of the init pid
164        vstrcpy(value, exec_path, CLIENT_PAGE_SIZE);
165        DPID("QEMU.trace: tgid=%d pid=%d name=%s\n", tgid, pid, exec_path);
166        if (trace_filename != NULL) {
167            D("QEMU.trace: kernel, init name %u [%s]\n", pid, exec_path);
168        }
169        exec_path[0] = 0;
170        break;
171
172    case TRACE_DEV_REG_DYN_SYM_ADDR:    // dynamic symbol address
173        dsaddr = value;
174        break;
175    case TRACE_DEV_REG_DYN_SYM:         // add dynamic symbol
176        vstrcpy(value, exec_arg, CLIENT_PAGE_SIZE);
177        if (trace_filename != NULL) {
178            D("QEMU.trace: dynamic symbol %lx:%s\n", dsaddr, exec_arg);
179        }
180        exec_arg[0] = 0;
181        break;
182    case TRACE_DEV_REG_REMOVE_ADDR:         // remove dynamic symbol addr
183        if (trace_filename != NULL) {
184            D("QEMU.trace: dynamic symbol remove %lx\n", dsaddr);
185        }
186        break;
187
188    case TRACE_DEV_REG_PRINT_STR:       // print string
189        vstrcpy(value, exec_arg, CLIENT_PAGE_SIZE);
190        printf("%s", exec_arg);
191        exec_arg[0] = 0;
192        break;
193    case TRACE_DEV_REG_PRINT_NUM_DEC:   // print number in decimal
194        printf("%d", value);
195        break;
196    case TRACE_DEV_REG_PRINT_NUM_HEX:   // print number in hexical
197        printf("%x", value);
198        break;
199
200    case TRACE_DEV_REG_STOP_EMU:        // stop the VM execution
201        cpu_single_env->exception_index = EXCP_HLT;
202        current_cpu->halted = 1;
203        qemu_system_shutdown_request();
204        cpu_loop_exit(cpu_single_env);
205        break;
206
207    case TRACE_DEV_REG_ENABLE:          // tracing enable: 0 = stop, 1 = start
208        break;
209
210    case TRACE_DEV_REG_UNMAP_START:
211        unmap_start = value;
212        break;
213    case TRACE_DEV_REG_UNMAP_END:
214        break;
215
216    case TRACE_DEV_REG_METHOD_ENTRY:
217    case TRACE_DEV_REG_METHOD_EXIT:
218    case TRACE_DEV_REG_METHOD_EXCEPTION:
219    case TRACE_DEV_REG_NATIVE_ENTRY:
220    case TRACE_DEV_REG_NATIVE_EXIT:
221    case TRACE_DEV_REG_NATIVE_EXCEPTION:
222        if (trace_filename != NULL) {
223            if (tracing) {
224                int __attribute__((unused)) call_type = (offset - 4096) >> 2;
225                //trace_interpreted_method(value, call_type);
226            }
227        }
228        break;
229
230    default:
231        if (offset < 4096) {
232            cpu_abort(cpu_single_env, "trace_dev_write: Bad offset %x\n", offset);
233        } else {
234            D("%s: offset=%d (0x%x) value=%d (0x%x)\n", __FUNCTION__, offset,
235              offset, value, value);
236        }
237        break;
238    }
239}
240
241/* I/O read */
242static uint32_t trace_dev_read(void *opaque, hwaddr offset)
243{
244    trace_dev_state *s = (trace_dev_state *)opaque;
245
246    (void)s;
247
248    switch (offset >> 2) {
249    case TRACE_DEV_REG_ENABLE:          // tracing enable
250        return tracing;
251
252    default:
253        if (offset < 4096) {
254            cpu_abort(cpu_single_env, "trace_dev_read: Bad offset %x\n", offset);
255        } else {
256            D("%s: offset=%d (0x%x)\n", __FUNCTION__, offset, offset);
257        }
258        return 0;
259    }
260    return 0;
261}
262
263static CPUReadMemoryFunc *trace_dev_readfn[] = {
264   trace_dev_read,
265   trace_dev_read,
266   trace_dev_read
267};
268
269static CPUWriteMemoryFunc *trace_dev_writefn[] = {
270   trace_dev_write,
271   trace_dev_write,
272   trace_dev_write
273};
274
275/* initialize the trace device */
276void trace_dev_init()
277{
278    trace_dev_state *s;
279
280    s = (trace_dev_state *)g_malloc0(sizeof(trace_dev_state));
281    s->dev.name = "qemu_trace";
282    s->dev.id = -1;
283    s->dev.base = 0;       // will be allocated dynamically
284    s->dev.size = 0x2000;
285    s->dev.irq = 0;
286    s->dev.irq_count = 0;
287
288    goldfish_device_add(&s->dev, trace_dev_readfn, trace_dev_writefn, s);
289
290    exec_path[0] = exec_arg[0] = '\0';
291}
292