From e8c4fac37eb3fd0134b8bb843c2901dd325be249 Mon Sep 17 00:00:00 2001 From: Chris Phlipot Date: Thu, 5 Dec 2024 13:10:16 -0800 Subject: [PATCH] tp: parse standalone batterystats checkin files Add the capability to parse standalone battery stats checkin files so if a user runs `adb shell dumpsys batterystats -c > checkin.csv`, they would now be able to open checkin.csv directly This has a few benefits: - battery stats checkin is much smaller and can be collected orders of magnitude faster than a full bugreport - It allows for creation of much simpler and more readable synthetic trace input for processor diff tests. Change-Id: I185f2640c180977eb8439da68a9267ec28bd133b --- .../forwarding_trace_parser.cc | 2 +- .../android_dumpstate_reader.cc | 10 +++++ .../android_dumpstate_reader.h | 7 +-- src/trace_processor/trace_processor_impl.cc | 3 ++ src/trace_processor/util/trace_type.cc | 4 ++ .../diff_tests/include_index.py | 3 ++ .../parser/android/tests_dumpstate.py | 44 +++++++++++++++++++ 7 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 test/trace_processor/diff_tests/parser/android/tests_dumpstate.py diff --git a/src/trace_processor/forwarding_trace_parser.cc b/src/trace_processor/forwarding_trace_parser.cc index 2bf7958b1f..de11f8f264 100644 --- a/src/trace_processor/forwarding_trace_parser.cc +++ b/src/trace_processor/forwarding_trace_parser.cc @@ -68,6 +68,7 @@ std::optional GetMinimumSortingMode( case kFuchsiaTraceType: case kZipFile: case kTarTraceType: + case kAndroidDumpstateTraceType: case kAndroidLogcatTraceType: case kGeckoTraceType: case kArtMethodTraceType: @@ -78,7 +79,6 @@ std::optional GetMinimumSortingMode( case kSymbolsTraceType: return ConvertSortingMode(context.config.sorting_mode); - case kAndroidDumpstateTraceType: case kAndroidBugreportTraceType: PERFETTO_FATAL( "This trace type should be handled at the ZipParser level"); diff --git a/src/trace_processor/importers/android_bugreport/android_dumpstate_reader.cc b/src/trace_processor/importers/android_bugreport/android_dumpstate_reader.cc index 6ae42cecb1..9ea1944ae6 100644 --- a/src/trace_processor/importers/android_bugreport/android_dumpstate_reader.cc +++ b/src/trace_processor/importers/android_bugreport/android_dumpstate_reader.cc @@ -23,8 +23,10 @@ #include "perfetto/base/status.h" #include "perfetto/ext/base/string_view.h" +#include "protos/perfetto/common/builtin_clock.pbzero.h" #include "src/trace_processor/importers/android_bugreport/android_battery_stats_reader.h" #include "src/trace_processor/importers/android_bugreport/android_log_reader.h" +#include "src/trace_processor/importers/common/clock_tracker.h" #include "src/trace_processor/storage/trace_storage.h" #include "src/trace_processor/types/trace_processor_context.h" #include "src/trace_processor/util/status_macros.h" @@ -42,6 +44,9 @@ AndroidDumpstateReader::AndroidDumpstateReader( AndroidDumpstateReader::~AndroidDumpstateReader() = default; base::Status AndroidDumpstateReader::ParseLine(base::StringView line) { + context_->clock_tracker->SetTraceTimeClock( + protos::pbzero::BUILTIN_CLOCK_REALTIME); + // Dumpstate is organized in a two level hierarchy, beautifully flattened into // one text file with load bearing ----- markers: // 1. Various dumpstate sections, examples: @@ -113,6 +118,11 @@ base::Status AndroidDumpstateReader::ParseLine(base::StringView line) { line.StartsWith("----------------------------------------------")) { return base::OkStatus(); } + // if we get the start of a standalone battery stats checkin, then set the + // section and deliberately fall though so we we can parse the line. + if (line.StartsWith("9,0,i,vers,")) { + current_section_ = Section::kBatteryStats; + } if (current_section_ == Section::kDumpsys && line.StartsWith("DUMP OF SERVICE")) { // DUMP OF SERVICE [CRITICAL|HIGH] ServiceName: diff --git a/src/trace_processor/importers/android_bugreport/android_dumpstate_reader.h b/src/trace_processor/importers/android_bugreport/android_dumpstate_reader.h index 65ad4eef2a..9921e36a78 100644 --- a/src/trace_processor/importers/android_bugreport/android_dumpstate_reader.h +++ b/src/trace_processor/importers/android_bugreport/android_dumpstate_reader.h @@ -31,9 +31,10 @@ class AndroidDumpstateReader : public ChunkedLineReader { public: // Create a reader that will only forward events that are not present in the // given list. - AndroidDumpstateReader(TraceProcessorContext* context, - int32_t year, - std::vector logcat_events); + AndroidDumpstateReader( + TraceProcessorContext* context, + int32_t year = 0, + std::vector logcat_events = {}); ~AndroidDumpstateReader() override; base::Status ParseLine(base::StringView line) override; diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc index 8b2efdd579..8fe29d8c0e 100644 --- a/src/trace_processor/trace_processor_impl.cc +++ b/src/trace_processor/trace_processor_impl.cc @@ -46,6 +46,7 @@ #include "perfetto/trace_processor/iterator.h" #include "perfetto/trace_processor/trace_blob_view.h" #include "perfetto/trace_processor/trace_processor.h" +#include "src/trace_processor/importers/android_bugreport/android_dumpstate_reader.h" #include "src/trace_processor/importers/android_bugreport/android_dumpstate_event_parser_impl.h" #include "src/trace_processor/importers/android_bugreport/android_log_event_parser_impl.h" #include "src/trace_processor/importers/android_bugreport/android_log_reader.h" @@ -404,6 +405,8 @@ std::pair GetTraceTimestampBoundsNs( TraceProcessorImpl::TraceProcessorImpl(const Config& cfg) : TraceProcessorStorageImpl(cfg), config_(cfg) { + context_.reader_registry->RegisterTraceReader( + kAndroidDumpstateTraceType); context_.android_dumpstate_event_parser = std::make_unique(&context_); diff --git a/src/trace_processor/util/trace_type.cc b/src/trace_processor/util/trace_type.cc index bec7279f55..37b174d5ff 100644 --- a/src/trace_processor/util/trace_type.cc +++ b/src/trace_processor/util/trace_type.cc @@ -248,6 +248,10 @@ TraceType GuessTraceType(const uint8_t* data, size_t size) { if (base::StartsWith(start, "\x0a")) return kProtoTraceType; + if (base::StartsWith(start, "9,0,i,vers,")) { + return kAndroidDumpstateTraceType; + } + return kUnknownTraceType; } diff --git a/test/trace_processor/diff_tests/include_index.py b/test/trace_processor/diff_tests/include_index.py index 7593d35b15..1e106c437b 100644 --- a/test/trace_processor/diff_tests/include_index.py +++ b/test/trace_processor/diff_tests/include_index.py @@ -50,6 +50,7 @@ from diff_tests.parser.android.tests import AndroidParser from diff_tests.parser.android.tests_android_input_event import AndroidInputEvent from diff_tests.parser.android.tests_bugreport import AndroidBugreport +from diff_tests.parser.android.tests_dumpstate import AndroidDumpstate from diff_tests.parser.android.tests_games import AndroidGames from diff_tests.parser.android.tests_inputmethod_clients import InputMethodClients from diff_tests.parser.android.tests_inputmethod_manager_service import InputMethodManagerService @@ -165,6 +166,8 @@ def fetch_all_diff_tests(index_path: str) -> List['testing.TestCase']: parser_tests = [ *AndroidBugreport(index_path, 'parser/android', 'AndroidBugreport').fetch(), + *AndroidDumpstate(index_path, 'parser/android', + 'AndroidDumpstate').fetch(), *AndroidFs(index_path, 'parser/android_fs', 'AndroidFs').fetch(), *AndroidGames(index_path, 'parser/android', 'AndroidGames').fetch(), *AndroidParser(index_path, 'parser/android', 'AndroidParser').fetch(), diff --git a/test/trace_processor/diff_tests/parser/android/tests_dumpstate.py b/test/trace_processor/diff_tests/parser/android/tests_dumpstate.py new file mode 100644 index 0000000000..a8ec1a6711 --- /dev/null +++ b/test/trace_processor/diff_tests/parser/android/tests_dumpstate.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# Copyright (C) 2024 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License a +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from python.generators.diff_tests.testing import Csv +from python.generators.diff_tests.testing import DiffTestBlueprint +from python.generators.diff_tests.testing import TestSuite + + +EXAMPLE_CHECKIN = """9,0,i,vers,36,214,AP1A.240305.019.A1,AP2A.240805.005.S4 +9,hsp,0,10216,"com.android.chrome" +9,h,0:RESET:TIME:1732816430344 +9,h,0,Bl=100,Bs=d,Bh=g,Bp=n,Bt=251,Bv=4395,Bcc=4263,Mrc=0,Wrc=0,+r,+w,+s,+Wr,+S,+BP,Pcn=lte,Pss=3,+W,Wss=4,Wsp=compl,+Chtp,Gss=none,nrs=1,Etp=0 +""" + + +class AndroidDumpstate(TestSuite): + def test_android_dumpstate_standalone_battery_stats_checkin(self): + return DiffTestBlueprint( + trace=Csv(EXAMPLE_CHECKIN), + query=""" + SELECT + name, ts, value + FROM counter + JOIN counter_track ON counter.track_id = counter_track.id + WHERE + name = 'ScreenState' + AND classification = 'screen_state' + """, + out=Csv(""" + "name","ts","value" + "ScreenState",1732816430344000000,2.000000 + """))