1 /*
2 * Copyright (C) 2024 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/zip/zip_trace_reader.h"
18
19 #include <algorithm>
20 #include <cinttypes>
21 #include <cstdint>
22 #include <cstring>
23 #include <memory>
24 #include <string>
25 #include <tuple>
26 #include <utility>
27 #include <vector>
28
29 #include "perfetto/base/logging.h"
30 #include "perfetto/base/status.h"
31 #include "perfetto/ext/base/status_or.h"
32 #include "perfetto/trace_processor/trace_blob.h"
33 #include "perfetto/trace_processor/trace_blob_view.h"
34 #include "src/trace_processor/forwarding_trace_parser.h"
35 #include "src/trace_processor/importers/android_bugreport/android_bugreport_parser.h"
36 #include "src/trace_processor/importers/proto/proto_trace_tokenizer.h"
37 #include "src/trace_processor/types/trace_processor_context.h"
38 #include "src/trace_processor/util/status_macros.h"
39 #include "src/trace_processor/util/trace_type.h"
40
41 namespace perfetto {
42 namespace trace_processor {
43 namespace {
44
45 // Proto traces should always parsed first as they might contains clock sync
46 // data needed to correctly parse other traces.
47 // The rest of the types are sorted by position in the enum but this is not
48 // something users should rely on.
49 // TODO(carlscab): Proto traces with just ModuleSymbols packets should be an
50 // exception. We actually need those are the very end (once whe have all the
51 // Frames). Alternatively we could build a map address -> symbol during
52 // tokenization and use this during parsing to resolve symbols.
CompareTraceType(TraceType lhs,TraceType rhs)53 bool CompareTraceType(TraceType lhs, TraceType rhs) {
54 if (rhs == TraceType::kProtoTraceType) {
55 return false;
56 }
57 if (lhs == TraceType::kProtoTraceType) {
58 return true;
59 }
60 return lhs < rhs;
61 }
62
HasSymbols(const TraceBlobView & blob)63 bool HasSymbols(const TraceBlobView& blob) {
64 bool has_symbols = false;
65 ProtoTraceTokenizer().Tokenize(blob.copy(), [&](TraceBlobView raw) {
66 protos::pbzero::TracePacket::Decoder packet(raw.data(), raw.size());
67 has_symbols = packet.has_module_symbols();
68 return base::ErrStatus("break");
69 });
70 return has_symbols;
71 }
72
73 } // namespace
74
ZipTraceReader(TraceProcessorContext * context)75 ZipTraceReader::ZipTraceReader(TraceProcessorContext* context)
76 : context_(context) {}
77 ZipTraceReader::~ZipTraceReader() = default;
78
operator <(const Entry & rhs) const79 bool ZipTraceReader::Entry::operator<(const Entry& rhs) const {
80 // Traces with symbols should be the last ones to be read.
81 if (has_symbols) {
82 return false;
83 }
84 if (rhs.has_symbols) {
85 return true;
86 }
87 if (CompareTraceType(trace_type, rhs.trace_type)) {
88 return true;
89 }
90 if (CompareTraceType(rhs.trace_type, trace_type)) {
91 return false;
92 }
93 return std::tie(name, index) < std::tie(rhs.name, rhs.index);
94 }
95
Parse(TraceBlobView blob)96 util::Status ZipTraceReader::Parse(TraceBlobView blob) {
97 zip_reader_.Parse(blob.data(), blob.size());
98 return base::OkStatus();
99 }
100
NotifyEndOfFile()101 void ZipTraceReader::NotifyEndOfFile() {
102 base::Status status = NotifyEndOfFileImpl();
103 if (!status.ok()) {
104 PERFETTO_ELOG("ZipTraceReader failed: %s", status.c_message());
105 }
106 }
107
NotifyEndOfFileImpl()108 base::Status ZipTraceReader::NotifyEndOfFileImpl() {
109 std::vector<util::ZipFile> files = zip_reader_.TakeFiles();
110
111 // Android bug reports are ZIP files and its files do not get handled
112 // separately.
113 if (AndroidBugreportParser::IsAndroidBugReport(files)) {
114 return AndroidBugreportParser::Parse(context_, std::move(files));
115 }
116
117 base::StatusOr<std::vector<Entry>> entries = ExtractEntries(std::move(files));
118 if (!entries.ok()) {
119 return entries.status();
120 }
121 std::sort(entries->begin(), entries->end());
122
123 for (Entry& e : *entries) {
124 parsers_.push_back(std::make_unique<ForwardingTraceParser>(context_));
125 auto& parser = *parsers_.back();
126 RETURN_IF_ERROR(parser.Parse(std::move(e.uncompressed_data)));
127 parser.NotifyEndOfFile();
128 }
129 return base::OkStatus();
130 }
131
132 base::StatusOr<std::vector<ZipTraceReader::Entry>>
ExtractEntries(std::vector<util::ZipFile> files) const133 ZipTraceReader::ExtractEntries(std::vector<util::ZipFile> files) const {
134 // TODO(carlsacab): There is a lot of unnecessary copying going on here.
135 // ZipTraceReader can directly parse the ZIP file and given that we know the
136 // decompressed size we could directly decompress into TraceBlob chunks and
137 // send them to the tokenizer.
138 std::vector<Entry> entries;
139 std::vector<uint8_t> buffer;
140 for (size_t i = 0; i < files.size(); ++i) {
141 const util::ZipFile& zip_file = files[i];
142 Entry entry;
143 entry.name = zip_file.name();
144 entry.index = i;
145 RETURN_IF_ERROR(files[i].Decompress(&buffer));
146 entry.uncompressed_data =
147 TraceBlobView(TraceBlob::CopyFrom(buffer.data(), buffer.size()));
148 entry.trace_type = GuessTraceType(entry.uncompressed_data.data(),
149 entry.uncompressed_data.size());
150 entry.has_symbols = entry.trace_type == TraceType::kProtoTraceType &&
151 HasSymbols(entry.uncompressed_data);
152 entries.push_back(std::move(entry));
153 }
154 return std::move(entries);
155 }
156
157 } // namespace trace_processor
158 } // namespace perfetto
159