• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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