| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "src/trace_processor/forwarding_trace_parser.h" |
| |
| #include "perfetto/base/logging.h" |
| #include "perfetto/ext/base/string_utils.h" |
| #include "src/trace_processor/importers/common/process_tracker.h" |
| #include "src/trace_processor/importers/proto/proto_trace_parser.h" |
| #include "src/trace_processor/importers/proto/proto_trace_reader.h" |
| #include "src/trace_processor/sorter/trace_sorter.h" |
| #include "src/trace_processor/types/trace_processor_context.h" |
| |
| namespace perfetto { |
| namespace trace_processor { |
| namespace { |
| |
| const char kNoZlibErr[] = |
| "Cannot open compressed trace. zlib not enabled in the build config"; |
| |
| inline bool isspace(unsigned char c) { |
| return ::isspace(c); |
| } |
| |
| std::string RemoveWhitespace(std::string str) { |
| str.erase(std::remove_if(str.begin(), str.end(), isspace), str.end()); |
| return str; |
| } |
| |
| TraceSorter::SortingMode ConvertSortingMode(SortingMode sorting_mode) { |
| switch (sorting_mode) { |
| case SortingMode::kDefaultHeuristics: |
| case SortingMode::kForceFlushPeriodWindowedSort: |
| return TraceSorter::SortingMode::kDefault; |
| case SortingMode::kForceFullSort: |
| return TraceSorter::SortingMode::kFullSort; |
| } |
| PERFETTO_FATAL("For GCC"); |
| } |
| |
| // Fuchsia traces have a magic number as documented here: |
| // https://fuchsia.googlesource.com/fuchsia/+/HEAD/docs/development/tracing/trace-format/README.md#magic-number-record-trace-info-type-0 |
| constexpr uint64_t kFuchsiaMagicNumber = 0x0016547846040010; |
| constexpr char kPerfMagic[] = "PERFILE2"; |
| |
| } // namespace |
| |
| ForwardingTraceParser::ForwardingTraceParser(TraceProcessorContext* context) |
| : context_(context) {} |
| |
| ForwardingTraceParser::~ForwardingTraceParser() {} |
| |
| base::Status ForwardingTraceParser::Parse(TraceBlobView blob) { |
| // If this is the first Parse() call, guess the trace type and create the |
| // appropriate parser. |
| if (!reader_) { |
| TraceType trace_type; |
| { |
| auto scoped_trace = context_->storage->TraceExecutionTimeIntoStats( |
| stats::guess_trace_type_duration_ns); |
| trace_type = GuessTraceType(blob.data(), blob.size()); |
| context_->trace_type = trace_type; |
| } |
| switch (trace_type) { |
| case kJsonTraceType: { |
| PERFETTO_DLOG("JSON trace detected"); |
| if (context_->json_trace_tokenizer && context_->json_trace_parser) { |
| reader_ = std::move(context_->json_trace_tokenizer); |
| |
| // JSON traces have no guarantees about the order of events in them. |
| context_->sorter.reset( |
| new TraceSorter(context_, std::move(context_->json_trace_parser), |
| TraceSorter::SortingMode::kFullSort)); |
| break; |
| } |
| return base::ErrStatus("JSON support is disabled"); |
| } |
| case kProtoTraceType: { |
| PERFETTO_DLOG("Proto trace detected"); |
| auto sorting_mode = ConvertSortingMode(context_->config.sorting_mode); |
| reader_.reset(new ProtoTraceReader(context_)); |
| context_->sorter.reset(new TraceSorter( |
| context_, |
| std::unique_ptr<TraceParser>(new ProtoTraceParser(context_)), |
| sorting_mode)); |
| context_->process_tracker->SetPidZeroIsUpidZeroIdleProcess(); |
| break; |
| } |
| case kNinjaLogTraceType: { |
| PERFETTO_DLOG("Ninja log detected"); |
| if (context_->ninja_log_parser) { |
| reader_ = std::move(context_->ninja_log_parser); |
| break; |
| } |
| return base::ErrStatus("Ninja support is disabled"); |
| } |
| case kFuchsiaTraceType: { |
| PERFETTO_DLOG("Fuchsia trace detected"); |
| if (context_->fuchsia_trace_parser && |
| context_->fuchsia_trace_tokenizer) { |
| reader_ = std::move(context_->fuchsia_trace_tokenizer); |
| |
| // Fuschia traces can have massively out of order events. |
| context_->sorter.reset(new TraceSorter( |
| context_, std::move(context_->fuchsia_trace_parser), |
| TraceSorter::SortingMode::kFullSort)); |
| break; |
| } |
| return base::ErrStatus("Fuchsia support is disabled"); |
| } |
| case kSystraceTraceType: |
| PERFETTO_DLOG("Systrace trace detected"); |
| context_->process_tracker->SetPidZeroIsUpidZeroIdleProcess(); |
| if (context_->systrace_trace_parser) { |
| reader_ = std::move(context_->systrace_trace_parser); |
| break; |
| } |
| return base::ErrStatus("Systrace support is disabled"); |
| case kGzipTraceType: |
| case kCtraceTraceType: |
| if (trace_type == kGzipTraceType) { |
| PERFETTO_DLOG("gzip trace detected"); |
| } else { |
| PERFETTO_DLOG("ctrace trace detected"); |
| } |
| if (context_->gzip_trace_parser) { |
| reader_ = std::move(context_->gzip_trace_parser); |
| break; |
| } |
| return base::ErrStatus(kNoZlibErr); |
| case kAndroidBugreportTraceType: |
| PERFETTO_DLOG("Android Bugreport detected"); |
| if (context_->android_bugreport_parser) { |
| reader_ = std::move(context_->android_bugreport_parser); |
| break; |
| } |
| return base::ErrStatus("Android Bugreport support is disabled. %s", |
| kNoZlibErr); |
| case kPerfDataTraceType: |
| PERFETTO_DLOG("perf data detected"); |
| if (context_->perf_data_trace_tokenizer && context_->perf_data_parser) { |
| reader_ = std::move(context_->perf_data_trace_tokenizer); |
| context_->sorter.reset( |
| new TraceSorter(context_, std::move(context_->perf_data_parser), |
| TraceSorter::SortingMode::kDefault)); |
| break; |
| } |
| return base::ErrStatus("perf.data parsing support is disabled."); |
| case kUnknownTraceType: |
| // If renaming this error message don't remove the "(ERR:fmt)" part. |
| // The UI's error_dialog.ts uses it to make the dialog more graceful. |
| return base::ErrStatus("Unknown trace type provided (ERR:fmt)"); |
| } |
| } |
| |
| return reader_->Parse(std::move(blob)); |
| } |
| |
| void ForwardingTraceParser::NotifyEndOfFile() { |
| reader_->NotifyEndOfFile(); |
| } |
| |
| TraceType GuessTraceType(const uint8_t* data, size_t size) { |
| if (size == 0) |
| return kUnknownTraceType; |
| std::string start(reinterpret_cast<const char*>(data), |
| std::min<size_t>(size, kGuessTraceMaxLookahead)); |
| if (size >= 8) { |
| uint64_t first_word; |
| memcpy(&first_word, data, sizeof(first_word)); |
| if (first_word == kFuchsiaMagicNumber) |
| return kFuchsiaTraceType; |
| } |
| if (base::StartsWith(start, kPerfMagic)) { |
| return kPerfDataTraceType; |
| } |
| std::string start_minus_white_space = RemoveWhitespace(start); |
| if (base::StartsWith(start_minus_white_space, "{\"")) |
| return kJsonTraceType; |
| if (base::StartsWith(start_minus_white_space, "[{\"")) |
| return kJsonTraceType; |
| |
| // Systrace with header but no leading HTML. |
| if (base::Contains(start, "# tracer")) |
| return kSystraceTraceType; |
| |
| // Systrace with leading HTML. |
| // Both: <!DOCTYPE html> and <!DOCTYPE HTML> have been observed. |
| std::string lower_start = base::ToLower(start); |
| if (base::StartsWith(lower_start, "<!doctype html>") || |
| base::StartsWith(lower_start, "<html>")) |
| return kSystraceTraceType; |
| |
| // Traces obtained from atrace -z (compress). |
| // They all have the string "TRACE:" followed by 78 9C which is a zlib header |
| // for "deflate, default compression, window size=32K" (see b/208691037) |
| if (base::Contains(start, "TRACE:\n\x78\x9c")) |
| return kCtraceTraceType; |
| |
| // Traces obtained from atrace without -z (no compression). |
| if (base::Contains(start, "TRACE:\n")) |
| return kSystraceTraceType; |
| |
| // Ninja's build log (.ninja_log). |
| if (base::StartsWith(start, "# ninja log")) |
| return kNinjaLogTraceType; |
| |
| // Systrace with no header or leading HTML. |
| if (base::StartsWith(start, " ")) |
| return kSystraceTraceType; |
| |
| // gzip'ed trace containing one of the other formats. |
| if (base::StartsWith(start, "\x1f\x8b")) |
| return kGzipTraceType; |
| |
| if (base::StartsWith(start, "\x0a")) |
| return kProtoTraceType; |
| |
| // Android bugreport.zip |
| // TODO(primiano). For now we assume any .zip file is a bugreport. In future, |
| // if we want to support different trace formats based on a .zip arachive we |
| // will need an extra layer similar to what we did kGzipTraceType. |
| if (base::StartsWith(start, "PK\x03\x04")) |
| return kAndroidBugreportTraceType; |
| |
| return kUnknownTraceType; |
| } |
| |
| } // namespace trace_processor |
| } // namespace perfetto |