212 lines
7.6 KiB
C++
212 lines
7.6 KiB
C++
/*
|
|
* Copyright (C) 2019 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "perfetto/ext/base/file_utils.h"
|
|
#include "perfetto/ext/base/scoped_file.h"
|
|
#include "perfetto/protozero/proto_utils.h"
|
|
#include "perfetto/protozero/scattered_heap_buffer.h"
|
|
#include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
|
|
#include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
|
|
#include "protos/perfetto/trace/ftrace/sched.pbzero.h"
|
|
#include "protos/perfetto/trace/trace.pbzero.h"
|
|
#include "protos/perfetto/trace/trace_packet.pbzero.h"
|
|
|
|
// Re-encodes the given trace, converting sched events to their compact
|
|
// representation.
|
|
//
|
|
// Notes:
|
|
// * doesn't do bundle splitting/merging, the original trace must already
|
|
// have multi-page bundles for the re-encoding to be realistic.
|
|
// * when importing the resulting trace into trace_processor, a few leading
|
|
// switch/wakeup events can be skipped (since there's not enough info to
|
|
// reconstruct the full events at that point), and this might change the
|
|
// trace_bounds.
|
|
|
|
namespace perfetto {
|
|
namespace compact_reencode {
|
|
namespace {
|
|
|
|
void WriteToFile(const std::string& out, const char* path) {
|
|
PERFETTO_CHECK(!remove(path) || errno == ENOENT);
|
|
auto out_fd = base::OpenFile(path, O_RDWR | O_CREAT, 0666);
|
|
if (!out_fd || base::WriteAll(out_fd.get(), out.data(), out.size()) !=
|
|
static_cast<ssize_t>(out.size())) {
|
|
PERFETTO_FATAL("WriteToFile");
|
|
}
|
|
}
|
|
|
|
static void CopyField(protozero::Message* out, const protozero::Field& field) {
|
|
using protozero::proto_utils::ProtoWireType;
|
|
if (field.type() == ProtoWireType::kVarInt) {
|
|
out->AppendVarInt(field.id(), field.as_uint64());
|
|
} else if (field.type() == ProtoWireType::kLengthDelimited) {
|
|
out->AppendBytes(field.id(), field.as_bytes().data, field.as_bytes().size);
|
|
} else if (field.type() == ProtoWireType::kFixed32) {
|
|
out->AppendFixed(field.id(), field.as_uint32());
|
|
} else if (field.type() == ProtoWireType::kFixed64) {
|
|
out->AppendFixed(field.id(), field.as_uint64());
|
|
} else {
|
|
PERFETTO_FATAL("unexpected wire type");
|
|
}
|
|
}
|
|
|
|
void ReEncodeBundle(protos::pbzero::TracePacket* packet_out,
|
|
const uint8_t* data,
|
|
size_t size) {
|
|
protos::pbzero::FtraceEventBundle::Decoder bundle(data, size);
|
|
auto* bundle_out = packet_out->set_ftrace_events();
|
|
|
|
if (bundle.has_lost_events())
|
|
bundle_out->set_lost_events(bundle.lost_events());
|
|
if (bundle.has_cpu())
|
|
bundle_out->set_cpu(bundle.cpu());
|
|
|
|
using protozero::PackedVarInt;
|
|
std::unique_ptr<PackedVarInt> switch_timestamp(new PackedVarInt());
|
|
std::unique_ptr<PackedVarInt> switch_prev_state(new PackedVarInt());
|
|
std::unique_ptr<PackedVarInt> switch_next_pid(new PackedVarInt());
|
|
std::unique_ptr<PackedVarInt> switch_next_prio(new PackedVarInt());
|
|
std::unique_ptr<PackedVarInt> switch_next_comm_index(new PackedVarInt());
|
|
|
|
uint64_t last_switch_timestamp = 0;
|
|
|
|
std::vector<std::string> string_table;
|
|
auto intern = [&string_table](std::string str) {
|
|
for (size_t i = 0; i < string_table.size(); i++) {
|
|
if (str == string_table[i])
|
|
return static_cast<uint32_t>(i);
|
|
}
|
|
size_t new_idx = string_table.size();
|
|
string_table.push_back(str);
|
|
return static_cast<uint32_t>(new_idx);
|
|
};
|
|
|
|
// sched_waking pieces
|
|
std::unique_ptr<PackedVarInt> waking_timestamp(new PackedVarInt());
|
|
std::unique_ptr<PackedVarInt> waking_pid(new PackedVarInt());
|
|
std::unique_ptr<PackedVarInt> waking_target_cpu(new PackedVarInt());
|
|
std::unique_ptr<PackedVarInt> waking_prio(new PackedVarInt());
|
|
std::unique_ptr<PackedVarInt> waking_comm_index(new PackedVarInt());
|
|
|
|
uint64_t last_waking_timestamp = 0;
|
|
|
|
for (auto event_it = bundle.event(); event_it; ++event_it) {
|
|
protos::pbzero::FtraceEvent::Decoder event(*event_it);
|
|
if (!event.has_sched_switch() && !event.has_sched_waking()) {
|
|
CopyField(bundle_out, event_it.field());
|
|
} else if (event.has_sched_switch()) {
|
|
switch_timestamp->Append(event.timestamp() - last_switch_timestamp);
|
|
last_switch_timestamp = event.timestamp();
|
|
|
|
protos::pbzero::SchedSwitchFtraceEvent::Decoder sswitch(
|
|
event.sched_switch());
|
|
|
|
auto iid = intern(sswitch.next_comm().ToStdString());
|
|
switch_next_comm_index->Append(iid);
|
|
|
|
switch_next_pid->Append(sswitch.next_pid());
|
|
switch_next_prio->Append(sswitch.next_prio());
|
|
switch_prev_state->Append(sswitch.prev_state());
|
|
} else {
|
|
waking_timestamp->Append(event.timestamp() - last_waking_timestamp);
|
|
last_waking_timestamp = event.timestamp();
|
|
|
|
protos::pbzero::SchedWakingFtraceEvent::Decoder swaking(
|
|
event.sched_waking());
|
|
|
|
auto iid = intern(swaking.comm().ToStdString());
|
|
waking_comm_index->Append(iid);
|
|
|
|
waking_pid->Append(swaking.pid());
|
|
waking_target_cpu->Append(swaking.target_cpu());
|
|
waking_prio->Append(swaking.prio());
|
|
}
|
|
}
|
|
|
|
auto* compact_sched = bundle_out->set_compact_sched();
|
|
|
|
for (const auto& s : string_table)
|
|
compact_sched->add_intern_table(s.data(), s.size());
|
|
|
|
compact_sched->set_switch_timestamp(*switch_timestamp);
|
|
compact_sched->set_switch_next_comm_index(*switch_next_comm_index);
|
|
compact_sched->set_switch_next_pid(*switch_next_pid);
|
|
compact_sched->set_switch_next_prio(*switch_next_prio);
|
|
compact_sched->set_switch_prev_state(*switch_prev_state);
|
|
|
|
compact_sched->set_waking_timestamp(*waking_timestamp);
|
|
compact_sched->set_waking_pid(*waking_pid);
|
|
compact_sched->set_waking_target_cpu(*waking_target_cpu);
|
|
compact_sched->set_waking_prio(*waking_prio);
|
|
compact_sched->set_waking_comm_index(*waking_comm_index);
|
|
}
|
|
|
|
std::string ReEncode(const std::string& raw) {
|
|
protos::pbzero::Trace::Decoder trace(raw);
|
|
protozero::HeapBuffered<protos::pbzero::Trace> output;
|
|
|
|
for (auto packet_it = trace.packet(); packet_it; ++packet_it) {
|
|
protozero::ProtoDecoder packet(*packet_it);
|
|
protos::pbzero::TracePacket* packet_out = output->add_packet();
|
|
|
|
for (auto field = packet.ReadField(); field.valid();
|
|
field = packet.ReadField()) {
|
|
if (field.id() == protos::pbzero::TracePacket::kFtraceEventsFieldNumber) {
|
|
ReEncodeBundle(packet_out, field.data(), field.size());
|
|
} else {
|
|
CopyField(packet_out, field);
|
|
}
|
|
}
|
|
}
|
|
// Minor technicality: we will be a tiny bit off the real encoding since
|
|
// we've encoded the top-level Trace & TracePacket sizes redundantly, while
|
|
// the tracing service writes them as a minimal varint (so only a few bytes
|
|
// off per trace packet).
|
|
return output.SerializeAsString();
|
|
}
|
|
|
|
int Main(int argc, const char** argv) {
|
|
if (argc < 3) {
|
|
PERFETTO_LOG("Usage: %s input output", argv[0]);
|
|
return 1;
|
|
}
|
|
const char* in_path = argv[1];
|
|
const char* out_path = argv[2];
|
|
|
|
std::string raw;
|
|
if (!base::ReadFile(in_path, &raw)) {
|
|
PERFETTO_PLOG("ReadFile");
|
|
return 1;
|
|
}
|
|
|
|
std::string raw_out = ReEncode(raw);
|
|
WriteToFile(raw_out, out_path);
|
|
return 0;
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace compact_reencode
|
|
} // namespace perfetto
|
|
|
|
int main(int argc, const char** argv) {
|
|
return perfetto::compact_reencode::Main(argc, argv);
|
|
}
|