| # -*- coding: utf-8 -*- |
| |
| #------------------------------------------------------------------------- |
| # drawElements Quality Program utilities |
| # -------------------------------------- |
| # |
| # Copyright 2015 The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| #------------------------------------------------------------------------- |
| |
| import sys |
| import os |
| import time |
| import string |
| import shutil |
| import subprocess |
| import signal |
| import argparse |
| |
| import common |
| |
| def getADBProgramPID(program): |
| adbCmd = common.shellquote(common.ADB_BIN) |
| pid = -1 |
| |
| process = subprocess.Popen("%s shell ps" % adbCmd, shell=True, stdout=subprocess.PIPE) |
| |
| firstLine = True |
| for line in process.stdout.readlines(): |
| if firstLine: |
| firstLine = False |
| continue |
| |
| fields = string.split(line) |
| fields = filter(lambda x: len(x) > 0, fields) |
| |
| if len(fields) < 9: |
| continue |
| |
| if fields[8] == program: |
| assert pid == -1 |
| pid = int(fields[1]) |
| |
| process.wait() |
| |
| if process.returncode != 0: |
| print("adb shell ps returned %s" % str(process.returncode)) |
| pid = -1 |
| |
| return pid |
| |
| def debug( |
| adbCmd, |
| deqpCmdLine, |
| targetGDBPort, |
| hostGDBPort, |
| jdbPort, |
| jdbCmd, |
| gdbCmd, |
| buildDir, |
| deviceLibs, |
| breakpoints |
| ): |
| |
| programPid = -1 |
| gdbServerProcess = None |
| gdbProcess = None |
| jdbProcess = None |
| curDir = os.getcwd() |
| debugDir = os.path.join(common.ANDROID_DIR, "debug") |
| |
| if os.path.exists(debugDir): |
| shutil.rmtree(debugDir) |
| |
| os.makedirs(debugDir) |
| os.chdir(debugDir) |
| |
| try: |
| # Start execution |
| print("Starting intent...") |
| common.execute("%s shell am start -W -D -n com.drawelements.deqp/android.app.NativeActivity -e cmdLine \"unused %s\"" % (adbCmd, deqpCmdLine.replace("\"", "\\\""))) |
| print("Intent started") |
| |
| # Kill existing gdbservers |
| print("Check and kill existing gdbserver") |
| gdbPid = getADBProgramPID("lib/gdbserver") |
| if gdbPid != -1: |
| print("Found gdbserver with PID %i" % gdbPid) |
| common.execute("%s shell run-as com.drawelements.deqp kill -9 %i" % (adbCmd, gdbPid)) |
| print("Killed gdbserver") |
| else: |
| print("Couldn't find existing gdbserver") |
| |
| programPid = getADBProgramPID("com.drawelements.deqp:testercore") |
| |
| print("Find process PID") |
| if programPid == -1: |
| common.die("Couldn't get PID of testercore") |
| print("Process running with PID %i" % programPid) |
| |
| # Start gdbserver |
| print("Start gdbserver for PID %i redirect stdout to gdbserver-stdout.txt" % programPid) |
| gdbServerProcess = subprocess.Popen("%s shell run-as com.drawelements.deqp lib/gdbserver localhost:%i --attach %i" % (adbCmd, targetGDBPort, programPid), shell=True, stdin=subprocess.PIPE, stdout=open("gdbserver-stdout.txt", "wb"), stderr=open("gdbserver-stderr.txt", "wb")) |
| print("gdbserver started") |
| |
| time.sleep(1) |
| |
| gdbServerProcess.poll() |
| |
| if gdbServerProcess.returncode != None: |
| common.die("gdbserver returned unexpectly with return code %i see gdbserver-stdout.txt for more info" % gdbServerProcess.returncode) |
| |
| # Setup port forwarding |
| print("Forwarding local port to gdbserver port") |
| common.execute("%s forward tcp:%i tcp:%i" % (adbCmd, hostGDBPort, targetGDBPort)) |
| |
| # Pull some data files for debugger |
| print("Pull /system/bin/app_process from device") |
| common.execute("%s pull /system/bin/app_process" % adbCmd) |
| |
| print("Pull /system/bin/linker from device") |
| common.execute("%s pull /system/bin/linker" % adbCmd) |
| |
| for lib in deviceLibs: |
| print("Pull library %s from device" % lib) |
| common.execute("%s pull %s" % (adbCmd, lib)) |
| |
| print("Copy %s from build dir" % common.NATIVE_LIB_NAME) |
| shutil.copyfile(os.path.join(buildDir, common.NATIVE_LIB_NAME), common.NATIVE_LIB_NAME) |
| |
| # Forward local port for jdb |
| print("Forward local port to jdb port") |
| common.execute("%s forward tcp:%i jdwp:%i" % (adbCmd, jdbPort, programPid)) |
| |
| # Connect JDB |
| print("Start jdb process redirectd stdout to jdb-stdout.txt") |
| jdbProcess = subprocess.Popen("%s -connect com.sun.jdi.SocketAttach:hostname=localhost,port=%i -sourcepath ../package" % (jdbCmd, jdbPort), shell=True, stdin=subprocess.PIPE, stdout=open("jdb-stdout.txt", "wb"), stderr=open("jdb-stderr.txt", "wb")) |
| print("Started jdb process") |
| |
| # Write gdb.setup |
| print("Write gdb.setup") |
| gdbSetup = open("gdb.setup", "wb") |
| gdbSetup.write("file app_process\n") |
| gdbSetup.write("set solib-search-path .\n") |
| gdbSetup.write("target remote :%i\n" % hostGDBPort) |
| gdbSetup.write("set breakpoint pending on\n") |
| |
| for breakpoint in breakpoints: |
| print("Set breakpoint at %s" % breakpoint) |
| gdbSetup.write("break %s\n" % breakpoint) |
| |
| gdbSetup.write("set breakpoint pending off\n") |
| gdbSetup.close() |
| |
| print("Start gdb") |
| gdbProcess = subprocess.Popen("%s -x gdb.setup" % common.shellquote(gdbCmd), shell=True) |
| |
| gdbProcess.wait() |
| |
| print("gdb returned with %i" % gdbProcess.returncode) |
| gdbProcess=None |
| |
| print("Close jdb process with 'quit'") |
| jdbProcess.stdin.write("quit\n") |
| jdbProcess.wait() |
| print("JDB returned %s" % str(jdbProcess.returncode)) |
| jdbProcess=None |
| |
| print("Kill gdbserver process") |
| gdbServerProcess.kill() |
| gdbServerProcess=None |
| print("Killed gdbserver process") |
| |
| print("Kill program %i" % programPid) |
| common.execute("%s shell run-as com.drawelements.deqp -9 %i" % (adbCmd, programPid)) |
| print("Killed program") |
| |
| finally: |
| if jdbProcess and jdbProcess.returncode == None: |
| print("Kill jdb") |
| jdbProcess.kill() |
| elif jdbProcess: |
| print("JDB returned %i" % jdbProcess.returncode) |
| |
| if gdbProcess and gdbProcess.returncode == None: |
| print("Kill gdb") |
| gdbProcess.kill() |
| elif gdbProcess: |
| print("GDB returned %i" % gdbProcess.returncode) |
| |
| if gdbServerProcess and gdbServerProcess.returncode == None: |
| print("Kill gdbserver") |
| gdbServerProcess.kill() |
| elif gdbServerProcess: |
| print("GDB server returned %i" % gdbServerProcess.returncode) |
| |
| print("Kill program %i" % programPid) |
| common.execute("%s shell run-as com.drawelements.deqp kill -9 %i" % (adbCmd, programPid)) |
| print("Killed program") |
| |
| os.chdir(curDir) |
| |
| if __name__ == "__main__": |
| parser = argparse.ArgumentParser() |
| |
| defaultDeviceLibs = { |
| "nexus-4" : [ |
| "/system/lib/libgenlock.so", |
| "/system/lib/libmemalloc.so", |
| "/system/lib/libqdutils.so", |
| "/system/lib/libsc-a3xx.so" |
| ] |
| } |
| |
| defaultDevices = [] |
| |
| for device in defaultDeviceLibs: |
| defaultDevices += [device] |
| |
| parser.add_argument('--adb', dest='adbCmd', default=common.shellquote(common.ADB_BIN), help="Path to adb command. Use absolute paths.") |
| parser.add_argument('--deqp-commandline', dest='deqpCmdLine', default="--deqp-log-filename=/sdcard/TestLog.qpa", help="Command line arguments passed to dEQP test binary.") |
| |
| if common.getPlatform() == "linux": |
| parser.add_argument('--gdb', dest='gdbCmd', default=common.shellquote(os.path.join(common.ANDROID_NDK_PATH, "toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86/bin/arm-linux-androideabi-gdb")), help="gdb command used by script. Use absolute paths") |
| else: |
| parser.add_argument('--gdb', dest='gdbCmd', default=common.shellquote(os.path.join(common.ANDROID_NDK_PATH, "toolchains/arm-linux-androideabi-4.8/prebuilt/windows/bin/arm-linux-androideabi-gdb")), help="gdb command used by script. Use absolute paths") |
| |
| parser.add_argument('--target-gdb-port', dest='targetGDBPort', default=60001, type=int, help="Port used by gdbserver on target.") |
| parser.add_argument('--host-gdb-port', dest='hostGDBPort', default=60002, type=int, help="Host port that is forwarded to device gdbserver port.") |
| parser.add_argument('--jdb', dest='jdbCmd', default="jdb", help="Path to jdb command. Use absolute paths.") |
| parser.add_argument('--jdb-port', dest='jdbPort', default=60003, type=int, help="Host port used to forward jdb commands to device.") |
| parser.add_argument('--build-dir', dest='buildDir', default="../../../deqp-build-android-9-armeabi-v7a-debug", help="Path to dEQP native build directory.") |
| parser.add_argument('--device-libs', dest='deviceLibs', default=[], nargs='+', help="List of libraries that should be pulled from device for debugging.") |
| parser.add_argument('--breakpoints', dest='breakpoints', default=["tcu::App::App"], nargs='+', help="List of breakpoints that are set by gdb.") |
| parser.add_argument('--device', dest='device', default=None, choices=defaultDevices, help="Pull default libraries for this device.") |
| |
| args = parser.parse_args() |
| |
| debug(adbCmd=os.path.normpath(args.adbCmd), |
| gdbCmd=os.path.normpath(args.gdbCmd), |
| targetGDBPort=args.targetGDBPort, |
| hostGDBPort=args.hostGDBPort, |
| jdbCmd=os.path.normpath(args.jdbCmd), |
| jdbPort=args.jdbPort, |
| deqpCmdLine=args.deqpCmdLine, |
| buildDir=args.buildDir, |
| deviceLibs=["/system/lib/libc.so", "/system/lib/libdl.so"] + args.deviceLibs + (defaultDeviceLibs[args.device] if args.device else []), |
| breakpoints=args.breakpoints) |