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