1 /*
2  * Copyright (C) 2022 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 at
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  */
16 
17 #include "src/trace_processor/importers/android_bugreport/android_dumpstate_reader.h"
18 
19 #include <cstddef>
20 #include <cstdint>
21 #include <utility>
22 #include <vector>
23 
24 #include "perfetto/base/status.h"
25 #include "perfetto/ext/base/string_view.h"
26 #include "protos/perfetto/common/builtin_clock.pbzero.h"
27 #include "src/trace_processor/importers/android_bugreport/android_battery_stats_reader.h"
28 #include "src/trace_processor/importers/android_bugreport/android_log_reader.h"
29 #include "src/trace_processor/importers/common/clock_tracker.h"
30 #include "src/trace_processor/storage/trace_storage.h"
31 #include "src/trace_processor/types/trace_processor_context.h"
32 #include "src/trace_processor/util/status_macros.h"
33 
34 namespace perfetto::trace_processor {
35 
AndroidDumpstateReader(TraceProcessorContext * context,int32_t year,std::vector<TimestampedAndroidLogEvent> logcat_events)36 AndroidDumpstateReader::AndroidDumpstateReader(
37     TraceProcessorContext* context,
38     int32_t year,
39     std::vector<TimestampedAndroidLogEvent> logcat_events)
40     : context_(context),
41       battery_stats_reader_(context),
42       log_reader_(context, year, std::move(logcat_events)) {}
43 
44 AndroidDumpstateReader::~AndroidDumpstateReader() = default;
45 
ParseLine(base::StringView line)46 base::Status AndroidDumpstateReader::ParseLine(base::StringView line) {
47   context_->clock_tracker->SetTraceTimeClock(
48       protos::pbzero::BUILTIN_CLOCK_REALTIME);
49 
50   // Dumpstate is organized in a two level hierarchy, beautifully flattened into
51   // one text file with load bearing ----- markers:
52   // 1. Various dumpstate sections, examples:
53   // ```
54   //   ------ DUMPSYS CRITICAL (/system/bin/dumpsys) ------
55   //   ...
56   //   ------ SYSTEM LOG (logcat -v threadtime -v printable -v uid) ------
57   //   ...
58   //   ------ IPTABLES (iptables -L -nvx) ------
59   //   ...
60   //   ------ DUMPSYS HIGH (/system/bin/dumpsys) ------
61   //   ...
62   //   ------ DUMPSYS (/system/bin/dumpsys) ------
63   // ```
64   //
65   // 2. Within the "------ DUMPSYS" section (note dumpsys != dumpstate), there
66   //    are multiple services. Note that there are at least 3 DUMPSYS sections
67   //    (CRITICAL, HIGH and default), with multiple services in each:
68   // ```
69   //    ------ DUMPSYS (/system/bin/dumpsys) ------
70   // DUMP OF SERVICE activity:
71   // ...
72   // ---------------------------------------------------------------------------
73   // DUMP OF SERVICE input_method:
74   // ...
75   // ---------------------------------------------------------------------------
76   // ```
77   // Here we put each line in a dedicated table, android_dumpstate, keeping
78   // track of the dumpstate `section` and dumpsys `service`.
79   static constexpr size_t npos = base::StringView::npos;
80   if (line.StartsWith("------ ") && line.EndsWith(" ------")) {
81     // These lines mark the beginning and end of dumpstate sections:
82     // ------ DUMPSYS CRITICAL (/system/bin/dumpsys) ------
83     // ------ 0.356s was the duration of 'DUMPSYS CRITICAL' ------
84     base::StringView section = line.substr(7);
85     section = section.substr(0, section.size() - 7);
86     bool end_marker = section.find("was the duration of") != npos;
87     current_service_id_ = StringId::Null();
88     if (end_marker) {
89       current_section_id_ = StringId::Null();
90     } else {
91       current_section_id_ = context_->storage->InternString(section);
92       current_section_ = Section::kOther;
93       if (section.StartsWith("DUMPSYS")) {
94         current_section_ = Section::kDumpsys;
95       } else if (section.StartsWith("SYSTEM LOG") ||
96                  section.StartsWith("EVENT LOG") ||
97                  section.StartsWith("RADIO LOG")) {
98         // KERNEL LOG is deliberately omitted because SYSTEM LOG is a
99         // superset. KERNEL LOG contains all dupes.
100         current_section_ = Section::kLog;
101       } else if (section.StartsWith("BLOCK STAT")) {
102         // Coalesce all the block stats into one section. Otherwise they
103         // pollute the table with one section per block device.
104         current_section_id_ = context_->storage->InternString("BLOCK STAT");
105       } else if (section.StartsWith("CHECKIN BATTERYSTATS")) {
106         current_section_ = Section::kBatteryStats;
107       }
108     }
109     return base::OkStatus();
110   }
111   // Skip end marker lines for dumpsys sections.
112   if (current_section_ == Section::kDumpsys && line.StartsWith("--------- ") &&
113       line.find("was the duration of dumpsys") != npos) {
114     current_service_id_ = StringId::Null();
115     return base::OkStatus();
116   }
117   if (current_section_ == Section::kDumpsys && current_service_id_.is_null() &&
118       line.StartsWith("----------------------------------------------")) {
119     return base::OkStatus();
120   }
121   // if we get the start of a standalone battery stats checkin, then set the
122   // section and deliberately fall though so we we can parse the line.
123   if (line.StartsWith("9,0,i,vers,")) {
124     current_section_ = Section::kBatteryStats;
125   }
126   if (current_section_ == Section::kDumpsys &&
127       line.StartsWith("DUMP OF SERVICE")) {
128     // DUMP OF SERVICE [CRITICAL|HIGH] ServiceName:
129     base::StringView svc = line.substr(line.rfind(' ') + 1);
130     svc = svc.substr(0, svc.size() - 1);
131     current_service_id_ = context_->storage->InternString(svc);
132   } else if (current_section_ == Section::kLog) {
133     RETURN_IF_ERROR(log_reader_.ParseLine(line));
134   } else if (current_section_ == Section::kBatteryStats) {
135     RETURN_IF_ERROR(battery_stats_reader_.ParseLine(line));
136   }
137 
138   // Append the line to the android_dumpstate table.
139   context_->storage->mutable_android_dumpstate_table()->Insert(
140       {current_section_id_, current_service_id_,
141        context_->storage->InternString(line)});
142 
143   return base::OkStatus();
144 }
145 
EndOfStream(base::StringView)146 void AndroidDumpstateReader::EndOfStream(base::StringView) {}
147 
148 }  // namespace perfetto::trace_processor
149