Make vgdb.c work on Android, so that the GDB server as a whole

will work on Android.  Fixes #283600.
(Philippe Waroquiers, philippe.waroquiers@skynet.be)



git-svn-id: svn://svn.valgrind.org/valgrind/trunk@12204
This commit is contained in:
Julian Seward 2011-10-22 20:38:08 +00:00
parent a07f759681
commit ba88dbd353
3 changed files with 157 additions and 39 deletions

View 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;

View File

@ -26,21 +26,6 @@
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) */

View 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