Home | History | Annotate | Download | only in goldfish
      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 */
     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 */
    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 */
    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