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_bugreport_reader.h"
18
19 #include <algorithm>
20 #include <cstddef>
21 #include <cstdint>
22 #include <optional>
23 #include <string>
24 #include <vector>
25
26 #include "perfetto/base/status.h"
27 #include "perfetto/ext/base/string_utils.h"
28 #include "perfetto/ext/base/string_view.h"
29 #include "protos/perfetto/common/builtin_clock.pbzero.h"
30 #include "src/trace_processor/importers/android_bugreport/android_dumpstate_reader.h"
31 #include "src/trace_processor/importers/android_bugreport/android_log_reader.h"
32 #include "src/trace_processor/importers/common/clock_tracker.h"
33 #include "src/trace_processor/importers/common/trace_file_tracker.h"
34 #include "src/trace_processor/types/trace_processor_context.h"
35 #include "src/trace_processor/util/status_macros.h"
36 #include "src/trace_processor/util/trace_type.h"
37 #include "src/trace_processor/util/zip_reader.h"
38
39 namespace perfetto::trace_processor {
40 namespace {
41
42 using ZipFileVector = std::vector<util::ZipFile>;
43
IsBugReportFile(const util::ZipFile & zip)44 bool IsBugReportFile(const util::ZipFile& zip) {
45 return base::StartsWith(zip.name(), "bugreport-") &&
46 base::EndsWith(zip.name(), ".txt");
47 }
48
IsLogFile(const util::ZipFile & file)49 bool IsLogFile(const util::ZipFile& file) {
50 return base::StartsWith(file.name(), "FS/data/misc/logd/logcat") &&
51 !base::EndsWith(file.name(), "logcat.id");
52 }
53
54 // Extracts the year field from the bugreport-xxx.txt file name.
55 // This is because logcat events have only the month and day.
56 // This is obviously bugged for cases of bugreports collected across new year
57 // but we'll live with that.
ExtractYearFromBugReportFilename(const std::string & filename)58 std::optional<int32_t> ExtractYearFromBugReportFilename(
59 const std::string& filename) {
60 // Typical name: "bugreport-product-TP1A.220623.001-2022-06-24-16-24-37.txt".
61 auto year_str =
62 filename.substr(filename.size() - strlen("2022-12-31-23-59-00.txt"), 4);
63 return base::StringToInt32(year_str);
64 }
65
66 struct FindBugReportFileResult {
67 size_t file_index;
68 int32_t year;
69 };
70
FindBugReportFile(const ZipFileVector & files)71 std::optional<FindBugReportFileResult> FindBugReportFile(
72 const ZipFileVector& files) {
73 for (size_t i = 0; i < files.size(); ++i) {
74 if (!IsBugReportFile(files[i])) {
75 continue;
76 }
77 std::optional<int32_t> year =
78 ExtractYearFromBugReportFilename(files[i].name());
79 if (!year.has_value()) {
80 continue;
81 }
82
83 return FindBugReportFileResult{i, *year};
84 }
85
86 return std::nullopt;
87 }
88
89 } // namespace
90
91 // static
IsAndroidBugReport(const std::vector<util::ZipFile> & files)92 bool AndroidBugreportReader::IsAndroidBugReport(
93 const std::vector<util::ZipFile>& files) {
94 return FindBugReportFile(files).has_value();
95 }
96
97 // static
Parse(TraceProcessorContext * context,std::vector<util::ZipFile> files)98 util::Status AndroidBugreportReader::Parse(TraceProcessorContext* context,
99 std::vector<util::ZipFile> files) {
100 auto res = FindBugReportFile(files);
101 if (!res.has_value()) {
102 return base::ErrStatus("Not a bug report");
103 }
104
105 // Move the file to the end move it out of the list and pop the back.
106 std::swap(files[res->file_index], files.back());
107 auto id = context->trace_file_tracker->AddFile(files.back().name());
108 BugReportFile bug_report{id, res->year, std::move(files.back())};
109 files.pop_back();
110
111 std::set<LogFile> ordered_log_files;
112 for (size_t i = 0; i < files.size(); ++i) {
113 id = context->trace_file_tracker->AddFile(files[i].name());
114 // Set size in case we end up not parsing this file.
115 context->trace_file_tracker->SetSize(id, files[i].compressed_size());
116 if (!IsLogFile(files[i])) {
117 continue;
118 }
119
120 int64_t timestamp = files[i].GetDatetime();
121 ordered_log_files.insert(LogFile{id, timestamp, std::move(files[i])});
122 }
123
124 return AndroidBugreportReader(context, std::move(bug_report),
125 std::move(ordered_log_files))
126 .ParseImpl();
127 }
128
AndroidBugreportReader(TraceProcessorContext * context,BugReportFile bug_report,std::set<LogFile> ordered_log_files)129 AndroidBugreportReader::AndroidBugreportReader(
130 TraceProcessorContext* context,
131 BugReportFile bug_report,
132 std::set<LogFile> ordered_log_files)
133 : context_(context),
134 bug_report_(std::move(bug_report)),
135 ordered_log_files_(std::move(ordered_log_files)) {}
136
137 AndroidBugreportReader::~AndroidBugreportReader() = default;
138
ParseImpl()139 util::Status AndroidBugreportReader::ParseImpl() {
140 // All logs in Android bugreports use wall time (which creates problems
141 // in case of early boot events before NTP kicks in, which get emitted as
142 // 1970), but that is the state of affairs.
143 context_->clock_tracker->SetTraceTimeClock(
144 protos::pbzero::BUILTIN_CLOCK_REALTIME);
145
146 ASSIGN_OR_RETURN(std::vector<TimestampedAndroidLogEvent> logcat_events,
147 ParsePersistentLogcat());
148 return ParseDumpstateTxt(std::move(logcat_events));
149 }
150
ParseDumpstateTxt(std::vector<TimestampedAndroidLogEvent> logcat_events)151 base::Status AndroidBugreportReader::ParseDumpstateTxt(
152 std::vector<TimestampedAndroidLogEvent> logcat_events) {
153 context_->trace_file_tracker->StartParsing(bug_report_.id,
154 kAndroidDumpstateTraceType);
155 AndroidDumpstateReader reader(context_, bug_report_.year,
156 std::move(logcat_events));
157 base::Status status = bug_report_.file.DecompressLines(
158 [&](const std::vector<base::StringView>& lines) {
159 for (const base::StringView& line : lines) {
160 reader.ParseLine(line);
161 }
162 });
163 context_->trace_file_tracker->DoneParsing(
164 bug_report_.id, bug_report_.file.uncompressed_size());
165 return status;
166 }
167
168 base::StatusOr<std::vector<TimestampedAndroidLogEvent>>
ParsePersistentLogcat()169 AndroidBugreportReader::ParsePersistentLogcat() {
170 BufferingAndroidLogReader log_reader(context_, bug_report_.year);
171
172 // Push all events into the AndroidLogParser. It will take care of string
173 // interning into the pool. Appends entries into `log_events`.
174 for (const auto& log_file : ordered_log_files_) {
175 context_->trace_file_tracker->StartParsing(log_file.id,
176 kAndroidLogcatTraceType);
177 RETURN_IF_ERROR(log_file.file.DecompressLines(
178 [&](const std::vector<base::StringView>& lines) {
179 for (const auto& line : lines) {
180 log_reader.ParseLine(line);
181 }
182 }));
183 context_->trace_file_tracker->DoneParsing(
184 log_file.id, log_file.file.uncompressed_size());
185 }
186
187 return std::move(log_reader).ConsumeBufferedEvents();
188 }
189
190 } // namespace perfetto::trace_processor
191