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