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