• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# Copyright (C) 2023 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License a
7#
8#      http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16from python.generators.diff_tests.testing import Path, DataPath, Metric
17from python.generators.diff_tests.testing import Csv, Json, TextProto
18from python.generators.diff_tests.testing import DiffTestBlueprint
19from python.generators.diff_tests.testing import TestSuite
20
21
22class Profiling(TestSuite):
23  # Perf profiling  tests.
24  def test_profiler_smaps(self):
25    return DiffTestBlueprint(
26        trace=TextProto(r"""
27        packet {
28          process_tree {
29            processes {
30              pid: 1
31              ppid: 0
32              cmdline: "init"
33              uid: 0
34            }
35            processes {
36              pid: 2
37              ppid: 1
38              cmdline: "system_server"
39              uid: 1000
40            }
41          }
42        }
43        packet {
44          trusted_packet_sequence_id: 999
45          timestamp: 10
46          smaps_packet {
47            pid: 2
48            entries {
49              path: "/system/lib64/libc.so"
50              size_kb: 20
51              private_dirty_kb: 4
52              swap_kb: 4
53            }
54            entries {
55              path: "[anon: libc_malloc]"
56              size_kb: 30
57              private_dirty_kb: 10
58              swap_kb: 10
59            }
60          }
61        }
62        """),
63        query="""
64        SELECT id, type, upid, ts, path, size_kb, private_dirty_kb, swap_kb
65        FROM profiler_smaps;
66        """,
67        out=Csv("""
68        "id","type","upid","ts","path","size_kb","private_dirty_kb","swap_kb"
69        0,"profiler_smaps",2,10,"/system/lib64/libc.so",20,4,4
70        1,"profiler_smaps",2,10,"[anon: libc_malloc]",30,10,10
71        """))
72
73  def test_profiler_smaps_metric(self):
74    return DiffTestBlueprint(
75        trace=TextProto(r"""
76        packet {
77          process_tree {
78            processes {
79              pid: 1
80              ppid: 0
81              cmdline: "init"
82              uid: 0
83            }
84            processes {
85              pid: 2
86              ppid: 1
87              cmdline: "system_server"
88              uid: 1000
89            }
90          }
91        }
92        packet {
93          trusted_packet_sequence_id: 999
94          timestamp: 10
95          smaps_packet {
96            pid: 2
97            entries {
98              path: "/system/lib64/libc.so"
99              size_kb: 20
100              private_dirty_kb: 4
101              swap_kb: 4
102            }
103            entries {
104              path: "[anon: libc_malloc]"
105              size_kb: 30
106              private_dirty_kb: 10
107              swap_kb: 10
108            }
109          }
110        }
111        """),
112        query=Metric('profiler_smaps'),
113        out=TextProto(r"""
114        profiler_smaps {
115          instance {
116            process {
117              name: "system_server"
118              uid: 1000
119              pid: 2
120            }
121            mappings {
122              path: "[anon: libc_malloc]"
123              size_kb: 30
124              private_dirty_kb: 10
125              swap_kb: 10
126            }
127            mappings {
128              path: "/system/lib64/libc.so"
129              size_kb: 20
130              private_dirty_kb: 4
131              swap_kb: 4
132            }
133          }
134        }
135        """))
136
137  # Regression test for b/222297079: when cumulative size in a flamegraph
138  # a signed 32-bit integer.
139  def test_heap_graph_flamegraph_matches_objects(self):
140    return DiffTestBlueprint(
141        trace=Path('heap_graph_huge_size.textproto'),
142        query="""
143        SELECT
144          obj.upid AS upid,
145          obj.graph_sample_ts AS ts,
146          SUM(obj.self_size + obj.native_size) AS total_objects_size,
147          (
148            SELECT SUM(cumulative_size)
149            FROM experimental_flamegraph(
150              'graph',
151              obj.graph_sample_ts,
152              NULL,
153              obj.upid,
154              NULL,
155              NULL
156            )
157            WHERE depth = 0 -- only the roots
158          ) AS total_flamegraph_size
159        FROM
160          heap_graph_object AS obj
161        WHERE
162          obj.reachable != 0
163        GROUP BY obj.upid, obj.graph_sample_ts;
164        """,
165        out=Csv("""
166        "upid","ts","total_objects_size","total_flamegraph_size"
167        1,10,3000000036,3000000036
168        """))
169
170  # TODO(b/153552977): Stop supporting legacy heap graphs. These never made it
171  # a public release, so we should eventually stop supporting workarounds for
172  def test_heap_graph_flamegraph(self):
173    return DiffTestBlueprint(
174        trace=Path('heap_graph_legacy.textproto'),
175        query="""
176        SELECT
177          id,
178          depth,
179          name,
180          map_name,
181          count,
182          cumulative_count,
183          size,
184          cumulative_size,
185          parent_id
186        FROM experimental_flamegraph(
187          'graph',
188          (SELECT max(graph_sample_ts) FROM heap_graph_object),
189          NULL,
190          (SELECT max(upid) FROM heap_graph_object),
191          NULL,
192          NULL
193        )
194        LIMIT 10;
195        """,
196        out=Path('heap_graph_flamegraph.out'))
197
198  def test_stack_profile_tracker_empty_callstack(self):
199    return DiffTestBlueprint(
200        trace=TextProto(r"""
201        packet {
202          clock_snapshot {
203            clocks: {
204              clock_id: 6 # BOOTTIME
205              timestamp: 0
206            }
207            clocks: {
208              clock_id: 4 # MONOTONIC_COARSE
209              timestamp: 0
210            }
211          }
212        }
213
214        packet {
215          previous_packet_dropped: true
216          incremental_state_cleared: true
217          trusted_packet_sequence_id: 1
218          timestamp: 0
219          interned_data {
220            callstacks {
221              iid: 1
222            }
223          }
224        }
225
226        packet {
227          trusted_packet_sequence_id: 1
228          timestamp: 0
229          profile_packet {
230            index: 0
231            continued: false
232            process_dumps {
233              samples {
234                callstack_id: 1
235                self_allocated: 1
236                alloc_count: 1
237              }
238              samples {
239                callstack_id: 1
240                self_allocated: 1
241                alloc_count: 1
242              }
243            }
244          }
245        }
246        """),
247        query="""
248        SELECT count(1) AS count FROM heap_profile_allocation;
249        """,
250        out=Csv("""
251        "count"
252        0
253        """))
254
255  # perf_sample table (traced_perf) with android R and S trace inputs.
256  def test_perf_sample_rvc(self):
257    return DiffTestBlueprint(
258        trace=DataPath('perf_sample.pb'),
259        query="""
260        SELECT ps.ts, ps.cpu, ps.cpu_mode, ps.unwind_error, ps.perf_session_id,
261               pct.name AS cntr_name, pct.is_timebase,
262               thread.tid,
263               spf.name
264        FROM experimental_annotated_callstack eac
265        JOIN perf_sample ps
266          ON (eac.start_id = ps.callsite_id)
267        JOIN perf_counter_track pct
268          USING(perf_session_id, cpu)
269        JOIN thread
270          USING(utid)
271        JOIN stack_profile_frame spf
272          ON (eac.frame_id = spf.id)
273        ORDER BY ps.ts ASC, eac.depth ASC;
274        """,
275        out=Path('perf_sample_rvc.out'))
276
277  def test_perf_sample_sc(self):
278    return DiffTestBlueprint(
279        trace=DataPath('perf_sample_sc.pb'),
280        query="""
281        SELECT ps.ts, ps.cpu, ps.cpu_mode, ps.unwind_error, ps.perf_session_id,
282               pct.name AS cntr_name, pct.is_timebase,
283               thread.tid,
284               spf.name
285        FROM experimental_annotated_callstack eac
286        JOIN perf_sample ps
287          ON (eac.start_id = ps.callsite_id)
288        JOIN perf_counter_track pct
289          USING(perf_session_id, cpu)
290        JOIN thread
291          USING(utid)
292        JOIN stack_profile_frame spf
293          ON (eac.frame_id = spf.id)
294        ORDER BY ps.ts ASC, eac.depth ASC;
295        """,
296        out=Path('perf_sample_sc.out'))
297
298  def test_annotations(self):
299    return DiffTestBlueprint(
300        trace=DataPath('perf_sample_annotations.pftrace'),
301        query="""
302        select
303          eac.depth, eac.annotation, spm.name as map_name,
304          ifnull(demangle(spf.name), spf.name) as frame_name
305        from experimental_annotated_callstack eac
306          join stack_profile_frame spf on (eac.frame_id = spf.id)
307          join stack_profile_mapping spm on (spf.mapping = spm.id)
308        where eac.start_id = (
309          select spc.id as callsite_id
310          from stack_profile_callsite spc
311          join stack_profile_frame spf on (spc.frame_id = spf.id)
312          where spf.name = "_ZN3art28ResolveFieldWithAccessChecksEPNS_6ThreadEPNS_11ClassLinkerEtPNS_9ArtMethodEbbm")
313          and depth != 10  -- Skipped because cause symbolization issues on clang vs gcc due to llvm-demangle
314        order by depth asc;
315        """,
316        out=Csv("""
317        "depth","annotation","map_name","frame_name"
318        0,"[NULL]","/apex/com.android.runtime/lib64/bionic/libc.so","__libc_init"
319        1,"[NULL]","/system/bin/app_process64","main"
320        2,"[NULL]","/system/lib64/libandroid_runtime.so","android::AndroidRuntime::start(char const*, android::Vector<android::String8> const&, bool)"
321        3,"[NULL]","/system/lib64/libandroid_runtime.so","_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...)"
322        4,"[NULL]","/apex/com.android.art/lib64/libart.so","art::JNI<true>::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)"
323        5,"[NULL]","/apex/com.android.art/lib64/libart.so","art::JValue art::InvokeWithVarArgs<_jmethodID*>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, std::__va_list)"
324        6,"[NULL]","/apex/com.android.art/lib64/libart.so","art_quick_invoke_static_stub"
325        7,"aot","/system/framework/arm64/boot-framework.oat","com.android.internal.os.ZygoteInit.main"
326        8,"aot","/system/framework/arm64/boot-framework.oat","com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run"
327        9,"common-frame","/system/framework/arm64/boot.oat","art_jni_trampoline"
328        11,"common-frame","/apex/com.android.art/lib64/libart.so","_jobject* art::InvokeMethod<(art::PointerSize)8>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jobject*, _jobject*, unsigned long)"
329        12,"common-frame","/apex/com.android.art/lib64/libart.so","art_quick_invoke_static_stub"
330        13,"aot","/system/framework/arm64/boot-framework.oat","android.app.ActivityThread.main"
331        14,"aot","/system/framework/arm64/boot-framework.oat","android.os.Looper.loop"
332        15,"aot","/system/framework/arm64/boot-framework.oat","android.os.Looper.loopOnce"
333        16,"aot","/system/framework/arm64/boot-framework.oat","android.os.Handler.dispatchMessage"
334        17,"aot","/system/framework/arm64/boot-framework.oat","android.view.Choreographer$FrameDisplayEventReceiver.run"
335        18,"aot","/system/framework/arm64/boot-framework.oat","android.view.Choreographer.doFrame"
336        19,"aot","/system/framework/arm64/boot-framework.oat","android.view.Choreographer.doCallbacks"
337        20,"aot","/system/framework/arm64/boot-framework.oat","android.view.ViewRootImpl$TraversalRunnable.run"
338        21,"aot","/system/framework/arm64/boot-framework.oat","android.view.ViewRootImpl.doTraversal"
339        22,"aot","/system/framework/arm64/boot-framework.oat","android.view.ViewRootImpl.performTraversals"
340        23,"interp","/system/framework/framework.jar","android.view.ViewRootImpl.notifyDrawStarted"
341        24,"common-frame-interp","/apex/com.android.art/lib64/libart.so","nterp_op_iget_object_slow_path"
342        25,"common-frame-interp","/apex/com.android.art/lib64/libart.so","nterp_get_instance_field_offset"
343        26,"common-frame-interp","/apex/com.android.art/lib64/libart.so","NterpGetInstanceFieldOffset"
344        27,"common-frame","/apex/com.android.art/lib64/libart.so","art::ResolveFieldWithAccessChecks(art::Thread*, art::ClassLinker*, unsigned short, art::ArtMethod*, bool, bool, unsigned long)"
345        """))
346
347  def test_annotations_switch_interpreter(self):
348    return DiffTestBlueprint(
349        trace=Path('perf_sample_switch_interp.textproto'),
350        query="""
351        select
352          eac.depth, eac.annotation, spm.name as map_name,
353          ifnull(demangle(spf.name), spf.name) as frame_name
354        from experimental_annotated_callstack eac
355          join stack_profile_frame spf on (eac.frame_id = spf.id)
356          join stack_profile_mapping spm on (spf.mapping = spm.id)
357        where eac.start_id = (select callsite_id from perf_sample)
358        order by depth asc;
359        """,
360        out=Csv("""
361        "depth","annotation","map_name","frame_name"
362        0,"interp","/example.vdex","com.example.managed.frame"
363        1,"common-frame-interp","/apex/com.android.art/lib64/libart.so","ExecuteSwitchImplAsm"
364        2,"common-frame-interp","/apex/com.android.art/lib64/libart.so","void art::interpreter::ExecuteSwitchImplCpp<false>(art::interpreter::SwitchImplContext*)"
365        3,"common-frame-interp","/apex/com.android.art/lib64/libart.so","bool art::interpreter::DoCall<true>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, bool, art::JValue*)"
366        4,"common-frame","/apex/com.android.art/lib64/libart.so","art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)"
367        """))
368