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