1// Copyright (C) 2023 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 15import {Plugin, PluginContextTrace, PluginDescriptor} from '../../public'; 16import {Engine} from '../../trace_processor/engine'; 17import { 18 SimpleSliceTrack, 19 SimpleSliceTrackConfig, 20} from '../../frontend/simple_slice_track'; 21import {CounterOptions} from '../../frontend/base_counter_track'; 22import { 23 SimpleCounterTrack, 24 SimpleCounterTrackConfig, 25} from '../../frontend/simple_counter_track'; 26 27interface ContainedTrace { 28 uuid: string; 29 subscription: string; 30 trigger: string; 31 // NB: these are millis. 32 ts: number; 33 dur: number; 34} 35 36const DEFAULT_NETWORK = ` 37 with base as ( 38 select 39 ts, 40 substr(s.name, 6) as conn 41 from track t join slice s on t.id = s.track_id 42 where t.name = 'battery_stats.conn' 43 ), 44 diff as ( 45 select 46 ts, 47 conn, 48 conn != lag(conn) over (order by ts) as keep 49 from base 50 ) 51 select 52 ts, 53 ifnull(lead(ts) over (order by ts), (select end_ts from trace_bounds)) - ts as dur, 54 case 55 when conn like '-1:%' then 'Disconnected' 56 when conn like '0:%' then 'Modem' 57 when conn like '1:%' then 'WiFi' 58 when conn like '4:%' then 'VPN' 59 else conn 60 end as name 61 from diff where keep is null or keep`; 62 63const RADIO_TRANSPORT_TYPE = ` 64 create or replace perfetto view radio_transport_data_conn as 65 select ts, safe_dur AS dur, value_name as data_conn, value AS data_conn_val 66 from android_battery_stats_state 67 where track_name = "battery_stats.data_conn"; 68 69 create or replace perfetto view radio_transport_nr_state as 70 select ts, safe_dur AS dur, value AS nr_state_val 71 from android_battery_stats_state 72 where track_name = "battery_stats.nr_state"; 73 74 drop table if exists radio_transport_join; 75 create virtual table radio_transport_join 76 using span_left_join(radio_transport_data_conn, radio_transport_nr_state); 77 78 create or replace perfetto view radio_transport as 79 select 80 ts, dur, 81 case data_conn_val 82 -- On LTE with NR connected is 5G NSA. 83 when 13 then iif(nr_state_val = 3, '5G (NSA)', data_conn) 84 -- On NR with NR state present, is 5G SA. 85 when 20 then iif(nr_state_val is null, '5G (SA or NSA)', '5G (SA)') 86 else data_conn 87 end as name 88 from radio_transport_join;`; 89 90const TETHERING = ` 91 with base as ( 92 select 93 ts as ts_end, 94 EXTRACT_ARG(arg_set_id, 'network_tethering_reported.duration_millis') * 1000000 as dur 95 from track t join slice s on t.id = s.track_id 96 where t.name = 'Statsd Atoms' 97 and s.name = 'network_tethering_reported' 98 ) 99 select ts_end - dur as ts, dur, 'Tethering' as name from base`; 100 101const NETWORK_SUMMARY = ` 102 create or replace perfetto table network_summary as 103 with base as ( 104 select 105 cast(ts / 5000000000 as int64) * 5000000000 AS ts, 106 case 107 when track_name glob '*wlan*' then 'wifi' 108 when track_name glob '*rmnet*' then 'modem' 109 else 'unknown' 110 end as dev_type, 111 package_name as pkg, 112 sum(packet_length) AS value 113 from android_network_packets 114 where (track_name glob '*wlan*' or track_name glob '*rmnet*') 115 group by 1,2,3 116 ), 117 zeroes as ( 118 select 119 ts, 120 dev_type, 121 pkg, 122 value 123 from base 124 union all 125 select 126 ts + 5000000000 as ts, 127 dev_type, 128 pkg, 129 0 as value 130 from base 131 ), 132 final as ( 133 select 134 ts, 135 dev_type, 136 pkg, 137 sum(value) as value 138 from zeroes 139 group by 1, 2, 3 140 ) 141 select * from final where ts is not null`; 142 143const MODEM_ACTIVITY_INFO = ` 144 drop table if exists modem_activity_info; 145 create table modem_activity_info as 146 with modem_raw as ( 147 select 148 ts, 149 EXTRACT_ARG(arg_set_id, 'modem_activity_info.timestamp_millis') as timestamp_millis, 150 EXTRACT_ARG(arg_set_id, 'modem_activity_info.sleep_time_millis') as sleep_time_millis, 151 EXTRACT_ARG(arg_set_id, 'modem_activity_info.controller_idle_time_millis') as controller_idle_time_millis, 152 EXTRACT_ARG(arg_set_id, 'modem_activity_info.controller_tx_time_pl0_millis') as controller_tx_time_pl0_millis, 153 EXTRACT_ARG(arg_set_id, 'modem_activity_info.controller_tx_time_pl1_millis') as controller_tx_time_pl1_millis, 154 EXTRACT_ARG(arg_set_id, 'modem_activity_info.controller_tx_time_pl2_millis') as controller_tx_time_pl2_millis, 155 EXTRACT_ARG(arg_set_id, 'modem_activity_info.controller_tx_time_pl3_millis') as controller_tx_time_pl3_millis, 156 EXTRACT_ARG(arg_set_id, 'modem_activity_info.controller_tx_time_pl4_millis') as controller_tx_time_pl4_millis, 157 EXTRACT_ARG(arg_set_id, 'modem_activity_info.controller_rx_time_millis') as controller_rx_time_millis 158 from track t join slice s on t.id = s.track_id 159 where t.name = 'Statsd Atoms' 160 and s.name = 'modem_activity_info' 161 ), 162 deltas as ( 163 select 164 timestamp_millis * 1000000 as ts, 165 lead(timestamp_millis) over (order by ts) - timestamp_millis as dur_millis, 166 lead(sleep_time_millis) over (order by ts) - sleep_time_millis as sleep_time_millis, 167 lead(controller_idle_time_millis) over (order by ts) - controller_idle_time_millis as controller_idle_time_millis, 168 lead(controller_tx_time_pl0_millis) over (order by ts) - controller_tx_time_pl0_millis as controller_tx_time_pl0_millis, 169 lead(controller_tx_time_pl1_millis) over (order by ts) - controller_tx_time_pl1_millis as controller_tx_time_pl1_millis, 170 lead(controller_tx_time_pl2_millis) over (order by ts) - controller_tx_time_pl2_millis as controller_tx_time_pl2_millis, 171 lead(controller_tx_time_pl3_millis) over (order by ts) - controller_tx_time_pl3_millis as controller_tx_time_pl3_millis, 172 lead(controller_tx_time_pl4_millis) over (order by ts) - controller_tx_time_pl4_millis as controller_tx_time_pl4_millis, 173 lead(controller_rx_time_millis) over (order by ts) - controller_rx_time_millis as controller_rx_time_millis 174 from modem_raw 175 ), 176 ratios as ( 177 select 178 ts, 179 100.0 * sleep_time_millis / dur_millis as sleep_time_ratio, 180 100.0 * controller_idle_time_millis / dur_millis as controller_idle_time_ratio, 181 100.0 * controller_tx_time_pl0_millis / dur_millis as controller_tx_time_pl0_ratio, 182 100.0 * controller_tx_time_pl1_millis / dur_millis as controller_tx_time_pl1_ratio, 183 100.0 * controller_tx_time_pl2_millis / dur_millis as controller_tx_time_pl2_ratio, 184 100.0 * controller_tx_time_pl3_millis / dur_millis as controller_tx_time_pl3_ratio, 185 100.0 * controller_tx_time_pl4_millis / dur_millis as controller_tx_time_pl4_ratio, 186 100.0 * controller_rx_time_millis / dur_millis as controller_rx_time_ratio 187 from deltas 188 ) 189 select * from ratios where sleep_time_ratio is not null and sleep_time_ratio >= 0`; 190 191const MODEM_RIL_STRENGTH = ` 192 DROP VIEW IF EXISTS ScreenOn; 193 CREATE VIEW ScreenOn AS 194 SELECT ts, dur FROM ( 195 SELECT 196 ts, value, 197 LEAD(ts, 1, TRACE_END()) OVER (ORDER BY ts)-ts AS dur 198 FROM counter, track ON (counter.track_id = track.id) 199 WHERE track.name = 'ScreenState' 200 ) WHERE value = 2; 201 202 DROP VIEW IF EXISTS RilSignalStrength; 203 CREATE VIEW RilSignalStrength AS 204 With RilMessages AS ( 205 SELECT 206 ts, slice.name, 207 LEAD(ts, 1, TRACE_END()) OVER (ORDER BY ts)-ts AS dur 208 FROM slice, track 209 ON (slice.track_id = track.id) 210 WHERE track.name = 'RIL' 211 AND slice.name GLOB 'UNSOL_SIGNAL_STRENGTH*' 212 ), 213 BandTypes(band_ril, band_name) AS ( 214 VALUES ("CellSignalStrengthLte:", "LTE"), 215 ("CellSignalStrengthNr:", "NR") 216 ), 217 ValueTypes(value_ril, value_name) AS ( 218 VALUES ("rsrp=", "rsrp"), 219 ("rssi=", "rssi") 220 ), 221 Extracted AS ( 222 SELECT ts, dur, band_name, value_name, ( 223 SELECT CAST(SUBSTR(key_str, start_idx+1, end_idx-start_idx-1) AS INT64) AS value 224 FROM ( 225 SELECT key_str, INSTR(key_str, "=") AS start_idx, INSTR(key_str, " ") AS end_idx 226 FROM ( 227 SELECT SUBSTR(band_str, INSTR(band_str, value_ril)) AS key_str 228 FROM (SELECT SUBSTR(name, INSTR(name, band_ril)) AS band_str) 229 ) 230 ) 231 ) AS value 232 FROM RilMessages 233 JOIN BandTypes 234 JOIN ValueTypes 235 ) 236 SELECT 237 ts, dur, band_name, value_name, value, 238 value_name || "=" || IIF(value = 2147483647, "unknown", ""||value) AS name, 239 ROW_NUMBER() OVER (ORDER BY ts) as id, 240 DENSE_RANK() OVER (ORDER BY band_name, value_name) AS track_id 241 FROM Extracted; 242 243 DROP TABLE IF EXISTS RilScreenOn; 244 CREATE VIRTUAL TABLE RilScreenOn 245 USING SPAN_JOIN(RilSignalStrength PARTITIONED track_id, ScreenOn)`; 246 247const MODEM_RIL_CHANNELS_PREAMBLE = ` 248 CREATE OR REPLACE PERFETTO FUNCTION EXTRACT_KEY_VALUE(source STRING, key_name STRING) RETURNS STRING AS 249 SELECT SUBSTR(trimmed, INSTR(trimmed, "=")+1, INSTR(trimmed, ",") - INSTR(trimmed, "=") - 1) 250 FROM (SELECT SUBSTR($source, INSTR($source, $key_name)) AS trimmed);`; 251 252const MODEM_RIL_CHANNELS = ` 253 With RawChannelConfig AS ( 254 SELECT ts, slice.name AS raw_config 255 FROM slice, track 256 ON (slice.track_id = track.id) 257 WHERE track.name = 'RIL' 258 AND slice.name LIKE 'UNSOL_PHYSICAL_CHANNEL_CONFIG%' 259 ), 260 Attributes(attribute, attrib_name) AS ( 261 VALUES ("mCellBandwidthDownlinkKhz", "downlink"), 262 ("mCellBandwidthUplinkKhz", "uplink"), 263 ("mNetworkType", "network"), 264 ("mBand", "band") 265 ), 266 Slots(idx, slot_name) AS ( 267 VALUES (0, "primary"), 268 (1, "secondary 1"), 269 (2, "secondary 2") 270 ), 271 Stage1 AS ( 272 SELECT *, IFNULL(EXTRACT_KEY_VALUE(STR_SPLIT(raw_config, "}, {", idx), attribute), "") AS name 273 FROM RawChannelConfig 274 JOIN Attributes 275 JOIN Slots 276 ), 277 Stage2 AS ( 278 SELECT *, LAG(name) OVER (PARTITION BY idx, attribute ORDER BY ts) AS last_name 279 FROM Stage1 280 ), 281 Stage3 AS ( 282 SELECT *, LEAD(ts, 1, TRACE_END()) OVER (PARTITION BY idx, attribute ORDER BY ts) - ts AS dur 283 FROM Stage2 WHERE name != last_name 284 ) 285 SELECT ts, dur, slot_name || "-" || attrib_name || "=" || name AS name 286 FROM Stage3`; 287 288const MODEM_CELL_RESELECTION = ` 289 with base as ( 290 select 291 ts, 292 s.name as raw_ril, 293 ifnull(str_split(str_split(s.name, 'CellIdentityLte{', 1), ', operatorNames', 0), 294 str_split(str_split(s.name, 'CellIdentityNr{', 1), ', operatorNames', 0)) as cell_id 295 from track t join slice s on t.id = s.track_id 296 where t.name = 'RIL' and s.name like '%DATA_REGISTRATION_STATE%' 297 ), 298 base2 as ( 299 select 300 ts, 301 raw_ril, 302 case 303 when cell_id like '%earfcn%' then 'LTE ' || cell_id 304 when cell_id like '%nrarfcn%' then 'NR ' || cell_id 305 when cell_id is null then 'Unknown' 306 else cell_id 307 end as cell_id 308 from base 309 ), 310 base3 as ( 311 select ts, cell_id , lag(cell_id) over (order by ts) as lag_cell_id, raw_ril 312 from base2 313 ) 314 select ts, 0 as dur, cell_id as name, raw_ril 315 from base3 316 where cell_id != lag_cell_id 317 order by ts`; 318 319const SUSPEND_RESUME = ` 320 SELECT 321 ts, 322 dur, 323 'Suspended' AS name 324 FROM android_suspend_state 325 WHERE power_state = 'suspended'`; 326 327const SCREEN_STATE = ` 328 WITH _counter AS ( 329 SELECT counter.id, ts, 0 AS track_id, value 330 FROM counter 331 JOIN counter_track ON counter_track.id = counter.track_id 332 WHERE name = 'ScreenState' 333 ) 334 SELECT 335 ts, 336 dur, 337 CASE value 338 WHEN 1 THEN 'Screen off' 339 WHEN 2 THEN 'Screen on' 340 WHEN 3 THEN 'Always-on display (doze)' 341 ELSE 'unknown' 342 END AS name 343 FROM counter_leading_intervals!(_counter)`; 344 345// See DeviceIdleController.java for where these states come from and how 346// they transition. 347const DOZE_LIGHT = ` 348 WITH _counter AS ( 349 SELECT counter.id, ts, 0 AS track_id, value 350 FROM counter 351 JOIN counter_track ON counter_track.id = counter.track_id 352 WHERE name = 'DozeLightState' 353 ) 354 SELECT 355 ts, 356 dur, 357 CASE value 358 WHEN 0 THEN 'active' 359 WHEN 1 THEN 'inactive' 360 WHEN 4 THEN 'idle' 361 WHEN 5 THEN 'waiting_for_network' 362 WHEN 6 THEN 'idle_maintenance' 363 WHEN 7 THEN 'override' 364 ELSE 'unknown' 365 END AS name 366 FROM counter_leading_intervals!(_counter)`; 367 368const DOZE_DEEP = ` 369 WITH _counter AS ( 370 SELECT counter.id, ts, 0 AS track_id, value 371 FROM counter 372 JOIN counter_track ON counter_track.id = counter.track_id 373 WHERE name = 'DozeDeepState' 374 ) 375 SELECT 376 ts, 377 dur, 378 CASE value 379 WHEN 0 THEN 'active' 380 WHEN 1 THEN 'inactive' 381 WHEN 2 THEN 'idle_pending' 382 WHEN 3 THEN 'sensing' 383 WHEN 4 THEN 'locating' 384 WHEN 5 THEN 'idle' 385 WHEN 6 THEN 'idle_maintenance' 386 WHEN 7 THEN 'quick_doze_delay' 387 ELSE 'unknown' 388 END AS name 389 FROM counter_leading_intervals!(_counter)`; 390 391const CHARGING = ` 392 WITH _counter AS ( 393 SELECT counter.id, ts, 0 AS track_id, value 394 FROM counter 395 JOIN counter_track ON counter_track.id = counter.track_id 396 WHERE name = 'BatteryStatus' 397 ) 398 SELECT 399 ts, 400 dur, 401 CASE value 402 -- 0 and 1 are both unknown 403 WHEN 2 THEN 'Charging' 404 WHEN 3 THEN 'Discharging' 405 -- special case when charger is present but battery isn't charging 406 WHEN 4 THEN 'Not charging' 407 WHEN 5 THEN 'Full' 408 ELSE 'unknown' 409 END AS name 410 FROM counter_leading_intervals!(_counter)`; 411 412const THERMAL_THROTTLING = ` 413 with step1 as ( 414 select 415 ts, 416 EXTRACT_ARG(arg_set_id, 'thermal_throttling_severity_state_changed.sensor_type') as sensor_type, 417 EXTRACT_ARG(arg_set_id, 'thermal_throttling_severity_state_changed.sensor_name') as sensor_name, 418 EXTRACT_ARG(arg_set_id, 'thermal_throttling_severity_state_changed.temperature_deci_celsius') / 10.0 as temperature_celcius, 419 EXTRACT_ARG(arg_set_id, 'thermal_throttling_severity_state_changed.severity') as severity 420 from track t join slice s on t.id = s.track_id 421 where t.name = 'Statsd Atoms' 422 and s.name = 'thermal_throttling_severity_state_changed' 423 ), 424 step2 as ( 425 select 426 ts, 427 lead(ts) over (partition by sensor_type, sensor_name order by ts) - ts as dur, 428 sensor_type, 429 sensor_name, 430 temperature_celcius, 431 severity 432 from step1 433 where sensor_type not like 'TEMPERATURE_TYPE_BCL_%' 434 ) 435 select 436 ts, 437 dur, 438 case sensor_name 439 when 'VIRTUAL-SKIN' then '' 440 else sensor_name || ' is ' 441 end || severity || ' (' || temperature_celcius || 'C)' as name 442 from step2 443 where severity != 'NONE'`; 444 445const KERNEL_WAKELOCKS = ` 446 create or replace perfetto table kernel_wakelocks as 447 with kernel_wakelock_args as ( 448 select 449 arg_set_id, 450 min(iif(key = 'kernel_wakelock.name', string_value, null)) as wakelock_name, 451 min(iif(key = 'kernel_wakelock.count', int_value, null)) as count, 452 min(iif(key = 'kernel_wakelock.time_micros', int_value, null)) as time_micros 453 from args 454 where key in ( 455 'kernel_wakelock.name', 456 'kernel_wakelock.count', 457 'kernel_wakelock.time_micros' 458 ) 459 group by 1 460 ), 461 interesting as ( 462 select wakelock_name 463 from ( 464 select wakelock_name, max(time_micros)-min(time_micros) as delta_us 465 from kernel_wakelock_args 466 group by 1 467 ) 468 -- Only consider wakelocks with over 1 second of time during the whole trace 469 where delta_us > 1e6 470 ), 471 step1 as ( 472 select ts, wakelock_name, count, time_micros 473 from kernel_wakelock_args 474 join interesting using (wakelock_name) 475 join slice using (arg_set_id) 476 ), 477 step2 as ( 478 select 479 ts, 480 wakelock_name, 481 lead(ts) over (partition by wakelock_name order by ts) as ts_end, 482 lead(count) over (partition by wakelock_name order by ts) - count as count, 483 (lead(time_micros) over (partition by wakelock_name order by ts) - time_micros) * 1000 as wakelock_dur 484 from step1 485 ), 486 step3 as ( 487 select 488 ts, 489 ts_end, 490 ifnull((select sum(dur) from android_suspend_state s 491 where power_state = 'suspended' 492 and s.ts > step2.ts 493 and s.ts < step2.ts_end), 0) as suspended_dur, 494 wakelock_name, 495 count, 496 wakelock_dur 497 from step2 498 where wakelock_dur is not null 499 and wakelock_dur >= 0 500 ) 501 select 502 ts, 503 ts_end - ts as dur, 504 wakelock_name, 505 min(100.0 * wakelock_dur / (ts_end - ts - suspended_dur), 100) as value 506 from step3`; 507 508const KERNEL_WAKELOCKS_SUMMARY = ` 509 select wakelock_name, max(value) as max_value 510 from kernel_wakelocks 511 where wakelock_name not in ('PowerManager.SuspendLockout', 'PowerManagerService.Display') 512 group by 1 513 having max_value > 1 514 order by 1;`; 515 516const HIGH_CPU = ` 517 create or replace perfetto table high_cpu as 518 with cpu_cycles_args AS ( 519 select 520 arg_set_id, 521 min(iif(key = 'cpu_cycles_per_uid_cluster.uid', int_value, null)) as uid, 522 min(iif(key = 'cpu_cycles_per_uid_cluster.cluster', int_value, null)) as cluster, 523 min(iif(key = 'cpu_cycles_per_uid_cluster.time_millis', int_value, null)) as time_millis 524 from args 525 where key in ( 526 'cpu_cycles_per_uid_cluster.uid', 527 'cpu_cycles_per_uid_cluster.cluster', 528 'cpu_cycles_per_uid_cluster.time_millis' 529 ) 530 group by 1 531 ), 532 interesting AS ( 533 select uid, cluster 534 from ( 535 select uid, cluster, max(time_millis)-min(time_millis) as delta_ms 536 from cpu_cycles_args 537 group by 1, 2 538 ) 539 -- Only consider tracks with over 1 second of cpu during the whole trace 540 where delta_ms > 1e3 541 ), 542 base as ( 543 select ts, uid, cluster, sum(time_millis) as time_millis 544 from cpu_cycles_args 545 join interesting using (uid, cluster) 546 join slice using (arg_set_id) 547 group by 1, 2, 3 548 ), 549 with_windows as ( 550 select 551 ts, 552 uid, 553 cluster, 554 lead(ts) over (partition by uid, cluster order by ts) - ts as dur, 555 (lead(time_millis) over (partition by uid, cluster order by ts) - time_millis) * 1000000.0 as cpu_dur 556 from base 557 ), 558 app_package_list as ( 559 select 560 uid, 561 group_concat(package_name) as package_name 562 from package_list 563 where uid >= 10000 564 group by 1 565 ), 566 with_ratio as ( 567 select 568 ts, 569 iif(dur is null, 0, max(0, 100.0 * cpu_dur / dur)) as value, 570 case cluster when 0 then 'little' when 1 then 'mid' when 2 then 'big' else 'cl-' || cluster end as cluster, 571 case 572 when uid = 0 then 'AID_ROOT' 573 when uid = 1000 then 'AID_SYSTEM_USER' 574 when uid = 1001 then 'AID_RADIO' 575 when uid = 1082 then 'AID_ARTD' 576 when pl.package_name is null then 'uid=' || uid 577 else pl.package_name 578 end as pkg 579 from with_windows left join app_package_list pl using(uid) 580 ) 581 select ts, sum(value) as value, cluster, pkg 582 from with_ratio 583 group by 1, 3, 4`; 584 585const WAKEUPS = ` 586 drop table if exists wakeups; 587 create table wakeups as 588 with wakeup_reason as ( 589 select 590 ts, 591 substr(i.name, 0, instr(i.name, ' ')) as id_timestamp, 592 substr(i.name, instr(i.name, ' ') + 1) as raw_wakeup 593 from track t join instant i on t.id = i.track_id 594 where t.name = 'wakeup_reason' 595 ), 596 wakeup_attribution as ( 597 select 598 substr(i.name, 0, instr(i.name, ' ')) as id_timestamp, 599 substr(i.name, instr(i.name, ' ') + 1) as attribution 600 from track t join instant i on t.id = i.track_id 601 where t.name = 'wakeup_attribution' 602 ), 603 step1 as( 604 select 605 ts, 606 raw_wakeup, 607 attribution, 608 null as raw_backoff 609 from wakeup_reason r 610 left outer join wakeup_attribution using(id_timestamp) 611 union all 612 select 613 ts, 614 null as raw_wakeup, 615 null as attribution, 616 i.name as raw_backoff 617 from track t join instant i on t.id = i.track_id 618 where t.name = 'suspend_backoff' 619 ), 620 step2 as ( 621 select 622 ts, 623 raw_wakeup, 624 attribution, 625 lag(raw_backoff) over (order by ts) as raw_backoff 626 from step1 627 ), 628 step3 as ( 629 select 630 ts, 631 raw_wakeup, 632 attribution, 633 str_split(raw_backoff, ' ', 0) as suspend_quality, 634 str_split(raw_backoff, ' ', 1) as backoff_state, 635 str_split(raw_backoff, ' ', 2) as backoff_reason, 636 cast(str_split(raw_backoff, ' ', 3) as int) as backoff_count, 637 cast(str_split(raw_backoff, ' ', 4) as int) as backoff_millis, 638 false as suspend_end 639 from step2 640 where raw_wakeup is not null 641 union all 642 select 643 ts, 644 null as raw_wakeup, 645 null as attribution, 646 null as suspend_quality, 647 null as backoff_state, 648 null as backoff_reason, 649 null as backoff_count, 650 null as backoff_millis, 651 true as suspend_end 652 from android_suspend_state 653 where power_state = 'suspended' 654 ), 655 step4 as ( 656 select 657 ts, 658 case suspend_quality 659 when 'good' then 660 min( 661 lead(ts, 1, ts + 5e9) over (order by ts) - ts, 662 5e9 663 ) 664 when 'bad' then backoff_millis * 1000000 665 else 0 666 end as dur, 667 raw_wakeup, 668 attribution, 669 suspend_quality, 670 backoff_state, 671 backoff_reason, 672 backoff_count, 673 backoff_millis, 674 suspend_end 675 from step3 676 ), 677 step5 as ( 678 select 679 ts, 680 dur, 681 raw_wakeup, 682 attribution, 683 suspend_quality, 684 backoff_state, 685 backoff_reason, 686 backoff_count, 687 backoff_millis 688 from step4 689 where not suspend_end 690 ), 691 step6 as ( 692 select 693 ts, 694 dur, 695 raw_wakeup, 696 attribution, 697 suspend_quality, 698 backoff_state, 699 backoff_reason, 700 backoff_count, 701 backoff_millis, 702 case 703 when raw_wakeup like 'Abort: Pending Wakeup Sources: %' then 'abort_pending' 704 when raw_wakeup like 'Abort: Last active Wakeup Source: %' then 'abort_last_active' 705 when raw_wakeup like 'Abort: %' then 'abort_other' 706 else 'normal' 707 end as type, 708 case 709 when raw_wakeup like 'Abort: Pending Wakeup Sources: %' then substr(raw_wakeup, 32) 710 when raw_wakeup like 'Abort: Last active Wakeup Source: %' then substr(raw_wakeup, 35) 711 when raw_wakeup like 'Abort: %' then substr(raw_wakeup, 8) 712 else raw_wakeup 713 end as main, 714 case 715 when raw_wakeup like 'Abort: Pending Wakeup Sources: %' then ' ' 716 when raw_wakeup like 'Abort: %' then 'no delimiter needed' 717 else ':' 718 end as delimiter 719 from step5 720 ), 721 step7 as ( 722 select 723 ts, 724 dur, 725 raw_wakeup, 726 attribution, 727 suspend_quality, 728 backoff_state, 729 backoff_reason, 730 backoff_count, 731 backoff_millis, 732 type, 733 str_split(main, delimiter, 0) as item_0, 734 str_split(main, delimiter, 1) as item_1, 735 str_split(main, delimiter, 2) as item_2, 736 str_split(main, delimiter, 3) as item_3 737 from step6 738 ), 739 step8 as ( 740 select ts, dur, raw_wakeup, attribution, suspend_quality, backoff_state, backoff_reason, backoff_count, backoff_millis, type, item_0 as item from step7 741 union all 742 select ts, dur, raw_wakeup, attribution, suspend_quality, backoff_state, backoff_reason, backoff_count, backoff_millis, type, item_1 as item from step7 where item_1 is not null 743 union all 744 select ts, dur, raw_wakeup, attribution, suspend_quality, backoff_state, backoff_reason, backoff_count, backoff_millis, type, item_2 as item from step7 where item_2 is not null 745 union all 746 select ts, dur, raw_wakeup, attribution, suspend_quality, backoff_state, backoff_reason, backoff_count, backoff_millis, type, item_3 as item from step7 where item_3 is not null 747 ) 748 select 749 ts, 750 dur, 751 ts + dur as ts_end, 752 raw_wakeup, 753 attribution, 754 suspend_quality, 755 backoff_state, 756 ifnull(backoff_reason, 'none') as backoff_reason, 757 backoff_count, 758 backoff_millis, 759 type, 760 case when type = 'normal' then ifnull(str_split(item, ' ', 1), item) else item end as item 761 from step8`; 762 763const WAKEUPS_COLUMNS = [ 764 'item', 765 'type', 766 'raw_wakeup', 767 'attribution', 768 'suspend_quality', 769 'backoff_state', 770 'backoff_reason', 771 'backoff_count', 772 'backoff_millis', 773]; 774 775function bleScanQuery(condition: string) { 776 return ` 777 with step1 as ( 778 select 779 ts, 780 extract_arg(arg_set_id, 'ble_scan_state_changed.attribution_node[0].tag') as name, 781 extract_arg(arg_set_id, 'ble_scan_state_changed.is_opportunistic') as opportunistic, 782 extract_arg(arg_set_id, 'ble_scan_state_changed.is_filtered') as filtered, 783 extract_arg(arg_set_id, 'ble_scan_state_changed.state') as state 784 from track t join slice s on t.id = s.track_id 785 where t.name = 'Statsd Atoms' 786 and s.name = 'ble_scan_state_changed' 787 ), 788 step2 as ( 789 select 790 ts, 791 name, 792 state, 793 opportunistic, 794 filtered, 795 lead(ts) over (partition by name order by ts) - ts as dur 796 from step1 797 ) 798 select ts, dur, name from step2 where state = 'ON' and ${condition} and dur is not null`; 799} 800 801const BLE_RESULTS = ` 802 with step1 as ( 803 select 804 ts, 805 extract_arg(arg_set_id, 'ble_scan_result_received.attribution_node[0].tag') as name, 806 extract_arg(arg_set_id, 'ble_scan_result_received.num_results') as num_results 807 from track t join slice s on t.id = s.track_id 808 where t.name = 'Statsd Atoms' 809 and s.name = 'ble_scan_result_received' 810 ) 811 select 812 ts, 813 0 as dur, 814 name || ' (' || num_results || ' results)' as name 815 from step1`; 816 817const BT_A2DP_AUDIO = ` 818 with step1 as ( 819 select 820 ts, 821 EXTRACT_ARG(arg_set_id, 'bluetooth_a2dp_playback_state_changed.playback_state') as playback_state, 822 EXTRACT_ARG(arg_set_id, 'bluetooth_a2dp_playback_state_changed.audio_coding_mode') as audio_coding_mode, 823 EXTRACT_ARG(arg_set_id, 'bluetooth_a2dp_playback_state_changed.metric_id') as metric_id 824 from track t join slice s on t.id = s.track_id 825 where t.name = 'Statsd Atoms' 826 and s.name = 'bluetooth_a2dp_playback_state_changed' 827 ), 828 step2 as ( 829 select 830 ts, 831 lead(ts) over (partition by metric_id order by ts) - ts as dur, 832 playback_state, 833 audio_coding_mode, 834 metric_id 835 from step1 836 ) 837 select 838 ts, 839 dur, 840 audio_coding_mode as name 841 from step2 842 where playback_state = 'PLAYBACK_STATE_PLAYING'`; 843 844const BT_CONNS_ACL = ` 845 with acl1 as ( 846 select 847 ts, 848 EXTRACT_ARG(arg_set_id, 'bluetooth_acl_connection_state_changed.state') as state, 849 EXTRACT_ARG(arg_set_id, 'bluetooth_acl_connection_state_changed.transport') as transport, 850 EXTRACT_ARG(arg_set_id, 'bluetooth_acl_connection_state_changed.metric_id') as metric_id 851 from track t join slice s on t.id = s.track_id 852 where t.name = 'Statsd Atoms' 853 and s.name = 'bluetooth_acl_connection_state_changed' 854 ), 855 acl2 as ( 856 select 857 ts, 858 lead(ts) over (partition by metric_id, transport order by ts) - ts as dur, 859 state, 860 transport, 861 metric_id 862 from acl1 863 ) 864 select 865 ts, 866 dur, 867 'Device ' || metric_id || 868 ' (' || case transport when 'TRANSPORT_TYPE_BREDR' then 'Classic' when 'TRANSPORT_TYPE_LE' then 'BLE' end || ')' as name 869 from acl2 870 where state != 'CONNECTION_STATE_DISCONNECTED' and dur is not null`; 871 872const BT_CONNS_SCO = ` 873 with sco1 as ( 874 select 875 ts, 876 EXTRACT_ARG(arg_set_id, 'bluetooth_sco_connection_state_changed.state') as state, 877 EXTRACT_ARG(arg_set_id, 'bluetooth_sco_connection_state_changed.codec') as codec, 878 EXTRACT_ARG(arg_set_id, 'bluetooth_sco_connection_state_changed.metric_id') as metric_id 879 from track t join slice s on t.id = s.track_id 880 where t.name = 'Statsd Atoms' 881 and s.name = 'bluetooth_sco_connection_state_changed' 882 ), 883 sco2 as ( 884 select 885 ts, 886 lead(ts) over (partition by metric_id, codec order by ts) - ts as dur, 887 state, 888 codec, 889 metric_id 890 from sco1 891 ) 892 select 893 ts, 894 dur, 895 case state when 'CONNECTION_STATE_CONNECTED' then '' when 'CONNECTION_STATE_CONNECTING' then 'Connecting ' when 'CONNECTION_STATE_DISCONNECTING' then 'Disconnecting ' else 'unknown ' end || 896 'Device ' || metric_id || ' (' || 897 case codec when 'SCO_CODEC_CVSD' then 'CVSD' when 'SCO_CODEC_MSBC' then 'MSBC' end || ')' as name 898 from sco2 899 where state != 'CONNECTION_STATE_DISCONNECTED' and dur is not null`; 900 901const BT_LINK_LEVEL_EVENTS = ` 902 with base as ( 903 select 904 ts, 905 EXTRACT_ARG(arg_set_id, 'bluetooth_link_layer_connection_event.direction') as direction, 906 EXTRACT_ARG(arg_set_id, 'bluetooth_link_layer_connection_event.type') as type, 907 EXTRACT_ARG(arg_set_id, 'bluetooth_link_layer_connection_event.hci_cmd') as hci_cmd, 908 EXTRACT_ARG(arg_set_id, 'bluetooth_link_layer_connection_event.hci_event') as hci_event, 909 EXTRACT_ARG(arg_set_id, 'bluetooth_link_layer_connection_event.hci_ble_event') as hci_ble_event, 910 EXTRACT_ARG(arg_set_id, 'bluetooth_link_layer_connection_event.cmd_status') as cmd_status, 911 EXTRACT_ARG(arg_set_id, 'bluetooth_link_layer_connection_event.reason_code') as reason_code, 912 EXTRACT_ARG(arg_set_id, 'bluetooth_link_layer_connection_event.metric_id') as metric_id 913 from track t join slice s on t.id = s.track_id 914 where t.name = 'Statsd Atoms' 915 and s.name = 'bluetooth_link_layer_connection_event' 916 ) 917 select 918 *, 919 0 as dur, 920 'Device '|| metric_id as name 921 from base`; 922 923const BT_LINK_LEVEL_EVENTS_COLUMNS = [ 924 'direction', 925 'type', 926 'hci_cmd', 927 'hci_event', 928 'hci_ble_event', 929 'cmd_status', 930 'reason_code', 931 'metric_id', 932]; 933 934const BT_QUALITY_REPORTS = ` 935 with base as ( 936 select 937 ts, 938 EXTRACT_ARG(arg_set_id, 'bluetooth_quality_report_reported.quality_report_id') as quality_report_id, 939 EXTRACT_ARG(arg_set_id, 'bluetooth_quality_report_reported.packet_types') as packet_types, 940 EXTRACT_ARG(arg_set_id, 'bluetooth_quality_report_reported.connection_handle') as connection_handle, 941 EXTRACT_ARG(arg_set_id, 'bluetooth_quality_report_reported.connection_role') as connection_role, 942 EXTRACT_ARG(arg_set_id, 'bluetooth_quality_report_reported.tx_power_level') as tx_power_level, 943 EXTRACT_ARG(arg_set_id, 'bluetooth_quality_report_reported.rssi') as rssi, 944 EXTRACT_ARG(arg_set_id, 'bluetooth_quality_report_reported.snr') as snr, 945 EXTRACT_ARG(arg_set_id, 'bluetooth_quality_report_reported.unused_afh_channel_count') as unused_afh_channel_count, 946 EXTRACT_ARG(arg_set_id, 'bluetooth_quality_report_reported.afh_select_unideal_channel_count') as afh_select_unideal_channel_count, 947 EXTRACT_ARG(arg_set_id, 'bluetooth_quality_report_reported.lsto') as lsto, 948 EXTRACT_ARG(arg_set_id, 'bluetooth_quality_report_reported.connection_piconet_clock') as connection_piconet_clock, 949 EXTRACT_ARG(arg_set_id, 'bluetooth_quality_report_reported.retransmission_count') as retransmission_count, 950 EXTRACT_ARG(arg_set_id, 'bluetooth_quality_report_reported.no_rx_count') as no_rx_count, 951 EXTRACT_ARG(arg_set_id, 'bluetooth_quality_report_reported.nak_count') as nak_count, 952 EXTRACT_ARG(arg_set_id, 'bluetooth_quality_report_reported.flow_off_count') as flow_off_count, 953 EXTRACT_ARG(arg_set_id, 'bluetooth_quality_report_reported.buffer_overflow_bytes') as buffer_overflow_bytes, 954 EXTRACT_ARG(arg_set_id, 'bluetooth_quality_report_reported.buffer_underflow_bytes') as buffer_underflow_bytes 955 from track t join slice s on t.id = s.track_id 956 where t.name = 'Statsd Atoms' 957 and s.name = 'bluetooth_quality_report_reported' 958 ) 959 select 960 *, 961 0 as dur, 962 'Connection '|| connection_handle as name 963 from base`; 964 965const BT_QUALITY_REPORTS_COLUMNS = [ 966 'quality_report_id', 967 'packet_types', 968 'connection_handle', 969 'connection_role', 970 'tx_power_level', 971 'rssi', 972 'snr', 973 'unused_afh_channel_count', 974 'afh_select_unideal_channel_count', 975 'lsto', 976 'connection_piconet_clock', 977 'retransmission_count', 978 'no_rx_count', 979 'nak_count', 980 'flow_off_count', 981 'buffer_overflow_bytes', 982 'buffer_underflow_bytes', 983]; 984 985const BT_RSSI_REPORTS = ` 986 with base as ( 987 select 988 ts, 989 EXTRACT_ARG(arg_set_id, 'bluetooth_device_rssi_reported.connection_handle') as connection_handle, 990 EXTRACT_ARG(arg_set_id, 'bluetooth_device_rssi_reported.hci_status') as hci_status, 991 EXTRACT_ARG(arg_set_id, 'bluetooth_device_rssi_reported.rssi') as rssi, 992 EXTRACT_ARG(arg_set_id, 'bluetooth_device_rssi_reported.metric_id') as metric_id 993 from track t join slice s on t.id = s.track_id 994 where t.name = 'Statsd Atoms' 995 and s.name = 'bluetooth_device_rssi_reported' 996 ) 997 select 998 *, 999 0 as dur, 1000 'Connection '|| connection_handle as name 1001 from base`; 1002 1003const BT_RSSI_REPORTS_COLUMNS = [ 1004 'connection_handle', 1005 'hci_status', 1006 'rssi', 1007 'metric_id', 1008]; 1009 1010const BT_CODE_PATH_COUNTER = ` 1011 with base as ( 1012 select 1013 ts, 1014 EXTRACT_ARG(arg_set_id, 'bluetooth_code_path_counter.key') as key, 1015 EXTRACT_ARG(arg_set_id, 'bluetooth_code_path_counter.number') as number 1016 from track t join slice s on t.id = s.track_id 1017 where t.name = 'Statsd Atoms' 1018 and s.name = 'bluetooth_code_path_counter' 1019 ) 1020 select 1021 *, 1022 0 as dur, 1023 key as name 1024 from base`; 1025 1026const BT_CODE_PATH_COUNTER_COLUMNS = ['key', 'number']; 1027 1028const BT_HAL_CRASHES = ` 1029 with base as ( 1030 select 1031 ts, 1032 EXTRACT_ARG(arg_set_id, 'bluetooth_hal_crash_reason_reported.metric_id') as metric_id, 1033 EXTRACT_ARG(arg_set_id, 'bluetooth_hal_crash_reason_reported.error_code') as error_code, 1034 EXTRACT_ARG(arg_set_id, 'bluetooth_hal_crash_reason_reported.vendor_error_code') as vendor_error_code 1035 from track t join slice s on t.id = s.track_id 1036 where t.name = 'Statsd Atoms' 1037 and s.name = 'bluetooth_hal_crash_reason_reported' 1038 ) 1039 select 1040 *, 1041 0 as dur, 1042 'Device ' || metric_id as name 1043 from base`; 1044 1045const BT_HAL_CRASHES_COLUMNS = ['metric_id', 'error_code', 'vendor_error_code']; 1046 1047const BT_BYTES = ` 1048 with step1 as ( 1049 select 1050 ts, 1051 EXTRACT_ARG(arg_set_id, 'bluetooth_bytes_transfer.uid') as uid, 1052 EXTRACT_ARG(arg_set_id, 'bluetooth_bytes_transfer.tx_bytes') as tx_bytes, 1053 EXTRACT_ARG(arg_set_id, 'bluetooth_bytes_transfer.rx_bytes') as rx_bytes 1054 from track t join slice s on t.id = s.track_id 1055 where t.name = 'Statsd Atoms' 1056 and s.name = 'bluetooth_bytes_transfer' 1057 ), 1058 step2 as ( 1059 select 1060 ts, 1061 lead(ts) over (partition by uid order by ts) - ts as dur, 1062 uid, 1063 lead(tx_bytes) over (partition by uid order by ts) - tx_bytes as tx_bytes, 1064 lead(rx_bytes) over (partition by uid order by ts) - rx_bytes as rx_bytes 1065 from step1 1066 ), 1067 step3 as ( 1068 select 1069 ts, 1070 dur, 1071 uid % 100000 as uid, 1072 sum(tx_bytes) as tx_bytes, 1073 sum(rx_bytes) as rx_bytes 1074 from step2 1075 where tx_bytes >=0 and rx_bytes >=0 1076 group by 1,2,3 1077 having tx_bytes > 0 or rx_bytes > 0 1078 ), 1079 app_package_list as ( 1080 select 1081 uid, 1082 group_concat(package_name) as package_name 1083 from package_list 1084 where uid >= 10000 1085 group by 1 1086 ) 1087 select 1088 ts, 1089 dur, 1090 case 1091 when pl.package_name is null then 'uid=' || uid 1092 else pl.package_name 1093 end || ' TX ' || tx_bytes || ' bytes / RX ' || rx_bytes || ' bytes' as name 1094 from step3 left join app_package_list pl using(uid) 1095`; 1096 1097// See go/bt_system_context_report for reference on the bit-twiddling. 1098const BT_ACTIVITY = ` 1099 create perfetto table bt_activity as 1100 with step1 as ( 1101 select 1102 EXTRACT_ARG(arg_set_id, 'bluetooth_activity_info.timestamp_millis') * 1000000 as ts, 1103 EXTRACT_ARG(arg_set_id, 'bluetooth_activity_info.bluetooth_stack_state') as bluetooth_stack_state, 1104 EXTRACT_ARG(arg_set_id, 'bluetooth_activity_info.controller_idle_time_millis') * 1000000 as controller_idle_dur, 1105 EXTRACT_ARG(arg_set_id, 'bluetooth_activity_info.controller_tx_time_millis') * 1000000 as controller_tx_dur, 1106 EXTRACT_ARG(arg_set_id, 'bluetooth_activity_info.controller_rx_time_millis') * 1000000 as controller_rx_dur 1107 from track t join slice s on t.id = s.track_id 1108 where t.name = 'Statsd Atoms' 1109 and s.name = 'bluetooth_activity_info' 1110 ), 1111 step2 as ( 1112 select 1113 ts, 1114 lead(ts) over (order by ts) - ts as dur, 1115 bluetooth_stack_state, 1116 lead(controller_idle_dur) over (order by ts) - controller_idle_dur as controller_idle_dur, 1117 lead(controller_tx_dur) over (order by ts) - controller_tx_dur as controller_tx_dur, 1118 lead(controller_rx_dur) over (order by ts) - controller_rx_dur as controller_rx_dur 1119 from step1 1120 ) 1121 select 1122 ts, 1123 dur, 1124 bluetooth_stack_state & 0x0000000F as acl_active_count, 1125 bluetooth_stack_state & 0x000000F0 >> 4 as acl_sniff_count, 1126 bluetooth_stack_state & 0x00000F00 >> 8 as acl_ble_count, 1127 bluetooth_stack_state & 0x0000F000 >> 12 as advertising_count, 1128 case bluetooth_stack_state & 0x000F0000 >> 16 1129 when 0 then 0 1130 when 1 then 5 1131 when 2 then 10 1132 when 3 then 25 1133 when 4 then 100 1134 else -1 1135 end as le_scan_duty_cycle, 1136 bluetooth_stack_state & 0x00100000 >> 20 as inquiry_active, 1137 bluetooth_stack_state & 0x00200000 >> 21 as sco_active, 1138 bluetooth_stack_state & 0x00400000 >> 22 as a2dp_active, 1139 bluetooth_stack_state & 0x00800000 >> 23 as le_audio_active, 1140 max(0, 100.0 * controller_idle_dur / dur) as controller_idle_pct, 1141 max(0, 100.0 * controller_tx_dur / dur) as controller_tx_pct, 1142 max(0, 100.0 * controller_rx_dur / dur) as controller_rx_pct 1143 from step2 1144`; 1145 1146class AndroidLongBatteryTracing implements Plugin { 1147 addSliceTrack( 1148 ctx: PluginContextTrace, 1149 name: string, 1150 query: string, 1151 groupName?: string, 1152 columns: string[] = [], 1153 ): void { 1154 const config: SimpleSliceTrackConfig = { 1155 data: { 1156 sqlSource: query, 1157 columns: ['ts', 'dur', 'name', ...columns], 1158 }, 1159 columns: {ts: 'ts', dur: 'dur', name: 'name'}, 1160 argColumns: columns, 1161 }; 1162 ctx.registerStaticTrack({ 1163 uri: `dev.perfetto.AndroidLongBatteryTracing#${name}`, 1164 displayName: name, 1165 trackFactory: (trackCtx) => { 1166 return new SimpleSliceTrack(ctx.engine, trackCtx, config); 1167 }, 1168 groupName, 1169 }); 1170 } 1171 1172 addCounterTrack( 1173 ctx: PluginContextTrace, 1174 name: string, 1175 query: string, 1176 groupName: string, 1177 options?: Partial<CounterOptions>, 1178 ): void { 1179 const config: SimpleCounterTrackConfig = { 1180 data: { 1181 sqlSource: query, 1182 columns: ['ts', 'value'], 1183 }, 1184 columns: {ts: 'ts', value: 'value'}, 1185 options, 1186 }; 1187 ctx.registerStaticTrack({ 1188 uri: `dev.perfetto.AndroidLongBatteryTracing#${name}`, 1189 displayName: name, 1190 trackFactory: (trackCtx) => { 1191 return new SimpleCounterTrack(ctx.engine, trackCtx, config); 1192 }, 1193 groupName, 1194 }); 1195 } 1196 1197 addBatteryStatsState( 1198 ctx: PluginContextTrace, 1199 name: string, 1200 track: string, 1201 groupName: string, 1202 features: Set<string>, 1203 ): void { 1204 if (!features.has(`track.${track}`)) { 1205 return; 1206 } 1207 this.addSliceTrack( 1208 ctx, 1209 name, 1210 `SELECT ts, safe_dur AS dur, value_name AS name 1211 FROM android_battery_stats_state 1212 WHERE track_name = "${track}"`, 1213 groupName, 1214 ); 1215 } 1216 1217 addBatteryStatsEvent( 1218 ctx: PluginContextTrace, 1219 name: string, 1220 track: string, 1221 groupName: string | undefined, 1222 features: Set<string>, 1223 ): void { 1224 if (!features.has(`track.${track}`)) { 1225 return; 1226 } 1227 1228 this.addSliceTrack( 1229 ctx, 1230 name, 1231 `SELECT ts, safe_dur AS dur, str_value AS name 1232 FROM android_battery_stats_event_slices 1233 WHERE track_name = "${track}"`, 1234 groupName, 1235 ); 1236 } 1237 1238 async addDeviceState( 1239 ctx: PluginContextTrace, 1240 features: Set<string>, 1241 ): Promise<void> { 1242 if (!features.has('track.battery_stats.*')) { 1243 return; 1244 } 1245 1246 const query = (name: string, track: string) => 1247 this.addBatteryStatsEvent(ctx, name, track, undefined, features); 1248 1249 const e = ctx.engine; 1250 await e.query(`INCLUDE PERFETTO MODULE android.battery_stats;`); 1251 await e.query(`INCLUDE PERFETTO MODULE android.suspend;`); 1252 await e.query(`INCLUDE PERFETTO MODULE counters.intervals;`); 1253 1254 this.addSliceTrack(ctx, 'Device State: Screen state', SCREEN_STATE); 1255 this.addSliceTrack(ctx, 'Device State: Charging', CHARGING); 1256 this.addSliceTrack(ctx, 'Device State: Suspend / resume', SUSPEND_RESUME); 1257 this.addSliceTrack(ctx, 'Device State: Doze light state', DOZE_LIGHT); 1258 this.addSliceTrack(ctx, 'Device State: Doze deep state', DOZE_DEEP); 1259 1260 query('Device State: Top app', 'battery_stats.top'); 1261 1262 this.addSliceTrack( 1263 ctx, 1264 'Device State: Long wakelocks', 1265 `SELECT 1266 ts - 60000000000 as ts, 1267 safe_dur + 60000000000 as dur, 1268 str_value AS name, 1269 ifnull( 1270 (select package_name from package_list where uid = int_value % 100000), 1271 "uid="||int_value) as package 1272 FROM android_battery_stats_event_slices 1273 WHERE track_name = "battery_stats.longwake"`, 1274 undefined, 1275 ['package'], 1276 ); 1277 1278 query('Device State: Foreground apps', 'battery_stats.fg'); 1279 query('Device State: Jobs', 'battery_stats.job'); 1280 1281 if (features.has('atom.thermal_throttling_severity_state_changed')) { 1282 this.addSliceTrack( 1283 ctx, 1284 'Device State: Thermal throttling', 1285 THERMAL_THROTTLING, 1286 ); 1287 } 1288 } 1289 1290 async addNetworkSummary( 1291 ctx: PluginContextTrace, 1292 features: Set<string>, 1293 ): Promise<void> { 1294 if (!features.has('net.modem') && !features.has('net.wifi')) { 1295 return; 1296 } 1297 1298 const groupName = 'Network Summary'; 1299 1300 const e = ctx.engine; 1301 await e.query(`INCLUDE PERFETTO MODULE android.battery_stats;`); 1302 await e.query(`INCLUDE PERFETTO MODULE android.network_packets;`); 1303 await e.query(NETWORK_SUMMARY); 1304 await e.query(RADIO_TRANSPORT_TYPE); 1305 1306 this.addSliceTrack(ctx, 'Default network', DEFAULT_NETWORK, groupName); 1307 1308 if (features.has('atom.network_tethering_reported')) { 1309 this.addSliceTrack(ctx, 'Tethering', TETHERING, groupName); 1310 } 1311 if (features.has('net.wifi')) { 1312 this.addCounterTrack( 1313 ctx, 1314 'Wifi total', 1315 `select ts, sum(value) as value from network_summary where dev_type = 'wifi' group by 1`, 1316 groupName, 1317 {yDisplay: 'log', yRangeSharingKey: 'net_bytes', unit: 'byte'}, 1318 ); 1319 const result = await e.query( 1320 `select pkg, sum(value) from network_summary where dev_type='wifi' group by 1 order by 2 desc limit 10`, 1321 ); 1322 const it = result.iter({pkg: 'str'}); 1323 for (; it.valid(); it.next()) { 1324 this.addCounterTrack( 1325 ctx, 1326 `Top wifi: ${it.pkg}`, 1327 `select ts, value from network_summary where dev_type = 'wifi' and pkg = '${it.pkg}'`, 1328 groupName, 1329 {yDisplay: 'log', yRangeSharingKey: 'net_bytes', unit: 'byte'}, 1330 ); 1331 } 1332 } 1333 this.addBatteryStatsState( 1334 ctx, 1335 'Wifi interface', 1336 'battery_stats.wifi_radio', 1337 groupName, 1338 features, 1339 ); 1340 this.addBatteryStatsState( 1341 ctx, 1342 'Wifi supplicant state', 1343 'battery_stats.wifi_suppl', 1344 groupName, 1345 features, 1346 ); 1347 this.addBatteryStatsState( 1348 ctx, 1349 'Wifi strength', 1350 'battery_stats.wifi_signal_strength', 1351 groupName, 1352 features, 1353 ); 1354 if (features.has('net.modem')) { 1355 this.addCounterTrack( 1356 ctx, 1357 'Modem total', 1358 `select ts, sum(value) as value from network_summary where dev_type = 'modem' group by 1`, 1359 groupName, 1360 {yDisplay: 'log', yRangeSharingKey: 'net_bytes', unit: 'byte'}, 1361 ); 1362 const result = await e.query( 1363 `select pkg, sum(value) from network_summary where dev_type='modem' group by 1 order by 2 desc limit 10`, 1364 ); 1365 const it = result.iter({pkg: 'str'}); 1366 for (; it.valid(); it.next()) { 1367 this.addCounterTrack( 1368 ctx, 1369 `Top modem: ${it.pkg}`, 1370 `select ts, value from network_summary where dev_type = 'modem' and pkg = '${it.pkg}'`, 1371 groupName, 1372 {yDisplay: 'log', yRangeSharingKey: 'net_bytes', unit: 'byte'}, 1373 ); 1374 } 1375 } 1376 this.addBatteryStatsState( 1377 ctx, 1378 'Cellular interface', 1379 'battery_stats.mobile_radio', 1380 groupName, 1381 features, 1382 ); 1383 this.addSliceTrack( 1384 ctx, 1385 'Cellular connection', 1386 `select ts, dur, name from radio_transport`, 1387 groupName, 1388 ); 1389 this.addBatteryStatsState( 1390 ctx, 1391 'Cellular strength', 1392 'battery_stats.phone_signal_strength', 1393 groupName, 1394 features, 1395 ); 1396 } 1397 1398 async addModemDetail( 1399 ctx: PluginContextTrace, 1400 features: Set<string>, 1401 ): Promise<void> { 1402 if (!features.has('atom.modem_activity_info')) { 1403 return; 1404 } 1405 const groupName = 'Modem Detail'; 1406 await this.addModemActivityInfo(ctx, groupName); 1407 if (features.has('track.ril')) { 1408 await this.addModemRil(ctx, groupName); 1409 } 1410 } 1411 1412 async addModemActivityInfo( 1413 ctx: PluginContextTrace, 1414 groupName: string, 1415 ): Promise<void> { 1416 const query = (name: string, col: string): void => 1417 this.addCounterTrack( 1418 ctx, 1419 name, 1420 `select ts, ${col}_ratio as value from modem_activity_info`, 1421 groupName, 1422 {yRangeSharingKey: 'modem_activity', unit: '%'}, 1423 ); 1424 1425 await ctx.engine.query(MODEM_ACTIVITY_INFO); 1426 query('Modem sleep', 'sleep_time'); 1427 query('Modem controller idle', 'controller_idle_time'); 1428 query('Modem RX time', 'controller_rx_time'); 1429 query('Modem TX time power 0', 'controller_tx_time_pl0'); 1430 query('Modem TX time power 1', 'controller_tx_time_pl1'); 1431 query('Modem TX time power 2', 'controller_tx_time_pl2'); 1432 query('Modem TX time power 3', 'controller_tx_time_pl3'); 1433 query('Modem TX time power 4', 'controller_tx_time_pl4'); 1434 } 1435 1436 async addModemRil(ctx: PluginContextTrace, groupName: string): Promise<void> { 1437 const rilStrength = (band: string, value: string): void => 1438 this.addSliceTrack( 1439 ctx, 1440 `Modem signal strength ${band} ${value}`, 1441 `SELECT ts, dur, name FROM RilScreenOn WHERE band_name = '${band}' AND value_name = '${value}'`, 1442 groupName, 1443 ); 1444 1445 const e = ctx.engine; 1446 await e.query(MODEM_RIL_STRENGTH); 1447 await e.query(MODEM_RIL_CHANNELS_PREAMBLE); 1448 1449 rilStrength('LTE', 'rsrp'); 1450 rilStrength('LTE', 'rssi'); 1451 rilStrength('NR', 'rsrp'); 1452 rilStrength('NR', 'rssi'); 1453 1454 this.addSliceTrack( 1455 ctx, 1456 'Modem channel config', 1457 MODEM_RIL_CHANNELS, 1458 groupName, 1459 ); 1460 1461 this.addSliceTrack( 1462 ctx, 1463 'Modem cell reselection', 1464 MODEM_CELL_RESELECTION, 1465 groupName, 1466 ['raw_ril'], 1467 ); 1468 } 1469 1470 async addKernelWakelocks( 1471 ctx: PluginContextTrace, 1472 features: Set<string>, 1473 ): Promise<void> { 1474 if (!features.has('atom.kernel_wakelock')) { 1475 return; 1476 } 1477 const groupName = 'Kernel Wakelock Summary'; 1478 1479 const e = ctx.engine; 1480 await e.query(`INCLUDE PERFETTO MODULE android.suspend;`); 1481 await e.query(KERNEL_WAKELOCKS); 1482 const result = await e.query(KERNEL_WAKELOCKS_SUMMARY); 1483 const it = result.iter({wakelock_name: 'str'}); 1484 for (; it.valid(); it.next()) { 1485 this.addCounterTrack( 1486 ctx, 1487 it.wakelock_name, 1488 `select ts, dur, value from kernel_wakelocks where wakelock_name = "${it.wakelock_name}"`, 1489 groupName, 1490 {yRangeSharingKey: 'kernel_wakelock', unit: '%'}, 1491 ); 1492 } 1493 } 1494 1495 async addWakeups( 1496 ctx: PluginContextTrace, 1497 features: Set<string>, 1498 ): Promise<void> { 1499 if (!features.has('track.suspend_backoff')) { 1500 return; 1501 } 1502 1503 const e = ctx.engine; 1504 const groupName = 'Wakeups'; 1505 await e.query(`INCLUDE PERFETTO MODULE android.suspend;`); 1506 await e.query(WAKEUPS); 1507 const result = await e.query(`select 1508 item, 1509 sum(dur) as sum_dur 1510 from wakeups 1511 group by 1 1512 having sum_dur > 600e9`); 1513 const it = result.iter({item: 'str'}); 1514 const sqlPrefix = `select 1515 ts, 1516 dur, 1517 item || case backoff_reason 1518 when 'short' then ' (Short suspend backoff)' 1519 when 'failed' then ' (Failed suspend backoff)' 1520 else '' 1521 end as name, 1522 item, 1523 type, 1524 raw_wakeup, 1525 attribution, 1526 suspend_quality, 1527 backoff_state, 1528 backoff_reason, 1529 backoff_count, 1530 backoff_millis 1531 from wakeups`; 1532 const items = []; 1533 let labelOther = false; 1534 for (; it.valid(); it.next()) { 1535 labelOther = true; 1536 this.addSliceTrack( 1537 ctx, 1538 `Wakeup ${it.item}`, 1539 `${sqlPrefix} where item="${it.item}"`, 1540 groupName, 1541 WAKEUPS_COLUMNS, 1542 ); 1543 items.push(it.item); 1544 } 1545 this.addSliceTrack( 1546 ctx, 1547 labelOther ? 'Other wakeups' : 'Wakeups', 1548 `${sqlPrefix} where item not in ('${items.join("','")}')`, 1549 groupName, 1550 WAKEUPS_COLUMNS, 1551 ); 1552 } 1553 1554 async addHighCpu( 1555 ctx: PluginContextTrace, 1556 features: Set<string>, 1557 ): Promise<void> { 1558 if (!features.has('atom.cpu_cycles_per_uid_cluster')) { 1559 return; 1560 } 1561 const groupName = 'CPU per UID (major users)'; 1562 1563 const e = ctx.engine; 1564 1565 await e.query(HIGH_CPU); 1566 const result = await e.query( 1567 `select distinct pkg, cluster from high_cpu where value > 10 order by 1, 2`, 1568 ); 1569 const it = result.iter({pkg: 'str', cluster: 'str'}); 1570 for (; it.valid(); it.next()) { 1571 this.addCounterTrack( 1572 ctx, 1573 `CPU (${it.cluster}): ${it.pkg}`, 1574 `select ts, value from high_cpu where pkg = "${it.pkg}" and cluster="${it.cluster}"`, 1575 groupName, 1576 {yOverrideMaximum: 100, unit: '%'}, 1577 ); 1578 } 1579 } 1580 1581 async addBluetooth( 1582 ctx: PluginContextTrace, 1583 features: Set<string>, 1584 ): Promise<void> { 1585 if ( 1586 !Array.from(features.values()).some( 1587 (f) => f.startsWith('atom.bluetooth_') || f.startsWith('atom.ble_'), 1588 ) 1589 ) { 1590 return; 1591 } 1592 const groupName = 'Bluetooth'; 1593 this.addSliceTrack( 1594 ctx, 1595 'BLE Scans (opportunistic)', 1596 bleScanQuery('opportunistic'), 1597 groupName, 1598 ); 1599 this.addSliceTrack( 1600 ctx, 1601 'BLE Scans (filtered)', 1602 bleScanQuery('filtered'), 1603 groupName, 1604 ); 1605 this.addSliceTrack( 1606 ctx, 1607 'BLE Scans (unfiltered)', 1608 bleScanQuery('not filtered'), 1609 groupName, 1610 ); 1611 this.addSliceTrack(ctx, 'BLE Scan Results', BLE_RESULTS, groupName); 1612 this.addSliceTrack(ctx, 'Connections (ACL)', BT_CONNS_ACL, groupName); 1613 this.addSliceTrack(ctx, 'Connections (SCO)', BT_CONNS_SCO, groupName); 1614 this.addSliceTrack( 1615 ctx, 1616 'Link-level Events', 1617 BT_LINK_LEVEL_EVENTS, 1618 groupName, 1619 BT_LINK_LEVEL_EVENTS_COLUMNS, 1620 ); 1621 this.addSliceTrack(ctx, 'A2DP Audio', BT_A2DP_AUDIO, groupName); 1622 this.addSliceTrack( 1623 ctx, 1624 'Bytes Transferred (L2CAP/RFCOMM)', 1625 BT_BYTES, 1626 groupName, 1627 ); 1628 await ctx.engine.query(BT_ACTIVITY); 1629 this.addCounterTrack( 1630 ctx, 1631 'ACL Classic Active Count', 1632 'select ts, dur, acl_active_count as value from bt_activity', 1633 groupName, 1634 ); 1635 this.addCounterTrack( 1636 ctx, 1637 'ACL Classic Sniff Count', 1638 'select ts, dur, acl_sniff_count as value from bt_activity', 1639 groupName, 1640 ); 1641 this.addCounterTrack( 1642 ctx, 1643 'ACL BLE Count', 1644 'select ts, dur, acl_ble_count as value from bt_activity', 1645 groupName, 1646 ); 1647 this.addCounterTrack( 1648 ctx, 1649 'Advertising Instance Count', 1650 'select ts, dur, advertising_count as value from bt_activity', 1651 groupName, 1652 ); 1653 this.addCounterTrack( 1654 ctx, 1655 'LE Scan Duty Cycle Maximum', 1656 'select ts, dur, le_scan_duty_cycle as value from bt_activity', 1657 groupName, 1658 {unit: '%'}, 1659 ); 1660 this.addSliceTrack( 1661 ctx, 1662 'Inquiry Active', 1663 "select ts, dur, 'Active' as name from bt_activity where inquiry_active", 1664 groupName, 1665 ); 1666 this.addSliceTrack( 1667 ctx, 1668 'SCO Active', 1669 "select ts, dur, 'Active' as name from bt_activity where sco_active", 1670 groupName, 1671 ); 1672 this.addSliceTrack( 1673 ctx, 1674 'A2DP Active', 1675 "select ts, dur, 'Active' as name from bt_activity where a2dp_active", 1676 groupName, 1677 ); 1678 this.addSliceTrack( 1679 ctx, 1680 'LE Audio Active', 1681 "select ts, dur, 'Active' as name from bt_activity where le_audio_active", 1682 groupName, 1683 ); 1684 this.addCounterTrack( 1685 ctx, 1686 'Controller Idle Time', 1687 'select ts, dur, controller_idle_pct as value from bt_activity', 1688 groupName, 1689 {yRangeSharingKey: 'bt_controller_time', unit: '%'}, 1690 ); 1691 this.addCounterTrack( 1692 ctx, 1693 'Controller TX Time', 1694 'select ts, dur, controller_tx_pct as value from bt_activity', 1695 groupName, 1696 {yRangeSharingKey: 'bt_controller_time', unit: '%'}, 1697 ); 1698 this.addCounterTrack( 1699 ctx, 1700 'Controller RX Time', 1701 'select ts, dur, controller_rx_pct as value from bt_activity', 1702 groupName, 1703 {yRangeSharingKey: 'bt_controller_time', unit: '%'}, 1704 ); 1705 this.addSliceTrack( 1706 ctx, 1707 'Quality reports', 1708 BT_QUALITY_REPORTS, 1709 groupName, 1710 BT_QUALITY_REPORTS_COLUMNS, 1711 ); 1712 this.addSliceTrack( 1713 ctx, 1714 'RSSI Reports', 1715 BT_RSSI_REPORTS, 1716 groupName, 1717 BT_RSSI_REPORTS_COLUMNS, 1718 ); 1719 this.addSliceTrack( 1720 ctx, 1721 'HAL Crashes', 1722 BT_HAL_CRASHES, 1723 groupName, 1724 BT_HAL_CRASHES_COLUMNS, 1725 ); 1726 this.addSliceTrack( 1727 ctx, 1728 'Code Path Counter', 1729 BT_CODE_PATH_COUNTER, 1730 groupName, 1731 BT_CODE_PATH_COUNTER_COLUMNS, 1732 ); 1733 } 1734 1735 async addContainedTraces( 1736 ctx: PluginContextTrace, 1737 containedTraces: ContainedTrace[], 1738 ): Promise<void> { 1739 const bySubscription = new Map<string, ContainedTrace[]>(); 1740 for (const trace of containedTraces) { 1741 if (!bySubscription.has(trace.subscription)) { 1742 bySubscription.set(trace.subscription, []); 1743 } 1744 bySubscription.get(trace.subscription)!.push(trace); 1745 } 1746 1747 bySubscription.forEach((traces, subscription) => 1748 this.addSliceTrack( 1749 ctx, 1750 subscription, 1751 traces 1752 .map( 1753 (t) => `SELECT 1754 CAST(${t.ts} * 1e6 AS int) AS ts, 1755 CAST(${t.dur} * 1e6 AS int) AS dur, 1756 '${t.trigger === '' ? 'Trace' : t.trigger}' AS name, 1757 'http://go/trace-uuid/${t.uuid}' AS link 1758 `, 1759 ) 1760 .join(' UNION ALL '), 1761 'Other traces', 1762 ['link'], 1763 ), 1764 ); 1765 } 1766 1767 async findFeatures(e: Engine): Promise<Set<string>> { 1768 const features = new Set<string>(); 1769 1770 const addFeatures = async (q: string) => { 1771 const result = await e.query(q); 1772 const it = result.iter({feature: 'str'}); 1773 for (; it.valid(); it.next()) { 1774 features.add(it.feature); 1775 } 1776 }; 1777 1778 await addFeatures(` 1779 select distinct 'atom.' || s.name as feature 1780 from track t join slice s on t.id = s.track_id 1781 where t.name = 'Statsd Atoms'`); 1782 1783 await addFeatures(` 1784 select distinct 1785 case when name like '%wlan%' then 'net.wifi' 1786 when name like '%rmnet%' then 'net.modem' 1787 else 'net.other' 1788 end as feature 1789 from track 1790 where name like '%Transmitted' or name like '%Received'`); 1791 1792 await addFeatures(` 1793 select distinct 'track.' || lower(name) as feature 1794 from track where name in ('RIL', 'suspend_backoff') or name like 'battery_stats.%'`); 1795 1796 await addFeatures(` 1797 select distinct 'track.battery_stats.*' as feature 1798 from track where name like 'battery_stats.%'`); 1799 1800 return features; 1801 } 1802 1803 async addTracks(ctx: PluginContextTrace): Promise<void> { 1804 const features: Set<string> = await this.findFeatures(ctx.engine); 1805 1806 const containedTraces = (ctx.openerPluginArgs?.containedTraces ?? 1807 []) as ContainedTrace[]; 1808 1809 await this.addNetworkSummary(ctx, features), 1810 await this.addModemDetail(ctx, features); 1811 await this.addKernelWakelocks(ctx, features); 1812 await this.addWakeups(ctx, features); 1813 await this.addDeviceState(ctx, features); 1814 await this.addHighCpu(ctx, features); 1815 await this.addBluetooth(ctx, features); 1816 await this.addContainedTraces(ctx, containedTraces); 1817 } 1818 1819 async onTraceLoad(ctx: PluginContextTrace): Promise<void> { 1820 await this.addTracks(ctx); 1821 } 1822} 1823 1824export const plugin: PluginDescriptor = { 1825 pluginId: 'dev.perfetto.AndroidLongBatteryTracing', 1826 plugin: AndroidLongBatteryTracing, 1827}; 1828