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
42 extern void cpu_loop_exit(CPUArchState* env);
43
44 extern const char *trace_filename;
45
46 /* for execve */
47 static char exec_path[CLIENT_PAGE_SIZE];
48 static char exec_arg[CLIENT_PAGE_SIZE];
49 static unsigned long vstart; // VM start
50 static unsigned long vend; // VM end
51 static unsigned long eoff; // offset in EXE file
52 static unsigned cmdlen; // cmdline length
53 static unsigned pid; // PID (really thread id)
54 static unsigned tgid; // thread group id (really process id)
55 static unsigned tid; // current thread id (same as pid, most of the time)
56 static unsigned long dsaddr; // dynamic symbol address
57 static 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 */
trace_dev_write(void * opaque,hwaddr offset,uint32_t value)63 static 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 */
trace_dev_read(void * opaque,hwaddr offset)242 static 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
263 static CPUReadMemoryFunc *trace_dev_readfn[] = {
264 trace_dev_read,
265 trace_dev_read,
266 trace_dev_read
267 };
268
269 static CPUWriteMemoryFunc *trace_dev_writefn[] = {
270 trace_dev_write,
271 trace_dev_write,
272 trace_dev_write
273 };
274
275 /* initialize the trace device */
trace_dev_init()276 void 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