• 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, upid, ts, path, size_kb, private_dirty_kb, swap_kb
65        FROM profiler_smaps;
66        """,
67        out=Csv("""
68        "id","upid","ts","path","size_kb","private_dirty_kb","swap_kb"
69        0,2,10,"/system/lib64/libc.so",20,4,4
70        1,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              android_user_id: 0
121              is_kernel_task: false
122            }
123            mappings {
124              path: "[anon: libc_malloc]"
125              size_kb: 30
126              private_dirty_kb: 10
127              swap_kb: 10
128            }
129            mappings {
130              path: "/system/lib64/libc.so"
131              size_kb: 20
132              private_dirty_kb: 4
133              swap_kb: 4
134            }
135          }
136        }
137        """))
138
139  # Regression test for b/222297079: when cumulative size in a flamegraph
140  # a signed 32-bit integer.
141  def test_heap_graph_flamegraph_matches_objects(self):
142    return DiffTestBlueprint(
143        trace=Path('heap_graph_huge_size.textproto'),
144        query="""
145        SELECT
146          obj.upid AS upid,
147          obj.graph_sample_ts AS ts,
148          SUM(obj.self_size + obj.native_size) AS total_objects_size,
149          (
150            SELECT SUM(cumulative_size)
151            FROM experimental_flamegraph(
152              'graph',
153              obj.graph_sample_ts,
154              NULL,
155              obj.upid,
156              NULL,
157              NULL
158            )
159            WHERE depth = 0 -- only the roots
160          ) AS total_flamegraph_size
161        FROM
162          heap_graph_object AS obj
163        WHERE
164          obj.reachable != 0
165        GROUP BY obj.upid, obj.graph_sample_ts;
166        """,
167        out=Csv("""
168        "upid","ts","total_objects_size","total_flamegraph_size"
169        1,10,3000000036,3000000036
170        """))
171
172  # TODO(b/153552977): Stop supporting legacy heap graphs. These never made it
173  # a public release, so we should eventually stop supporting workarounds for
174  def test_heap_graph_flamegraph(self):
175    return DiffTestBlueprint(
176        trace=Path('heap_graph_legacy.textproto'),
177        query="""
178        SELECT
179          id,
180          depth,
181          name,
182          map_name,
183          count,
184          cumulative_count,
185          size,
186          cumulative_size,
187          parent_id
188        FROM experimental_flamegraph(
189          'graph',
190          (SELECT max(graph_sample_ts) FROM heap_graph_object),
191          NULL,
192          (SELECT max(upid) FROM heap_graph_object),
193          NULL,
194          NULL
195        )
196        LIMIT 10;
197        """,
198        out=Path('heap_graph_flamegraph.out'))
199
200  def test_stack_profile_tracker_empty_callstack(self):
201    return DiffTestBlueprint(
202        trace=TextProto(r"""
203        packet {
204          clock_snapshot {
205            clocks: {
206              clock_id: 6 # BOOTTIME
207              timestamp: 0
208            }
209            clocks: {
210              clock_id: 4 # MONOTONIC_COARSE
211              timestamp: 0
212            }
213          }
214        }
215
216        packet {
217          previous_packet_dropped: true
218          incremental_state_cleared: true
219          trusted_packet_sequence_id: 1
220          timestamp: 0
221          interned_data {
222            callstacks {
223              iid: 1
224            }
225          }
226        }
227
228        packet {
229          trusted_packet_sequence_id: 1
230          timestamp: 0
231          profile_packet {
232            index: 0
233            continued: false
234            process_dumps {
235              samples {
236                callstack_id: 1
237                self_allocated: 1
238                alloc_count: 1
239              }
240              samples {
241                callstack_id: 1
242                self_allocated: 1
243                alloc_count: 1
244              }
245            }
246          }
247        }
248        """),
249        query="""
250        SELECT count(1) AS count FROM heap_profile_allocation;
251        """,
252        out=Csv("""
253        "count"
254        0
255        """))
256
257  # perf_sample table (traced_perf) with android R and S trace inputs.
258  def test_perf_sample_rvc(self):
259    return DiffTestBlueprint(
260        trace=DataPath('perf_sample.pb'),
261        query="""
262        SELECT ps.ts, ps.cpu, ps.cpu_mode, ps.unwind_error, ps.perf_session_id,
263               pct.name AS cntr_name, pct.is_timebase,
264               thread.tid,
265               spf.name
266        FROM experimental_annotated_callstack eac
267        JOIN perf_sample ps
268          ON (eac.start_id = ps.callsite_id)
269        JOIN perf_counter_track pct
270          USING(perf_session_id, cpu)
271        JOIN thread
272          USING(utid)
273        JOIN stack_profile_frame spf
274          ON (eac.frame_id = spf.id)
275        ORDER BY ps.ts ASC, eac.depth ASC;
276        """,
277        out=Path('perf_sample_rvc.out'))
278
279  def test_perf_sample_sc(self):
280    return DiffTestBlueprint(
281        trace=DataPath('perf_sample_sc.pb'),
282        query="""
283        SELECT ps.ts, ps.cpu, ps.cpu_mode, ps.unwind_error, ps.perf_session_id,
284               pct.name AS cntr_name, pct.is_timebase,
285               thread.tid,
286               spf.name
287        FROM experimental_annotated_callstack eac
288        JOIN perf_sample ps
289          ON (eac.start_id = ps.callsite_id)
290        JOIN perf_counter_track pct
291          USING(perf_session_id, cpu)
292        JOIN thread
293          USING(utid)
294        JOIN stack_profile_frame spf
295          ON (eac.frame_id = spf.id)
296        ORDER BY ps.ts ASC, eac.depth ASC;
297        """,
298        out=Path('perf_sample_sc.out'))
299
300  def test_annotations(self):
301    return DiffTestBlueprint(
302        trace=DataPath('perf_sample_annotations.pftrace'),
303        query="""
304        select
305          eac.depth, eac.annotation, spm.name as map_name,
306          ifnull(demangle(spf.name), spf.name) as frame_name
307        from experimental_annotated_callstack eac
308          join stack_profile_frame spf on (eac.frame_id = spf.id)
309          join stack_profile_mapping spm on (spf.mapping = spm.id)
310        where eac.start_id = (
311          select spc.id as callsite_id
312          from stack_profile_callsite spc
313          join stack_profile_frame spf on (spc.frame_id = spf.id)
314          where spf.name = "_ZN3art28ResolveFieldWithAccessChecksEPNS_6ThreadEPNS_11ClassLinkerEtPNS_9ArtMethodEbbm")
315          and depth != 10  -- Skipped because cause symbolization issues on clang vs gcc due to llvm-demangle
316        order by depth asc;
317        """,
318        out=Csv("""
319        "depth","annotation","map_name","frame_name"
320        0,"[NULL]","/apex/com.android.runtime/lib64/bionic/libc.so","__libc_init"
321        1,"[NULL]","/system/bin/app_process64","main"
322        2,"[NULL]","/system/lib64/libandroid_runtime.so","android::AndroidRuntime::start(char const*, android::Vector<android::String8> const&, bool)"
323        3,"[NULL]","/system/lib64/libandroid_runtime.so","_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...)"
324        4,"[NULL]","/apex/com.android.art/lib64/libart.so","art::JNI<true>::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)"
325        5,"[NULL]","/apex/com.android.art/lib64/libart.so","art::JValue art::InvokeWithVarArgs<_jmethodID*>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, std::__va_list)"
326        6,"[NULL]","/apex/com.android.art/lib64/libart.so","art_quick_invoke_static_stub"
327        7,"aot","/system/framework/arm64/boot-framework.oat","com.android.internal.os.ZygoteInit.main"
328        8,"aot","/system/framework/arm64/boot-framework.oat","com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run"
329        9,"common-frame","/system/framework/arm64/boot.oat","art_jni_trampoline"
330        11,"common-frame","/apex/com.android.art/lib64/libart.so","_jobject* art::InvokeMethod<(art::PointerSize)8>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jobject*, _jobject*, unsigned long)"
331        12,"common-frame","/apex/com.android.art/lib64/libart.so","art_quick_invoke_static_stub"
332        13,"aot","/system/framework/arm64/boot-framework.oat","android.app.ActivityThread.main"
333        14,"aot","/system/framework/arm64/boot-framework.oat","android.os.Looper.loop"
334        15,"aot","/system/framework/arm64/boot-framework.oat","android.os.Looper.loopOnce"
335        16,"aot","/system/framework/arm64/boot-framework.oat","android.os.Handler.dispatchMessage"
336        17,"aot","/system/framework/arm64/boot-framework.oat","android.view.Choreographer$FrameDisplayEventReceiver.run"
337        18,"aot","/system/framework/arm64/boot-framework.oat","android.view.Choreographer.doFrame"
338        19,"aot","/system/framework/arm64/boot-framework.oat","android.view.Choreographer.doCallbacks"
339        20,"aot","/system/framework/arm64/boot-framework.oat","android.view.ViewRootImpl$TraversalRunnable.run"
340        21,"aot","/system/framework/arm64/boot-framework.oat","android.view.ViewRootImpl.doTraversal"
341        22,"aot","/system/framework/arm64/boot-framework.oat","android.view.ViewRootImpl.performTraversals"
342        23,"interp","/system/framework/framework.jar","android.view.ViewRootImpl.notifyDrawStarted"
343        24,"common-frame-interp","/apex/com.android.art/lib64/libart.so","nterp_op_iget_object_slow_path"
344        25,"common-frame-interp","/apex/com.android.art/lib64/libart.so","nterp_get_instance_field_offset"
345        26,"common-frame-interp","/apex/com.android.art/lib64/libart.so","NterpGetInstanceFieldOffset"
346        27,"common-frame","/apex/com.android.art/lib64/libart.so","art::ResolveFieldWithAccessChecks(art::Thread*, art::ClassLinker*, unsigned short, art::ArtMethod*, bool, bool, unsigned long)"
347        """))
348
349  def test_annotations_switch_interpreter(self):
350    return DiffTestBlueprint(
351        trace=Path('perf_sample_switch_interp.textproto'),
352        query="""
353        select
354          eac.depth, eac.annotation, spm.name as map_name,
355          ifnull(demangle(spf.name), spf.name) as frame_name
356        from experimental_annotated_callstack eac
357          join stack_profile_frame spf on (eac.frame_id = spf.id)
358          join stack_profile_mapping spm on (spf.mapping = spm.id)
359        where eac.start_id = (select callsite_id from perf_sample)
360        order by depth asc;
361        """,
362        out=Csv("""
363        "depth","annotation","map_name","frame_name"
364        0,"interp","/example.vdex","com.example.managed.frame"
365        1,"common-frame-interp","/apex/com.android.art/lib64/libart.so","ExecuteSwitchImplAsm"
366        2,"common-frame-interp","/apex/com.android.art/lib64/libart.so","void art::interpreter::ExecuteSwitchImplCpp<false>(art::interpreter::SwitchImplContext*)"
367        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*)"
368        4,"common-frame","/apex/com.android.art/lib64/libart.so","art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)"
369        """))
370
371  def test_perf_sample_followers(self):
372    return DiffTestBlueprint(
373        trace=TextProto(r"""
374        packet {
375          clock_snapshot {
376            primary_trace_clock: BUILTIN_CLOCK_BOOTTIME
377            clocks {
378              clock_id: 6
379              timestamp: 273574904041306
380            }
381            clocks {
382              clock_id: 2
383              timestamp: 1737644264730557105
384            }
385            clocks {
386              clock_id: 4
387              timestamp: 106208706668231
388            }
389            clocks {
390              clock_id: 1
391              timestamp: 1737644264734590878
392            }
393            clocks {
394              clock_id: 3
395              timestamp: 106208710702167
396            }
397            clocks {
398              clock_id: 5
399              timestamp: 106208710702494
400            }
401          }
402          trusted_packet_sequence_id: 1
403        }
404        packet {
405          first_packet_on_sequence: true
406          timestamp: 273574983771490
407          timestamp_clock_id: 6
408          sequence_flags: 1
409          trace_packet_defaults {
410            timestamp_clock_id: 3
411            perf_sample_defaults {
412              timebase {
413                frequency: 100
414                counter: SW_CPU_CLOCK
415              }
416              followers {
417                counter: HW_CPU_CYCLES
418              }
419              followers {
420                counter: HW_INSTRUCTIONS
421              }
422            }
423          }
424          trusted_packet_sequence_id: 4
425          previous_packet_dropped: true
426        }
427        packet {
428          interned_data {
429            build_ids {
430              iid: 0
431              str: ""
432            }
433            mapping_paths {
434              iid: 0
435              str: ""
436            }
437            function_names {
438              iid: 0
439              str: ""
440            }
441          }
442          sequence_flags: 2
443          trusted_packet_sequence_id: 4
444        }
445        packet {
446          sequence_flags: 2
447          timestamp: 106208800213886
448          interned_data {
449            callstacks {
450              iid: 1
451            }
452          }
453          perf_sample {
454            cpu: 0
455            pid: 0
456            tid: 0
457            cpu_mode: MODE_KERNEL
458            timebase_count: 10020141
459            follower_counts: 4672142
460            follower_counts: 1144537
461            callstack_iid: 1
462          }
463          trusted_packet_sequence_id: 4
464        }
465        """),
466        query="""
467        select
468          c.ts,
469          c.value,
470          pct.cpu,
471          pct.perf_session_id,
472          pct.is_timebase
473        from
474          counter c join perf_counter_track pct on c.track_id = pct.id
475        order by ts, c.id
476        """,
477        out=Csv("""
478        "ts","value","cpu","perf_session_id","is_timebase"
479        273574993553025,10020141.000000,0,0,1
480        273574993553025,4672142.000000,0,0,0
481        273574993553025,1144537.000000,0,0,0
482        """))
483