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