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