1# Copyright (C) 2016 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15from __future__ import absolute_import
16
17import itertools
18
19from harness.test_base_remote import TestBaseRemote
20from harness.decorators import (
21    ordered_test,
22    wimpy,
23)
24from harness.assert_mixins import CoordinateAssertionsMixin
25
26from reduce_common import (
27    REDUCE_ITERATIONS,
28    REDUCE_STARTVAL,
29    REDUCE_SCRIPT,
30    X_TESTS,
31    Y_TESTS,
32    Z_TESTS,
33    ReductionMixin,
34)
35
36
37def coords_range_3d(x_range, y_range, z_range):
38    count = max((x_range, y_range, z_range))
39    x = itertools.cycle(range(x_range))
40    y = itertools.cycle(range(y_range))
41    z = itertools.cycle(range(z_range))
42    return itertools.islice(
43        itertools.izip(x, y, z),
44        count
45    )
46
47
48class TestReduce1DSingleThreaded(
49        TestBaseRemote, CoordinateAssertionsMixin, ReductionMixin):
50    """
51    Reduction kernels for RenderScript are launched using
52    a different `.expand` function than regular `ForEach` kernels and reflect a
53    different API to the invoking program
54
55    Although the debugger implementation for accessing these features tracks
56    this slightly differently for reduction kernels, the user interface should
57    still offer the basic functionality:
58        - breakpoints on a coordinate
59        - tracking, viewing and dumping allocations
60        - listing modules and constituent kernels and types
61    """
62
63    bundle_target = {
64        'java': 'Reduction',
65    }
66
67    def _delete_breakpoints(self):
68        try:
69            self.do_command('breakpoint delete -f')
70        except self.TestFail:
71            pass
72
73    def setup(self, android):
74        """This test requires to be run on one thread."""
75        android.push_prop('debug.rs.max-threads', 1)
76
77    def teardown(self, android):
78        """Reset the number of RS threads to the previous value."""
79        android.pop_prop('debug.rs.max-threads')
80
81    @ordered_test(0)
82    @wimpy
83    def test_setup(self):
84        self.try_command('language renderscript status', [])
85        self.try_command('b find_min_user_type_accum', [])
86        self.try_command('c', [])
87
88    @ordered_test(1)
89    @wimpy
90    def test_renderscript_module_dump(self):
91        """
92        Generalised Reduction kernels for RenderScript are not tracked in the
93        same way as `ForEach` kernels, and do not have `__attribute__((kernel))`
94        so we need to make sure that when a module contains reduction kernels,
95        `language renderscript module dump` in lldb prints the correct kernels.
96        """
97        self.try_command(
98            'language renderscript module dump',
99            [
100                'Reductions: 1',
101                'find_min_user_type',
102                'accumulator: find_min_user_type_accum',
103                'combiner: find_min_user_type_comb',
104                'outconverter: find_min_user_type_outc'
105            ]
106        )
107
108    @ordered_test(2)
109    @wimpy
110    def test_module_dump_with_foreach_kernel_separate(self):
111        """
112        The reduction breakpoint is separate from that of a standard kernel
113        function breakpoint, so we need to make sure that when we dump a module,
114        reductions are properly collected and displayed alongside the standard
115        __attribute__((kernel)) functions.
116        Assert that `... module dump` can correctly distinguish between `reduce`
117        kernels and `ForEach` kernels.
118        """
119        self.try_command(
120            'language renderscript module dump',
121            [
122                'Kernels: 2',
123                'Reductions: 1',
124                'accumulator: find_min_user_type_accum',
125                'initializer: find_min_user_type_init',
126                'combiner: find_min_user_type_comb',
127                'outconverter: find_min_user_type_outc'
128            ]
129        )
130
131    @wimpy
132    @ordered_test(3)
133    def test_reduction_breakpoint_set_all_roles_resolved(self):
134        """
135        Assert that a reduction breakpoint successfully resolves all the
136        functions that make up the reduction kernel
137        """
138        self.try_command(
139            'language renderscript reduction breakpoint set find_min_user_type',
140            ['Breakpoint(s) created']
141        )
142
143        self.try_command(
144            'process continue',
145            expected_regex=[
146                r'Process \d+ stopped',
147                r'librs.reduce.so`find_min_user_type',
148                r'stop reason = breakpoint'
149            ]
150        )
151        name = REDUCE_SCRIPT
152        self.try_command(
153            'breakpoint list',
154            expected_regex=[
155                "RenderScript reduce breakpoint for 'find_min_user_type', locations = 4, resolved = 4",
156                'where = librs.reduce.so`find_min_user_type_init (\+ \d+ )?at %s(.+, resolved,)' % name,
157                'where = librs.reduce.so`find_min_user_type_accum (\+ \d+ )?at %s(.+, resolved,)' % name,
158                'where = librs.reduce.so`find_min_user_type_comb (\+ \d+ )?at %s(.+, resolved,)' % name,
159                'where = librs.reduce.so`find_min_user_type_outc (\+ \d+ )?at %s(.+, resolved,)' % name,
160            ]
161        )
162
163    @ordered_test(4)
164    def test_reduce_iterations(self):
165        """
166        Given a reduction, we want to make sure that we break on
167        every accumulator invocation before seeing the outconverter called.
168        This requires the tests to be run single threaded
169        """
170        self._delete_breakpoints()
171        self.try_command(
172            'language renderscript reduction breakpoint set find_min_user_type -t initializer',
173        )
174        self.try_command(
175            'process continue',
176            expected_regex=[
177                r'Process \d+ stopped',
178                r'librs.reduce.so`find_min_user_type_init',
179                r'stop reason = breakpoint',
180            ]
181        )
182        self._delete_breakpoints()
183
184        self.try_command((
185            'language renderscript reduction breakpoint '
186            'set find_min_user_type --function-role accumulator,outconverter'),
187            ['Breakpoint(s) created']
188        )
189        for i in range(REDUCE_ITERATIONS):
190            self.try_command(
191                'process continue',
192                expected_regex=[
193                    r'Process \d+ resuming',
194                    r'Process \d+ stopped',
195                    r'librs.reduce.so`find_min_user_type_accum',
196                    r'stop reason = breakpoint'
197                ]
198            )
199            self.try_command('p val')
200            self.try_command(
201                'p val.b',
202                expected_regex=[
203                    r'^\((const )?int32_t\)\s*\$\d+ = %s\s*$' % (
204                        i + REDUCE_STARTVAL)
205                ]
206            )
207        # We should then finally break on the outconverter
208        self.try_command(
209            'process continue',
210            expected_regex=[
211                r'Process \d+ resuming',
212                r'Process \d+ stopped',
213                r'librs.reduce.so`find_min_user_type_outc',
214                r'stop reason = breakpoint'
215            ]
216        )
217
218    @ordered_test(5)
219    def test_function_role_breakpoints_combinations(self):
220        func_role_combinations = itertools.combinations(
221            ('accumulator', 'initializer'),
222            r=2
223        )
224        self._test_func_role_combinations(func_role_combinations)
225
226    @wimpy
227    @ordered_test(6)
228    def test_resolve_function_role_all_reduce_functions(self):
229        """
230        Assert that a reduction breakpoint successfully resolves all the
231        functions that make up the reduction kernel when the parameter `all` is
232        passed to `--function-role` for the breakpoint command
233        """
234        self._delete_breakpoints()
235        self.try_command(
236            'language renderscript reduction breakpoint set find_min_user_type -t all',
237            [r'Breakpoint(s) created']
238        )
239        self.try_command('c', [])
240        breakpoints_match = [
241            r"where = librs.reduce.so`%s (\+ \d+ )?at %s:\d+, address = 0x[0-9a-fA-F]+, resolved" % (
242                 'find_min_user_type_%s' % func_match,
243                 REDUCE_SCRIPT
244            )
245            for func_match in ('accum', 'init', 'comb', 'outc')
246        ]
247        self.try_command(
248            'breakpoint list',
249            expected_regex=[
250                r"Current breakpoints:",
251                r"RenderScript reduce breakpoint for 'find_min_user_type', locations = 4, resolved = 4",
252                r"Names:",
253                r"RenderScriptReduction",
254            ] + breakpoints_match
255        )
256
257    @ordered_test(8)
258    def test_reduce_breakpoint_conditional_1d_coordinate(self):
259        """
260        Assert that breakpoints conditional on an allocation coordinate
261        are only triggered on that coordinate
262        """
263        for x, _, __ in sorted(coords_range_3d(X_TESTS, Y_TESTS, Z_TESTS)):
264            self._delete_breakpoints()
265            self.assert_coord_bp_set(
266                'find_min_user_type -t accumulator',
267                x,
268                kernel_type='reduction'
269            )
270            self.assert_coord_stop('reduce', 'find_min_user_type', x)
271            # Step *into* the function so locals are available
272            # FIXME remove the need for `next` here; skip the function prologue
273            self.try_command('n')
274            self.try_command('p accum->a')
275            self.try_command('p accum->b')
276
277    @ordered_test('last')
278    def test_exit(self):
279        self.try_command('process kill', [])
280