1 /*
2 * Copyright (C) 2019 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/forwarding_trace_parser.h"
18
19 #include "perfetto/base/logging.h"
20 #include "perfetto/ext/base/string_utils.h"
21 #include "src/trace_processor/importers/common/process_tracker.h"
22 #include "src/trace_processor/importers/proto/proto_trace_parser.h"
23 #include "src/trace_processor/importers/proto/proto_trace_reader.h"
24 #include "src/trace_processor/sorter/trace_sorter.h"
25
26 namespace perfetto {
27 namespace trace_processor {
28 namespace {
29
30 const char kNoZlibErr[] =
31 "Cannot open compressed trace. zlib not enabled in the build config";
32
isspace(unsigned char c)33 inline bool isspace(unsigned char c) {
34 return ::isspace(c);
35 }
36
RemoveWhitespace(std::string str)37 std::string RemoveWhitespace(std::string str) {
38 str.erase(std::remove_if(str.begin(), str.end(), isspace), str.end());
39 return str;
40 }
41
ConvertSortingMode(SortingMode sorting_mode)42 TraceSorter::SortingMode ConvertSortingMode(SortingMode sorting_mode) {
43 switch (sorting_mode) {
44 case SortingMode::kDefaultHeuristics:
45 case SortingMode::kForceFlushPeriodWindowedSort:
46 return TraceSorter::SortingMode::kDefault;
47 case SortingMode::kForceFullSort:
48 return TraceSorter::SortingMode::kFullSort;
49 }
50 PERFETTO_FATAL("For GCC");
51 }
52
53 // Fuchsia traces have a magic number as documented here:
54 // https://fuchsia.googlesource.com/fuchsia/+/HEAD/docs/development/tracing/trace-format/README.md#magic-number-record-trace-info-type-0
55 constexpr uint64_t kFuchsiaMagicNumber = 0x0016547846040010;
56
57 } // namespace
58
ForwardingTraceParser(TraceProcessorContext * context)59 ForwardingTraceParser::ForwardingTraceParser(TraceProcessorContext* context)
60 : context_(context) {}
61
~ForwardingTraceParser()62 ForwardingTraceParser::~ForwardingTraceParser() {}
63
Parse(TraceBlobView blob)64 util::Status ForwardingTraceParser::Parse(TraceBlobView blob) {
65 // If this is the first Parse() call, guess the trace type and create the
66 // appropriate parser.
67 if (!reader_) {
68 TraceType trace_type;
69 {
70 auto scoped_trace = context_->storage->TraceExecutionTimeIntoStats(
71 stats::guess_trace_type_duration_ns);
72 trace_type = GuessTraceType(blob.data(), blob.size());
73 context_->trace_type = trace_type;
74 }
75 switch (trace_type) {
76 case kJsonTraceType: {
77 PERFETTO_DLOG("JSON trace detected");
78 if (context_->json_trace_tokenizer && context_->json_trace_parser) {
79 reader_ = std::move(context_->json_trace_tokenizer);
80
81 // JSON traces have no guarantees about the order of events in them.
82 context_->sorter.reset(
83 new TraceSorter(context_, std::move(context_->json_trace_parser),
84 TraceSorter::SortingMode::kFullSort));
85 } else {
86 return util::ErrStatus("JSON support is disabled");
87 }
88 break;
89 }
90 case kProtoTraceType: {
91 PERFETTO_DLOG("Proto trace detected");
92 auto sorting_mode = ConvertSortingMode(context_->config.sorting_mode);
93 reader_.reset(new ProtoTraceReader(context_));
94 context_->sorter.reset(new TraceSorter(
95 context_,
96 std::unique_ptr<TraceParser>(new ProtoTraceParser(context_)),
97 sorting_mode));
98 context_->process_tracker->SetPidZeroIsUpidZeroIdleProcess();
99 break;
100 }
101 case kNinjaLogTraceType: {
102 PERFETTO_DLOG("Ninja log detected");
103 if (context_->ninja_log_parser) {
104 reader_ = std::move(context_->ninja_log_parser);
105 break;
106 }
107 return util::ErrStatus("Ninja support is disabled");
108 }
109 case kFuchsiaTraceType: {
110 PERFETTO_DLOG("Fuchsia trace detected");
111 if (context_->fuchsia_trace_parser &&
112 context_->fuchsia_trace_tokenizer) {
113 reader_ = std::move(context_->fuchsia_trace_tokenizer);
114
115 // Fuschia traces can have massively out of order events.
116 context_->sorter.reset(new TraceSorter(
117 context_, std::move(context_->fuchsia_trace_parser),
118 TraceSorter::SortingMode::kFullSort));
119 } else {
120 return util::ErrStatus("Fuchsia support is disabled");
121 }
122 break;
123 }
124 case kSystraceTraceType:
125 PERFETTO_DLOG("Systrace trace detected");
126 context_->process_tracker->SetPidZeroIsUpidZeroIdleProcess();
127 if (context_->systrace_trace_parser) {
128 reader_ = std::move(context_->systrace_trace_parser);
129 break;
130 } else {
131 return util::ErrStatus("Systrace support is disabled");
132 }
133 case kGzipTraceType:
134 case kCtraceTraceType:
135 if (trace_type == kGzipTraceType) {
136 PERFETTO_DLOG("gzip trace detected");
137 } else {
138 PERFETTO_DLOG("ctrace trace detected");
139 }
140 if (context_->gzip_trace_parser) {
141 reader_ = std::move(context_->gzip_trace_parser);
142 break;
143 } else {
144 return util::ErrStatus(kNoZlibErr);
145 }
146 case kAndroidBugreportTraceType:
147 if (context_->android_bugreport_parser) {
148 reader_ = std::move(context_->android_bugreport_parser);
149 break;
150 }
151 return util::ErrStatus("Android Bugreport support is disabled. %s",
152 kNoZlibErr);
153 case kUnknownTraceType:
154 // If renaming this error message don't remove the "(ERR:fmt)" part.
155 // The UI's error_dialog.ts uses it to make the dialog more graceful.
156 return util::ErrStatus("Unknown trace type provided (ERR:fmt)");
157 }
158 }
159
160 return reader_->Parse(std::move(blob));
161 }
162
NotifyEndOfFile()163 void ForwardingTraceParser::NotifyEndOfFile() {
164 reader_->NotifyEndOfFile();
165 }
166
GuessTraceType(const uint8_t * data,size_t size)167 TraceType GuessTraceType(const uint8_t* data, size_t size) {
168 if (size == 0)
169 return kUnknownTraceType;
170 std::string start(reinterpret_cast<const char*>(data),
171 std::min<size_t>(size, kGuessTraceMaxLookahead));
172 if (size >= 8) {
173 uint64_t first_word;
174 memcpy(&first_word, data, sizeof(first_word));
175 if (first_word == kFuchsiaMagicNumber)
176 return kFuchsiaTraceType;
177 }
178 std::string start_minus_white_space = RemoveWhitespace(start);
179 if (base::StartsWith(start_minus_white_space, "{\""))
180 return kJsonTraceType;
181 if (base::StartsWith(start_minus_white_space, "[{\""))
182 return kJsonTraceType;
183
184 // Systrace with header but no leading HTML.
185 if (base::Contains(start, "# tracer"))
186 return kSystraceTraceType;
187
188 // Systrace with leading HTML.
189 // Both: <!DOCTYPE html> and <!DOCTYPE HTML> have been observed.
190 std::string lower_start = base::ToLower(start);
191 if (base::StartsWith(lower_start, "<!doctype html>") ||
192 base::StartsWith(lower_start, "<html>"))
193 return kSystraceTraceType;
194
195 // Traces obtained from atrace -z (compress).
196 // They all have the string "TRACE:" followed by 78 9C which is a zlib header
197 // for "deflate, default compression, window size=32K" (see b/208691037)
198 if (base::Contains(start, "TRACE:\n\x78\x9c"))
199 return kCtraceTraceType;
200
201 // Traces obtained from atrace without -z (no compression).
202 if (base::Contains(start, "TRACE:\n"))
203 return kSystraceTraceType;
204
205 // Ninja's build log (.ninja_log).
206 if (base::StartsWith(start, "# ninja log"))
207 return kNinjaLogTraceType;
208
209 // Systrace with no header or leading HTML.
210 if (base::StartsWith(start, " "))
211 return kSystraceTraceType;
212
213 // gzip'ed trace containing one of the other formats.
214 if (base::StartsWith(start, "\x1f\x8b"))
215 return kGzipTraceType;
216
217 if (base::StartsWith(start, "\x0a"))
218 return kProtoTraceType;
219
220 // Android bugreport.zip
221 // TODO(primiano). For now we assume any .zip file is a bugreport. In future,
222 // if we want to support different trace formats based on a .zip arachive we
223 // will need an extra layer similar to what we did kGzipTraceType.
224 if (base::StartsWith(start, "PK\x03\x04"))
225 return kAndroidBugreportTraceType;
226
227 return kUnknownTraceType;
228 }
229
230 } // namespace trace_processor
231 } // namespace perfetto
232