Android: Implement debugging without relying on shell run-as

This uses explicit handshakes between the application and the
gdbserver start and the host side by using the gdbserver socket
and two files ("ping" file in the application dir, "pong" file
in /data/local/tmp/qt)

The sequence is as follows:

 host: adb forward debugsocket :5039

 host: adb shell rm pong file
 host: adb shell am start
 host: loop until ping file appears

     app start up: launch gdbserver --multi +debug-socket
     app start up: loop until debug socket appear

         gdbserver: normal start up including opening debug-socket,
                    not yet attached to any process

     app start up: touch ping file
     app start up: loop until pong file appears

 host: start gdb
 host: gdb: set up binary, breakpoints, path etc
 host: gdb: target extended-remote :5039

         gdbserver: accepts connection from gdb

 host: gdb: attach <application-pid>

         gdbserver: attaches to the application
                    and stops it

     app start up: stopped now (it is still waiting for
                   the pong anyway)

 host: gdb: continue

         gdbserver: resumes application

     app start up: resumed (still waiting for the pong)

 host: write pong file

     app start up: java code continues now, the process
                   is already fully under control
                   of gdbserver. Breakpoints are set etc,
                   we are before main.
     app start up: native code launches

Change-Id: Iaa28b8664dbebc39022d1be7ff5533c52ce39715
Reviewed-by: Paul Olav Tvete <paul.tvete@digia.com>
Reviewed-by: Daniel Teske <daniel.teske@digia.com>
Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@digia.com>
bb10
hjk 2013-03-08 16:09:08 +01:00 committed by The Qt Project
parent f5ba2b8a91
commit 06d4c0b2e8
1 changed files with 107 additions and 15 deletions

View File

@ -43,6 +43,7 @@
package org.qtproject.qt5.android;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
@ -368,6 +369,11 @@ public class QtActivityDelegate
return true;
}
public void debugLog(String msg)
{
Log.i(QtNative.QtTAG, "DEBUGGER: " + msg);
}
public boolean startApplication()
{
// start application
@ -414,25 +420,111 @@ public class QtActivityDelegate
&&*/ extras != null
&& extras.containsKey("debug_ping")
&& extras.getString("debug_ping").equals("true")) {
String packagePath =
m_activity.getPackageManager().getApplicationInfo(m_activity.getPackageName(),
PackageManager.GET_CONFIGURATIONS).dataDir + "/";
String debugPing = packagePath + "debug_ping";
int i = 0;
while (true) {
++i;
Log.i(QtNative.QtTAG, "DEBUGGER: WAITING FOR PING AT " + debugPing + ", ATTEMPT " + i);
File file = new File(debugPing);
if (file.exists()) {
file.delete();
break;
try {
debugLog("extra parameters: " + extras);
String packageName = m_activity.getPackageName();
String pingFile = extras.getString("ping_file");
String pongFile = extras.getString("pong_file");
String gdbserverSocket = extras.getString("gdbserver_socket");
String gdbserverCommand = extras.getString("gdbserver_command");
boolean usePing = pingFile != null;
boolean usePong = pongFile != null;
boolean useSocket = gdbserverSocket != null;
int napTime = 200; // milliseconds between file accesses
int timeOut = 30000; // ms until we give up on ping and pong
int maxAttempts = timeOut / napTime;
if (usePing) {
debugLog("removing ping file " + pingFile);
File ping = new File(pingFile);
if (ping.exists()) {
if (!ping.delete())
debugLog("ping file cannot be deleted");
}
Thread.sleep(1000);
}
Log.i(QtNative.QtTAG, "DEBUGGER: GOT PING " + debugPing);
}
if (usePong) {
debugLog("removing pong file " + pongFile);
File pong = new File(pongFile);
if (pong.exists()) {
if (!pong.delete())
debugLog("pong file cannot be deleted");
}
}
debugLog("starting " + gdbserverCommand);
m_debuggerProcess = Runtime.getRuntime().exec(gdbserverCommand);
debugLog("gdbserver started");
if (useSocket) {
int i;
for (i = 0; i < maxAttempts; ++i) {
debugLog("waiting for socket at " + gdbserverSocket + ", attempt " + i);
File file = new File(gdbserverSocket);
if (file.exists()) {
file.setReadable(true, false);
file.setWritable(true, false);
file.setExecutable(true, false);
break;
}
Thread.sleep(napTime);
}
if (i == maxAttempts) {
debugLog("time out when waiting for socket");
return false;
}
debugLog("socket ok");
} else {
debugLog("socket not used");
}
if (usePing) {
// Tell we are ready.
debugLog("writing ping at " + pingFile);
FileWriter writer = new FileWriter(pingFile);
writer.write("" + android.os.Process.myPid());
writer.close();
File file = new File(pingFile);
file.setReadable(true, false);
file.setWritable(true, false);
file.setExecutable(true, false);
debugLog("wrote ping");
} else {
debugLog("ping not requested");
}
// Wait until other side is ready.
if (usePong) {
int i;
for (i = 0; i < maxAttempts; ++i) {
debugLog("waiting for pong at " + pongFile + ", attempt " + i);
File file = new File(pongFile);
if (file.exists()) {
file.delete();
break;
}
debugLog("go to sleep");
Thread.sleep(napTime);
}
if (i == maxAttempts) {
debugLog("time out when waiting for pong file");
return false;
}
debugLog("got pong " + pongFile);
} else {
debugLog("pong not requested");
}
} catch (IOException ioe) {
Log.e(QtNative.QtTAG,"Can't start debugger" + ioe.getMessage());
} catch (SecurityException se) {
Log.e(QtNative.QtTAG,"Can't start debugger" + se.getMessage());
}
}
if (/*(ai.flags&ApplicationInfo.FLAG_DEBUGGABLE) != 0
&&*/ extras != null