Add a debugger hook point during script group compilation at -O0.

This hook supplies the debugger with the name of the script group and the
addresses of its kernels.  This allows breakpoints to be placed on a script
group, which has the effect of placing a breakpoint on its constituent kernels.
This is only executed when compiling at -O0.

Test: Run the renderscript lldb test suite.
Change-Id: I7c688d59aa702696406214423303853ec8dfa6b7
Signed-off-by: Aidan Dodds <[email protected]>
diff --git a/cpu_ref/rsCpuScriptGroup2.cpp b/cpu_ref/rsCpuScriptGroup2.cpp
index 2382cd6..2dd43ff 100644
--- a/cpu_ref/rsCpuScriptGroup2.cpp
+++ b/cpu_ref/rsCpuScriptGroup2.cpp
@@ -331,12 +331,46 @@
 
 }  // anonymous namespace
 
+extern __attribute__((noinline))
+void debugHintScriptGroup2(const char* groupName,
+                           const uint32_t groupNameSize,
+                           const ExpandFuncTy* kernel,
+                           const uint32_t kernelCount) {
+    ALOGV("group name: %d:%s\n", groupNameSize, groupName);
+    for (uint32_t i=0; i < kernelCount; ++i) {
+        const char* f1 = (const char*)(kernel[i]);
+        ALOGV("  closure: %p\n", (const void*)f1);
+    }
+    // do nothing, this is just a hook point for the debugger.
+    return;
+}
+
 void CpuScriptGroup2Impl::compile(const char* cacheDir) {
 #ifndef RS_COMPATIBILITY_LIB
     if (mGroup->mClosures.size() < 2) {
         return;
     }
 
+    const int optLevel = getCpuRefImpl()->getContext()->getOptLevel();
+    if (optLevel == 0) {
+        std::vector<ExpandFuncTy> kernels;
+        for (const Batch* b : mBatches)
+            for (const CPUClosure* c : b->mClosures)
+                kernels.push_back(c->mFunc);
+
+        if (kernels.size()) {
+            // pass this information on to the debugger via a hint function.
+            debugHintScriptGroup2(mGroup->mName,
+                                  strlen(mGroup->mName),
+                                  kernels.data(),
+                                  kernels.size());
+        }
+
+        // skip script group compilation forcing the driver to use the fallback
+        // execution path which currently has better support for debugging.
+        return;
+    }
+
     auto comparator = [](const char* str1, const char* str2) -> bool {
         return strcmp(str1, str2) < 0;
     };
@@ -392,8 +426,6 @@
     const string& coreLibPath = getCoreLibPath(getCpuRefImpl()->getContext(),
                                                &coreLibRelaxedPath);
 
-    int optLevel = getCpuRefImpl()->getContext()->getOptLevel();
-
     vector<const char*> arguments;
     bool emitGlobalInfo = getCpuRefImpl()->getEmbedGlobalInfo();
     bool emitGlobalInfoSkipConstant = getCpuRefImpl()->getEmbedGlobalInfoSkipConstant();
diff --git a/tests/lldb/java/ScriptGroup/Android.mk b/tests/lldb/java/ScriptGroup/Android.mk
new file mode 100644
index 0000000..3540ed4
--- /dev/null
+++ b/tests/lldb/java/ScriptGroup/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+
+LOCAL_PACKAGE_NAME := ScriptGroup
+
+LOCAL_RENDERSCRIPT_FLAGS := -g -O0 -target-api 0
+
+include $(BUILD_PACKAGE)
diff --git a/tests/lldb/java/ScriptGroup/AndroidManifest.xml b/tests/lldb/java/ScriptGroup/AndroidManifest.xml
new file mode 100644
index 0000000..5288c74
--- /dev/null
+++ b/tests/lldb/java/ScriptGroup/AndroidManifest.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.rs.scriptgroup">
+    <uses-sdk android:minSdkVersion="21" />
+    <application android:label="scriptgroup"
+                 android:hardwareAccelerated="true">
+        <activity android:name="MainActivity">
+            <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/lldb/java/ScriptGroup/res/layout/main_layout.xml b/tests/lldb/java/ScriptGroup/res/layout/main_layout.xml
new file mode 100644
index 0000000..4ef172f
--- /dev/null
+++ b/tests/lldb/java/ScriptGroup/res/layout/main_layout.xml
@@ -0,0 +1,14 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="#0099cc"
+    tools:context=".MainActivity">
+
+    <ImageView
+        android:id="@+id/imageView"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:scaleType="fitCenter" />
+
+</FrameLayout>
diff --git a/tests/lldb/java/ScriptGroup/src/com/android/rs/scriptgroup/MainActivity.java b/tests/lldb/java/ScriptGroup/src/com/android/rs/scriptgroup/MainActivity.java
new file mode 100644
index 0000000..f4c81fd
--- /dev/null
+++ b/tests/lldb/java/ScriptGroup/src/com/android/rs/scriptgroup/MainActivity.java
@@ -0,0 +1,49 @@
+package com.android.rs.scriptgroup;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.widget.ImageView;
+import android.renderscript.*;
+
+public class MainActivity extends Activity {
+    private static final int ARRAY_SIZE = 8;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main_layout);
+
+        // create renderscript context
+        RenderScript pRS = RenderScript.create(this, RenderScript.ContextType.NORMAL,
+            RenderScript.CREATE_FLAG_WAIT_FOR_ATTACH | RenderScript.CREATE_FLAG_LOW_LATENCY);
+
+        ScriptC_scriptgroup script = new ScriptC_scriptgroup(pRS);
+
+        // create and initalize a simple input allocation
+        int[] array = new int[ARRAY_SIZE];
+        for (int i = 0; i < ARRAY_SIZE; i++) {
+            array[i] = i;
+        }
+        Allocation input = Allocation.createSized(pRS, Element.I32(pRS), ARRAY_SIZE);
+        input.copyFrom(array);
+
+        ScriptGroup.Builder2 builder = new ScriptGroup.Builder2(pRS);
+
+        ScriptGroup.Input unbound = builder.addInput();
+
+        ScriptGroup.Closure c0 = builder.addKernel(
+            script.getKernelID_foo(), Type.createX(pRS, Element.I32(pRS), ARRAY_SIZE), unbound);
+
+        ScriptGroup.Closure c1 = builder.addKernel(script.getKernelID_goo(),
+            Type.createX(pRS, Element.I32(pRS), ARRAY_SIZE), c0.getReturn());
+
+        ScriptGroup group = builder.create("scriptgroup_test", c1.getReturn());
+
+        int[] a = new int[ARRAY_SIZE];
+        ((Allocation) group.execute(input)[0]).copyTo(a);
+
+        pRS.finish();
+        pRS.destroy();
+    }
+}
diff --git a/tests/lldb/java/ScriptGroup/src/rs/scriptgroup.rs b/tests/lldb/java/ScriptGroup/src/rs/scriptgroup.rs
new file mode 100644
index 0000000..f364114
--- /dev/null
+++ b/tests/lldb/java/ScriptGroup/src/rs/scriptgroup.rs
@@ -0,0 +1,11 @@
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.scriptgroup)
+#pragma rs_fp_full
+
+int __attribute__((kernel)) foo(int a) {
+    return a * a;
+}
+
+int __attribute__((kernel)) goo(int a) {
+    return a + a;
+}
diff --git a/tests/lldb/tests/harness/util_bundle.py b/tests/lldb/tests/harness/util_bundle.py
index 34c0228..cadf10c 100644
--- a/tests/lldb/tests/harness/util_bundle.py
+++ b/tests/lldb/tests/harness/util_bundle.py
@@ -22,7 +22,8 @@
         'KernelVariables': 'com.android.rs.kernelvariables',
         'Allocations': 'com.android.rs.allocations',
         'MultipleRSFiles': 'com.android.rs.multiplersfiles',
-        'SingleSource': 'com.android.rs.singlesource'
+        'SingleSource': 'com.android.rs.singlesource',
+        'ScriptGroup': 'com.android.rs.scriptgroup'
     }
 
     _tests_jni = {
diff --git a/tests/lldb/tests/testcases/test_script_group.py b/tests/lldb/tests/testcases/test_script_group.py
new file mode 100644
index 0000000..9655cd8
--- /dev/null
+++ b/tests/lldb/tests/testcases/test_script_group.py
@@ -0,0 +1,95 @@
+'''Module that contains the test TestScriptGroup.'''
+
+from __future__ import absolute_import
+
+from harness.test_base_remote import TestBaseRemote
+from harness.decorators import wimpy
+
+
+class TestScriptGroup(TestBaseRemote):
+    bundle_target = {
+        'java': 'ScriptGroup'
+    }
+
+    def setup(self, android):
+        '''This test requires to be run on one thread.'''
+        android.push_prop('debug.rs.max-threads', 1)
+
+    def teardown(self, android):
+        '''Reset the number of RS threads to the previous value.'''
+        android.pop_prop('debug.rs.max-threads')
+
+    @wimpy
+    def test_kernel_backtrace(self):
+        # number of allocation elements
+        array_size = 8
+
+        self.try_command('language renderscript status',
+                         ['Runtime Library discovered',
+                          'Runtime Driver discovered',
+                          'rsdDebugHintScriptGroup2'])
+
+        self.try_command('language renderscript scriptgroup breakpoint set scriptgroup_test',
+                         ['Breakpoint 1: no locations (pending)'])
+
+        self.try_command('language renderscript scriptgroup list',
+                         ['0 script groups'])
+
+        self.try_command('process continue',
+                         ['resuming',
+                          'stopped',
+                          'stop reason = breakpoint',
+                          'librs.scriptgroup.so`foo',
+                          'scriptgroup.rs'])
+
+        self.try_command('breakpoint list',
+                         ['scriptgroup_test',
+                          'locations = 1'])
+
+        self.try_command('language renderscript scriptgroup list',
+                         ['1 script group',
+                          'scriptgroup_test',
+                          'foo',
+                          'goo'])
+
+        self.try_command('language renderscript scriptgroup breakpoint set --stop-on-all scriptgroup_test',
+                         ['Breakpoint 2: 2 locations'])
+
+        self.try_command('breakpoint list',
+                         ['scriptgroup_test',
+                          'librs.scriptgroup.so`foo',
+                          'librs.scriptgroup.so`goo'])
+
+        # iterate over foo kernels
+        self.try_command('bt',
+                         ['scriptgroup.rs:',
+                          'frame #0', 'librs.scriptgroup.so`foo',
+                          'frame #1', 'librs.scriptgroup.so`foo.expand'])
+
+        for x in range(array_size):
+            self.try_command('frame var',
+                             ['(int) a = {0}'.format(x)])
+            self.try_command('process continue',
+                             ['resuming',
+                              'stopped',
+                              'stop reason = breakpoint',
+                              'librs.scriptgroup.so`{0}'.format(
+                                  'foo' if x < 7 else 'goo')])
+
+        # iterate over goo kernels
+        self.try_command('bt',
+                         ['stop reason = breakpoint',
+                          'scriptgroup.rs:',
+                          'frame #0', 'librs.scriptgroup.so`goo',
+                          'frame #1', 'librs.scriptgroup.so`goo.expand'])
+
+        for x in range(array_size):
+            self.try_command('frame var',
+                             ['(int) a = {0}'.format(x * x)])
+
+            if x < 7:
+                self.try_command('process continue',
+                                 ['resuming',
+                                  'stopped',
+                                  'stop reason = breakpoint',
+                                  'librs.scriptgroup.so`goo'])