goldfish_trace: Hook with goldfish_pipe.h functions.

Note that this is also forces tracing support during machine
initialization. Measurements show that this doesn't have any
significant impact on emulation performance.

Change-Id: Ie32352659611531ce65ba2c95c5896e2421f509d
diff --git a/hw/android_arm.c b/hw/android_arm.c
index 6062981..664ae36 100644
--- a/hw/android_arm.c
+++ b/hw/android_arm.c
@@ -146,6 +146,7 @@
 #ifdef CONFIG_MEMCHECK
         || memcheck_enabled
 #endif  // CONFIG_MEMCHECK
+        || 1  /* XXX: ALWAYS AVAILABLE FOR QEMUD PIPES */
        ) {
         trace_dev_init();
     }
diff --git a/hw/goldfish_trace.c b/hw/goldfish_trace.c
index fc338c8..ee51a8b 100644
--- a/hw/goldfish_trace.c
+++ b/hw/goldfish_trace.c
@@ -15,6 +15,7 @@
  */
 #include "qemu_file.h"
 #include "goldfish_trace.h"
+#include "goldfish_pipe.h"
 #include "sysemu.h"
 #include "trace.h"
 #ifdef CONFIG_MEMCHECK
@@ -22,7 +23,23 @@
 #include "memcheck/memcheck_util.h"
 #endif  // CONFIG_MEMCHECK
 
-//#define DEBUG   1
+/* Set to 1 to debug tracing */
+#define DEBUG   0
+
+#if DEBUG
+#  define D(...)  printf(__VA_ARGS__), fflush(stdout)
+#else
+#  define D(...)  ((void)0)
+#endif
+
+/* Set to 1 to debug PID tracking */
+#define  DEBUG_PID  0
+
+#if DEBUG_PID
+#  define  DPID(...)  printf(__VA_ARGS__), fflush(stdout)
+#else
+#  define  DPID(...)  ((void)0)
+#endif
 
 extern void cpu_loop_exit(void);
 
@@ -38,6 +55,7 @@
 static unsigned cmdlen;         // cmdline length
 static unsigned pid;            // PID (really thread id)
 static unsigned tgid;           // thread group id (really process id)
+static unsigned tid;            // current thread id (same as pid, most of the time)
 static unsigned long dsaddr;    // dynamic symbol address
 static unsigned long unmap_start; // start address to unmap
 
@@ -53,32 +71,30 @@
 
     switch (offset >> 2) {
     case TRACE_DEV_REG_SWITCH:  // context switch, switch to pid
+        DPID("QEMU.trace: context switch tid=%u\n", value);
         if (trace_filename != NULL) {
             trace_switch(value);
-#ifdef DEBUG
-            printf("QEMU.trace: kernel, context switch %u\n", value);
-#endif
+            D("QEMU.trace: kernel, context switch %u\n", value);
         }
 #ifdef CONFIG_MEMCHECK
         if (memcheck_enabled) {
             memcheck_switch(value);
         }
 #endif  // CONFIG_MEMCHECK
+        tid = (unsigned) value;
         break;
     case TRACE_DEV_REG_TGID:    // save the tgid for the following fork/clone
+        DPID("QEMU.trace: tgid=%u\n", value);
         tgid = value;
-#ifdef DEBUG
         if (trace_filename != NULL) {
-            printf("QEMU.trace: kernel, tgid %u\n", value);
+            D("QEMU.trace: kernel, tgid %u\n", value);
         }
-#endif
         break;
     case TRACE_DEV_REG_FORK:    // fork, fork new pid
+        DPID("QEMU.trace: fork (pid=%d tgid=%d value=%d)\n", pid, tgid, value);
         if (trace_filename != NULL) {
             trace_fork(tgid, value);
-#ifdef DEBUG
-            printf("QEMU.trace: kernel, fork %u\n", value);
-#endif
+            D("QEMU.trace: kernel, fork %u\n", value);
         }
 #ifdef CONFIG_MEMCHECK
         if (memcheck_enabled) {
@@ -87,11 +103,10 @@
 #endif  // CONFIG_MEMCHECK
         break;
     case TRACE_DEV_REG_CLONE:    // fork, clone new pid (i.e. thread)
+        DPID("QEMU.trace: clone (pid=%d tgid=%d value=%d)\n", pid, tgid, value);
         if (trace_filename != NULL) {
             trace_clone(tgid, value);
-#ifdef DEBUG
-            printf("QEMU.trace: kernel, clone %u\n", value);
-#endif
+            D("QEMU.trace: kernel, clone %u\n", value);
         }
 #ifdef CONFIG_MEMCHECK
         if (memcheck_enabled) {
@@ -112,10 +127,8 @@
         vstrcpy(value, exec_path, CLIENT_PAGE_SIZE);
         if (trace_filename != NULL) {
             trace_init_exec(vstart, vend, eoff, exec_path);
-#ifdef DEBUG
-            printf("QEMU.trace: kernel, init exec [%lx,%lx]@%lx [%s]\n",
-                   vstart, vend, eoff, exec_path);
-#endif
+            D("QEMU.trace: kernel, init exec [%lx,%lx]@%lx [%s]\n",
+              vstart, vend, eoff, exec_path);
         }
 #ifdef CONFIG_MEMCHECK
         if (memcheck_enabled) {
@@ -142,7 +155,7 @@
             memcheck_set_cmd_line(exec_arg, cmdlen);
         }
 #endif  // CONFIG_MEMCHECK
-#ifdef DEBUG
+#if DEBUG || DEBUG_PID
         if (trace_filename != NULL) {
             int i;
             for (i = 0; i < cmdlen; i ++)
@@ -154,20 +167,21 @@
 #endif
         break;
     case TRACE_DEV_REG_EXIT:            // exit, exit current process with exit code
+        DPID("QEMU.trace: exit tid=%u\n", value);
         if (trace_filename != NULL) {
             trace_exit(value);
-#ifdef DEBUG
-            printf("QEMU.trace: kernel, exit %x\n", value);
-#endif
+            D("QEMU.trace: kernel, exit %x\n", value);
         }
 #ifdef CONFIG_MEMCHECK
         if (memcheck_enabled) {
             memcheck_exit(value);
         }
 #endif  // CONFIG_MEMCHECK
+        goldfish_pipe_thread_death((int)tid);
         break;
     case TRACE_DEV_REG_NAME:            // record thread name
         vstrcpy(value, exec_path, CLIENT_PAGE_SIZE);
+        DPID("QEMU.trace: thread name=%s\n", exec_path);
 
         // Remove the trailing newline if it exists
         int len = strlen(exec_path);
@@ -176,18 +190,15 @@
         }
         if (trace_filename != NULL) {
             trace_name(exec_path);
-#ifdef DEBUG
-            printf("QEMU.trace: kernel, name %s\n", exec_path);
-#endif
+            D("QEMU.trace: kernel, name %s\n", exec_path);
         }
         break;
     case TRACE_DEV_REG_MMAP_EXEPATH:    // mmap, path of EXE, the others are same as execve
         vstrcpy(value, exec_path, CLIENT_PAGE_SIZE);
+        DPID("QEMU.trace: mmap exe=%s\n", exec_path);
         if (trace_filename != NULL) {
             trace_mmap(vstart, vend, eoff, exec_path);
-#ifdef DEBUG
-            printf("QEMU.trace: kernel, mmap [%lx,%lx]@%lx [%s]\n", vstart, vend, eoff, exec_path);
-#endif
+            D("QEMU.trace: kernel, mmap [%lx,%lx]@%lx [%s]\n", vstart, vend, eoff, exec_path);
         }
 #ifdef CONFIG_MEMCHECK
         if (memcheck_enabled) {
@@ -203,6 +214,7 @@
         break;
     case TRACE_DEV_REG_INIT_PID:        // init, name the pid that starts before device registered
         pid = value;
+        DPID("QEMU.trace: pid=%d\n", value);
 #ifdef CONFIG_MEMCHECK
         if (memcheck_enabled) {
             memcheck_init_pid(value);
@@ -211,11 +223,10 @@
         break;
     case TRACE_DEV_REG_INIT_NAME:       // init, the comm of the init pid
         vstrcpy(value, exec_path, CLIENT_PAGE_SIZE);
+        DPID("QEMU.trace: tgid=%d pid=%d name=%s\n", tgid, pid, exec_path);
         if (trace_filename != NULL) {
             trace_init_name(tgid, pid, exec_path);
-#ifdef DEBUG
-            printf("QEMU.trace: kernel, init name %u [%s]\n", pid, exec_path);
-#endif
+            D("QEMU.trace: kernel, init name %u [%s]\n", pid, exec_path);
         }
         exec_path[0] = 0;
         break;
@@ -227,18 +238,14 @@
         vstrcpy(value, exec_arg, CLIENT_PAGE_SIZE);
         if (trace_filename != NULL) {
             trace_dynamic_symbol_add(dsaddr, exec_arg);
-#ifdef DEBUG
-            printf("QEMU.trace: dynamic symbol %lx:%s\n", dsaddr, exec_arg);
-#endif
+            D("QEMU.trace: dynamic symbol %lx:%s\n", dsaddr, exec_arg);
         }
         exec_arg[0] = 0;
         break;
     case TRACE_DEV_REG_REMOVE_ADDR:         // remove dynamic symbol addr
         if (trace_filename != NULL) {
             trace_dynamic_symbol_remove(value);
-#ifdef DEBUG
-            printf("QEMU.trace: dynamic symbol remove %lx\n", dsaddr);
-#endif
+            D("QEMU.trace: dynamic symbol remove %lx\n", dsaddr);
         }
         break;
 
@@ -343,9 +350,19 @@
         break;
 #endif // CONFIG_MEMCHECK
 
+    case TRACE_DEV_PIPE_COMMAND:
+    case TRACE_DEV_PIPE_ADDRESS:
+    case TRACE_DEV_PIPE_SIZE:
+    case TRACE_DEV_PIPE_CHANNEL:
+        goldfish_pipe_write(tid, ((offset >> 2) - TRACE_DEV_PIPE_BASE), value);
+        break;
+
     default:
         if (offset < 4096) {
             cpu_abort(cpu_single_env, "trace_dev_write: Bad offset %x\n", offset);
+        } else {
+            D("%s: offset=%d (0x%x) value=%d (0x%x)\n", __FUNCTION__, offset,
+              offset, value, value);
         }
         break;
     }
@@ -361,9 +378,18 @@
     switch (offset >> 2) {
     case TRACE_DEV_REG_ENABLE:          // tracing enable
         return tracing;
+
+    case TRACE_DEV_PIPE_COMMAND:
+    case TRACE_DEV_PIPE_ADDRESS:
+    case TRACE_DEV_PIPE_SIZE:
+    case TRACE_DEV_PIPE_CHANNEL:
+        return goldfish_pipe_read(tid, (offset >> 2) - TRACE_DEV_PIPE_BASE);
+
     default:
         if (offset < 4096) {
             cpu_abort(cpu_single_env, "trace_dev_read: Bad offset %x\n", offset);
+        } else {
+            D("%s: offset=%d (0x%x)\n", __FUNCTION__, offset, offset);
         }
         return 0;
     }
diff --git a/hw/goldfish_trace.h b/hw/goldfish_trace.h
index 76b61a8..61d3f4f 100644
--- a/hw/goldfish_trace.h
+++ b/hw/goldfish_trace.h
@@ -17,6 +17,11 @@
 #define CLIENT_PAGE_SIZE        4096
 
 /* trace device registers */
+
+/* The indices below all corresponds to slots that can only be accessed
+ * by the guest kernel. See below for indices reachable from the guest
+ * user-land.
+ */
 #define TRACE_DEV_REG_SWITCH            0
 #define TRACE_DEV_REG_FORK              1
 #define TRACE_DEV_REG_EXECVE_PID        2
@@ -43,6 +48,22 @@
 #define TRACE_DEV_REG_PRINT_NUM_HEX     62
 #define TRACE_DEV_REG_STOP_EMU          90
 #define TRACE_DEV_REG_ENABLE            100
+
+/* NOTE: The device's second physical page is mapped to /dev/qemu_trace
+ *        This means that if you do the following:
+ *
+ *           magicPage = my_mmap("/dev/qemu_trace", ...);
+ *           *(uint32_t*)magicPage[index] = value;
+ *
+ *        The write at address magicPage+index*4 here will be seen
+ *        by the device as a write to the i/o offset 4096 + index*4,
+ *        i.e. (1024 + index)*4.
+ *
+ *        As a consequence, any index defined below corresponds to
+ *        location (index-1024)*4 in the mmapped page in the guest.
+ */
+
+/* The first 64 entries are reserved for VM instrumentation */
 #define TRACE_DEV_REG_METHOD_ENTRY      1024
 #define TRACE_DEV_REG_METHOD_EXIT       1025
 #define TRACE_DEV_REG_METHOD_EXCEPTION  1026
@@ -50,6 +71,24 @@
 #define TRACE_DEV_REG_NATIVE_EXIT       1029
 #define TRACE_DEV_REG_NATIVE_EXCEPTION  1030
 
+/* Next, QEMUD fast pipes */
+#define TRACE_DEV_PIPE_BASE             1280    /* 1024 + (64*4) */
+#define TRACE_DEV_PIPE_COMMAND          (TRACE_DEV_PIPE_BASE + 0)
+#define TRACE_DEV_PIPE_STATUS           (TRACE_DEV_PIPE_BASE + 0)
+#define TRACE_DEV_PIPE_ADDRESS          (TRACE_DEV_PIPE_BASE + 1)
+#define TRACE_DEV_PIPE_SIZE             (TRACE_DEV_PIPE_BASE + 2)
+#define TRACE_DEV_PIPE_CHANNEL          (TRACE_DEV_PIPE_BASE + 3)
+
+/* These entries are reserved for libc instrumentation, i.e. memcheck */
+#if 0  /* see memcheck_common.h */
+#define TRACE_DEV_REG_MEMCHECK              1536  /* 1024 + (128*4) */
+#define TRACE_DEV_REG_LIBC_INIT             (TRACE_DEV_REG_MEMCHECK + MEMCHECK_EVENT_LIBC_INIT)
+#define TRACE_DEV_REG_MALLOC                (TRACE_DEV_REG_MEMCHECK + MEMCHECK_EVENT_MALLOC)
+#define TRACE_DEV_REG_FREE_PTR              (TRACE_DEV_REG_MEMCHECK + MEMCHECK_EVENT_FREE_PTR)
+#define TRACE_DEV_REG_QUERY_MALLOC          (TRACE_DEV_REG_MEMCHECK + MEMCHECK_EVENT_QUERY_MALLOC)
+#define TRACE_DEV_REG_PRINT_USER_STR        (TRACE_DEV_REG_MEMCHECK + MEMCHECK_EVENT_PRINT_USER_STR)
+#endif
+
 /* the virtual trace device state */
 typedef struct {
     struct goldfish_device dev;