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