• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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