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