xref: /aosp_15_r20/external/perfetto/src/trace_processor/util/trace_type.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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/util/trace_type.h"
18 
19 #include <algorithm>
20 #include <cctype>
21 #include <cstddef>
22 #include <cstdint>
23 #include <string>
24 
25 #include "perfetto/base/logging.h"
26 #include "perfetto/ext/base/string_utils.h"
27 #include "perfetto/protozero/proto_utils.h"
28 #include "src/trace_processor/importers/android_bugreport/android_log_event.h"
29 #include "src/trace_processor/importers/perf_text/perf_text_sample_line_parser.h"
30 
31 #include "protos/perfetto/trace/trace.pbzero.h"
32 #include "protos/perfetto/trace/trace_packet.pbzero.h"
33 
34 namespace perfetto::trace_processor {
35 namespace {
36 // Fuchsia traces have a magic number as documented here:
37 // https://fuchsia.googlesource.com/fuchsia/+/HEAD/docs/development/tracing/trace-format/README.md#magic-number-record-trace-info-type-0
38 constexpr char kFuchsiaMagic[] = {'\x10', '\x00', '\x04', '\x46',
39                                   '\x78', '\x54', '\x16', '\x00'};
40 constexpr char kPerfMagic[] = {'P', 'E', 'R', 'F', 'I', 'L', 'E', '2'};
41 constexpr char kZipMagic[] = {'P', 'K', '\x03', '\x04'};
42 constexpr char kGzipMagic[] = {'\x1f', '\x8b'};
43 constexpr char kArtMethodStreamingMagic[] = {'S', 'L', 'O', 'W'};
44 constexpr char kTarPosixMagic[] = {'u', 's', 't', 'a', 'r', '\0'};
45 constexpr char kTarGnuMagic[] = {'u', 's', 't', 'a', 'r', ' ', ' ', '\0'};
46 constexpr size_t kTarMagicOffset = 257;
47 
48 constexpr uint8_t kTracePacketTag =
49     protozero::proto_utils::MakeTagLengthDelimited(
50         protos::pbzero::Trace::kPacketFieldNumber);
51 constexpr uint16_t kModuleSymbolsTag =
52     protozero::proto_utils::MakeTagLengthDelimited(
53         protos::pbzero::TracePacket::kModuleSymbolsFieldNumber);
54 
isspace(unsigned char c)55 inline bool isspace(unsigned char c) {
56   return ::isspace(c);
57 }
58 
RemoveWhitespace(std::string str)59 std::string RemoveWhitespace(std::string str) {
60   str.erase(std::remove_if(str.begin(), str.end(), isspace), str.end());
61   return str;
62 }
63 
64 template <size_t N>
MatchesMagic(const uint8_t * data,size_t size,const char (& magic)[N],size_t offset=0)65 bool MatchesMagic(const uint8_t* data,
66                   size_t size,
67                   const char (&magic)[N],
68                   size_t offset = 0) {
69   if (size < N + offset) {
70     return false;
71   }
72 
73   return memcmp(data + offset, magic, N) == 0;
74 }
75 
IsProtoTraceWithSymbols(const uint8_t * ptr,size_t size)76 bool IsProtoTraceWithSymbols(const uint8_t* ptr, size_t size) {
77   const uint8_t* const end = ptr + size;
78 
79   uint64_t tag;
80   const uint8_t* next = protozero::proto_utils::ParseVarInt(ptr, end, &tag);
81 
82   if (next == ptr || tag != kTracePacketTag) {
83     return false;
84   }
85 
86   ptr = next;
87   uint64_t field_length;
88   next = protozero::proto_utils::ParseVarInt(ptr, end, &field_length);
89   if (next == ptr) {
90     return false;
91   }
92   ptr = next;
93 
94   if (field_length == 0) {
95     return false;
96   }
97 
98   next = protozero::proto_utils::ParseVarInt(ptr, end, &tag);
99   if (next == ptr) {
100     return false;
101   }
102 
103   return tag == kModuleSymbolsTag;
104 }
105 
106 }  // namespace
107 
TraceTypeToString(TraceType trace_type)108 const char* TraceTypeToString(TraceType trace_type) {
109   switch (trace_type) {
110     case kJsonTraceType:
111       return "json";
112     case kProtoTraceType:
113       return "proto";
114     case kSymbolsTraceType:
115       return "symbols";
116     case kNinjaLogTraceType:
117       return "ninja_log";
118     case kFuchsiaTraceType:
119       return "fuchsia";
120     case kSystraceTraceType:
121       return "systrace";
122     case kGzipTraceType:
123       return "gzip";
124     case kCtraceTraceType:
125       return "ctrace";
126     case kZipFile:
127       return "zip";
128     case kPerfDataTraceType:
129       return "perf";
130     case kInstrumentsXmlTraceType:
131       return "instruments_xml";
132     case kAndroidLogcatTraceType:
133       return "android_logcat";
134     case kAndroidDumpstateTraceType:
135       return "android_dumpstate";
136     case kAndroidBugreportTraceType:
137       return "android_bugreport";
138     case kGeckoTraceType:
139       return "gecko";
140     case kArtMethodTraceType:
141       return "art_method";
142     case kPerfTextTraceType:
143       return "perf_text";
144     case kUnknownTraceType:
145       return "unknown";
146     case kTarTraceType:
147       return "tar";
148   }
149   PERFETTO_FATAL("For GCC");
150 }
151 
GuessTraceType(const uint8_t * data,size_t size)152 TraceType GuessTraceType(const uint8_t* data, size_t size) {
153   if (size == 0) {
154     return kUnknownTraceType;
155   }
156 
157   if (MatchesMagic(data, size, kTarPosixMagic, kTarMagicOffset)) {
158     return kTarTraceType;
159   }
160 
161   if (MatchesMagic(data, size, kTarGnuMagic, kTarMagicOffset)) {
162     return kTarTraceType;
163   }
164 
165   if (MatchesMagic(data, size, kFuchsiaMagic)) {
166     return kFuchsiaTraceType;
167   }
168 
169   if (MatchesMagic(data, size, kPerfMagic)) {
170     return kPerfDataTraceType;
171   }
172 
173   if (MatchesMagic(data, size, kZipMagic)) {
174     return kZipFile;
175   }
176 
177   if (MatchesMagic(data, size, kGzipMagic)) {
178     return kGzipTraceType;
179   }
180 
181   if (MatchesMagic(data, size, kArtMethodStreamingMagic)) {
182     return kArtMethodTraceType;
183   }
184 
185   std::string start(reinterpret_cast<const char*>(data),
186                     std::min<size_t>(size, kGuessTraceMaxLookahead));
187 
188   std::string start_minus_white_space = RemoveWhitespace(start);
189   // Generated by the Gecko conversion script built into perf.
190   if (base::StartsWith(start_minus_white_space, "{\"meta\""))
191     return kGeckoTraceType;
192   // Generated by the simpleperf conversion script.
193   if (base::StartsWith(start_minus_white_space, "{\"libs\""))
194     return kGeckoTraceType;
195   if (base::StartsWith(start_minus_white_space, "{\""))
196     return kJsonTraceType;
197   if (base::StartsWith(start_minus_white_space, "[{\""))
198     return kJsonTraceType;
199 
200   // ART method traces (non-streaming).
201   if (base::StartsWith(start, "*version\n"))
202     return kArtMethodTraceType;
203 
204   // Systrace with header but no leading HTML.
205   if (base::Contains(start, "# tracer"))
206     return kSystraceTraceType;
207 
208   // Systrace with leading HTML.
209   // Both: <!DOCTYPE html> and <!DOCTYPE HTML> have been observed.
210   std::string lower_start = base::ToLower(start);
211   if (base::StartsWith(lower_start, "<!doctype html>") ||
212       base::StartsWith(lower_start, "<html>"))
213     return kSystraceTraceType;
214 
215   // MacOS Instruments XML export.
216   if (base::StartsWith(start, "<?xml version=\"1.0\"?>\n<trace-query-result>"))
217     return kInstrumentsXmlTraceType;
218 
219   // Traces obtained from atrace -z (compress).
220   // They all have the string "TRACE:" followed by 78 9C which is a zlib header
221   // for "deflate, default compression, window size=32K" (see b/208691037)
222   if (base::Contains(start, "TRACE:\n\x78\x9c"))
223     return kCtraceTraceType;
224 
225   // Traces obtained from atrace without -z (no compression).
226   if (base::Contains(start, "TRACE:\n"))
227     return kSystraceTraceType;
228 
229   // Ninja's build log (.ninja_log).
230   if (base::StartsWith(start, "# ninja log"))
231     return kNinjaLogTraceType;
232 
233   if (AndroidLogEvent::IsAndroidLogcat(data, size)) {
234     return kAndroidLogcatTraceType;
235   }
236 
237   // Perf text format.
238   if (perf_text_importer::IsPerfTextFormatTrace(data, size))
239     return kPerfTextTraceType;
240 
241   // Systrace with no header or leading HTML.
242   if (base::StartsWith(start, " "))
243     return kSystraceTraceType;
244 
245   if (IsProtoTraceWithSymbols(data, size))
246     return kSymbolsTraceType;
247 
248   if (base::StartsWith(start, "\x0a"))
249     return kProtoTraceType;
250 
251   if (base::StartsWith(start, "9,0,i,vers,")) {
252     return kAndroidDumpstateTraceType;
253   }
254 
255   return kUnknownTraceType;
256 }
257 
258 }  // namespace perfetto::trace_processor
259