• 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            WHERE experimental_flamegraph.upid = obj.upid
151              AND experimental_flamegraph.ts = obj.graph_sample_ts
152              AND profile_type = 'graph'
153              AND depth = 0 -- only the roots
154          ) AS total_flamegraph_size
155        FROM
156          heap_graph_object AS obj
157        WHERE
158          obj.reachable != 0
159        GROUP BY obj.upid, obj.graph_sample_ts;
160        """,
161        out=Csv("""
162        "upid","ts","total_objects_size","total_flamegraph_size"
163        1,10,3000000036,3000000036
164        """))
165
166  # TODO(b/153552977): Stop supporting legacy heap graphs. These never made it
167  # a public release, so we should eventually stop supporting workarounds for
168  def test_heap_graph_flamegraph(self):
169    return DiffTestBlueprint(
170        trace=Path('heap_graph_legacy.textproto'),
171        query="""
172        SELECT
173          id,
174          depth,
175          name,
176          map_name,
177          count,
178          cumulative_count,
179          size,
180          cumulative_size,
181          parent_id
182        FROM experimental_flamegraph
183        WHERE upid = (SELECT max(upid) FROM heap_graph_object)
184          AND profile_type = 'graph'
185          AND ts = (SELECT max(graph_sample_ts) FROM heap_graph_object)
186        LIMIT 10;
187        """,
188        out=Path('heap_graph_flamegraph.out'))
189
190  def test_stack_profile_tracker_empty_callstack(self):
191    return DiffTestBlueprint(
192        trace=TextProto(r"""
193        packet {
194          clock_snapshot {
195            clocks: {
196              clock_id: 6 # BOOTTIME
197              timestamp: 0
198            }
199            clocks: {
200              clock_id: 4 # MONOTONIC_COARSE
201              timestamp: 0
202            }
203          }
204        }
205
206        packet {
207          previous_packet_dropped: true
208          incremental_state_cleared: true
209          trusted_packet_sequence_id: 1
210          timestamp: 0
211          interned_data {
212            callstacks {
213              iid: 1
214            }
215          }
216        }
217
218        packet {
219          trusted_packet_sequence_id: 1
220          timestamp: 0
221          profile_packet {
222            index: 0
223            continued: false
224            process_dumps {
225              samples {
226                callstack_id: 1
227                self_allocated: 1
228                alloc_count: 1
229              }
230              samples {
231                callstack_id: 1
232                self_allocated: 1
233                alloc_count: 1
234              }
235            }
236          }
237        }
238        """),
239        query="""
240        SELECT count(1) AS count FROM heap_profile_allocation;
241        """,
242        out=Csv("""
243        "count"
244        0
245        """))
246
247  # perf_sample table (traced_perf) with android R and S trace inputs.
248  def test_perf_sample_rvc(self):
249    return DiffTestBlueprint(
250        trace=DataPath('perf_sample.pb'),
251        query="""
252        SELECT ps.ts, ps.cpu, ps.cpu_mode, ps.unwind_error, ps.perf_session_id,
253               pct.name AS cntr_name, pct.is_timebase,
254               thread.tid,
255               spf.name
256        FROM experimental_annotated_callstack eac
257        JOIN perf_sample ps
258          ON (eac.start_id = ps.callsite_id)
259        JOIN perf_counter_track pct
260          USING(perf_session_id, cpu)
261        JOIN thread
262          USING(utid)
263        JOIN stack_profile_frame spf
264          ON (eac.frame_id = spf.id)
265        ORDER BY ps.ts ASC, eac.depth ASC;
266        """,
267        out=Path('perf_sample_rvc.out'))
268
269  def test_perf_sample_sc(self):
270    return DiffTestBlueprint(
271        trace=DataPath('perf_sample_sc.pb'),
272        query="""
273        SELECT ps.ts, ps.cpu, ps.cpu_mode, ps.unwind_error, ps.perf_session_id,
274               pct.name AS cntr_name, pct.is_timebase,
275               thread.tid,
276               spf.name
277        FROM experimental_annotated_callstack eac
278        JOIN perf_sample ps
279          ON (eac.start_id = ps.callsite_id)
280        JOIN perf_counter_track pct
281          USING(perf_session_id, cpu)
282        JOIN thread
283          USING(utid)
284        JOIN stack_profile_frame spf
285          ON (eac.frame_id = spf.id)
286        ORDER BY ps.ts ASC, eac.depth ASC;
287        """,
288        out=Path('perf_sample_sc.out'))
289
290  def test_annotations(self):
291    return DiffTestBlueprint(
292        trace=DataPath('perf_sample_annotations.pftrace'),
293        query="""
294        select
295          eac.depth, eac.annotation, spm.name as map_name,
296          ifnull(demangle(spf.name), spf.name) as frame_name
297        from experimental_annotated_callstack eac
298          join stack_profile_frame spf on (eac.frame_id = spf.id)
299          join stack_profile_mapping spm on (spf.mapping = spm.id)
300        where eac.start_id = (
301          select spc.id as callsite_id
302          from stack_profile_callsite spc
303          join stack_profile_frame spf on (spc.frame_id = spf.id)
304          where spf.name = "_ZN3art28ResolveFieldWithAccessChecksEPNS_6ThreadEPNS_11ClassLinkerEtPNS_9ArtMethodEbbm")
305        order by depth asc;
306        """,
307        out=Csv("""
308        "depth","annotation","map_name","frame_name"
309        0,"[NULL]","/apex/com.android.runtime/lib64/bionic/libc.so","__libc_init"
310        1,"[NULL]","/system/bin/app_process64","main"
311        2,"[NULL]","/system/lib64/libandroid_runtime.so","android::AndroidRuntime::start(char const*, android::Vector<android::String8> const&, bool)"
312        3,"[NULL]","/system/lib64/libandroid_runtime.so","_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...)"
313        4,"[NULL]","/apex/com.android.art/lib64/libart.so","art::JNI<true>::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)"
314        5,"[NULL]","/apex/com.android.art/lib64/libart.so","art::JValue art::InvokeWithVarArgs<_jmethodID*>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, std::__va_list)"
315        6,"[NULL]","/apex/com.android.art/lib64/libart.so","art_quick_invoke_static_stub"
316        7,"aot","/system/framework/arm64/boot-framework.oat","com.android.internal.os.ZygoteInit.main"
317        8,"aot","/system/framework/arm64/boot-framework.oat","com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run"
318        9,"common-frame","/system/framework/arm64/boot.oat","art_jni_trampoline"
319        10,"[NULL]","/apex/com.android.art/lib64/libart.so","art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobjectArray*) (.__uniq.165753521025965369065708152063621506277)"
320        11,"common-frame","/apex/com.android.art/lib64/libart.so","_jobject* art::InvokeMethod<(art::PointerSize)8>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jobject*, _jobject*, unsigned long)"
321        12,"common-frame","/apex/com.android.art/lib64/libart.so","art_quick_invoke_static_stub"
322        13,"aot","/system/framework/arm64/boot-framework.oat","android.app.ActivityThread.main"
323        14,"aot","/system/framework/arm64/boot-framework.oat","android.os.Looper.loop"
324        15,"aot","/system/framework/arm64/boot-framework.oat","android.os.Looper.loopOnce"
325        16,"aot","/system/framework/arm64/boot-framework.oat","android.os.Handler.dispatchMessage"
326        17,"aot","/system/framework/arm64/boot-framework.oat","android.view.Choreographer$FrameDisplayEventReceiver.run"
327        18,"aot","/system/framework/arm64/boot-framework.oat","android.view.Choreographer.doFrame"
328        19,"aot","/system/framework/arm64/boot-framework.oat","android.view.Choreographer.doCallbacks"
329        20,"aot","/system/framework/arm64/boot-framework.oat","android.view.ViewRootImpl$TraversalRunnable.run"
330        21,"aot","/system/framework/arm64/boot-framework.oat","android.view.ViewRootImpl.doTraversal"
331        22,"aot","/system/framework/arm64/boot-framework.oat","android.view.ViewRootImpl.performTraversals"
332        23,"interp","/system/framework/framework.jar","android.view.ViewRootImpl.notifyDrawStarted"
333        24,"common-frame-interp","/apex/com.android.art/lib64/libart.so","nterp_op_iget_object_slow_path"
334        25,"common-frame-interp","/apex/com.android.art/lib64/libart.so","nterp_get_instance_field_offset"
335        26,"common-frame-interp","/apex/com.android.art/lib64/libart.so","NterpGetInstanceFieldOffset"
336        27,"common-frame","/apex/com.android.art/lib64/libart.so","art::ResolveFieldWithAccessChecks(art::Thread*, art::ClassLinker*, unsigned short, art::ArtMethod*, bool, bool, unsigned long)"
337        """))
338
339  def test_annotations_switch_interpreter(self):
340    return DiffTestBlueprint(
341        trace=Path('perf_sample_switch_interp.textproto'),
342        query="""
343        select
344          eac.depth, eac.annotation, spm.name as map_name,
345          ifnull(demangle(spf.name), spf.name) as frame_name
346        from experimental_annotated_callstack eac
347          join stack_profile_frame spf on (eac.frame_id = spf.id)
348          join stack_profile_mapping spm on (spf.mapping = spm.id)
349        where eac.start_id = (select callsite_id from perf_sample)
350        order by depth asc;
351        """,
352        out=Csv("""
353        "depth","annotation","map_name","frame_name"
354        0,"interp","/example.vdex","com.example.managed.frame"
355        1,"common-frame-interp","/apex/com.android.art/lib64/libart.so","ExecuteSwitchImplAsm"
356        2,"common-frame-interp","/apex/com.android.art/lib64/libart.so","void art::interpreter::ExecuteSwitchImplCpp<false>(art::interpreter::SwitchImplContext*)"
357        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*)"
358        4,"common-frame","/apex/com.android.art/lib64/libart.so","art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)"
359        """))
360