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