xref: /aosp_15_r20/external/perfetto/src/traceconv/trace_to_text.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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