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");