• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 <stdint.h>
18 #include <stdio.h>
19 #include <string>
20 #include <vector>
21 
22 #include "perfetto/ext/base/file_utils.h"
23 #include "perfetto/ext/base/scoped_file.h"
24 #include "perfetto/protozero/proto_utils.h"
25 #include "perfetto/protozero/scattered_heap_buffer.h"
26 #include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
27 #include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
28 #include "protos/perfetto/trace/ftrace/sched.pbzero.h"
29 #include "protos/perfetto/trace/trace.pbzero.h"
30 #include "protos/perfetto/trace/trace_packet.pbzero.h"
31 
32 // Re-encodes the given trace, converting sched events to their compact
33 // representation.
34 //
35 // Notes:
36 // * doesn't do bundle splitting/merging, the original trace must already
37 //   have multi-page bundles for the re-encoding to be realistic.
38 // * when importing the resulting trace into trace_processor, a few leading
39 //   switch/wakeup events can be skipped (since there's not enough info to
40 //   reconstruct the full events at that point), and this might change the
41 //   trace_bounds.
42 
43 namespace perfetto {
44 namespace compact_reencode {
45 namespace {
46 
WriteToFile(const std::string & out,const char * path)47 void WriteToFile(const std::string& out, const char* path) {
48   PERFETTO_CHECK(!remove(path) || errno == ENOENT);
49   auto out_fd = base::OpenFile(path, O_RDWR | O_CREAT, 0666);
50   if (!out_fd || base::WriteAll(out_fd.get(), out.data(), out.size()) !=
51                      static_cast<ssize_t>(out.size())) {
52     PERFETTO_FATAL("WriteToFile");
53   }
54 }
55 
CopyField(protozero::Message * out,const protozero::Field & field)56 static void CopyField(protozero::Message* out, const protozero::Field& field) {
57   using protozero::proto_utils::ProtoWireType;
58   if (field.type() == ProtoWireType::kVarInt) {
59     out->AppendVarInt(field.id(), field.as_uint64());
60   } else if (field.type() == ProtoWireType::kLengthDelimited) {
61     out->AppendBytes(field.id(), field.as_bytes().data, field.as_bytes().size);
62   } else if (field.type() == ProtoWireType::kFixed32) {
63     out->AppendFixed(field.id(), field.as_uint32());
64   } else if (field.type() == ProtoWireType::kFixed64) {
65     out->AppendFixed(field.id(), field.as_uint64());
66   } else {
67     PERFETTO_FATAL("unexpected wire type");
68   }
69 }
70 
ReEncodeBundle(protos::pbzero::TracePacket * packet_out,const uint8_t * data,size_t size)71 void ReEncodeBundle(protos::pbzero::TracePacket* packet_out,
72                     const uint8_t* data,
73                     size_t size) {
74   protos::pbzero::FtraceEventBundle::Decoder bundle(data, size);
75   auto* bundle_out = packet_out->set_ftrace_events();
76 
77   if (bundle.has_lost_events())
78     bundle_out->set_lost_events(bundle.lost_events());
79   if (bundle.has_cpu())
80     bundle_out->set_cpu(bundle.cpu());
81 
82   protozero::PackedVarInt switch_timestamp;
83   protozero::PackedVarInt switch_prev_state;
84   protozero::PackedVarInt switch_next_pid;
85   protozero::PackedVarInt switch_next_prio;
86   protozero::PackedVarInt switch_next_comm_index;
87 
88   uint64_t last_switch_timestamp = 0;
89 
90   std::vector<std::string> string_table;
91   auto intern = [&string_table](std::string str) {
92     for (size_t i = 0; i < string_table.size(); i++) {
93       if (str == string_table[i])
94         return static_cast<uint32_t>(i);
95     }
96     size_t new_idx = string_table.size();
97     string_table.push_back(str);
98     return static_cast<uint32_t>(new_idx);
99   };
100 
101   // sched_waking pieces
102   protozero::PackedVarInt waking_timestamp;
103   protozero::PackedVarInt waking_pid;
104   protozero::PackedVarInt waking_target_cpu;
105   protozero::PackedVarInt waking_prio;
106   protozero::PackedVarInt waking_comm_index;
107 
108   uint64_t last_waking_timestamp = 0;
109 
110   for (auto event_it = bundle.event(); event_it; ++event_it) {
111     protos::pbzero::FtraceEvent::Decoder event(*event_it);
112     if (!event.has_sched_switch() && !event.has_sched_waking()) {
113       CopyField(bundle_out, event_it.field());
114     } else if (event.has_sched_switch()) {
115       switch_timestamp.Append(event.timestamp() - last_switch_timestamp);
116       last_switch_timestamp = event.timestamp();
117 
118       protos::pbzero::SchedSwitchFtraceEvent::Decoder sswitch(
119           event.sched_switch());
120 
121       auto iid = intern(sswitch.next_comm().ToStdString());
122       switch_next_comm_index.Append(iid);
123 
124       switch_next_pid.Append(sswitch.next_pid());
125       switch_next_prio.Append(sswitch.next_prio());
126       switch_prev_state.Append(sswitch.prev_state());
127     } else {
128       waking_timestamp.Append(event.timestamp() - last_waking_timestamp);
129       last_waking_timestamp = event.timestamp();
130 
131       protos::pbzero::SchedWakingFtraceEvent::Decoder swaking(
132           event.sched_waking());
133 
134       auto iid = intern(swaking.comm().ToStdString());
135       waking_comm_index.Append(iid);
136 
137       waking_pid.Append(swaking.pid());
138       waking_target_cpu.Append(swaking.target_cpu());
139       waking_prio.Append(swaking.prio());
140     }
141   }
142 
143   auto* compact_sched = bundle_out->set_compact_sched();
144 
145   for (const auto& s : string_table)
146     compact_sched->add_intern_table(s.data(), s.size());
147 
148   compact_sched->set_switch_timestamp(switch_timestamp);
149   compact_sched->set_switch_next_comm_index(switch_next_comm_index);
150   compact_sched->set_switch_next_pid(switch_next_pid);
151   compact_sched->set_switch_next_prio(switch_next_prio);
152   compact_sched->set_switch_prev_state(switch_prev_state);
153 
154   compact_sched->set_waking_timestamp(waking_timestamp);
155   compact_sched->set_waking_pid(waking_pid);
156   compact_sched->set_waking_target_cpu(waking_target_cpu);
157   compact_sched->set_waking_prio(waking_prio);
158   compact_sched->set_waking_comm_index(waking_comm_index);
159 }
160 
ReEncode(const std::string & raw)161 std::string ReEncode(const std::string& raw) {
162   protos::pbzero::Trace::Decoder trace(raw);
163   protozero::HeapBuffered<protos::pbzero::Trace> output;
164 
165   for (auto packet_it = trace.packet(); packet_it; ++packet_it) {
166     protozero::ProtoDecoder packet(*packet_it);
167     protos::pbzero::TracePacket* packet_out = output->add_packet();
168 
169     for (auto field = packet.ReadField(); field.valid();
170          field = packet.ReadField()) {
171       if (field.id() == protos::pbzero::TracePacket::kFtraceEventsFieldNumber) {
172         ReEncodeBundle(packet_out, field.data(), field.size());
173       } else {
174         CopyField(packet_out, field);
175       }
176     }
177   }
178   // Minor technicality: we will be a tiny bit off the real encoding since
179   // we've encoded the top-level Trace & TracePacket sizes redundantly, while
180   // the tracing service writes them as a minimal varint (so only a few bytes
181   // off per trace packet).
182   return output.SerializeAsString();
183 }
184 
Main(int argc,const char ** argv)185 int Main(int argc, const char** argv) {
186   if (argc < 3) {
187     PERFETTO_LOG("Usage: %s input output", argv[0]);
188     return 1;
189   }
190   const char* in_path = argv[1];
191   const char* out_path = argv[2];
192 
193   std::string raw;
194   if (!base::ReadFile(in_path, &raw)) {
195     PERFETTO_PLOG("ReadFile");
196     return 1;
197   }
198 
199   std::string raw_out = ReEncode(raw);
200   WriteToFile(raw_out, out_path);
201   return 0;
202 }
203 
204 }  // namespace
205 }  // namespace compact_reencode
206 }  // namespace perfetto
207 
main(int argc,const char ** argv)208 int main(int argc, const char** argv) {
209   return perfetto::compact_reencode::Main(argc, argv);
210 }
211