]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Make vgdb.c work on Android, so that the GDB server as a whole
authorJulian Seward <jseward@acm.org>
Sat, 22 Oct 2011 20:38:08 +0000 (20:38 +0000)
committerJulian Seward <jseward@acm.org>
Sat, 22 Oct 2011 20:38:08 +0000 (20:38 +0000)
will work on Android.  Fixes #283600.
(Philippe Waroquiers, philippe.waroquiers@skynet.be)

git-svn-id: svn://svn.valgrind.org/valgrind/trunk@12204

coregrind/m_options.c
coregrind/vgdb.c
docs/xml/manual-core-adv.xml

index 7ae9a7a2b94d2f0c4b9cf5cae48d12fba23bb9c4..a1bceace0c36fab4c7d3339e398628f9ad3189f8 100644 (file)
@@ -47,11 +47,7 @@ VexControl VG_(clo_vex_control);
 Bool   VG_(clo_error_limit)    = True;
 Int    VG_(clo_error_exitcode) = 0;
 
-#if defined(VGPV_arm_linux_android)
-VgVgdb VG_(clo_vgdb)           = Vg_VgdbNo; // currently disabled on Android
-#else
 VgVgdb VG_(clo_vgdb)           = Vg_VgdbYes;
-#endif
 Int    VG_(clo_vgdb_poll)      = 5000; 
 Int    VG_(clo_vgdb_error)     = 999999999;
 HChar* VG_(clo_vgdb_prefix)    = NULL;
index e250e93019a85e624f907c9d9bcd0bb8d00b5279..3c5c9d6e645fd3ca2f7e5bfa9b307cd3ddf4b9a9 100644 (file)
    The GNU General Public License is contained in the file COPYING.
 */
 
-/* Too difficult to make this work on Android right now.  Let's
-   skip for the time being at least. */
-#if defined(VGPV_arm_linux_android)
-
-#include <stdio.h>
-int main (int argc, char** argv)
-{
-   fprintf(stderr,
-           "%s: is not currently available on Android, sorry.\n",
-           argv[0]);
-   return 0;
-}
-
-#else /* all other (Linux?) platforms */
-
 #include "pub_core_basics.h"
 #include "pub_core_vki.h"
 #include "pub_core_libcsetjmp.h"
@@ -61,17 +46,14 @@ int main (int argc, char** argv)
 #include <sys/time.h>
 #include <errno.h>
 #include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
 #include <sys/mman.h>
 #include <sys/ptrace.h>
 #include <sys/wait.h>
 #include <assert.h>
-#include <sys/user.h>
-
-#if defined(VGO_linux)
-#  include <sys/prctl.h>
-#  include <linux/ptrace.h>
-#endif
-
 /* vgdb has two usages:
    1. relay application between gdb and the gdbserver embedded in valgrind.
    2. standalone to send monitor commands to a running valgrind-ified process
@@ -111,6 +93,19 @@ I_die_here : (PTRACEINVOKER) architecture missing in vgdb.c
 #undef PTRACEINVOKER
 #endif
 
+#if defined(VGPV_arm_linux_android)
+#undef PTRACEINVOKER
+#endif
+
+#if defined(PTRACEINVOKER)
+#include <sys/user.h>
+#if defined(VGO_linux)
+#  include <sys/prctl.h>
+#  include <linux/ptrace.h>
+#endif
+#endif
+
+
 // Outputs information for the user about ptrace_scope protection
 // or ptrace not working.
 static void ptrace_restrictions_msg(void);
@@ -1194,11 +1189,8 @@ void *invoke_gdbserver_in_valgrind(void *v_pid)
          if (usecs == 0 || usecs > 1000 * 1000)
             usecs = 1000 * 1000;
       }
-      if (usleep(usecs) != 0) {
-         if (errno == EINTR)
-            continue;
-         XERROR (errno, "error usleep\n");
-      }
+      usleep(usecs);
+
       /* If nothing happened during our sleep, let's try to wake up valgrind
          or check for cmd time out. */
       if (written_by_vgdb_before_sleep == VS_written_by_vgdb
@@ -1272,7 +1264,12 @@ int open_fifo (char* name, int flags, char* desc)
 static
 void acquire_lock (int fd, int valgrind_pid)
 {
-   if (lockf(fd, F_TLOCK, 1) < 0) {
+   struct flock fl;
+   fl.l_type = F_WRLCK;
+   fl.l_whence = SEEK_SET;
+   fl.l_start = 0;
+   fl.l_len = 1;
+   if (fcntl(fd, F_SETLK, &fl) < 0) {
       if (errno == EAGAIN || errno == EACCES) {
          XERROR(errno, 
                 "Cannot acquire lock.\n"
@@ -1359,7 +1356,7 @@ char *ppConnectionKind (ConnectionKind con)
 
 static char *shared_mem;
 
-static const int from_gdb = 0;
+static int from_gdb = 0; /* stdin by default, changed if --port is given. */
 static char *from_gdb_to_pid; /* fifo name to write gdb command to pid */
 /* Returns True in case read/write operations were done properly.
    Returns False in case of error.
@@ -1383,7 +1380,7 @@ Bool read_from_gdb_write_to_pid(int to_pid)
    return write_buf(to_pid, buf, nrread, "to_pid", /* notify */ True);
 }
 
-static const int to_gdb = 1;
+static int to_gdb = 1; /* stdout by default, changed if --port is given. */
 static char *to_gdb_from_pid; /* fifo name to read pid replies */
 /* Returns True in case read/write operations were done properly.
    Returns False in case of error.
@@ -1407,6 +1404,44 @@ Bool read_from_pid_write_to_gdb(int from_pid)
    return write_buf(to_gdb, buf, nrread, "to_gdb", /* notify */ False);
 }
 
+static
+void wait_for_gdb_connect (int in_port)
+{
+   struct sockaddr_in addr;
+
+   int listen_gdb = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+   int gdb_connect;
+   if (-1 == listen_gdb) {
+      XERROR(errno, "cannot create socket");
+   }
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons((unsigned short int)in_port);
+    addr.sin_addr.s_addr = INADDR_ANY;
+    if (-1 == bind(listen_gdb,(struct sockaddr *)&addr, sizeof(addr))) {
+      XERROR(errno, "bind failed");
+    }
+    fprintf(stderr, "listening on port %d ...", in_port);
+    fflush(stderr);
+    if (-1 == listen(listen_gdb, 1)) {
+      XERROR(errno, "error listen failed");
+    }
+
+    gdb_connect = accept(listen_gdb, NULL, NULL);
+    if (gdb_connect < 0) {
+        XERROR(errno, "accept failed");
+    }
+    fprintf(stderr, "connected.\n");
+    fflush(stderr);
+    close(listen_gdb);
+    from_gdb = gdb_connect;
+    to_gdb = gdb_connect;
+}
+
 /* prepares the FIFOs filenames, map the shared memory. */
 static
 void prepare_fifos_and_shared_mem(int pid)
@@ -1584,12 +1619,19 @@ void received_signal (int signum)
       sigpipe++;
    } else if (signum == SIGALRM) {
       sigalrm++;
-      DEBUG(1, "pthread_cancel invoke_gdbserver_in_valgrind_thread\n");
+#if defined(VGPV_arm_linux_android)
+      /* Android has no pthread_cancel. As it also does not have
+         PTRACE_INVOKER, there is no need for cleanup action.
+         So, we just do nothing. */
+      DEBUG(1, "sigalrm received, no action on android\n");
+#else
       /* Note: we cannot directly invoke restore_and_detach : this must
          be done by the thread that has attached. 
          We have in this thread pushed a cleanup handler that will
          cleanup what is needed. */
+      DEBUG(1, "pthread_cancel invoke_gdbserver_in_valgrind_thread\n");
       pthread_cancel(invoke_gdbserver_in_valgrind_thread);
+#endif
    } else {
       ERROR(0, "unexpected signal %d\n", signum);
    }
@@ -2008,6 +2050,7 @@ void usage(void)
 "\n"
 " OPTIONS are [--pid=<number>] [--vgdb-prefix=<prefix>]\n"
 "             [--wait=<number>] [--max-invoke-ms=<number>]\n"
+"             [--port=<portnr>\n"
 "             [--cmd-time-out=<number>] [-l] [-D] [-d]\n"
 "             \n"
 "  --pid arg must be given if multiple Valgrind gdbservers are found.\n"
@@ -2019,6 +2062,7 @@ void usage(void)
 "  --max-invoke-ms (default 100) gives the nr of milli-seconds after which vgdb\n"
 "      will force the invocation of the Valgrind gdbserver (if the Valgrind\n"
 "         process is blocked in a system call).\n"
+"  --port instructs vgdb to listen for gdb on the specified port nr.\n"
 "  --cmd-time-out (default 99999999) tells vgdb to exit if the found Valgrind\n"
 "     gdbserver has not processed a command after number seconds\n"
 "  -l  arg tells to show the list of running Valgrind gdbserver and then exit.\n"
@@ -2230,6 +2274,7 @@ void parse_options(int argc, char** argv,
                    Bool *p_show_list,
                    int *p_arg_pid,
                    int *p_check_trials,
+                   int *p_port,
                    int *p_last_command,
                    char *commands[])
 {
@@ -2238,6 +2283,7 @@ void parse_options(int argc, char** argv,
    int arg_pid = -1;
    int check_trials = 1;
    int last_command = -1;
+   int int_port = 0;
 
    int i;
    int arg_errors = 0;
@@ -2278,6 +2324,11 @@ void parse_options(int argc, char** argv,
             fprintf (stderr, "invalid --cmd-time-out argument %s\n", argv[i]);
             arg_errors++;
          }
+      } else if (is_opt(argv[i], "--port=")) {
+         if (!numeric_val(argv[i], &int_port)) {
+            fprintf (stderr, "invalid --port argument %s\n", argv[i]);
+            arg_errors++;
+         }
       } else if (is_opt(argv[i], "--vgdb-prefix=")) {
          vgdb_prefix = argv[i] + 14;
       } else if (is_opt(argv[i], "-c")) {
@@ -2315,6 +2366,7 @@ void parse_options(int argc, char** argv,
    if (isatty(0) 
        && !show_shared_mem 
        && !show_list
+       && int_port == 0
        && last_command == -1) {
       arg_errors++;
       fprintf (stderr, 
@@ -2341,6 +2393,12 @@ void parse_options(int argc, char** argv,
                "Can't use both --pid and -l options\n");
    }
 
+   if (int_port > 0 && last_command != -1) {
+      arg_errors++;
+      fprintf (stderr,
+               "Can't use --port to send commands\n");
+   }
+
    if (arg_errors > 0) {
       fprintf (stderr, "args error. Try `vgdb --help` for more information\n");
       exit(1);
@@ -2350,6 +2408,7 @@ void parse_options(int argc, char** argv,
    *p_show_list = show_list;
    *p_arg_pid = arg_pid;
    *p_check_trials = check_trials;
+   *p_port = int_port;
    *p_last_command = last_command;
 }
 
@@ -2362,6 +2421,7 @@ int main(int argc, char** argv)
    Bool show_list;
    int arg_pid;
    int check_trials;
+   int in_port;
    int last_command;
    char *commands[argc]; // we will never have more commands than args.
 
@@ -2370,6 +2430,7 @@ int main(int argc, char** argv)
                  &show_list,
                  &arg_pid,
                  &check_trials,
+                 &in_port,
                  &last_command,
                  commands);
   
@@ -2384,6 +2445,9 @@ int main(int argc, char** argv)
 
    prepare_fifos_and_shared_mem(pid);
 
+   if (in_port > 0)
+      wait_for_gdb_connect(in_port);
+
    if (show_shared_mem) {
       fprintf(stderr, 
               "vgdb %d "
@@ -2412,5 +2476,3 @@ int main(int argc, char** argv)
       free (commands[i]);
    return 0;
 }
-
-#endif /* !defined(VGPV_arm_linux_android) */
index 042c2c3d9ebce326a6ec924912c57c83f7a508a6..0ef20d2e9f0ff64a64611b8b84ccce066e3e4c15 100644 (file)
@@ -482,6 +482,44 @@ description of GDB's functionality.
 
 </sect2>
 
+<sect2 id="manual-core-adv.gdbserver-gdb-android"
+       xreflabel="Connecting to an Android gdbserver">
+<title>Connecting to an Android gdbserver</title>
+<para> When developping applications for Android, you will typically use
+a development system (on which the Android NDK is installed) to compile your
+application. An Android target system or emulator will be used to run
+the application.
+In this setup, Valgrind and vgdb will run on the Android system,
+while GDB will run on the development system. GDB will connect
+to the vgdb running on the Android system using the Android NDK
+'adb forward' application.
+</para>
+<para> Example: on the Android system, execute the following:
+    <screen><![CDATA[
+valgrind --vgdb-error=0 prog
+# and then in another shell, run:
+vgdb --port=1234
+]]></screen>
+</para>
+
+<para> On the development system, execute the following commands:
+<screen><![CDATA[
+adb forward tcp:1234 tcp:1234
+gdb prog
+(gdb) target remote :1234
+]]></screen>
+GDB will use a local tcp/ip connection to connect to the Android adb forwarder.
+Adb will establish a relay connection between the host system and the Android
+target system. Pay attention to use the GDB delivered in the
+Android NDK system (typically, arm-linux-androideabi-gdb), as the host
+GDB is probably not able to debug Android arm applications.
+Note that the local port nr (used by GDB) must not necessarily be equal
+to the port number used by vgdb: adb can forward tcp/ip between different
+port numbers.
+</para>
+
+</sect2>
+
 <sect2 id="manual-core-adv.gdbserver-commandhandling"
        xreflabel="Monitor command handling by the Valgrind gdbserver">
 <title>Monitor command handling by the Valgrind gdbserver</title>
@@ -899,8 +937,9 @@ $5 = 36
      </para>
 
      <para>Unblocking processes blocked in system calls is not
-     currently implemented on Mac OS X.  So you cannot connect to or
-     interrupt a process blocked in a system call on Mac OS X.
+     currently implemented on Mac OS X and Android.  So you cannot
+     connect to or interrupt a process blocked in a system call on Mac
+     OS X or Android.
      </para>
 
    </listitem>
@@ -1033,6 +1072,27 @@ options:</para>
     The default value is to never time out.</para>
   </listitem>
 
+  <listitem>
+    <para><option>--port=&lt;portnr&gt;</option> instructs vgdb to
+    use tcp/ip and listen for GDB on the specified port nr rather than
+    to use a pipe to communicate with GDB. Using tcp/ip allows to have
+    GDB running on one computer and debugging a Valgrind process
+    running on another target computer.
+    Example: 
+    <screen><![CDATA[
+# On the target computer, start your program under valgrind using
+valgrind --vgdb-error=0 prog
+# and then in another shell, run:
+vgdb --port=1234
+]]></screen></para>
+    <para>On the computer which hosts GDB, execute the command:
+    <screen><![CDATA[
+gdb prog
+(gdb) target remote targetip:1234
+]]></screen>
+    where targetip is the ip address or hostname of the target computer.
+    </para>
+  </listitem>
    
   <listitem>
     <para><option>-c</option> To give more than one command to a