1 /*
2 * Copyright (C) 2018 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/traceconv/trace_to_text.h"
18
19 #include "perfetto/base/logging.h"
20 #include "perfetto/ext/base/file_utils.h"
21 #include "perfetto/ext/base/scoped_file.h"
22 #include "perfetto/ext/protozero/proto_ring_buffer.h"
23 #include "src/traceconv/trace.descriptor.h"
24 #include "src/traceconv/winscope.descriptor.h"
25 #include "src/traceconv/utils.h"
26
27 #include "protos/perfetto/trace/trace.pbzero.h"
28 #include "protos/perfetto/trace/trace_packet.pbzero.h"
29
30 #include "src/trace_processor/util/descriptors.h"
31 #include "src/trace_processor/util/gzip_utils.h"
32 #include "src/trace_processor/util/protozero_to_text.h"
33 #include "src/trace_processor/util/trace_type.h"
34
35 namespace perfetto {
36 namespace trace_to_text {
37 namespace {
38
39 using perfetto::trace_processor::DescriptorPool;
40 using trace_processor::TraceType;
41 using trace_processor::util::GzipDecompressor;
42
43 template <size_t N>
WriteToOutput(std::ostream * output,const char (& str)[N])44 static void WriteToOutput(std::ostream* output, const char (&str)[N]) {
45 output->write(str, sizeof(str) - 1);
46 }
47
48 // Online algorithm to covert trace binary to text format.
49 // Usage:
50 // - Feed the trace-binary in a sequence of memblock, and it will continue to
51 // write the output in given std::ostream*.
52 class OnlineTraceToText {
53 public:
OnlineTraceToText(std::ostream * output)54 OnlineTraceToText(std::ostream* output) : output_(output) {
55 pool_.AddFromFileDescriptorSet(kTraceDescriptor.data(),
56 kTraceDescriptor.size());
57 pool_.AddFromFileDescriptorSet(kWinscopeDescriptor.data(),
58 kWinscopeDescriptor.size());
59 }
60 OnlineTraceToText(const OnlineTraceToText&) = delete;
61 OnlineTraceToText& operator=(const OnlineTraceToText&) = delete;
62 void Feed(const uint8_t* data, size_t len);
ok() const63 bool ok() const { return ok_; }
64
65 private:
66 std::string TracePacketToText(protozero::ConstBytes packet,
67 uint32_t indent_depth);
68 void PrintCompressedPackets(protozero::ConstBytes packets);
69
70 bool ok_ = true;
71 std::ostream* output_;
72 protozero::ProtoRingBuffer ring_buffer_;
73 DescriptorPool pool_;
74 size_t bytes_processed_ = 0;
75 size_t packet_ = 0;
76 };
77
TracePacketToText(protozero::ConstBytes packet,uint32_t indent_depth)78 std::string OnlineTraceToText::TracePacketToText(protozero::ConstBytes packet,
79 uint32_t indent_depth) {
80 namespace pb0_to_text = trace_processor::protozero_to_text;
81 return pb0_to_text::ProtozeroToText(pool_, ".perfetto.protos.TracePacket",
82 packet, pb0_to_text::kIncludeNewLines,
83 indent_depth);
84 }
85
PrintCompressedPackets(protozero::ConstBytes packets)86 void OnlineTraceToText::PrintCompressedPackets(protozero::ConstBytes packets) {
87 WriteToOutput(output_, "compressed_packets {\n");
88 if (trace_processor::util::IsGzipSupported()) {
89 std::vector<uint8_t> whole_data =
90 GzipDecompressor::DecompressFully(packets.data, packets.size);
91 protos::pbzero::Trace::Decoder decoder(whole_data.data(),
92 whole_data.size());
93 for (auto it = decoder.packet(); it; ++it) {
94 WriteToOutput(output_, " packet {\n");
95 std::string text = TracePacketToText(*it, 2);
96 output_->write(text.data(), std::streamsize(text.size()));
97 WriteToOutput(output_, "\n }\n");
98 }
99 } else {
100 static const char kErrMsg[] =
101 "Cannot decode compressed packets. zlib not enabled in the build "
102 "config";
103 WriteToOutput(output_, kErrMsg);
104 static bool log_once = [] {
105 PERFETTO_ELOG("%s", kErrMsg);
106 return true;
107 }();
108 base::ignore_result(log_once);
109 }
110 WriteToOutput(output_, "}\n");
111 }
112
Feed(const uint8_t * data,size_t len)113 void OnlineTraceToText::Feed(const uint8_t* data, size_t len) {
114 ring_buffer_.Append(data, static_cast<size_t>(len));
115 while (true) {
116 auto token = ring_buffer_.ReadMessage();
117 if (token.fatal_framing_error) {
118 PERFETTO_ELOG("Failed to tokenize trace packet");
119 ok_ = false;
120 return;
121 }
122 if (!token.valid()) {
123 // no need to set `ok_ = false` here because this just means
124 // we've run out of packets in the ring buffer.
125 break;
126 }
127
128 if (token.field_id != protos::pbzero::Trace::kPacketFieldNumber) {
129 PERFETTO_ELOG("Skipping invalid field");
130 continue;
131 }
132 protos::pbzero::TracePacket::Decoder decoder(token.start, token.len);
133 bytes_processed_ += token.len;
134 if ((packet_++ & 0x3f) == 0) {
135 fprintf(stderr, "Processing trace: %8zu KB%c", bytes_processed_ / 1024,
136 kProgressChar);
137 fflush(stderr);
138 }
139 if (decoder.has_compressed_packets()) {
140 PrintCompressedPackets(decoder.compressed_packets());
141 } else {
142 WriteToOutput(output_, "packet {\n");
143 protozero::ConstBytes packet = {token.start, token.len};
144 std::string text = TracePacketToText(packet, 1 /* indent_depth */);
145 output_->write(text.data(), std::streamsize(text.size()));
146 WriteToOutput(output_, "\n}\n");
147 }
148 }
149 }
150
151 class InputReader {
152 public:
InputReader(std::istream * input)153 InputReader(std::istream* input) : input_(input) {}
154 // Request the input-stream to read next |len_limit| bytes and load
155 // it in |data|. It also updates the |len| with actual number of bytes loaded
156 // in |data|. This can be less than requested |len_limit| if we have reached
157 // at the end of the file.
Read(uint8_t * data,uint32_t * len,uint32_t len_limit)158 bool Read(uint8_t* data, uint32_t* len, uint32_t len_limit) {
159 if (input_->eof())
160 return false;
161 input_->read(reinterpret_cast<char*>(data), std::streamsize(len_limit));
162 if (input_->bad() || (input_->fail() && !input_->eof())) {
163 PERFETTO_ELOG("Failed while reading trace");
164 ok_ = false;
165 return false;
166 }
167 *len = uint32_t(input_->gcount());
168 return true;
169 }
ok() const170 bool ok() const { return ok_; }
171
172 private:
173 std::istream* input_;
174 bool ok_ = true;
175 };
176
177 } // namespace
178
TraceToText(std::istream * input,std::ostream * output)179 bool TraceToText(std::istream* input, std::ostream* output) {
180 constexpr size_t kMaxMsgSize = protozero::ProtoRingBuffer::kMaxMsgSize;
181 std::unique_ptr<uint8_t[]> buffer(new uint8_t[kMaxMsgSize]);
182 uint32_t buffer_len = 0;
183
184 InputReader input_reader(input);
185 OnlineTraceToText online_trace_to_text(output);
186
187 input_reader.Read(buffer.get(), &buffer_len, kMaxMsgSize);
188 TraceType type = trace_processor::GuessTraceType(buffer.get(), buffer_len);
189
190 if (type == TraceType::kGzipTraceType) {
191 GzipDecompressor decompressor;
192 auto consumer = [&](const uint8_t* data, size_t len) {
193 online_trace_to_text.Feed(data, len);
194 };
195 using ResultCode = GzipDecompressor::ResultCode;
196 do {
197 ResultCode code =
198 decompressor.FeedAndExtract(buffer.get(), buffer_len, consumer);
199 if (code == ResultCode::kError || !online_trace_to_text.ok())
200 return false;
201 } while (input_reader.Read(buffer.get(), &buffer_len, kMaxMsgSize));
202 return input_reader.ok();
203 } else if (type == TraceType::kProtoTraceType) {
204 do {
205 online_trace_to_text.Feed(buffer.get(), buffer_len);
206 if (!online_trace_to_text.ok())
207 return false;
208 } while (input_reader.Read(buffer.get(), &buffer_len, kMaxMsgSize));
209 return input_reader.ok();
210 } else {
211 PERFETTO_ELOG("Unrecognised file.");
212 return false;
213 }
214 }
215
216 } // namespace trace_to_text
217 } // namespace perfetto
218