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