Enable debugging of RS code under GDB
- Add/integrate GDBJITRegistrar support class for interfacing with GDB
-- Once the GDBJITRegistrar is merged into LLVM trunk (and AOSP upgrades LLVM)
all files GDB* should be removed, and replaced with appropriate includes
- Basic [host|target]-side integration tests
-- host-tests: use bcc driver and clang to verify gdb output
-- target-tests: run skeleton apk on target and verify gdb output
- Add support for optimization_level metadata in bcinfo, libbcc
-- Disabled some LTO passes when optimization_level = 0
-- move register allocator registration after metadata inspection
- Initial version of android-commands.py GDB plugin (for test infrastructure)
-- relevant commands: load-android-app, run-android-app, start-android-app
-- tested versions: gdb (7.2, 7.3), python (2.6, 2.7)
- build 'bcc' driver tool by default in eng builds
Change-Id: I99e0c11c8591c6d911632c1dcc82dd8fbe1244a8
diff --git a/bcinfo/MetadataExtractor.cpp b/bcinfo/MetadataExtractor.cpp
index 763b5f4..dadca08 100644
--- a/bcinfo/MetadataExtractor.cpp
+++ b/bcinfo/MetadataExtractor.cpp
@@ -21,6 +21,7 @@
#include "llvm/ADT/OwningPtr.h"
#include "llvm/Bitcode/ReaderWriter.h"
+#include "llvm/Constants.h"
#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
#include "llvm/Support/MemoryBuffer.h"
@@ -49,12 +50,17 @@
// synced with slang_rs_metadata.h)
static const llvm::StringRef ObjectSlotMetadataName = "#rs_object_slots";
+// Name of metadata node where RS optimization level resides (should be
+// synced with slang_rs_metadata.h)
+static const llvm::StringRef OptimizationLevelMetadataName = "#optimization_level";
+
MetadataExtractor::MetadataExtractor(const char *bitcode, size_t bitcodeSize)
: mBitcode(bitcode), mBitcodeSize(bitcodeSize), mExportVarCount(0),
mExportFuncCount(0), mExportForEachSignatureCount(0),
mExportForEachSignatureList(NULL), mPragmaCount(0), mPragmaKeyList(NULL),
- mPragmaValueList(NULL), mObjectSlotCount(0), mObjectSlotList(NULL) {
+ mPragmaValueList(NULL), mObjectSlotCount(0), mObjectSlotList(NULL),
+ mOptimizationLevel(3) {
}
@@ -241,6 +247,9 @@
module->getNamedMetadata(PragmaMetadataName);
const llvm::NamedMDNode *ObjectSlotMetadata =
module->getNamedMetadata(ObjectSlotMetadataName);
+ const llvm::NamedMDNode *OptimizationLevelMetadata =
+ module->getNamedMetadata(OptimizationLevelMetadataName);
+
if (ExportVarMetadata) {
mExportVarCount = ExportVarMetadata->getNumOperands();
@@ -262,6 +271,13 @@
return false;
}
+ if (OptimizationLevelMetadata) {
+ llvm::ConstantInt* OL = llvm::dyn_cast<llvm::ConstantInt>(
+ OptimizationLevelMetadata->getOperand(0)->getOperand(0));
+ mOptimizationLevel = OL->getZExtValue();
+ }
+
+
return true;
}
diff --git a/bcinfo/tools/main.cpp b/bcinfo/tools/main.cpp
index 35b077f..09b7992 100644
--- a/bcinfo/tools/main.cpp
+++ b/bcinfo/tools/main.cpp
@@ -103,6 +103,8 @@
printf("objectSlotList[%u]: %u\n", i, slotList[i]);
}
+ printf("optimizationLevel: %u\n", ME->getOptimizationLevel());
+
return;
}
diff --git a/gdb_plugin/android-commands.py b/gdb_plugin/android-commands.py
new file mode 100644
index 0000000..b512ed0
--- /dev/null
+++ b/gdb_plugin/android-commands.py
@@ -0,0 +1,771 @@
+#
+# Copyright (C) 2012 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.
+#
+
+#
+# GDB plugin to allow debugging of apps on remote Android systems using gdbserver.
+#
+# To use this plugin, source this file from a Python-enabled GDB client, then use:
+# load-android-app <app-source-dir> to tell GDB about the app you are debugging
+# run-android-app to start the app in a running state
+# start-android-app to start the app in a paused state
+# attach-android-ap to attach to an existing (running) instance of app
+# set-android-device to select a target (only if multiple devices are attached)
+
+import fnmatch
+import gdb
+import os
+import shutil
+import subprocess
+import tempfile
+import time
+
+be_verbose = False
+enable_renderscript_dumps = True
+local_symbols_library_directory = os.path.join(os.getenv('ANDROID_PRODUCT_OUT', 'out'),
+ 'symbols', 'system', 'lib')
+local_library_directory = os.path.join(os.getenv('ANDROID_PRODUCT_OUT', 'out'),
+ 'system', 'lib')
+
+# ADB - Basic ADB wrapper, far from complete
+# DebugAppInfo - App configuration struct, as far as GDB cares
+# StartAndroidApp - Implementation of GDB start (for android apps)
+# RunAndroidApp - Implementation of GDB run (for android apps)
+# AttachAndroidApp - GDB command to attach to an existing android app process
+# AndroidStatus - app status query command (not needed, mostly harmless)
+# LoadAndroidApp - Sets the package and intent names for an app
+
+def _interesting_libs():
+ return ['libc', 'libbcc', 'libRS', 'libandroid_runtime', 'libdvm']
+
+# In python 2.6, subprocess.check_output does not exist, so it is implemented here
+def check_output(*popenargs, **kwargs):
+ p = subprocess.Popen(stdout=subprocess.PIPE, stderr=subprocess.STDOUT, *popenargs, **kwargs)
+ out, err = p.communicate()
+ retcode = p.poll()
+ if retcode != 0:
+ c = kwargs.get("args")
+ if c is None:
+ c = popenargs[0]
+ e = subprocess.CalledProcessError(retcode, c)
+ e.output = str(out) + str(err)
+ raise e
+ return out
+
+class DebugAppInfo:
+ """Stores information from an app manifest"""
+
+ def __init__(self):
+ self.name = None
+ self.intent = None
+
+ def get_name(self):
+ return self.name
+
+ def get_intent(self):
+ return self.intent
+
+ def get_data_directory(self):
+ return self.data_directory
+
+ def get_gdbserver_path(self):
+ return os.path.join(self.data_directory, "lib", "gdbserver")
+
+ def set_info(self, name, intent, data_directory):
+ self.name = name
+ self.intent = intent
+ self.data_directory = data_directory
+
+ def unset_info():
+ self.name = None
+ self.intent = None
+ self.data_directory = None
+
+class ADB:
+ """
+ Python class implementing a basic ADB wrapper for useful commands.
+ Uses subprocess to invoke adb.
+ """
+
+ def __init__(self, device=None, verbose=False):
+ self.verbose = verbose
+ self.current_device = device
+ self.temp_libdir = None
+ self.background_processes = []
+ self.android_build_top = os.getenv('ANDROID_BUILD_TOP', None)
+ if not self.android_build_top:
+ raise gdb.GdbError("Unable to read ANDROID_BUILD_TOP. " \
+ + "Is your environment setup correct?")
+
+ self.adb_path = os.path.join(self.android_build_top,
+ 'out', 'host', 'linux-x86', 'bin', 'adb')
+
+ if not self.current_device:
+ devices = self.devices()
+ if len(devices) == 1:
+ self.set_current_device(devices[0])
+ return
+ else:
+ msg = ""
+ if len(devices) == 0:
+ msg = "No devices detected. Please connect a device and "
+ else:
+ msg = "Too many devices (" + ", ".join(devices) + ") detected. " \
+ + "Please "
+
+ print "Warning: " + msg + " use the set-android-device command."
+
+
+ def _prepare_adb_args(self, args):
+ largs = list(args)
+
+ # Prepare serial number option from current_device
+ if self.current_device and len(self.current_device) > 0:
+ largs.insert(0, self.current_device)
+ largs.insert(0, "-s")
+
+ largs.insert(0, self.adb_path)
+ return largs
+
+
+ def _background_adb(self, *args):
+ largs = self._prepare_adb_args(args)
+ p = None
+ try:
+ if self.verbose:
+ print "### " + str(largs)
+ p = subprocess.Popen(largs)
+ self.background_processes.append(p)
+ except CalledProcessError, e:
+ raise gdb.GdbError("Error starting background adb " + str(largs))
+ except:
+ raise gdb.GdbError("Unknown error starting background adb " + str(largs))
+
+ return p
+
+ def _call_adb(self, *args):
+ output = ""
+ largs = self._prepare_adb_args(args)
+ try:
+ if self.verbose:
+ print "### " + str(largs)
+ output = check_output(largs)
+ except subprocess.CalledProcessError, e:
+ raise gdb.GdbError("Error starting adb " + str(largs))
+ except Exception as e:
+ raise gdb.GdbError("Unknown error starting adb " + str(largs))
+
+ return output
+
+ def _shell(self, *args):
+ args = ["shell"] + list(args)
+ return self._call_adb(*args)
+
+ def _background_shell(self, *args):
+ args = ["shell"] + list(args)
+ return self._background_adb(*args)
+
+ def _cleanup_background_processes(self):
+ for handle in self.background_processes:
+ try:
+ handle.terminate()
+ except OSError, e:
+ # Background process died already
+ pass
+
+ def _cleanup_temp(self):
+ if self.temp_libdir:
+ shutil.rmtree(self.temp_libdir)
+ self.temp_libdir = None
+
+ def __del__(self):
+ self._cleanup_temp()
+ self._cleanup_background_processes()
+
+ def _get_local_libs(self):
+ ret = []
+ for lib in _interesting_libs():
+ lib_path = os.path.join(local_library_directory, lib + ".so")
+ if not os.path.exists(lib_path) and self.verbose:
+ print "Warning: unable to find expected library " \
+ + lib_path + "."
+ ret.append(lib_path)
+
+ return ret
+
+ def _check_remote_libs_match_local_libs(self):
+ ret = []
+ all_remote_libs = self._shell("ls", "/system/lib/*.so").split()
+ local_libs = self._get_local_libs()
+
+ self.temp_libdir = tempfile.mkdtemp()
+
+ for lib in _interesting_libs():
+ lib += ".so"
+ for remote_lib in all_remote_libs:
+ if lib in remote_lib:
+ # Pull lib from device and compute hash
+ tmp_path = os.path.join(self.temp_libdir, lib)
+ self.pull(remote_lib, tmp_path)
+ remote_hash = self._md5sum(tmp_path)
+
+ # Find local lib and compute hash
+ built_library = filter(lambda l: lib in l, local_libs)[0]
+ built_hash = self._md5sum(built_library)
+
+ # Alert user if library mismatch is detected
+ if built_hash != remote_hash:
+ self._cleanup_temp()
+ raise gdb.GdbError("Library mismatch between:\n" \
+ + "\t(" + remote_hash + ") " + tmp_path + " (from target) and\n " \
+ + "\t(" + built_hash + ") " + built_library + " (on host)\n" \
+ + "The target is running a different build than the host." \
+ + " This situation is not debuggable.")
+
+ self._cleanup_temp()
+
+ def _md5sum(self, file):
+ try:
+ return check_output(["md5sum", file]).strip().split()[0]
+ except subprocess.CalledProcessError, e:
+ raise gdb.GdbError("Error invoking md5sum commandline utility")
+
+ # Returns the list of serial numbers of connected devices
+ def devices(self):
+ ret = []
+ raw_output = self._call_adb("devices").split()
+ if len(raw_output) < 5:
+ return None
+ else:
+ for serial_num_index in range(4, len(raw_output), 2):
+ ret.append(raw_output[serial_num_index])
+ return ret
+
+ def set_current_device(self, serial):
+ if self.current_device == str(serial):
+ print "Current device already is: " + str(serial)
+ return
+
+ # TODO: this function should probably check the serial is valid.
+ self.current_device = str(serial)
+
+ api_version = self.getprop("ro.build.version.sdk")
+ if api_version < 15:
+ print "Warning: untested API version. Upgrade to 15 or higher"
+
+ # Verify the local libraries loaded by GDB are identical to those
+ # sitting on the device actually executing. Alert the user if
+ # this is happening
+ self._check_remote_libs_match_local_libs()
+
+ # adb getprop [property]
+ # if property is not None, returns the given property, otherwise
+ # returns all properties.
+ def getprop(self, property=None):
+ if property == None:
+ # get all the props
+ return self._call_adb(*["shell", "getprop"]).split('\n')
+ else:
+ return str(self._call_adb(*["shell", "getprop",
+ str(property)]).split('\n')[0])
+
+ # adb push
+ def push(self, source, destination):
+ self._call_adb(*["push", source, destination])
+
+ # adb forward <source> <destination>
+ def forward(self, source, destination):
+ self._call_adb(*["forward", source, destination])
+
+ # Returns true if filename exists on Android fs, false otherwise
+ def exists(self, filename):
+ raw_listing = self._shell(*["ls", filename])
+ return "No such file or directory" not in raw_listing
+
+ # adb pull <remote_path> <local_path>
+ def pull(self, remote_path, local_path):
+ self._call_adb(*["pull", remote_path, local_path])
+
+ #wrapper for adb shell ps. leave process_name=None for list of all processes
+ #Otherwise, returns triple with process name, pid and owner,
+ def get_process_info(self, process_name=None):
+ ret = []
+ raw_output = self._shell("ps")
+ for raw_line in raw_output.splitlines()[1:]:
+ line = raw_line.split()
+ name = line[-1]
+
+ if process_name == None or name == process_name:
+ user = line[0]
+ pid = line[1]
+
+ if process_name != None:
+ return (pid, user)
+ else:
+ ret.append((pid, user))
+
+ # No match in target process
+ if process_name != None:
+ return (None, None)
+
+ return ret
+
+ def kill_by_pid(self, pid):
+ self._shell(*["kill", "-9", pid])
+
+ def kill_by_name(self, process_name):
+ (pid, user) = self.get_process_info(process_name)
+ while pid != None:
+ self.kill_by_pid(pid)
+ (pid, user) = self.get_process_info(process_name)
+
+class AndroidStatus(gdb.Command):
+ """Implements the android-status gdb command."""
+
+ def __init__(self, adb, name="android-status", cat=gdb.COMMAND_OBSCURE, verbose=False):
+ super (AndroidStatus, self).__init__(name, cat)
+ self.verbose = verbose
+ self.adb = adb
+
+ def _update_status(self, process_name, gdbserver_process_name):
+ self._check_app_is_loaded()
+
+ # Update app status
+ (self.pid, self.owner_user) = \
+ self.adb.get_process_info(process_name)
+ self.running = self.pid != None
+
+ # Update gdbserver status
+ (self.gdbserver_pid, self.gdbserver_user) = \
+ self.adb.get_process_info(gdbserver_process_name)
+ self.gdbserver_running = self.gdbserver_pid != None
+
+ # Print results
+ if self.verbose:
+ print "--==Android GDB Plugin Status Update==--"
+ print "\tinferior name: " + process_name
+ print "\trunning: " + str(self.running)
+ print "\tpid: " + str(self.pid)
+ print "\tgdbserver running: " + str(self.gdbserver_running)
+ print "\tgdbserver pid: " + str(self.gdbserver_pid)
+ print "\tgdbserver user: " + str(self.gdbserver_user)
+
+ def _check_app_is_loaded(self):
+ if not currentAppInfo.get_name():
+ raise gdb.GdbError("Error: no app loaded. Try load-android-app.")
+
+ def invoke(self, arg, from_tty):
+ self._check_app_is_loaded()
+ self._update_status(currentAppInfo.get_name(),
+ currentAppInfo.get_gdbserver_path())
+ # TODO: maybe print something if verbose is off
+
+class StartAndroidApp (AndroidStatus):
+ """Implements the 'start-android-app' gdb command."""
+
+ def _update_status(self):
+ AndroidStatus._update_status(self, self.process_name, \
+ self.gdbserver_path)
+
+ # Calls adb shell ps every retry_delay seconds and returns
+ # the pid when process_name show up in output, or return 0
+ # after num_retries attempts. num_retries=0 means retry
+ # indefinitely.
+ def _wait_for_process(self, process_name, retry_delay=1, num_retries=10):
+ """ This function is a hack and should not be required"""
+ (pid, user) = self.adb.get_process_info(process_name)
+ retries_left = num_retries
+ while pid == None and retries_left != 0:
+ (pid, user) = self.adb.get_process_info(process_name)
+ time.sleep(retry_delay)
+ retries_left -= 1
+
+ return pid
+
+ def _gdbcmd(self, cmd, from_tty=False):
+ if self.verbose:
+ print '### GDB Command: ' + str(cmd)
+
+ gdb.execute(cmd, from_tty)
+
+ # Remove scratch directory if any
+ def _cleanup_temp(self):
+ if self.temp_dir:
+ shutil.rmtree(self.temp_dir)
+ self.temp_dir = None
+
+ def _cleanup_jdb(self):
+ if self.jdb_handle:
+ try:
+ self.jdb_handle.terminate()
+ except OSError, e:
+ # JDB process has likely died
+ pass
+
+ self.jdb_handle = None
+
+ def _load_local_libs(self):
+ for lib in _interesting_libs():
+ self._gdbcmd("shar " + lib)
+
+ def __del__(self):
+ self._cleanup_temp()
+ self._cleanup_jdb()
+
+ def __init__ (self, adb, name="start-android-app", cat=gdb.COMMAND_RUNNING, verbose=False):
+ super (StartAndroidApp, self).__init__(adb, name, cat, verbose)
+ self.adb = adb
+
+ self.jdb_handle = None
+ # TODO: handle possibility that port 8700 is in use (may help with
+ # Eclipse problems)
+ self.jdwp_port = 8700
+
+ # Port for gdbserver
+ self.gdbserver_port = 5039
+
+ self.temp_dir = None
+
+ def start_process(self, start_running=False):
+ #TODO: implement libbcc cache removal if needed
+
+ args = ["am", "start"]
+
+ # If we are to start running, we can take advantage of am's -W flag to wait
+ # for the process to start before returning. That way, we don't have to
+ # emulate the behaviour (poorly) through the sleep-loop below.
+ if not start_running:
+ args.append("-D")
+ else:
+ args.append("-W")
+
+ args.append(self.process_name + "/" + self.intent)
+ am_output = self.adb._shell(*args)
+ if "Error:" in am_output:
+ raise gdb.GdbError("Cannot start app. Activity Manager returned:\n"\
+ + am_output)
+
+ # Gotta wait until the process starts if we can't use -W
+ if not start_running:
+ self.pid = self._wait_for_process(self.process_name)
+
+ if not self.pid:
+ raise gdb.GdbError("Unable to detect running app remotely." \
+ + "Is " + self.process_name + " installed correctly?")
+
+ if self.verbose:
+ print "--==Android App Started: " + self.process_name \
+ + " (pid=" + self.pid + ")==--"
+
+ # Forward port for java debugger to Dalvik
+ self.adb.forward("tcp:" + str(self.jdwp_port), \
+ "jdwp:" + str(self.pid))
+
+ def start_gdbserver(self):
+ # TODO: adjust for architecture...
+ gdbserver_local_path = os.path.join(os.getenv('ANDROID_BUILD_TOP'),
+ 'prebuilt', 'android-x86', 'gdbserver', 'gdbserver')
+
+ if not self.adb.exists(self.gdbserver_path):
+ # Install gdbserver
+ try:
+ self.adb.push(gdbserver_local_path, self.gdbserver_path)
+ except gdb.GdbError, e:
+ print "Unable to push gdbserver to device. Try re-installing app."
+ raise e
+
+ self.adb._background_shell(*[self.gdbserver_path, "--attach",
+ ":" + str(self.gdbserver_port), self.pid])
+
+ self._wait_for_process(self.gdbserver_path)
+ self._update_status()
+
+ if self.verbose:
+ print "--==Remote gdbserver Started " \
+ + " (pid=" + str(self.gdbserver_pid) \
+ + " port=" + str(self.gdbserver_port) + ") ==--"
+
+ # Forward port for gdbserver
+ self.adb.forward("tcp:" + str(self.gdbserver_port), \
+ "tcp:" + str(5039))
+
+ def attach_gdb(self, from_tty):
+ self._gdbcmd("target remote :" + str(self.gdbserver_port), False)
+ if self.verbose:
+ print "--==GDB Plugin requested attach (port=" \
+ + str(self.gdbserver_port) + ")==-"
+
+ # If GDB has no file set, things start breaking...so grab the same
+ # binary the NDK grabs from the filesystem and continue
+ self._cleanup_temp()
+ self.temp_dir = tempfile.mkdtemp()
+ self.gdb_inferior = os.path.join(self.temp_dir, 'app_process')
+ self.adb.pull("/system/bin/app_process", self.gdb_inferior)
+ self._gdbcmd('file ' + self.gdb_inferior)
+
+ def start_jdb(self, port):
+ # Kill if running
+ self._cleanup_jdb()
+
+ # Start the java debugger
+ args = ["jdb", "-connect",
+ "com.sun.jdi.SocketAttach:hostname=localhost,port=" + str(port)]
+ if self.verbose:
+ self.jdb_handle = subprocess.Popen(args, \
+ stdin=subprocess.PIPE)
+ else:
+ # Unix-only bit here..
+ self.jdb_handle = subprocess.Popen(args, \
+ stdin=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ stdout=open('/dev/null', 'w'))
+
+ def invoke (self, arg, from_tty):
+ # TODO: self._check_app_is_installed()
+ self._check_app_is_loaded()
+
+ self.intent = currentAppInfo.get_intent()
+ self.process_name = currentAppInfo.get_name()
+ self.data_directory = currentAppInfo.get_data_directory()
+ self.gdbserver_path = currentAppInfo.get_gdbserver_path()
+
+ self._update_status()
+
+ if self.gdbserver_running:
+ self.adb.kill_by_name(self.gdbserver_path)
+ if self.verbose:
+ print "--==Killed gdbserver process (pid=" \
+ + str(self.gdbserver_pid) + ")==--"
+ self._update_status()
+
+ if self.running:
+ self.adb.kill_by_name(self.process_name)
+ if self.verbose:
+ print "--==Killed app process (pid=" + str(self.pid) + ")==--"
+ self._update_status()
+
+ self.start_process()
+
+ # Start remote gdbserver
+ self.start_gdbserver()
+
+ # Attach the gdb
+ self.attach_gdb(from_tty)
+
+ # Load symbolic libraries
+ self._load_local_libs()
+
+ # Set the debug output directory (for JIT debugging)
+ if enable_renderscript_dumps:
+ self._gdbcmd('set gDebugDumpDirectory="' + self.data_directory + '"')
+
+ # Start app
+ # unblock the gdb by connecting with jdb
+ self.start_jdb(self.jdwp_port)
+
+class RunAndroidApp(StartAndroidApp):
+ """Implements the run-android-app gdb command."""
+
+ def __init__(self, adb, name="run-android-app", cat=gdb.COMMAND_RUNNING, verbose=False):
+ super (RunAndroidApp, self).__init__(adb, name, cat, verbose)
+
+ def invoke(self, arg, from_tty):
+ StartAndroidApp.invoke(self, arg, from_tty)
+ self._gdbcmd("continue")
+
+class AttachAndroidApp(StartAndroidApp):
+ """Implements the attach-android-app gdb command."""
+
+ def __init__(self, adb, name="attach-android-app", cat=gdb.COMMAND_RUNNING, verbose=False):
+ super (AttachAndroidApp, self).__init__(adb, name, cat, verbose)
+
+ def invoke(self, arg, from_tty):
+ # TODO: self._check_app_is_installed()
+ self._check_app_is_loaded()
+
+ self.intent = currentAppInfo.get_intent()
+ self.process_name = currentAppInfo.get_name()
+ self.data_directory = currentAppInfo.get_data_directory()
+ self.gdbserver_path = currentAppInfo.get_gdbserver_path()
+
+ self._update_status()
+
+ if self.gdbserver_running:
+ self.adb.kill_by_name(self.gdbserver_path)
+ if self.verbose:
+ print "--==Killed gdbserver process (pid=" \
+ + str(self.gdbserver_pid) + ")==--"
+ self._update_status()
+
+ # Start remote gdbserver
+ self.start_gdbserver()
+
+ # Attach the gdb
+ self.attach_gdb(from_tty)
+
+ # Load symbolic libraries
+ self._load_local_libs()
+
+ # Set the debug output directory (for JIT debugging)
+ if enable_renderscript_dumps:
+ self._gdbcmd('set gDebugDumpDirectory="' + self.data_directory + '"')
+
+class LoadApp(AndroidStatus):
+ """ Implements the load-android-app gbd command.
+ """
+ def _awk_script_path(self, script_name):
+ if os.path.exists(script_name):
+ return script_name
+
+ script_root = os.path.join(os.getenv('ANDROID_BUILD_TOP'), \
+ 'ndk', 'build', 'awk')
+
+ path_in_root = os.path.join(script_root, script_name)
+ if os.path.exists(path_in_root):
+ return path_in_root
+
+ raise gdb.GdbError("Unable to find awk script " \
+ + str(script_name) + " in " + path_in_root)
+
+ def _awk(self, script, command):
+ args = ["awk", "-f", self._awk_script_path(script), str(command)]
+
+ if self.verbose:
+ print "### awk command: " + str(args)
+
+ awk_output = ""
+ try:
+ awk_output = check_output(args)
+ except subprocess.CalledProcessError, e:
+ raise gdb.GdbError("### Error in subprocess awk " + str(args))
+ except:
+ print "### Random error calling awk " + str(args)
+
+ return awk_output.rstrip()
+
+ def __init__(self, adb, name="load-android-app", cat=gdb.COMMAND_RUNNING, verbose=False):
+ super (LoadApp, self).__init__(adb, name, cat, verbose)
+ self.manifest_name = "AndroidManifest.xml"
+ self.verbose = verbose
+ self.adb = adb
+ self.temp_libdir = None
+
+ def _find_manifests(self, path):
+ manifests = []
+ for root, dirnames, filenames in os.walk(path):
+ for filename in fnmatch.filter(filenames, self.manifest_name):
+ manifests.append(os.path.join(root, filename))
+ return manifests
+
+ def _usage(self):
+ return "Usage: load-android-app [<path-to-AndroidManifest.xml>" \
+ + " | <package-name> <intent-name>]"
+
+ def invoke(self, arg, from_tty):
+
+ package_name = ''
+ launchable = ''
+ args = arg.strip('"').split()
+ if len(args) == 2:
+ package_name = args[0]
+ launchable = args[1]
+ elif len(args) == 1:
+ if os.path.isfile(args[0]) and os.path.basename(args[0]) == self.manifest_name:
+ self.manifest_path = args[0]
+ elif os.path.isdir(args[0]):
+ manifests = self._find_manifests(args[0])
+ if len(manifests) == 0:
+ raise gdb.GdbError(self.manifest_name + " not found in: " \
+ + args[0] + "\n" + self._usage())
+ elif len(manifests) > 1:
+ raise gdb.GdbError("Ambiguous argument! Found too many " \
+ + self.manifest_name + " files found:\n" + "\n".join(manifests))
+ else:
+ self.manifest_path = manifests[0]
+ else:
+ raise gdb.GdbError("Invalid path: " + args[0] + "\n" + self._usage())
+
+ package_name = self._awk("extract-package-name.awk",
+ self.manifest_path)
+ launchable = self._awk("extract-launchable.awk",
+ self.manifest_path)
+ else:
+ raise gdb.GdbError(self._usage())
+
+
+ data_directory = self.adb._shell("run-as", package_name,
+ "/system/bin/sh", "-c", "pwd").rstrip()
+
+ if not data_directory \
+ or len(data_directory) == 0 \
+ or not self.adb.exists(data_directory):
+ data_directory = os.path.join('/data', 'data', package_name)
+ print "Warning: unable to read data directory for package " \
+ + package_name + ". Meh, defaulting to " + data_directory
+
+ currentAppInfo.set_info(package_name, launchable, data_directory)
+
+ if self.verbose:
+ print "--==Android App Loaded==--"
+ print "\tname=" + currentAppInfo.get_name()
+ print "\tintent=" + currentAppInfo.get_intent()
+
+ # TODO: Check status of app on device
+
+class SetAndroidDevice (gdb.Command):
+ def __init__(self, adb, name="set-android-device", cat=gdb.COMMAND_RUNNING, verbose=False):
+ super (SetAndroidDevice, self).__init__(name, cat)
+ self.verbose = verbose
+ self.adb = adb
+
+ def _usage(self):
+ return "Usage: set-android-device <serial>"
+
+ def invoke(self, arg, from_tty):
+ if not arg or len(arg) == 0:
+ raise gdb.GdbError(self._usage)
+
+ serial = str(arg)
+ devices = adb.devices()
+ if serial in devices:
+ adb.set_current_device(serial)
+ else:
+ raise gdb.GdbError("Invalid serial. Serial numbers of connected " \
+ + "device(s): \n" + "\n".join(devices))
+
+# Global initialization
+def initOnce(adb):
+ # Try to speed up startup by skipping most android shared objects
+ gdb.execute("set auto-solib-add 0", False);
+
+ # Set shared object search path
+ gdb.execute("set solib-search-path " + local_symbols_library_directory, False)
+
+# Global instance of the object containing the info for current app
+currentAppInfo = DebugAppInfo ()
+
+# Global instance of ADB helper
+adb = ADB(verbose=be_verbose)
+
+# Perform global initialization
+initOnce(adb)
+
+# Command registration
+StartAndroidApp (adb, "start-android-app", gdb.COMMAND_RUNNING, be_verbose)
+RunAndroidApp (adb, "run-android-app", gdb.COMMAND_RUNNING, be_verbose)
+AndroidStatus (adb, "android-status", gdb.COMMAND_OBSCURE, be_verbose)
+LoadApp (adb, "load-android-app", gdb.COMMAND_RUNNING, be_verbose)
+SetAndroidDevice (adb, "set-android-device", gdb.COMMAND_RUNNING, be_verbose)
+AttachAndroidApp (adb, "attach-android-app", gdb.COMMAND_RUNNING, be_verbose)
diff --git a/include/bcinfo/MetadataExtractor.h b/include/bcinfo/MetadataExtractor.h
index e904238..b773dd7 100644
--- a/include/bcinfo/MetadataExtractor.h
+++ b/include/bcinfo/MetadataExtractor.h
@@ -43,6 +43,8 @@
size_t mObjectSlotCount;
const uint32_t *mObjectSlotList;
+ uint32_t mOptimizationLevel;
+
// Helper functions for extraction
bool populateForEachMetadata(const llvm::NamedMDNode *ExportForEachMetadata);
bool populateObjectSlotMetadata(const llvm::NamedMDNode *ObjectSlotMetadata);
@@ -129,6 +131,10 @@
const uint32_t *getObjectSlotList() const {
return mObjectSlotList;
}
+
+ uint32_t getOptimizationLevel() const {
+ return mOptimizationLevel;
+ }
};
} // namespace bcinfo
diff --git a/lib/ExecutionEngine/Android.mk b/lib/ExecutionEngine/Android.mk
index 695a975..4aa6a3f 100644
--- a/lib/ExecutionEngine/Android.mk
+++ b/lib/ExecutionEngine/Android.mk
@@ -25,6 +25,8 @@
libbcc_executionengine_SRC_FILES := \
Compiler.cpp \
FileHandle.cpp \
+ GDBJIT.cpp \
+ GDBJITRegistrar.cpp \
Runtime.c \
RuntimeStub.c \
Script.cpp \
diff --git a/lib/ExecutionEngine/Compiler.cpp b/lib/ExecutionEngine/Compiler.cpp
index be67bdf..b35b191 100644
--- a/lib/ExecutionEngine/Compiler.cpp
+++ b/lib/ExecutionEngine/Compiler.cpp
@@ -46,6 +46,7 @@
#include "llvm/CodeGen/RegAllocRegistry.h"
#include "llvm/CodeGen/SchedulerRegistry.h"
+#include "llvm/MC/MCContext.h"
#include "llvm/MC/SubtargetFeature.h"
#include "llvm/Transforms/IPO.h"
@@ -60,14 +61,16 @@
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/raw_ostream.h"
-#include "llvm/Type.h"
+#include "llvm/Constants.h"
#include "llvm/GlobalValue.h"
#include "llvm/Linker.h"
#include "llvm/LLVMContext.h"
#include "llvm/Metadata.h"
#include "llvm/Module.h"
#include "llvm/PassManager.h"
+#include "llvm/Type.h"
#include "llvm/Value.h"
#include <errno.h>
@@ -83,6 +86,8 @@
#include <string>
#include <vector>
+extern char* gDebugDumpDirectory;
+
namespace bcc {
//////////////////////////////////////////////////////////////////////////////
@@ -116,6 +121,12 @@
// synced with slang_rs_metadata.h)
const llvm::StringRef Compiler::ObjectSlotMetadataName = "#rs_object_slots";
+// Name of metadata node where RS optimization level resides (should be
+// synced with slang_rs_metadata.h)
+const llvm::StringRef OptimizationLevelMetadataName = "#optimization_level";
+
+
+
//////////////////////////////////////////////////////////////////////////////
// Compiler
//////////////////////////////////////////////////////////////////////////////
@@ -170,13 +181,6 @@
#if USE_DISASSEMBLER
InitializeDisassembler();
#endif
-
- // -O0: llvm::CodeGenOpt::None
- // -O1: llvm::CodeGenOpt::Less
- // -O2: llvm::CodeGenOpt::Default
- // -O3: llvm::CodeGenOpt::Aggressive
- CodeGenOptLevel = llvm::CodeGenOpt::Aggressive;
-
// Below are the global settings to LLVM
// Disable frame pointer elimination optimization
@@ -196,14 +200,6 @@
// Register the scheduler
llvm::RegisterScheduler::setDefault(llvm::createDefaultScheduler);
- // Register allocation policy:
- // createFastRegisterAllocator: fast but bad quality
- // createLinearScanRegisterAllocator: not so fast but good quality
- llvm::RegisterRegAlloc::setDefault
- ((CodeGenOptLevel == llvm::CodeGenOpt::None) ?
- llvm::createFastRegisterAllocator :
- llvm::createLinearScanRegisterAllocator);
-
#if USE_CACHE
// Read in SHA1 checksum of libbcc and libRS.
readSHA1(sha1LibBCC_SHA1, sizeof(sha1LibBCC_SHA1), pathLibBCC_SHA1);
@@ -288,13 +284,46 @@
std::string FeaturesStr;
+ if (mModule == NULL) // No module was loaded
+ return 0;
+
llvm::NamedMDNode const *PragmaMetadata;
llvm::NamedMDNode const *ExportVarMetadata;
llvm::NamedMDNode const *ExportFuncMetadata;
llvm::NamedMDNode const *ObjectSlotMetadata;
- if (mModule == NULL) // No module was loaded
- return 0;
+ llvm::NamedMDNode const *OptimizationLevelMetadata =
+ mModule->getNamedMetadata(OptimizationLevelMetadataName);
+
+ // Default to maximum optimization in the absence of named metadata node
+ int OptimizationLevel = 3;
+ if (OptimizationLevelMetadata) {
+ llvm::ConstantInt* OL = llvm::dyn_cast<llvm::ConstantInt>(
+ OptimizationLevelMetadata->getOperand(0)->getOperand(0));
+ OptimizationLevel = OL->getZExtValue();
+ }
+
+ if (OptimizationLevel == 0) {
+ CodeGenOptLevel = llvm::CodeGenOpt::None;
+ } else if (OptimizationLevel == 1) {
+ CodeGenOptLevel = llvm::CodeGenOpt::Less;
+ } else if (OptimizationLevel == 2) {
+ CodeGenOptLevel = llvm::CodeGenOpt::Default;
+ } else if (OptimizationLevel == 3) {
+ CodeGenOptLevel = llvm::CodeGenOpt::Aggressive;
+ }
+
+ // not the best place for this, but we need to set the register allocation
+ // policy after we read the optimization_level metadata from the bitcode
+
+ // Register allocation policy:
+ // createFastRegisterAllocator: fast but bad quality
+ // createLinearScanRegisterAllocator: not so fast but good quality
+ llvm::RegisterRegAlloc::setDefault
+ ((CodeGenOptLevel == llvm::CodeGenOpt::None) ?
+ llvm::createFastRegisterAllocator :
+ llvm::createLinearScanRegisterAllocator);
+
// Create TargetMachine
Target = llvm::TargetRegistry::lookupTarget(Triple, mError);
@@ -341,7 +370,8 @@
// Perform link-time optimization if we have multiple modules
if (mHasLinked) {
- runLTO(new llvm::TargetData(*TD), ExportVarMetadata, ExportFuncMetadata);
+ runLTO(new llvm::TargetData(*TD), ExportVarMetadata, ExportFuncMetadata,
+ CodeGenOptLevel);
}
// Perform code generation
@@ -371,6 +401,9 @@
goto on_bcc_compile_error;
}
+ rsloaderUpdateSectionHeaders(mRSExecutable,
+ (unsigned char*) mEmittedELFExecutable.begin());
+
if (ExportVarMetadata) {
ScriptCompiled::ExportVarList &varList = mpResult->mExportVars;
std::vector<std::string> &varNameList = mpResult->mExportVarsName;
@@ -662,7 +695,7 @@
MCCodeGenPasses.add(TD);
// Add MC code generation passes to pass manager
- llvm::MCContext *Ctx;
+ llvm::MCContext *Ctx = NULL;
if (TM->addPassesToEmitMC(MCCodeGenPasses, Ctx, OutSVOS,
CodeGenOptLevel, false)) {
setError("Fail to add passes to emit file");
@@ -678,12 +711,8 @@
int Compiler::runLTO(llvm::TargetData *TD,
llvm::NamedMDNode const *ExportVarMetadata,
- llvm::NamedMDNode const *ExportFuncMetadata) {
- llvm::PassManager LTOPasses;
-
- // Add TargetData to LTO passes
- LTOPasses.add(TD);
-
+ llvm::NamedMDNode const *ExportFuncMetadata,
+ llvm::CodeGenOpt::Level OptimizationLevel) {
// Collect All Exported Symbols
std::vector<const char*> ExportSymbols;
@@ -733,85 +762,111 @@
UserDefinedExternalSymbols.end(),
std::back_inserter(ExportSymbols));
+ llvm::PassManager LTOPasses;
+
+ // Add TargetData to LTO passes
+ LTOPasses.add(TD);
+
// We now create passes list performing LTO. These are copied from
// (including comments) llvm::createStandardLTOPasses().
+ // Only a subset of these LTO passes are enabled in optimization level 0
+ // as they interfere with interactive debugging.
+ // FIXME: figure out which passes (if any) makes sense for levels 1 and 2
- // Internalize all other symbols not listed in ExportSymbols
- LTOPasses.add(llvm::createInternalizePass(ExportSymbols));
+ if (OptimizationLevel != llvm::CodeGenOpt::None) {
+ // Internalize all other symbols not listed in ExportSymbols
+ LTOPasses.add(llvm::createInternalizePass(ExportSymbols));
- // Propagate constants at call sites into the functions they call. This
- // opens opportunities for globalopt (and inlining) by substituting
- // function pointers passed as arguments to direct uses of functions.
- LTOPasses.add(llvm::createIPSCCPPass());
+ // Propagate constants at call sites into the functions they call. This
+ // opens opportunities for globalopt (and inlining) by substituting
+ // function pointers passed as arguments to direct uses of functions.
+ LTOPasses.add(llvm::createIPSCCPPass());
- // Now that we internalized some globals, see if we can hack on them!
- LTOPasses.add(llvm::createGlobalOptimizerPass());
+ // Now that we internalized some globals, see if we can hack on them!
+ LTOPasses.add(llvm::createGlobalOptimizerPass());
- // Linking modules together can lead to duplicated global constants, only
- // keep one copy of each constant...
- LTOPasses.add(llvm::createConstantMergePass());
+ // Linking modules together can lead to duplicated global constants, only
+ // keep one copy of each constant...
+ LTOPasses.add(llvm::createConstantMergePass());
- // Remove unused arguments from functions...
- LTOPasses.add(llvm::createDeadArgEliminationPass());
+ // Remove unused arguments from functions...
+ LTOPasses.add(llvm::createDeadArgEliminationPass());
- // Reduce the code after globalopt and ipsccp. Both can open up
- // significant simplification opportunities, and both can propagate
- // functions through function pointers. When this happens, we often have
- // to resolve varargs calls, etc, so let instcombine do this.
- LTOPasses.add(llvm::createInstructionCombiningPass());
+ // Reduce the code after globalopt and ipsccp. Both can open up
+ // significant simplification opportunities, and both can propagate
+ // functions through function pointers. When this happens, we often have
+ // to resolve varargs calls, etc, so let instcombine do this.
+ LTOPasses.add(llvm::createInstructionCombiningPass());
- // Inline small functions
- LTOPasses.add(llvm::createFunctionInliningPass());
+ // Inline small functions
+ LTOPasses.add(llvm::createFunctionInliningPass());
- // Remove dead EH info.
- LTOPasses.add(llvm::createPruneEHPass());
+ // Remove dead EH info.
+ LTOPasses.add(llvm::createPruneEHPass());
- // Internalize the globals again after inlining
- LTOPasses.add(llvm::createGlobalOptimizerPass());
+ // Internalize the globals again after inlining
+ LTOPasses.add(llvm::createGlobalOptimizerPass());
- // Remove dead functions.
- LTOPasses.add(llvm::createGlobalDCEPass());
+ // Remove dead functions.
+ LTOPasses.add(llvm::createGlobalDCEPass());
- // If we didn't decide to inline a function, check to see if we can
- // transform it to pass arguments by value instead of by reference.
- LTOPasses.add(llvm::createArgumentPromotionPass());
+ // If we didn't decide to inline a function, check to see if we can
+ // transform it to pass arguments by value instead of by reference.
+ LTOPasses.add(llvm::createArgumentPromotionPass());
- // The IPO passes may leave cruft around. Clean up after them.
- LTOPasses.add(llvm::createInstructionCombiningPass());
- LTOPasses.add(llvm::createJumpThreadingPass());
+ // The IPO passes may leave cruft around. Clean up after them.
+ LTOPasses.add(llvm::createInstructionCombiningPass());
+ LTOPasses.add(llvm::createJumpThreadingPass());
- // Break up allocas
- LTOPasses.add(llvm::createScalarReplAggregatesPass());
+ // Break up allocas
+ LTOPasses.add(llvm::createScalarReplAggregatesPass());
- // Run a few AA driven optimizations here and now, to cleanup the code.
- LTOPasses.add(llvm::createFunctionAttrsPass()); // Add nocapture.
- LTOPasses.add(llvm::createGlobalsModRefPass()); // IP alias analysis.
+ // Run a few AA driven optimizations here and now, to cleanup the code.
+ LTOPasses.add(llvm::createFunctionAttrsPass()); // Add nocapture.
+ LTOPasses.add(llvm::createGlobalsModRefPass()); // IP alias analysis.
- // Hoist loop invariants.
- LTOPasses.add(llvm::createLICMPass());
+ // Hoist loop invariants.
+ LTOPasses.add(llvm::createLICMPass());
- // Remove redundancies.
- LTOPasses.add(llvm::createGVNPass());
+ // Remove redundancies.
+ LTOPasses.add(llvm::createGVNPass());
- // Remove dead memcpys.
- LTOPasses.add(llvm::createMemCpyOptPass());
+ // Remove dead memcpys.
+ LTOPasses.add(llvm::createMemCpyOptPass());
- // Nuke dead stores.
- LTOPasses.add(llvm::createDeadStoreEliminationPass());
+ // Nuke dead stores.
+ LTOPasses.add(llvm::createDeadStoreEliminationPass());
- // Cleanup and simplify the code after the scalar optimizations.
- LTOPasses.add(llvm::createInstructionCombiningPass());
+ // Cleanup and simplify the code after the scalar optimizations.
+ LTOPasses.add(llvm::createInstructionCombiningPass());
- LTOPasses.add(llvm::createJumpThreadingPass());
+ LTOPasses.add(llvm::createJumpThreadingPass());
- // Delete basic blocks, which optimization passes may have killed.
- LTOPasses.add(llvm::createCFGSimplificationPass());
+ // Delete basic blocks, which optimization passes may have killed.
+ LTOPasses.add(llvm::createCFGSimplificationPass());
- // Now that we have optimized the program, discard unreachable functions.
- LTOPasses.add(llvm::createGlobalDCEPass());
+ // Now that we have optimized the program, discard unreachable functions.
+ LTOPasses.add(llvm::createGlobalDCEPass());
+
+ } else {
+ LTOPasses.add(llvm::createInternalizePass(ExportSymbols));
+ LTOPasses.add(llvm::createGlobalOptimizerPass());
+ LTOPasses.add(llvm::createConstantMergePass());
+ }
LTOPasses.run(*mModule);
+#if ANDROID_ENGINEERING_BUILD
+ if (0 != gDebugDumpDirectory) {
+ std::string errs;
+ std::string Filename(gDebugDumpDirectory);
+ Filename += "/post-lto-module.ll";
+ llvm::raw_fd_ostream FS(Filename.c_str(), errs);
+ mModule->print(FS, 0);
+ FS.close();
+ }
+#endif
+
return 0;
}
diff --git a/lib/ExecutionEngine/Compiler.h b/lib/ExecutionEngine/Compiler.h
index 6030e40..4063edb 100644
--- a/lib/ExecutionEngine/Compiler.h
+++ b/lib/ExecutionEngine/Compiler.h
@@ -177,7 +177,8 @@
int runLTO(llvm::TargetData *TD,
llvm::NamedMDNode const *ExportVarMetadata,
- llvm::NamedMDNode const *ExportFuncMetadata);
+ llvm::NamedMDNode const *ExportFuncMetadata,
+ llvm::CodeGenOpt::Level OptimizationLevel);
bool hasError() const {
return !mError.empty();
diff --git a/lib/ExecutionEngine/GDBJIT.cpp b/lib/ExecutionEngine/GDBJIT.cpp
new file mode 100644
index 0000000..4038098
--- /dev/null
+++ b/lib/ExecutionEngine/GDBJIT.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2012, 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.
+ */
+
+// This file contains portions derived from LLVM, with the original copyright
+// header below:
+//===-- GDBJIT.cpp - Common Implementation shared by GDB-JIT users --------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is used to support GDB's JIT interface
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/Compiler.h"
+
+// This interface must be kept in sync with gdb/gdb/jit.h .
+extern "C" {
+
+ // GDB 7.0+ puts a (silent) breakpoint in this function.
+ LLVM_ATTRIBUTE_NOINLINE void __jit_debug_register_code() { }
+
+}
diff --git a/lib/ExecutionEngine/GDBJIT.h b/lib/ExecutionEngine/GDBJIT.h
new file mode 100644
index 0000000..442b666
--- /dev/null
+++ b/lib/ExecutionEngine/GDBJIT.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2012, 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.
+ */
+
+// This file contains portions derived from LLVM, with the original copyright
+// header below:
+//===-------------- GDBJIT.h - Register debug symbols for JIT -------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the data structures used by JIT engines to register object
+// files (ideally containing debug info) with GDB.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef BCC_EXECUTION_ENGINE_GDB_JIT_H
+#define BCC_EXECUTION_ENGINE_GDB_JIT_H
+
+#include "llvm/Support/DataTypes.h"
+#include "llvm/Support/Compiler.h"
+
+// This must be kept in sync with gdb/gdb/jit.h .
+extern "C" {
+
+ typedef enum {
+ JIT_NOACTION = 0,
+ JIT_REGISTER_FN,
+ JIT_UNREGISTER_FN
+ } jit_actions_t;
+
+ struct jit_code_entry {
+ struct jit_code_entry *next_entry;
+ struct jit_code_entry *prev_entry;
+ const char *symfile_addr;
+ uint64_t symfile_size;
+ };
+
+ struct jit_descriptor {
+ uint32_t version;
+ // This should be jit_actions_t, but we want to be specific about the
+ // bit-width.
+ uint32_t action_flag;
+ struct jit_code_entry *relevant_entry;
+ struct jit_code_entry *first_entry;
+ };
+
+ // GDB 7.0+ puts a (silent) breakpoint in this function.
+ LLVM_ATTRIBUTE_NOINLINE void __jit_debug_register_code();
+
+}
+
+#endif // BCC_EXECUTION_ENGINE_GDB_JIT_H
diff --git a/lib/ExecutionEngine/GDBJITRegistrar.cpp b/lib/ExecutionEngine/GDBJITRegistrar.cpp
new file mode 100644
index 0000000..5d8428a
--- /dev/null
+++ b/lib/ExecutionEngine/GDBJITRegistrar.cpp
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2012, 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.
+ */
+
+// This file contains portions derived from LLVM, with the original copyright
+// header below:
+//==----- GDBJITRegistrar.cpp - Notify GDB about in-memory object files ---==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the GDBJITRegistrar object which is used by JIT engines to
+// register in-memory object files with GDB for debugging.
+//
+//===----------------------------------------------------------------------===//
+
+#include "GDBJITRegistrar.h"
+#include "llvm/ADT/DenseMap.h"
+#include "GDBJIT.h"
+#include "llvm/Support/Memory.h"
+#include "llvm/Support/MutexGuard.h"
+#include "llvm/Support/Mutex.h"
+#include "llvm/Support/ErrorHandling.h"
+
+#include <fstream>
+
+#ifdef ANDROID_ENGINEERING_BUILD
+// Path to write dump output.
+// It is expected that a debugger (plugin) sets this
+// string to a writeable directory where files (such as JITted object files,
+// IR dumps) are to be written. If this variable is 0, no debug dumps
+// are generated.
+char* gDebugDumpDirectory = 0;
+#endif // ANDROID_ENGINEERING_BUILD
+
+//************************************************************************
+// COPIED/ADAPTED FROM llvm/lib/ExecutionEngine/JIT/JITDebugRegisterer.cpp
+//************************************************************************
+// This must be kept in sync with gdb/gdb/jit.h .
+extern "C" {
+
+ // We put information about the JITed function in this global, which the
+ // debugger reads. Make sure to specify the version statically, because the
+ // debugger checks the version before we can set it during runtime.
+ static struct jit_descriptor __jit_debug_descriptor = { 1, 0, 0, 0 };
+
+}
+//****************************************************************************
+// END COPIED/ADAPTED FROM llvm/lib/ExecutionEngine/JIT/JITDebugRegisterer.cpp
+//****************************************************************************
+
+namespace {
+
+// Buffer for an in-memory object file in executable memory
+typedef llvm::DenseMap< const ObjectBuffer*, std::pair<std::size_t, jit_code_entry*> >
+ RegisteredObjectBufferMap;
+
+/// Global access point for the GDB JIT interface designed for use with a
+/// singleton toolbox. Handles thread-safe registration and deregistration of
+/// object files that are in executable memory managed by the client of this
+/// class.
+class GDBJITRegistrar {
+ /// A map of in-memory object files that have been registered with the GDB JIT interface.
+ RegisteredObjectBufferMap ObjectBufferMap;
+
+public:
+ /// Instantiates the GDB JIT service.
+ GDBJITRegistrar() : ObjectBufferMap() {}
+
+ /// Unregisters each object that was previously registered with GDB, and
+ /// releases all internal resources.
+ ~GDBJITRegistrar();
+
+ /// Creates an entry in the GDB JIT registry for the buffer @p Object,
+ /// which must contain an object file in executable memory with any
+ /// debug information for GDB.
+ void registerObject(const ObjectBuffer* Object, std::size_t Size);
+
+ /// Removes the internal registration of @p Object, and
+ /// frees associated resources.
+ /// Returns true if @p Object was found in ObjectBufferMap.
+ bool deregisterObject(const ObjectBuffer* Object);
+
+private:
+ /// Deregister the debug info for the given object file from the debugger
+ /// and delete any temporary copies. This private method does not remove
+ /// the function from Map so that it can be called while iterating over Map.
+ void deregisterObjectInternal(RegisteredObjectBufferMap::iterator I);
+};
+
+/// Lock used to serialize all gdb-jit registration events, since they
+/// modify global variables.
+llvm::sys::Mutex JITDebugLock;
+
+/// Acquire the lock and do the registration.
+void NotifyGDB(jit_code_entry* JITCodeEntry) {
+ llvm::MutexGuard locked(JITDebugLock);
+ __jit_debug_descriptor.action_flag = JIT_REGISTER_FN;
+
+ // Insert this entry at the head of the list.
+ JITCodeEntry->prev_entry = NULL;
+ jit_code_entry* NextEntry = __jit_debug_descriptor.first_entry;
+ JITCodeEntry->next_entry = NextEntry;
+ if (NextEntry != NULL) {
+ NextEntry->prev_entry = JITCodeEntry;
+ }
+ __jit_debug_descriptor.first_entry = JITCodeEntry;
+ __jit_debug_descriptor.relevant_entry = JITCodeEntry;
+ __jit_debug_register_code();
+}
+
+GDBJITRegistrar* RegistrarSingleton() {
+ static GDBJITRegistrar* sRegistrar = NULL;
+ if (sRegistrar == NULL) {
+ // The mutex is here so that it won't slow down access once the registrar
+ // is instantiated
+ llvm::MutexGuard locked(JITDebugLock);
+ // Check again to be sure another thread didn't create this while we waited
+ if (sRegistrar == NULL) {
+ sRegistrar = new GDBJITRegistrar;
+ }
+ }
+ return sRegistrar;
+}
+
+GDBJITRegistrar::~GDBJITRegistrar() {
+ // Free all registered object files.
+ for (RegisteredObjectBufferMap::iterator I = ObjectBufferMap.begin(), E = ObjectBufferMap.end();
+ I != E; ++I) {
+ // Call the private method that doesn't update the map so our iterator
+ // doesn't break.
+ deregisterObjectInternal(I);
+ }
+ ObjectBufferMap.clear();
+}
+
+void GDBJITRegistrar::registerObject(const ObjectBuffer* Object, std::size_t Size) {
+
+ assert(Object && "Attempt to register a null object with a debugger.");
+ assert(ObjectBufferMap.find(Object) == ObjectBufferMap.end()
+ && "Second attempt to perform debug registration.");
+
+ jit_code_entry* JITCodeEntry = new jit_code_entry();
+
+ if (JITCodeEntry == 0) {
+ llvm::report_fatal_error("Allocation failed when registering a GDB-JIT entry!\n");
+ }
+ else {
+ JITCodeEntry->symfile_addr = Object;
+ JITCodeEntry->symfile_size = Size;
+
+ ObjectBufferMap[Object] = std::make_pair(Size, JITCodeEntry);
+ NotifyGDB(JITCodeEntry);
+
+#ifdef ANDROID_ENGINEERING_BUILD
+ if (0 != gDebugDumpDirectory) {
+ std::string Filename(gDebugDumpDirectory);
+ Filename += "/jit_registered.o";
+
+ std::ofstream outfile(Filename.c_str(), std::ofstream::binary);
+ outfile.write((char*)JITCodeEntry->symfile_addr, JITCodeEntry->symfile_size);
+ outfile.close();
+ }
+#endif
+ }
+}
+
+bool GDBJITRegistrar::deregisterObject(const ObjectBuffer *Object) {
+ RegisteredObjectBufferMap::iterator I = ObjectBufferMap.find(Object);
+
+ if (I != ObjectBufferMap.end()) {
+ deregisterObjectInternal(I);
+ ObjectBufferMap.erase(I);
+ return true;
+ }
+ return false;
+}
+
+void GDBJITRegistrar::deregisterObjectInternal(
+ RegisteredObjectBufferMap::iterator I) {
+
+ jit_code_entry*& JITCodeEntry = I->second.second;
+
+ // Acquire the lock and do the unregistration.
+ {
+ llvm::MutexGuard locked(JITDebugLock);
+ __jit_debug_descriptor.action_flag = JIT_UNREGISTER_FN;
+
+ // Remove the jit_code_entry from the linked list.
+ jit_code_entry* PrevEntry = JITCodeEntry->prev_entry;
+ jit_code_entry* NextEntry = JITCodeEntry->next_entry;
+
+ if (NextEntry) {
+ NextEntry->prev_entry = PrevEntry;
+ }
+ if (PrevEntry) {
+ PrevEntry->next_entry = NextEntry;
+ }
+ else {
+ assert(__jit_debug_descriptor.first_entry == JITCodeEntry);
+ __jit_debug_descriptor.first_entry = NextEntry;
+ }
+
+ // Tell GDB which entry we removed, and unregister the code.
+ __jit_debug_descriptor.relevant_entry = JITCodeEntry;
+ __jit_debug_register_code();
+ }
+
+ delete JITCodeEntry;
+ JITCodeEntry = NULL;
+}
+
+} // end namespace
+
+void registerObjectWithGDB(const ObjectBuffer* Object, std::size_t Size) {
+ GDBJITRegistrar* Registrar = RegistrarSingleton();
+ if (Registrar) {
+ Registrar->registerObject(Object, Size);
+ }
+}
+
+void deregisterObjectWithGDB(const ObjectBuffer* Object) {
+ GDBJITRegistrar* Registrar = RegistrarSingleton();
+ if (Registrar) {
+ Registrar->deregisterObject(Object);
+ }
+}
diff --git a/lib/ExecutionEngine/GDBJITRegistrar.h b/lib/ExecutionEngine/GDBJITRegistrar.h
new file mode 100644
index 0000000..debf503
--- /dev/null
+++ b/lib/ExecutionEngine/GDBJITRegistrar.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2012, 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.
+ */
+
+// This file contains portions derived from LLVM, with the original copyright
+// header below:
+//===-- GDBJITRegistrar.h - Common Implementation shared by GDB-JIT users --===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains declarations of the interface an ExecutionEngine would use
+// to register an in-memory object file with GDB.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef BCC_GDBJITREGISTRAR_H
+#define BCC_GDBJITREGISTRAR_H
+
+#include <cstddef>
+
+// Buffer for an in-memory object file in executable memory
+typedef char ObjectBuffer;
+
+void registerObjectWithGDB(const ObjectBuffer* Object, std::size_t Size);
+void deregisterObjectWithGDB(const ObjectBuffer* Object);
+
+#endif // BCC_GDBJITREGISTRAR_H
diff --git a/lib/ExecutionEngine/MCCacheReader.cpp b/lib/ExecutionEngine/MCCacheReader.cpp
index 39e567f..a88d4d4 100644
--- a/lib/ExecutionEngine/MCCacheReader.cpp
+++ b/lib/ExecutionEngine/MCCacheReader.cpp
@@ -424,21 +424,25 @@
}
bool MCCacheReader::readObjFile() {
- llvm::SmallVector<char, 1024> mEmittedELFExecutable;
+ if (mpResult->mCachedELFExecutable.size() != 0) {
+ LOGE("Attempted to read cached object into a non-empty script");
+ return false;
+ }
char readBuffer[1024];
int readSize;
while ((readSize = mObjFile->read(readBuffer, 1024)) > 0) {
- mEmittedELFExecutable.append(readBuffer, readBuffer + readSize);
+ mpResult->mCachedELFExecutable.append(readBuffer, readBuffer + readSize);
}
if (readSize != 0) {
LOGE("Read file Error");
return false;
}
- LOGD("Read object file size %d", (int)mEmittedELFExecutable.size());
+ LOGD("Read object file size %d", (int)mpResult->mCachedELFExecutable.size());
mpResult->mRSExecutable =
- rsloaderCreateExec((unsigned char *)&*mEmittedELFExecutable.begin(),
- mEmittedELFExecutable.size(),
+ rsloaderCreateExec((unsigned char *)&*(mpResult->mCachedELFExecutable.begin()),
+ mpResult->mCachedELFExecutable.size(),
&resolveSymbolAdapter, this);
+
return true;
}
diff --git a/lib/ExecutionEngine/Script.cpp b/lib/ExecutionEngine/Script.cpp
index f772c64..97d8803 100644
--- a/lib/ExecutionEngine/Script.cpp
+++ b/lib/ExecutionEngine/Script.cpp
@@ -32,6 +32,7 @@
#include "DebugHelper.h"
#include "FileHandle.h"
+#include "GDBJITRegistrar.h"
#include "ScriptCompiled.h"
#include "ScriptCached.h"
#include "Sha1Helper.h"
@@ -46,7 +47,6 @@
#include <string.h>
#include <cutils/properties.h>
-
namespace {
bool getBooleanProp(const char *str) {
@@ -215,6 +215,7 @@
return 1;
}
+ int status = -1;
#if USE_CACHE
if (cacheDir && cacheName) {
// Set Cache Directory and File Name
@@ -227,19 +228,25 @@
// Load Cache File
if (internalLoadCache(false) == 0) {
- return 0;
+ status = 0;
}
}
#endif
- int status = internalCompile(false);
- if (status != 0) {
- LOGE("LLVM error message: %s\n", getCompilerErrorMessage());
+ if (status == -1) {
+ status = internalCompile(false);
+ if (status != 0) {
+ LOGE("LLVM error message: %s\n", getCompilerErrorMessage());
+ }
+ }
+
+ // FIXME: Registration can be conditional on the presence of debug metadata
+ if (status == 0) {
+ registerObjectWithGDB(getELF(), getELFSize()); // thread-safe registration
}
return status;
}
-
#if USE_CACHE
int Script::internalLoadCache(bool checkOnly) {
if (getBooleanProp("debug.bcc.nocache")) {
@@ -762,7 +769,11 @@
case ScriptStatus::Compiled: {
return mCompiled->getELFSize();
}
-
+#if USE_CACHE
+ case ScriptStatus::Cached: {
+ return mCached->getELFSize();
+ }
+#endif
default: {
return 0;
}
@@ -774,7 +785,11 @@
case ScriptStatus::Compiled: {
return mCompiled->getELF();
}
-
+#if USE_CACHE
+ case ScriptStatus::Cached: {
+ return mCached->getELF();
+ }
+#endif
default: {
return NULL;
}
diff --git a/lib/ExecutionEngine/Script.h b/lib/ExecutionEngine/Script.h
index 2855240..ebe9f38 100644
--- a/lib/ExecutionEngine/Script.h
+++ b/lib/ExecutionEngine/Script.h
@@ -29,6 +29,7 @@
namespace llvm {
class Module;
+ class GDBJITRegistrar;
}
namespace bcc {
@@ -180,7 +181,6 @@
int internalLoadCache(bool checkOnly);
#endif
int internalCompile(bool compileOnly);
-
};
} // namespace bcc
diff --git a/lib/ExecutionEngine/ScriptCached.h b/lib/ExecutionEngine/ScriptCached.h
index a627e5c..2ee211a 100644
--- a/lib/ExecutionEngine/ScriptCached.h
+++ b/lib/ExecutionEngine/ScriptCached.h
@@ -72,6 +72,7 @@
#if USE_MCJIT
RSExecRef mRSExecutable;
+ llvm::SmallVector<char, 1024> mCachedELFExecutable;
#endif
OBCC_StringPool *mpStringPoolRaw;
@@ -136,6 +137,15 @@
}
#endif
+#if USE_MCJIT
+ const char *getELF() const {
+ return &*mCachedELFExecutable.begin();
+ }
+
+ size_t getELFSize() const {
+ return mCachedELFExecutable.size();
+ }
+#endif
// Dirty hack for libRS.
// TODO(all): This should be removed in the future.
bool isLibRSThreadable() const {
diff --git a/libbcc-config.mk b/libbcc-config.mk
index 43c7897..dc3ab33 100644
--- a/libbcc-config.mk
+++ b/libbcc-config.mk
@@ -68,6 +68,8 @@
libbcc_CFLAGS := -Wall -Wno-unused-parameter -Werror
ifneq ($(TARGET_BUILD_VARIANT),eng)
libbcc_CFLAGS += -D__DISABLE_ASSERTS
+else
+libbcc_CFLAGS += -DANDROID_ENGINEERING_BUILD
endif
# Include File Search Path
diff --git a/tests/Android.mk b/tests/Android.mk
index f57fe47..752e5ca 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -31,7 +31,7 @@
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/../include
-LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_TAGS := tests eng
LOCAL_LDLIBS = -ldl
diff --git a/tests/debuginfo/README b/tests/debuginfo/README
new file mode 100644
index 0000000..80e2cc9
--- /dev/null
+++ b/tests/debuginfo/README
@@ -0,0 +1,63 @@
+
+Summary
+=======
+This directory contains integration tests for debug information in libbcc.
+
+The tests come in two flavours: host and target. Host tests are run on the
+build machine (currently, only Linux has been tested extensively) and target
+tests run on a live Android system (emulator or device.)
+
+Host tests use clang to build bytecode (bc) files, which are then executed
+by the libbcc driver utility (bcc) on the host through GDB. The debugger
+output is verified against expected output by the llvm tool FileCheck.
+Both the debugger commands and the expected output are embedded in the
+original sources as comments of the form "DEBUGGER: " and "CHECK: ".
+
+Target tests are similar, but instead of using clang, they use ant and
+llvm-rs-cc from the Android SDK to build a test binary package (apk)
+that is uploaded to the device (or emulator) and run with GDB attached.
+The output is verified in the same way as host side tests, and the format
+of the tests is the same.
+
+*** If you are running target-side tests, you must disable parallel
+*** execution with the "-j1" flag to llvm-lit
+
+
+Prerequisites
+=============
+To run the tests, you must have built the android source tree and have
+the build environment variables set (i.e. ANDROID_BUILD_TOP)
+
+You need the following tools (not built by the android build system) on
+your PATH:
+- gdb (Tested with gdb 7.3 from Ubuntu 11.10)
+
+In addition, you need a build of gdbserver available in the prebuilt directory.
+
+Customizing
+===========
+By default, llvm-lit will use the clang and bcc driver built in the android
+output directory. If you wish to use different versions of these tools,
+set the following environment variables:
+CLANG - path to clang
+BCC_DRIVER - path to bcc
+FILECHECK - path to FileCheck
+GDB - path to GDB
+
+Further customization is possible by modifying the lit.cfg file.
+
+
+Running
+=======
+To execute all the tests from this directory, use the llvm-lit tool:
+$ ./llvm-lit host-tests
+$ ./llvm-lit target-tests -j 1
+
+The tool can be run from any directory.
+-j controls the number of tests to run in parallel
+-v enables additional verbosity (useful when examining unexpected failures)
+
+Adding new tests
+================
+To add new tests, just add a .c, .cpp, or .rs file to a test directory with
+similar RUN/DEBUGGER/CHECK directives in comments as the existing tests.
diff --git a/tests/debuginfo/build_test_apk.sh b/tests/debuginfo/build_test_apk.sh
new file mode 100755
index 0000000..0f0d541
--- /dev/null
+++ b/tests/debuginfo/build_test_apk.sh
@@ -0,0 +1,245 @@
+#!/bin/bash -e
+
+# Copyright 2012, 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.
+
+# Creates and builds projects from a RenderScript testcase and a set of Java templates
+
+HELP=no
+VERBOSE=no
+MINSDK=1
+TARGET=1
+NAME=""
+OUT_DIR=
+ACTIVITY=""
+PACKAGE=""
+SDK=""
+TESTCASE_PATH=""
+DRIVER=""
+
+check_param ()
+{
+ if [ -z "$2" ]; then
+ echo "ERROR: Missing parameter after option '$1'"
+ exit 1
+ fi
+}
+
+check_required_param()
+{
+ if [ -z "$1" ]; then
+ echo "ERROR: Missing required parameter $2"
+ exit 1
+ fi
+}
+
+run ()
+{
+ if [ "$VERBOSE" = "yes" ] ; then
+ echo "## COMMAND: $@"
+ fi
+ $@ 2>&1
+}
+
+process_template()
+{
+ src=$1
+ dest=$2
+ sed -e "s/%ACTIVITY%/$3/g" -e "s/%PACKAGE%/$4/g" -e "s/%TESTCASE%/$5/g" -e "s/%MINSDK%/$6/g" < $src > $dest;
+ echo "processed $src ==> $dest"
+}
+
+while [ -n "$1" ]; do
+ opt="$1"
+ case "$opt" in
+ --help|-h|-\?)
+ HELP=yes
+ ;;
+ --verbose|-v)
+ VERBOSE=yes
+ ;;
+ --sdk)
+ check_param $1 $2
+ SDK="$2"
+ ;;
+ --name)
+ check_param $1 $2
+ NAME="$2"
+ ;;
+ --out)
+ check_param $1 $2
+ OUT_DIR="$2"
+ ;;
+ --activity)
+ check_param $1 $2
+ ACTIVITY="$2"
+ ;;
+ --package)
+ check_param $1 $2
+ PACKAGE="$2"
+ ;;
+ --minsdk)
+ check_param $1 $2
+ MINSDK="$2"
+ ;;
+ --target)
+ check_param $1 $2
+ TARGET="$2"
+ ;;
+ --testcase)
+ check_param $1 $2
+ TESTCASE_PATH="$2"
+ ;;
+ --driver)
+ check_param $1 $2
+ DRIVER="${2%/}"
+ ;;
+ -*) # unknown options
+ echo "ERROR: Unknown option '$opt', use --help for list of valid ones."
+ exit 1
+ ;;
+ *) # Simply record parameter
+ if [ -z "$PARAMETERS" ] ; then
+ PARAMETERS="$opt"
+ else
+ PARAMETERS="$PARAMETERS $opt"
+ fi
+ ;;
+ esac
+ shift
+done
+
+if [ "$HELP" = "yes" ] ; then
+ echo "Usage: $PROGNAME [options]"
+ echo ""
+ echo "Build a test project from a RS testcase and a java driver template."
+ echo ""
+ echo "Required Parameters:"
+ echo " --sdk Location of Android SDK installation"
+ echo " --out <path> Location of your project directory"
+ echo " --testcase <name> The .rs testcase file with which to build the project"
+ echo " --driver <name> The java template directory with which to build the project"
+ echo ""
+ echo "Optional Parameters (reasonable defaults are used if not specified)"
+ echo " --activity <name> Name for your default Activity class"
+ echo " --package <name> Package namespace for your project"
+ echo " --target <name> Android build target. Execute 'android list targets' to list available targets and their ID's."
+ echo " --minsdk <name> minSdkVersion attribute to embed in AndroidManifest.xml of test project."
+ echo " --help|-h|-? Print this help"
+ echo " --verbose|-v Enable verbose mode"
+ echo ""
+ exit 0
+fi
+
+# Verify required parameters are non-empty
+check_required_param "$SDK" "--sdk"
+check_required_param "$OUT_DIR" "--out"
+check_required_param "$TESTCASE_PATH" "--testcase"
+check_required_param "$DRIVER" "--driver"
+
+# Compute name of testcase
+TESTCASE=`basename $TESTCASE_PATH .rs`
+
+# Compute activity, appname, and java package, if not specified via parameters
+if [ -z "$ACTIVITY" ]; then
+ ACTIVITY="$TESTCASE";
+fi
+
+if [ -z "$NAME" ]; then
+ NAME="$ACTIVITY"
+fi
+
+if [ -z "$PACKAGE" ]; then
+ PACKAGE=com.android.test.rsdebug.$TESTCASE
+fi
+
+# Create the project
+run $SDK/tools/android create project --target $TARGET --name $NAME --path $OUT_DIR --activity $ACTIVITY --package $PACKAGE
+
+if [ $? != 0 ] ; then
+ echo "ERROR: Could not create Android project."
+ echo " Check parameters and try again."
+ exit 1
+fi
+
+# Compute name of destination source directory
+DEST_SRC_DIR=$OUT_DIR/src/`echo $PACKAGE | sed 's/\./\//g'`
+
+if [ ! -d "$DRIVER" ]; then
+ # If driver directory does not exist, try to fix it up by searching the
+ # testcase directory as well
+ DRIVER=`dirname $TESTCASE_PATH`/"$DRIVER"
+ if [ ! -d $DRIVER ]; then
+ echo "unable to find driver in $DRIVER, please check --driver"
+ exit 1;
+ fi
+fi
+
+echo "Copying driver template from $DRIVER -> $DEST_SRC_DIR"
+if [ ! -d "$DEST_SRC_DIR" ]; then
+ echo "Error, destination directory does not exist: $DEST_SRC_DIR";
+ exit 1;
+fi
+echo "Performing template substitutions:"
+echo " %ACTIVITY% ==> $ACTIVITY"
+echo " %PACKAGE% ==> $PACKAGE"
+echo " %TESTCASE% ==> $TESTCASE"
+echo " %MINSDK% ==> $MINSDK"
+SUBST_PARAMS="$ACTIVITY $PACKAGE $TESTCASE $MINSDK"
+
+# If it exists, use contents of driver-common directory to seed
+# the testcase project
+DRIVER_COMMON="`dirname $TESTCASE_PATH`/driver-common"
+if [ -d $DRIVER_COMMON ]; then
+ echo "Found common driver directory: $DRIVER_COMMON"
+ ls $DRIVER_COMMON/SRC/*.java.template | while read src; do
+ SRC_BASENAME=`basename $src .java.template`;
+ dest=$DEST_SRC_DIR/`echo $SRC_BASENAME | sed "s/ACTIVITY/$ACTIVITY/g"`.java
+ process_template $src $dest $SUBST_PARAMS
+ done;
+
+ # Copy AndroidManifest.xml
+ COMMON_MANIFEST="$DRIVER_COMMON/AndroidManifest.xml"
+ if [ -e $COMMON_MANIFEST ]; then
+ process_template $COMMON_MANIFEST $OUT_DIR/AndroidManifest.xml $SUBST_PARAMS
+ fi
+fi
+
+# Copy Java source to project directory.
+ls $DRIVER/*.java.template | while read src; do
+ SRC_BASENAME=`basename $src .java.template`
+ dest=$DEST_SRC_DIR/`echo $SRC_BASENAME | sed "s/ACTIVITY/$ACTIVITY/g"`.java
+ process_template $src $dest $SUBST_PARAMS
+done;
+
+# Copy AndroidManifest.xml override, if it exists
+OVERRIDE_MANIFEST="$DRIVER/AndroidManifest.xml"
+if [ -e $OVERRIDE_MANIFEST ]; then
+ process_template $OVERRIDE_MANIFEST $OUT_DIR/AndroidManifest.xml $SUBST_PARAMS
+fi
+
+# Copy RS testcase to project directory.
+TESTCASE_DEST=$DEST_SRC_DIR/`basename $TESTCASE_PATH`
+process_template $TESTCASE_PATH $TESTCASE_DEST $SUBST_PARAMS
+
+# Buid signed and aligned apk
+cd $OUT_DIR
+run ant clean debug install
+
+if [ $? != 0 ] ; then
+ echo "ERROR: Apk build and install failed"
+ exit 1
+fi
+
+exit 0
diff --git a/tests/debuginfo/host-tests/aggregate-indirect-arg.cpp b/tests/debuginfo/host-tests/aggregate-indirect-arg.cpp
new file mode 100644
index 0000000..bf38030
--- /dev/null
+++ b/tests/debuginfo/host-tests/aggregate-indirect-arg.cpp
@@ -0,0 +1,32 @@
+// RUN: %clangxx %s -O0 -g -fexceptions %extra-clang-opts -o %t
+// RUN: %Test_jit_debuginfo %s %t
+// XFAIL: host-bcc
+// DEBUGGER: set breakpoint pending on
+// DEBUGGER: break aggregate-indirect-arg.cpp:22
+// DEBUGGER: r
+// DEBUGGER: p v
+// CHECK: $1 = (SVal &)
+// CHECK: Data = 0x0,
+// CHECK: Kind = 2142
+
+class SVal {
+public:
+ ~SVal() {}
+ const void* Data;
+ unsigned Kind;
+};
+
+void bar(SVal &v) {}
+class A {
+public:
+ void foo(SVal v) { bar(v); }
+};
+
+int main() {
+ SVal v;
+ v.Data = 0;
+ v.Kind = 2142;
+ A a;
+ a.foo(v);
+ return 0;
+}
diff --git a/tests/debuginfo/host-tests/forward-declare-class.cpp b/tests/debuginfo/host-tests/forward-declare-class.cpp
new file mode 100644
index 0000000..2f06dbe
--- /dev/null
+++ b/tests/debuginfo/host-tests/forward-declare-class.cpp
@@ -0,0 +1,30 @@
+// RUN: %clangxx %s -g -fexceptions %extra-clang-opts -o %t
+// RUN: %Test_jit_debuginfo %s %t
+// Radar 9168773
+
+// DEBUGGER: set breakpoint pending on
+// DEBUGGER: b forward-declare-class.cpp:28
+// DEBUGGER: r
+// DEBUGGER: ptype A
+// CHECK: type = class A {
+// CHECK-NEXT: public:
+// CHECK-NEXT: int MyData;
+// CHECK-NEXT: }
+class A;
+class B {
+public:
+ void foo(const A *p);
+};
+
+B iEntry;
+
+class A {
+public:
+ int MyData;
+};
+
+A irp;
+
+int main() {
+ return 0;
+}
diff --git a/tests/debuginfo/host-tests/func_invoke_and_crash.cpp b/tests/debuginfo/host-tests/func_invoke_and_crash.cpp
new file mode 100644
index 0000000..0f39f41
--- /dev/null
+++ b/tests/debuginfo/host-tests/func_invoke_and_crash.cpp
@@ -0,0 +1,29 @@
+// RUN: %clangxx %s -g -fexceptions %extra-clang-opts -o %t
+// RUN: %Test_jit_debuginfo %s %t
+// DEBUGGER: set verbose on
+// DEBUGGER: run
+// DEBUGGER: bt 2
+// CHECK: function_with_a_segfault
+// CHECK: some_function
+
+static int function_with_a_segfault() {
+ int* bla = 0;
+ *bla = 5;
+ return 0;
+}
+
+static int some_function() {
+ return function_with_a_segfault();
+}
+
+static int foo() {
+ return some_function();
+}
+
+static int bar() {
+ return foo();
+}
+
+int main() {
+ return bar();
+}
diff --git a/tests/debuginfo/host-tests/global_struct.c b/tests/debuginfo/host-tests/global_struct.c
new file mode 100644
index 0000000..344972e
--- /dev/null
+++ b/tests/debuginfo/host-tests/global_struct.c
@@ -0,0 +1,36 @@
+// RUN: %clang %s -g -fexceptions %extra-clang-opts -o %t
+// RUN: %Test_jit_debuginfo %s %t
+// XFAIL: host-bcc
+// DEBUGGER: set breakpoint pending on
+// DEBUGGER: break %s:34
+// DEBUGGER: run
+// DEBUGGER: print s
+// CHECK: $1 = {d = 0.001, d2 = {10000, 100.5}}
+// DEBUGGER: continue
+
+struct double_struct {
+ double d;
+ double d2[2];
+} compound_double;
+
+
+float f = 0.f;
+float *pf = &f;
+
+const double d[2][2] = {{0, 1}, {2, 3.0}};
+struct double_struct s;
+
+unsigned short us = -1;
+const unsigned long l = 1;
+
+int main(int argc, char* argv[])
+{
+ int f = 10; // shadow
+
+ s.d = 10e-4;
+ s.d2[0] = 1e4;
+ s.d2[1] = 100.5;
+
+ double result = pf[0] * d[1][1] * s.d * us * l;
+ return (result == 0 ? 0 : -1);
+}
diff --git a/tests/debuginfo/host-tests/globals.c b/tests/debuginfo/host-tests/globals.c
new file mode 100644
index 0000000..f6150ae
--- /dev/null
+++ b/tests/debuginfo/host-tests/globals.c
@@ -0,0 +1,43 @@
+// RUN: %clang %s -g -fexceptions %extra-clang-opts -o %t
+// RUN: %Test_jit_debuginfo %s %t
+// DEBUGGER: set breakpoint pending on
+// DEBUGGER: break %s:42
+// DEBUGGER: run
+// DEBUGGER: print pf[0]
+// CHECK: $1 = 0
+// DEBUGGER: print d[0][0]
+// CHECK: $2 = 0
+// DEBUGGER: print us
+// CHECK: $3 = 65535
+// DEBUGGER: print l
+// CHECK: $4 = 1
+// DEBUGGER: print f
+// CHECK: $5 = 10
+// DEBUGGER: continue
+
+struct double_struct {
+ double d;
+ double d2[2];
+} compound_double;
+
+
+float f = 0.f;
+float *pf = &f;
+
+const double d[2][2] = {{0, 1}, {2, 3.0}};
+struct double_struct s;
+
+unsigned short us = -1;
+const unsigned long l = 1;
+
+int main(int argc, char* argv[])
+{
+ int f = 10; // shadow
+
+ s.d = 10e-4;
+ s.d2[0] = 1e4;
+ s.d2[1] = 100.5;
+
+ double result = pf[0] * d[1][1] * s.d * us * l;
+ return (result == 0 ? 0 : -1);
+}
diff --git a/tests/debuginfo/host-tests/jit.cpp b/tests/debuginfo/host-tests/jit.cpp
new file mode 100644
index 0000000..2da7b23
--- /dev/null
+++ b/tests/debuginfo/host-tests/jit.cpp
@@ -0,0 +1,34 @@
+// RUN: %clang %s -g -fexceptions %extra-clang-opts -o %t
+// RUN: %Test_jit_debuginfo %s %t
+// DEBUGGER: set breakpoint pending on
+// DEBUGGER: break three
+// DEBUGGER: run
+// DEBUGGER: bt 4
+// CHECK: #0
+// CHECK: three () at
+// CHECK: #1
+// CHECK: in two
+// CHECK: #2
+// CHECK: in one
+// CHECK: #3
+// CHECK: in main
+
+int three()
+{
+ return 0;
+}
+
+int two()
+{
+ return three();
+}
+
+int one()
+{
+ return two();
+}
+
+int main(int argc, char** argv)
+{
+ return one();
+}
diff --git a/tests/debuginfo/host-tests/lit.cfg b/tests/debuginfo/host-tests/lit.cfg
new file mode 100644
index 0000000..b212daa
--- /dev/null
+++ b/tests/debuginfo/host-tests/lit.cfg
@@ -0,0 +1,66 @@
+# -*- Python -*-
+#
+# Copyright (C) 2012 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.
+#
+
+#
+### Configuration file for target side debugger integration tests
+#
+
+# Set up the suite name, extensions that are recognized as testcases, and
+# the target triple string that must be used in cases marked expected failures
+config.name = 'host_bcc_debugger_integration'
+config.suffixes = ['.cpp', '.c']
+config.target_triple = 'host-bcc'
+
+# If the user is running an individual tests directory, we have to load
+# the libbcc site configuration first
+build_top = getattr(config, 'build_top', None)
+if build_top is None:
+ lit.load_config(config, os.path.join(os.getenv('ANDROID_BUILD_TOP',
+ '../../../../../'), 'frameworks', 'compile', 'libbcc', 'tests',
+ 'debuginfo', 'lit.site.cfg'))
+ build_top = config.build_top
+
+# Output directory in the android source tree
+config.test_exec_root = os.path.join(config.build_top, 'out', 'host',
+ 'tests', 'bcc-host')
+
+#
+## Set up environment variables
+#
+
+# - LD_LIBRARY_PATH for finding libbcc.so from the android build
+config.environment['LD_LIBRARY_PATH'] = \
+ os.path.join(config.base_build_path, 'lib') + ":" + \
+ config.environment['LD_LIBRARY_PATH']
+
+# - DEBUGGER and DEBUGGER_ARGS denote how to invoke the debugger
+config.environment['DEBUGGER'] = config.gdb
+config.environment['DEBUGGER_ARGS'] = '-q -batch -n --args ' \
+ + config.bcc_driver + ' -R '
+
+if not lit.quiet:
+ lit.note('using clang: %r' % config.clang)
+ lit.note('using bcc driver: %r' % config.bcc_driver)
+ lit.note('LD_LIBRARY_PATH is %r' % config.environment['LD_LIBRARY_PATH'])
+
+# Apply host-side test macro substitutions
+config.substitutions.append( ('%clangxx', ' ' + config.clang + \
+ ' -ccc-clang-cxx -ccc-cxx ') )
+
+config.substitutions.append( ('%extra-clang-opts', ' -emit-llvm -c ') )
+
+config.substitutions.append( ('%clang', ' ' + config.clang + ' ') )
diff --git a/tests/debuginfo/host-tests/locals.cpp b/tests/debuginfo/host-tests/locals.cpp
new file mode 100644
index 0000000..0dcaec0
--- /dev/null
+++ b/tests/debuginfo/host-tests/locals.cpp
@@ -0,0 +1,46 @@
+// RUN: %clangxx %s -g -fexceptions %extra-clang-opts -o %t
+// RUN: %Test_jit_debuginfo %s %t
+// DEBUGGER: set breakpoint pending on
+// DEBUGGER: break %s:45
+// DEBUGGER: run
+// DEBUGGER: info locals
+// CHECK: pf = 0x
+// CHECK: s = {f = 0.00100000005, f2 = {10000, 100.5}}
+// CHECK: us = 65535
+// CHECK: f = 0
+// CHECK: d = {{[{][{]}}0, 1}, {2, 3{{[}][}]}}
+// CHECK: l = 0
+// CHECK: result = 0
+// DEBUGGER: continue
+
+struct float_struct {
+ float f;
+ float f2[2];
+} compound_float;
+
+
+int main(int argc, char* argv[])
+{
+ float f = 0.f;
+ float *pf = &f;
+
+ double d[2][2] = {{0, 1}, {2, 3.0}};
+ struct float_struct s;
+
+ unsigned short us = -1;
+ const unsigned long l = static_cast<unsigned long>(-1.0e8f);
+
+ {
+ int** ppn = 0;
+ if (ppn) {
+ return -1;
+ }
+ }
+
+ s.f = 10e-4f;
+ s.f2[0] = 1e4f;
+ s.f2[1] = 100.5f;
+
+ double result = pf[0] * d[1][1] * s.f * us * l;
+ return (result == 0 ? 0 : -1);
+}
diff --git a/tests/debuginfo/host-tests/nested-struct.cpp b/tests/debuginfo/host-tests/nested-struct.cpp
new file mode 100644
index 0000000..ccfa20b
--- /dev/null
+++ b/tests/debuginfo/host-tests/nested-struct.cpp
@@ -0,0 +1,29 @@
+// RUN: %clang %s -g -fexceptions %extra-clang-opts -o %t
+// RUN: %Test_jit_debuginfo %s %t
+
+// If debug info for my_number() is emitted outside function foo's scope
+// then a debugger may not be able to handle it. At least one version of
+// gdb crashes in such cases.
+
+// DEBUGGER: set breakpoint pending on
+// DEBUGGER: b nested-struct.cpp:28
+// DEBUGGER: run
+// DEBUGGER: ptype foo
+// CHECK: type = int (void)
+
+int foo() {
+ struct Local {
+ static int my_number() {
+ return 42;
+ }
+ };
+
+ int i = 0;
+ i = Local::my_number();
+ return i + 1;
+}
+
+int main() {
+ foo();
+ return 0;
+}
diff --git a/tests/debuginfo/host-tests/parameters.cpp b/tests/debuginfo/host-tests/parameters.cpp
new file mode 100644
index 0000000..bdc86f5
--- /dev/null
+++ b/tests/debuginfo/host-tests/parameters.cpp
@@ -0,0 +1,46 @@
+// RUN: %clang %s -g -fexceptions %extra-clang-opts -o %t
+// RUN: %Test_jit_debuginfo %s %t
+// DEBUGGER: set breakpoint pending on
+// DEBUGGER: break test_parameters
+// DEBUGGER: run
+// DEBUGGER: step
+// DEBUGGER: print pf[0]
+// CHECK: $1 = 0
+// DEBUGGER: print ppd[1][1]
+// CHECK: $2 = 3
+// DEBUGGER: print s
+// CHECK: $3 = (char_struct &)
+// CHECK: {c = 97 'a', c2 = "01"}
+// DEBUGGER: print ppn
+// CHECK: $4 = (int **) 0x0
+// DEBUGGER: print us
+// CHECK: $5 = 10
+// DEBUGGER: print l
+// CHECK: $6 = 42
+// DEBUGGER: continue
+
+struct char_struct {
+ char c;
+ char c2[2];
+} compound_char;
+
+
+double test_parameters(float* pf, double ppd[][2], struct char_struct& s, int** ppn = 0, unsigned short us = 10u, const unsigned long l = 42)
+{
+ double result = pf[0] * ppd[1][1] * s.c * us * l;
+ return result;
+}
+
+int main(int argc, char* argv[])
+{
+ struct char_struct s;
+ float f = 0.f;
+ double d[2][2] = {{0, 1}, {2, 3.0}};
+
+ s.c = 'a';
+ s.c2[0] = '0';
+ s.c2[1] = '1';
+
+ double result = test_parameters(&f, d, s);
+ return(result == 0 ? 0 : -1);
+}
diff --git a/tests/debuginfo/host-tests/pass-function.c b/tests/debuginfo/host-tests/pass-function.c
new file mode 100644
index 0000000..41b0082
--- /dev/null
+++ b/tests/debuginfo/host-tests/pass-function.c
@@ -0,0 +1,75 @@
+// RUN: %clangxx %s -g -fexceptions %extra-clang-opts -o %t
+// RUN: %Test_jit_debuginfo %s %t
+// DEBUGGER: set breakpoint pending on
+// DEBUGGER: break function_test
+// DEBUGGER: break %s:47
+// DEBUGGER: break %s:55
+// DEBUGGER: break %s:60
+// DEBUGGER: break %s:66
+// DEBUGGER: break %s:69
+// DEBUGGER: run
+// DEBUGGER: bt 2
+// CHECK: #0
+// CHECK: function_test
+// CHECK: #1
+// CHECK: main
+// DEBUGGER: continue
+// DEBUGGER: print j
+// CHECK: $1 = 0
+// DEBUGGER: step
+// DEBUGGER: print j
+// CHECK: $2 = 1
+// DEBUGGER: continue
+// DEBUGGER: print j
+// CHECK: $3 = -1
+// DEBUGGER: continue
+// DEBUGGER: bt 3
+// CHECK: #0
+// CHECK: inline_test
+// CHECK: #1
+// CHECK: function_test
+// CHECK: #2
+// CHECK: main
+// DEBUGGER: continue
+// DEBUGGER: print j
+// CHECK: $4 = 2
+// DEBUGGER: continue
+// DEBUGGER: print j
+// CHECK: $5 = 0
+// DEBUGGER: continue
+
+__attribute__((noinline)) static int function_test();
+__attribute__((always_inline)) static int inline_test();
+
+int inline_test()
+{
+ int i = 0;
+ i++;
+ return i;
+}
+
+int function_test(int c)
+{
+ int i, j = 0;
+ for (i = 0; i < c; i++) {
+ j++;
+ }
+
+ {
+ int j = -1;
+ j++;
+ }
+
+ j += inline_test();
+
+ if (j > 0) {
+ j = 0;
+ }
+
+ return j;
+}
+
+int main(int argc, char** argv)
+{
+ return function_test(1);
+}
diff --git a/tests/debuginfo/host-tests/pass-struct.c b/tests/debuginfo/host-tests/pass-struct.c
new file mode 100644
index 0000000..4014cfc
--- /dev/null
+++ b/tests/debuginfo/host-tests/pass-struct.c
@@ -0,0 +1,37 @@
+// RUN: %clangxx %s -O0 -g -fexceptions %extra-clang-opts -o %t
+// RUN: %Test_jit_debuginfo %s %t
+// XFAIL: host-bcc
+// (This testcase is expected to fail because of bcc optimizations that
+// are enabled by default in the absence of metadata)
+
+// DEBUGGER: set breakpoint pending on
+// DEBUGGER: break test_struct
+// DEBUGGER: run
+// DEBUGGER: step
+// DEBUGGER: print s
+// CHECK: $1 = {n = 10, n2 = {20, 21}}
+// DEBUGGER: continue
+
+struct int_struct {
+ int n;
+ int n2[2];
+} compound_int;
+
+
+int test_struct(struct int_struct s)
+{
+ s.n2[1]++;
+ return s.n > s.n2[0] ? s.n : s.n2[0];
+}
+
+int main(int argc, char* argv[])
+{
+ struct int_struct s;
+
+ s.n = 10;
+ s.n2[0] = 20;
+ s.n2[1] = 21;
+
+ int result = test_struct(s);
+ return(result == 20 ? 0 : -1);
+}
diff --git a/tests/debuginfo/host-tests/simple_func_invoke_and_crash.cpp b/tests/debuginfo/host-tests/simple_func_invoke_and_crash.cpp
new file mode 100644
index 0000000..590b958
--- /dev/null
+++ b/tests/debuginfo/host-tests/simple_func_invoke_and_crash.cpp
@@ -0,0 +1,16 @@
+// RUN: %clangxx %s -g -fexceptions %extra-clang-opts -o %t
+// RUN: %Test_jit_debuginfo %s %t
+// DEBUGGER: run
+// DEBUGGER: bt 2
+// CHECK: function_with_a_segfault
+// CHECK: main
+
+static int function_with_a_segfault() {
+ int* bla = 0;
+ *bla = 5;
+ return 0;
+}
+
+int main() {
+ return function_with_a_segfault();
+}
diff --git a/tests/debuginfo/host-tests/test_info_sources.cpp b/tests/debuginfo/host-tests/test_info_sources.cpp
new file mode 100644
index 0000000..4937aa1
--- /dev/null
+++ b/tests/debuginfo/host-tests/test_info_sources.cpp
@@ -0,0 +1,31 @@
+// RUN: %clangxx %s -g -fexceptions %extra-clang-opts -o %t
+// RUN: %Test_jit_debuginfo %s %t
+// DEBUGGER: set verbose on
+// DEBUGGER: b __jit_debug_register_code
+// DEBUGGER: run
+// DEBUGGER: info sources
+// CHECK: test_info_sources.cpp
+// DEBUGGER: c
+
+
+static int function_with_a_segfault() {
+ int* bla = 0;
+ *bla = 5;
+ return 0;
+}
+
+static int some_function() {
+ return function_with_a_segfault();
+}
+
+static int foo() {
+ return some_function();
+}
+
+static int bar() {
+ return foo();
+}
+
+int main() {
+ return bar();
+}
diff --git a/tests/debuginfo/lit.site.cfg b/tests/debuginfo/lit.site.cfg
new file mode 100644
index 0000000..a41edd3
--- /dev/null
+++ b/tests/debuginfo/lit.site.cfg
@@ -0,0 +1,101 @@
+# -*- Python -*-
+#
+# Copyright (C) 2012 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.
+#
+
+# Configuration file for the 'lit' test runner in Android libbcc
+# This file is common to both host and target side tests
+
+import os
+
+# Used to determine the absolute path of a tool. If env_var is set, it
+# overrides the default behaviour of searching PATH for binary_name
+def inferTool(lit, binary_name, env_var, PATH):
+ # Determine which tool to use.
+ tool = os.getenv(env_var)
+
+ # If the user set the overriding environment variable, use it
+ if tool and os.path.isfile(tool):
+ return tool
+
+ # Otherwise look in the path.
+ tool = lit.util.which(binary_name, PATH)
+
+ if not tool:
+ lit.fatal("couldn't find " + binary_name + " program in " + PATH + " \
+ , try setting " + env_var + " in your environment")
+
+ return os.path.abspath(tool)
+
+# Get the base build directory for the android source tree from environment.
+config.build_top = os.getenv('ANDROID_BUILD_TOP')
+
+config.base_build_path = os.path.join(config.build_top, 'out', 'host',
+ 'linux-x86')
+
+# testFormat: The test format to use to interpret tests.
+config.test_format = lit.formats.ShTest()
+
+# Tool used to verify debugger output against expected output in source
+config.filecheck = inferTool(lit, 'FileCheck', 'FILECHECK', \
+ os.path.join(config.base_build_path, 'bin'))
+
+# Invokes GDB and captures output
+config.test_bcc_debuginfo = inferTool(lit, 'test_bcc_debuginfo.pl', \
+ 'TEST_JIT_DEBUGINFO', os.path.join(config.build_top, 'frameworks', \
+ 'compile', 'libbcc', 'tests', 'debuginfo'))
+
+# GDB
+config.gdb = inferTool(lit, 'gdb', 'GDB', config.environment['PATH'])
+
+# GDB python plugin
+config.gdb_plugin = inferTool(lit, 'android-commands.py',
+ 'ANDROID_GDB_PLUGIN', os.path.join(config.build_top, 'frameworks',
+ 'compile', 'libbcc', 'gdb_plugin'))
+config.gdb_plugin_directory = os.path.dirname(config.gdb_plugin)
+
+# Script interpreters that are not python
+config.perl = inferTool(lit, 'perl', 'PERL', config.environment['PATH'])
+config.sh = inferTool(lit, 'bash', 'BASH', config.environment['PATH'])
+
+# Tools that are specific to running host-side debugger integration tests:
+config.clang = inferTool(lit, 'clang', 'CLANG',
+ os.path.join(config.base_build_path, 'bin')).replace('\\', '/')
+config.bcc_driver = inferTool(lit, 'bcc', 'BCC_DRIVER',
+ os.path.join(config.base_build_path, 'obj', 'EXECUTABLES', \
+ 'bcc_intermediates')).replace('\\', '/')
+
+# Tools that are specific to running target-side debugger integration tests:
+config.build_test_apk = inferTool(lit, 'build_test_apk.sh',
+ 'BUILD_TEST_APK',
+ os.path.join(config.build_top, 'frameworks', 'compile', 'libbcc',
+ 'tests', 'debuginfo'))
+
+#
+## Apply common substitutions
+#
+config.substitutions.append( ('%Test_jit_debuginfo', config.perl \
+ + ' ' + config.test_bcc_debuginfo \
+ + ' ' + config.filecheck + ' ' ) )
+
+#
+## Print common configuration
+#
+if not lit.quiet:
+ lit.note('using bash: %r' % config.sh)
+ lit.note('using perl: %r' % config.perl)
+ lit.note('using verification script: %r' % config.test_bcc_debuginfo)
+ lit.note('using FileCheck: %r' % config.filecheck)
+ lit.note('using GDB: %r' % config.gdb)
diff --git a/tests/debuginfo/llvm-lit b/tests/debuginfo/llvm-lit
new file mode 100755
index 0000000..117fe76
--- /dev/null
+++ b/tests/debuginfo/llvm-lit
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+
+import os
+import sys
+
+# In the Android tree, use the environment variables set by envsetup.sh
+# to determine correct path for the root of the source tree.
+# TODO: To run clang tests, @LLVM_BINARY_DIR@ must be substituted also.
+android_source_root = os.getenv('ANDROID_BUILD_TOP', ".")
+llvm_source_root = os.path.join(android_source_root, 'external', 'llvm')
+libbcc_source_root = os.path.join(android_source_root, 'frameworks', 'compile',
+ 'libbcc')
+
+# Make sure we can find the lit package.
+sys.path.append(os.path.join(llvm_source_root, 'utils', 'lit'))
+
+# Set up some builtin parameters, so that by default the LLVM test suite
+# configuration file knows how to find the object tree.
+builtin_parameters = {
+ 'llvm_site_config' : os.path.join(libbcc_source_root, 'test', 'debuginfo',
+ 'lit.site.cfg')
+ }
+
+if __name__=='__main__':
+ import lit
+ lit.main(builtin_parameters)
diff --git a/tests/debuginfo/target-tests/breakpoint_function.rs b/tests/debuginfo/target-tests/breakpoint_function.rs
new file mode 100644
index 0000000..bb5f59e
--- /dev/null
+++ b/tests/debuginfo/target-tests/breakpoint_function.rs
@@ -0,0 +1,37 @@
+// RUN: %build_test_apk --driver driver-simple-exit --out %t --testcase %s %build_test_apk_opts
+// RUN: %Test_jit_debuginfo %s %t
+// DEBUGGER: source android-commands.py
+// DEBUGGER: load-android-app %t
+// DEBUGGER: set breakpoint pending on
+// DEBUGGER: b entry
+// DEBUGGER: run-android-app
+// DEBUGGER: bt
+// CHECK: entry
+// CHECK: breakpoint_function.rs
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.test.rsdebug.breakpoint_function)
+
+static int twenty() {
+ return 20;
+}
+
+static int some_function() {
+ return twenty();
+}
+
+static int foo() {
+ return some_function();
+}
+
+static int bar() {
+ return foo();
+}
+
+int root() {
+ return bar();
+}
+
+void entry() {
+ bar();
+}
diff --git a/tests/debuginfo/target-tests/breakpoint_inlined_function.rs b/tests/debuginfo/target-tests/breakpoint_inlined_function.rs
new file mode 100644
index 0000000..c769c89
--- /dev/null
+++ b/tests/debuginfo/target-tests/breakpoint_inlined_function.rs
@@ -0,0 +1,41 @@
+// RUN: %build_test_apk --driver driver-simple-exit --out %t --testcase %s %build_test_apk_opts
+// RUN: %Test_jit_debuginfo %s %t
+// DEBUGGER: source android-commands.py
+// DEBUGGER: load-android-app %t
+// DEBUGGER: set breakpoint pending on
+// DEBUGGER: b twenty
+// DEBUGGER: run-android-app
+// DEBUGGER: bt
+// CHECK: twenty
+// CHECK: some_function
+// CHECK: foo
+// CHECK: bar
+// CHECK: entry
+// CHECK: breakpoint_inlined_function.rs:
+
+#pragma version(1)
+#pragma rs java_package_name(%PACKAGE%)
+
+static int twenty() {
+ return 20;
+}
+
+static int some_function() {
+ return twenty();
+}
+
+static int foo() {
+ return some_function();
+}
+
+static int bar() {
+ return foo();
+}
+
+int root() {
+ return bar();
+}
+
+void entry() {
+ bar();
+}
diff --git a/tests/debuginfo/target-tests/breakpoint_inlined_sourceline.rs b/tests/debuginfo/target-tests/breakpoint_inlined_sourceline.rs
new file mode 100644
index 0000000..9022a87
--- /dev/null
+++ b/tests/debuginfo/target-tests/breakpoint_inlined_sourceline.rs
@@ -0,0 +1,40 @@
+// RUN: %build_test_apk --driver driver-simple-exit --out %t --testcase %s %build_test_apk_opts
+// RUN: %Test_jit_debuginfo %s %t
+// DEBUGGER: source android-commands.py
+// DEBUGGER: load-android-app %t
+// DEBUGGER: set breakpoint pending on
+// DEBUGGER: b %s:18
+// DEBUGGER: run-android-app
+// DEBUGGER: bt
+// CHECK: some_function
+// CHECK: foo
+// CHECK: bar
+// CHECK: entry
+// CHECK: breakpoint_inlined_sourceline.rs:
+
+#pragma version(1)
+#pragma rs java_package_name(%PACKAGE%)
+
+static int twenty() {
+ return 20;
+}
+
+static int some_function() {
+ return twenty();
+}
+
+static int foo() {
+ return some_function();
+}
+
+static int bar() {
+ return foo();
+}
+
+int root() {
+ return bar();
+}
+
+void entry() {
+ bar();
+}
diff --git a/tests/debuginfo/target-tests/breakpoint_sourceline.rs b/tests/debuginfo/target-tests/breakpoint_sourceline.rs
new file mode 100644
index 0000000..c0700a6
--- /dev/null
+++ b/tests/debuginfo/target-tests/breakpoint_sourceline.rs
@@ -0,0 +1,36 @@
+// RUN: %build_test_apk --driver driver-simple-exit --out %t --testcase %s %build_test_apk_opts
+// RUN: %Test_jit_debuginfo %s %t
+// DEBUGGER: source android-commands.py
+// DEBUGGER: load-android-app %t
+// DEBUGGER: set breakpoint pending on
+// DEBUGGER: b %s:35
+// DEBUGGER: run-android-app
+// DEBUGGER: bt
+// CHECK: entry
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.test.rsdebug.breakpoint_sourceline)
+
+static int twenty() {
+ return 20;
+}
+
+static int some_function() {
+ return twenty();
+}
+
+static int foo() {
+ return some_function();
+}
+
+static int bar() {
+ return foo();
+}
+
+int root() {
+ return bar();
+}
+
+void entry() {
+ bar();
+}
diff --git a/tests/debuginfo/target-tests/crash.rs b/tests/debuginfo/target-tests/crash.rs
new file mode 100644
index 0000000..c4980bc
--- /dev/null
+++ b/tests/debuginfo/target-tests/crash.rs
@@ -0,0 +1,36 @@
+// RUN: %build_test_apk --driver driver-simple --out %t --testcase %s %build_test_apk_opts
+// RUN: %Test_jit_debuginfo %s %t
+// DEBUGGER: source android-commands.py
+// DEBUGGER: load-android-app %t
+// DEBUGGER: run-android-app
+// DEBUGGER: bt
+// CHECK: entry
+
+#pragma version(1)
+#pragma rs java_package_name(%PACKAGE%)
+
+static int function_with_a_segfault() {
+ int* bla = 0;
+ *bla = 5;
+ return 0;
+}
+
+static int some_function() {
+ return function_with_a_segfault();
+}
+
+static int foo() {
+ return some_function();
+}
+
+static int bar() {
+ return foo();
+}
+
+int root() {
+ return bar();
+}
+
+void entry() {
+ bar();
+}
diff --git a/tests/debuginfo/target-tests/driver-common/AndroidManifest.xml b/tests/debuginfo/target-tests/driver-common/AndroidManifest.xml
new file mode 100644
index 0000000..7e3e058
--- /dev/null
+++ b/tests/debuginfo/target-tests/driver-common/AndroidManifest.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="%PACKAGE%"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <uses-sdk android:minSdkVersion="%MINSDK%" />
+ <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
+ <activity android:name="%ACTIVITY%"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/debuginfo/target-tests/driver-common/SRC/DriverRS.java.template b/tests/debuginfo/target-tests/driver-common/SRC/DriverRS.java.template
new file mode 100644
index 0000000..5706fa2
--- /dev/null
+++ b/tests/debuginfo/target-tests/driver-common/SRC/DriverRS.java.template
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+package %PACKAGE%;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+
+// This is the renderer for the driver
+public class DriverRS {
+ private Resources mRes;
+ private RenderScriptGL mRS;
+
+ private ScriptC_%TESTCASE% mScript;
+
+ public DriverRS() {
+ }
+
+ // This provides us with the renderscript context and resources that
+ // allow us to create the script that does rendering
+ public void init(RenderScriptGL rs, Resources res) {
+ mRS = rs;
+ mRes = res;
+ initRS();
+ }
+
+ private void initRS() {
+ mScript = new ScriptC_%TESTCASE% (mRS, mRes, R.raw.%TESTCASE%);
+ mScript.invoke_entry();
+ }
+}
+
diff --git a/tests/debuginfo/target-tests/driver-common/SRC/DriverView.java.template b/tests/debuginfo/target-tests/driver-common/SRC/DriverView.java.template
new file mode 100644
index 0000000..4c99c08
--- /dev/null
+++ b/tests/debuginfo/target-tests/driver-common/SRC/DriverView.java.template
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+package %PACKAGE%;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScriptGL;
+
+import android.content.Context;
+import android.view.MotionEvent;
+
+public class DriverView extends RSSurfaceView {
+ // Renderscipt context
+ private RenderScriptGL mRS;
+ // Script that does the rendering
+ private DriverRS mRender;
+
+ public DriverView(Context context) {
+ super(context);
+ ensureRenderScript();
+ }
+
+ private void ensureRenderScript() {
+ if (mRS == null) {
+ // Initialize renderscript with desired surface characteristics.
+ // In this case, just use the defaults
+ RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
+ mRS = createRenderScriptGL(sc);
+ // Create an instance of the script that does the rendering
+ mRender = new DriverRS();
+ mRender.init(mRS, getResources());
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ ensureRenderScript();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ // Handle the system event and clean up
+ mRender = null;
+ if (mRS != null) {
+ mRS = null;
+ destroyRenderScriptGL();
+ }
+ }
+}
+
+
diff --git a/tests/debuginfo/target-tests/driver-int-param/ACTIVITY.java.template b/tests/debuginfo/target-tests/driver-int-param/ACTIVITY.java.template
new file mode 100644
index 0000000..e90da34
--- /dev/null
+++ b/tests/debuginfo/target-tests/driver-int-param/ACTIVITY.java.template
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+package %PACKAGE%;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+// Renderscript activity
+public class %ACTIVITY% extends Activity {
+
+ // Custom view to use with RenderScript
+ private DriverView mView;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ // Create our view and set it as the content of our Activity
+ mView = new DriverView(this);
+ setContentView(mView);
+
+ // explicit kill
+ this.finish();
+ android.os.Process.killProcess(android.os.Process.myPid());
+ }
+
+ @Override
+ protected void onResume() {
+ // Ideally an app should implement onResume() and onPause()
+ // to take appropriate action when the activity loses focus
+ super.onResume();
+ mView.resume();
+ }
+
+ @Override
+ protected void onPause() {
+ // Ideally an app should implement onResume() and onPause()
+ // to take appropriate action when the activity loses focus
+ super.onPause();
+ mView.pause();
+ }
+
+}
+
diff --git a/tests/debuginfo/target-tests/driver-int-param/DriverRS.java.template b/tests/debuginfo/target-tests/driver-int-param/DriverRS.java.template
new file mode 100644
index 0000000..9a8bb17
--- /dev/null
+++ b/tests/debuginfo/target-tests/driver-int-param/DriverRS.java.template
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+package %PACKAGE%;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+
+// This is the renderer for the driver
+public class DriverRS {
+ private Resources mRes;
+ private RenderScriptGL mRS;
+
+ private ScriptC_%TESTCASE% mScript;
+
+ public DriverRS() {
+ }
+
+ // This provides us with the renderscript context and resources that
+ // allow us to create the script that does rendering
+ public void init(RenderScriptGL rs, Resources res) {
+ mRS = rs;
+ mRes = res;
+ initRS();
+ }
+
+ private void initRS() {
+ mScript = new ScriptC_%TESTCASE% (mRS, mRes, R.raw.%TESTCASE%);
+ mScript.invoke_entry(40);
+ }
+}
+
diff --git a/tests/debuginfo/target-tests/driver-simple-exit/ACTIVITY.java.template b/tests/debuginfo/target-tests/driver-simple-exit/ACTIVITY.java.template
new file mode 100644
index 0000000..e90da34
--- /dev/null
+++ b/tests/debuginfo/target-tests/driver-simple-exit/ACTIVITY.java.template
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+package %PACKAGE%;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+// Renderscript activity
+public class %ACTIVITY% extends Activity {
+
+ // Custom view to use with RenderScript
+ private DriverView mView;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ // Create our view and set it as the content of our Activity
+ mView = new DriverView(this);
+ setContentView(mView);
+
+ // explicit kill
+ this.finish();
+ android.os.Process.killProcess(android.os.Process.myPid());
+ }
+
+ @Override
+ protected void onResume() {
+ // Ideally an app should implement onResume() and onPause()
+ // to take appropriate action when the activity loses focus
+ super.onResume();
+ mView.resume();
+ }
+
+ @Override
+ protected void onPause() {
+ // Ideally an app should implement onResume() and onPause()
+ // to take appropriate action when the activity loses focus
+ super.onPause();
+ mView.pause();
+ }
+
+}
+
diff --git a/tests/debuginfo/target-tests/driver-simple/ACTIVITY.java.template b/tests/debuginfo/target-tests/driver-simple/ACTIVITY.java.template
new file mode 100644
index 0000000..71e4539
--- /dev/null
+++ b/tests/debuginfo/target-tests/driver-simple/ACTIVITY.java.template
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+package %PACKAGE%;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+// Renderscript activity
+public class %ACTIVITY% extends Activity {
+
+ // Custom view to use with RenderScript
+ private DriverView mView;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ // Create our view and set it as the content of our Activity
+ mView = new DriverView(this);
+ setContentView(mView);
+ }
+
+ @Override
+ protected void onResume() {
+ // Ideally an app should implement onResume() and onPause()
+ // to take appropriate action when the activity loses focus
+ super.onResume();
+ mView.resume();
+ }
+
+ @Override
+ protected void onPause() {
+ // Ideally an app should implement onResume() and onPause()
+ // to take appropriate action when the activity loses focus
+ super.onPause();
+ mView.pause();
+ }
+
+}
+
diff --git a/tests/debuginfo/target-tests/global_int.rs b/tests/debuginfo/target-tests/global_int.rs
new file mode 100644
index 0000000..6560e3c
--- /dev/null
+++ b/tests/debuginfo/target-tests/global_int.rs
@@ -0,0 +1,48 @@
+// RUN: %build_test_apk --driver driver-int-param --out %t --testcase %s %build_test_apk_opts
+// RUN: %Test_jit_debuginfo %s %t
+// DEBUGGER: source android-commands.py
+// DEBUGGER: load-android-app %t
+// DEBUGGER: set breakpoint pending on
+// DEBUGGER: b %s:46
+// DEBUGGER: run-android-app
+// DEBUGGER: p global_zero
+// DEBUGGER: p global_value
+// CHECK: $1 = 0
+// CHECK: $2 = 11
+
+#pragma version(1)
+#pragma rs java_package_name(%PACKAGE%)
+
+// a global value
+int global_zero = 0;
+int global_value = 1;
+
+static int twenty() {
+ return 20;
+}
+
+static int some_function() {
+ return twenty();
+}
+
+static int foo() {
+ return some_function();
+}
+
+static int bar() {
+ return foo();
+}
+
+int root() {
+ return bar();
+}
+
+void entry(int parameter) {
+ bar();
+ if (parameter != 0) {
+ global_value += 10;
+ } else {
+ global_zero += 1;
+ }
+ global_zero += global_value;
+}
diff --git a/tests/debuginfo/target-tests/info_sources.rs b/tests/debuginfo/target-tests/info_sources.rs
new file mode 100644
index 0000000..546333d
--- /dev/null
+++ b/tests/debuginfo/target-tests/info_sources.rs
@@ -0,0 +1,20 @@
+// RUN: %build_test_apk --driver driver-simple --out %t --testcase %s %build_test_apk_opts
+// RUN: %Test_jit_debuginfo %s %t
+// DEBUGGER: source android-commands.py
+// DEBUGGER: load-android-app %t
+// DEBUGGER: run-android-app
+// DEBUGGER: info sources
+// CHECK: info_sources/info_sources.rs
+
+#pragma version(1)
+#pragma rs java_package_name(%PACKAGE%)
+
+static int function_with_a_segfault() {
+ int* bla = 0;
+ *bla = 5;
+ return 0;
+}
+
+void entry() {
+ function_with_a_segfault();
+}
diff --git a/tests/debuginfo/target-tests/lit.cfg b/tests/debuginfo/target-tests/lit.cfg
new file mode 100644
index 0000000..6693781
--- /dev/null
+++ b/tests/debuginfo/target-tests/lit.cfg
@@ -0,0 +1,90 @@
+# -*- Python -*-
+#
+# Copyright (C) 2012 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.
+#
+
+#
+### Configuration file for target side debugger integration tests
+#
+# Parameters available through lit --param options:
+# android_sdk - Path to the android SDK directory
+# sdk_version - SDK target to pass to 'android' for creating test projects
+# minimum_sdk - SDK minimum version to embed in AndroidManifest.xml
+
+# If the user is running an individual tests directory, we have to load
+# the libbcc site configuration first
+build_top = getattr(config, 'build_top', None)
+if build_top is None:
+ lit.load_config(config, os.path.join(os.getenv('ANDROID_BUILD_TOP',
+ '../../../../../'), 'frameworks', 'compile', 'libbcc', 'tests',
+ 'debuginfo', 'lit.site.cfg'))
+ build_top = config.build_top
+
+# Default SDK path and version
+default_sdk_dir = os.path.join(config.base_build_path, 'sdk', 'android-sdk_' \
+ + os.getenv('TARGET_BUILD_VARIANT') + '.' \
+ + os.getenv('USER') + '_linux-x86')
+default_sdk_version = "android-AOSP"
+default_minimum_sdk = "AOSP"
+
+# Set up the suite name, extensions that are recognized as testcases, and
+# the target triple string that must be used in cases marked expected failures
+config.name = 'target_renderscript_debug'
+config.suffixes = ['.rs']
+config.target_triple = 'target-bcc'
+
+# Output directory in the android source tree
+if os.getenv('TARGET_BUILD_TYPE', None) == 'debug':
+ config.test_exec_root = os.path.join(config.build_top, 'out', 'debug',
+ 'target', 'tests', 'rsdebug')
+else:
+ config.test_exec_root = os.path.join(config.build_top, 'out', 'target',
+ 'tests', 'rsdebug')
+
+#
+## Set up SDK path and version
+#
+config.sdk_dir = lit.params.get('android_sdk', default_sdk_dir)
+if not os.path.isdir(config.sdk_dir):
+ lit.fatal("Android SDK directory " + config.sdk_dir + " does " \
+ + "not exist. Check --param android_sdk=<path> lit parameter in test " \
+ + "suite invocation.")
+
+config.sdk_version = lit.params.get('sdk_version', default_sdk_version)
+config.minimum_sdk = lit.params.get('minimum_sdk', default_minimum_sdk)
+
+#
+## Set up environment variables
+#
+
+# Propagate ANDROID_PRODUCT_OUT to child environment
+config.environment['ANDROID_PRODUCT_OUT'] = os.getenv('ANDROID_PRODUCT_OUT')
+config.environment['ANDROID_BUILD_TOP'] = os.getenv('ANDROID_BUILD_TOP')
+
+config.environment['DEBUGGER'] = config.gdb
+config.environment['DEBUGGER_ARGS'] = "-d " + config.gdb_plugin_directory + ' '
+
+if not lit.quiet:
+ lit.note('using Android SDK: %r' % config.sdk_dir)
+ lit.note('using Android SDK Version: %r' % config.sdk_version)
+ lit.note('using test apk builder: %r' % config.build_test_apk)
+ lit.note('using GDB plugin directory: %r' % config.gdb_plugin_directory)
+
+# Apply target-side test macro substitutions
+config.substitutions.append( ('%build_test_apk_opts', ' --sdk ' + config.sdk_dir \
+ + ' --target ' + config.sdk_version \
+ + ' --minsdk ' + config.minimum_sdk))
+config.substitutions.append( ('%build_test_apk', ' ' + config.sh \
+ + ' ' + config.build_test_apk + ' '))
diff --git a/tests/debuginfo/target-tests/locals.rs b/tests/debuginfo/target-tests/locals.rs
new file mode 100644
index 0000000..b1e4682
--- /dev/null
+++ b/tests/debuginfo/target-tests/locals.rs
@@ -0,0 +1,57 @@
+// RUN: %build_test_apk --driver driver-simple-exit --out %t --testcase %s %build_test_apk_opts
+// RUN: %Test_jit_debuginfo %s %t
+// DEBUGGER: source android-commands.py
+// DEBUGGER: load-android-app %t
+// DEBUGGER: set breakpoint pending on
+// DEBUGGER: break locals.rs:48
+// DEBUGGER: run-android-app
+// DEBUGGER: info locals
+// CHECK: pf = 0x
+// CHECK: s = {f = 0.00100000005, f2 = {10000, 100.5}}
+// CHECK: us = 65535
+// CHECK: f = 0
+// CHECK: d = {{[{][{]}}0, 1}, {2, 3{{[}][}]}}
+// CHECK: l = 0
+// CHECK: result = 0
+// DEBUGGER: continue
+
+struct float_struct {
+ float f;
+ float f2[2];
+} compound_float;
+
+
+static
+int main(int argc, char* argv[])
+{
+ float f = 0.f;
+ float *pf = &f;
+
+ double d[2][2] = {{0, 1}, {2, 3.0}};
+ struct float_struct s;
+
+ unsigned short us = -1;
+ const unsigned long l = (unsigned long) -1.0e8f;
+
+ {
+ int** ppn = 0;
+ if (ppn) {
+ return -1;
+ }
+ }
+
+ s.f = 10e-4f;
+ s.f2[0] = 1e4f;
+ s.f2[1] = 100.5f;
+
+ double result = pf[0] * d[1][1] * s.f * us * l;
+ return (result == 0 ? 0 : -1);
+}
+
+void entry() {
+ main(0, 0);
+}
+
+#pragma version(1)
+#pragma rs java_package_name(%PACKAGE%)
+
diff --git a/tests/debuginfo/test_bcc_debuginfo.pl b/tests/debuginfo/test_bcc_debuginfo.pl
new file mode 100755
index 0000000..fd2057d
--- /dev/null
+++ b/tests/debuginfo/test_bcc_debuginfo.pl
@@ -0,0 +1,90 @@
+#!/usr/bin/perl
+#===-- test_bcc_debuginfo.pl - Debugger integration test driver script ---===#
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+#===----------------------------------------------------------------------===#
+#
+# This script tests debugging information generated by a compiler.
+# Input arguments
+# - Path to FileCheck tool.
+# - Input source program. Usually this source file is decorated using
+# special comments (//DEBUGGER:, //CHECK:)to communicate debugger commands.
+# - Executable file. This file is generated by the compiler.
+#
+# This perl script extracts debugger commands from input source program
+# comments in a script. A debugger is used to load the executable file
+# and run the script generated from source program comments. Finally,
+# the debugger output is checked, using FileCheck, to validate
+# debugging information.
+#
+#===----------------------------------------------------------------------===#
+
+use File::Basename;
+
+my $filecheck_tool = $ARGV[0];
+my $testcase_file = $ARGV[1];
+my $testcase_output = $ARGV[2];
+
+my $input_filename = basename $testcase_file;
+my $output_dir = dirname $testcase_output;
+
+my $debugger_script_file = "$output_dir/$input_filename.debugger.script";
+my $output_file = "$output_dir/$input_filename.gdb.output";
+
+open(OUTPUT, ">$debugger_script_file");
+
+# Enable extra verbosity in GDB
+print OUTPUT "set verbose on\n";
+
+# Extract debugger commands from testcase. They are marked with DEBUGGER:
+# at the beginning of a comment line.
+open(INPUT, $testcase_file);
+while(<INPUT>) {
+ my($line) = $_;
+ $i = index($line, "DEBUGGER:");
+ if ( $i >= 0) {
+ $l = length("DEBUGGER:");
+ $s = substr($line, $i + $l);
+ $s =~ s/\%s/$input_filename/g;
+ $s =~ s/\%t/$testcase_output/g;
+ print OUTPUT "$s";
+ }
+}
+print OUTPUT "\n";
+print OUTPUT "quit\n";
+close(INPUT);
+close(OUTPUT);
+
+# setup debugger and debugger options to run a script.
+my $debugger = $ENV{'DEBUGGER'};
+my $debugger_options = $ENV{'DEBUGGER_ARGS'};
+if (!$debugger) {
+ print "Please set DEBUGGER prior to using this script";
+ exit 1;
+}
+$debugger_options = "-x $debugger_script_file $debugger_options $testcase_output";
+
+# run debugger and capture output.
+system("$debugger $debugger_options > $output_file 2>&1") ;
+if ($?>>8 != 0) {
+ print "Error during debugger invocation. Command used was: \n";
+ print("$debugger $debugger_options > $output_file 2>&1\n") ;
+ exit 1;
+}
+
+# validate output.
+system("$filecheck_tool", "-input-file", "$output_file", "$testcase_file");
+if ($?>>8 != 0) {
+ print "Error during verification. Debugger command used was: \n";
+ print("$debugger $debugger_options > $output_file 2>&1\n") ;
+ print "Verification command used was: \n";
+ print "$filecheck_tool -input-file $output_file $testcase_file\n";
+ exit 1;
+}
+else {
+ exit 0;
+}
diff --git a/tests/main.cpp b/tests/main.cpp
index 189d7e6..8749a0b 100644
--- a/tests/main.cpp
+++ b/tests/main.cpp
@@ -229,11 +229,17 @@
}
static int runMain(BCCScriptRef script, int argc, char** argv) {
- MainPtr mainPointer = (MainPtr)bccGetFuncAddr(script, "root");
+ MainPtr mainPointer = (MainPtr)bccGetFuncAddr(script, "main");
if (!mainPointer) {
- fprintf(stderr, "Could not find root.\n");
- return 0;
+ mainPointer = (MainPtr)bccGetFuncAddr(script, "root");
+ }
+ if (!mainPointer) {
+ mainPointer = (MainPtr)bccGetFuncAddr(script, "_Z4rootv");
+ }
+ if (!mainPointer) {
+ fprintf(stderr, "Could not find root or main or mangled root.\n");
+ return 1;
}
fprintf(stderr, "Executing compiled code:\n");