blob: c171993d3a8da8beda4b6abc0bff4ffae4da9771 [file] [log] [blame]
/*
* 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 "tools/trace_to_text/trace_symbol_table.h"
#include <algorithm>
#include <map>
#include <set>
#include <string>
#include <vector>
#include <inttypes.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "perfetto/protozero/proto_utils.h"
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/optional.h"
#include "perfetto/ext/base/pipe.h"
#include "perfetto/ext/base/utils.h"
#include "tools/trace_to_text/symbolizer.h"
#include "tools/trace_to_text/utils.h"
#include "protos/perfetto/trace/profiling/profile_common.pb.h"
#include "protos/perfetto/trace/profiling/profile_packet.pb.h"
#include "protos/perfetto/trace/trace.pbzero.h"
#include "protos/perfetto/trace/trace_packet.pb.h"
#include "protos/perfetto/trace/interned_data/interned_data.pb.h"
namespace perfetto {
namespace trace_to_text {
namespace {
using ::protozero::proto_utils::kMessageLengthFieldSize;
using ::protozero::proto_utils::MakeTagLengthDelimited;
using ::protozero::proto_utils::WriteVarInt;
using ::perfetto::protos::Callstack;
using ::perfetto::protos::Frame;
using ::perfetto::protos::InternedData;
using ::perfetto::protos::InternedString;
using ::perfetto::protos::Mapping;
using ::perfetto::protos::ProfiledFrameSymbols;
using ::perfetto::protos::ProfilePacket;
void WriteTracePacket(const std::string& str, std::ostream* output) {
constexpr char kPreamble =
MakeTagLengthDelimited(protos::pbzero::Trace::kPacketFieldNumber);
uint8_t length_field[10];
uint8_t* end = WriteVarInt(str.size(), length_field);
*output << kPreamble;
*output << std::string(length_field, end);
*output << str;
}
} // namespace
bool TraceSymbolTable::AddInternedString(const InternedString& string) {
interned_strings_.emplace(string.iid(), string.str());
max_string_intern_id_ =
std::max<uint64_t>(string.iid(), max_string_intern_id_);
return true;
}
bool TraceSymbolTable::AddMapping(const Mapping& mapping) {
mappings_.emplace(mapping.iid(), ResolveMapping(mapping));
return true;
}
bool TraceSymbolTable::AddFrame(const Frame& frame) {
if (symbols_for_frame_.find(frame.iid()) == symbols_for_frame_.end()) {
to_symbolize_[frame.mapping_id()].emplace_back(frame.iid());
rel_pc_for_frame_[frame.iid()] = frame.rel_pc();
}
return true;
}
void TraceSymbolTable::WriteResult(std::ostream* output,
uint32_t seq_id) const {
protos::TracePacket intern_packet;
intern_packet.set_trusted_packet_sequence_id(seq_id);
std::vector<protos::TracePacket> frame_symbol_packets;
auto max_string_intern_id = max_string_intern_id_;
std::map<std::string, uint64_t> new_interned_strings;
protos::InternedData* interned_data = intern_packet.mutable_interned_data();
for (const auto& p : symbols_for_frame_) {
uint64_t frame_iid = p.first;
const std::vector<SymbolizedFrame>& frames = p.second;
for (const SymbolizedFrame& frame : frames) {
uint64_t& function_name_id = new_interned_strings[frame.function_name];
if (function_name_id == 0) {
function_name_id = ++max_string_intern_id;
protos::InternedString* str = interned_data->add_function_names();
str->set_iid(function_name_id);
str->set_str(
reinterpret_cast<const uint8_t*>(frame.function_name.c_str()),
frame.function_name.size());
}
uint64_t& source_file_id = new_interned_strings[frame.file_name];
if (source_file_id == 0) {
source_file_id = ++max_string_intern_id;
protos::InternedString* str = interned_data->add_source_paths();
str->set_iid(source_file_id);
str->set_str(reinterpret_cast<const uint8_t*>(frame.file_name.c_str()),
frame.file_name.size());
}
}
protos::TracePacket symbol_packet;
symbol_packet.set_trusted_packet_sequence_id(seq_id);
protos::ProfiledFrameSymbols* sym =
symbol_packet.mutable_profiled_frame_symbols();
sym->set_frame_iid(frame_iid);
for (const SymbolizedFrame& frame : frames) {
sym->add_function_name_id(new_interned_strings[frame.function_name]);
sym->add_line_number(frame.line);
sym->add_file_name_id(new_interned_strings[frame.file_name]);
}
// TODO(138725313): Cleanup - this should be outside the interned section.
*interned_data->add_profiled_frame_symbols() = *sym;
frame_symbol_packets.push_back(std::move(symbol_packet));
}
WriteTracePacket(intern_packet.SerializeAsString(), output);
for (const auto& frame_symbol_packet : frame_symbol_packets) {
WriteTracePacket(frame_symbol_packet.SerializeAsString(), output);
}
}
bool TraceSymbolTable::AddProfiledFrameSymbols(
const protos::ProfiledFrameSymbols& symbol) {
std::vector<SymbolizedFrame> frames;
const auto& name_ids = symbol.function_name_id();
const auto& file_ids = symbol.file_name_id();
const auto& lines = symbol.line_number();
if (name_ids.size() != file_ids.size() || file_ids.size() != lines.size()) {
PERFETTO_DFATAL_OR_ELOG("Invalid ProfiledFrameSymbols");
return false;
}
for (int i = 0; i < name_ids.size(); ++i) {
SymbolizedFrame frame{ResolveString(name_ids.Get(i)),
ResolveString(file_ids.Get(i)), lines.Get(i)};
frames.emplace_back(std::move(frame));
}
symbols_for_frame_[symbol.frame_iid()] = std::move(frames);
return true;
}
bool TraceSymbolTable::Finalize() {
if (symbolizer_ == nullptr)
return true;
for (const auto& mapping_and_frame_iids : to_symbolize_) {
auto it = mappings_.find(mapping_and_frame_iids.first);
if (it == mappings_.end()) {
PERFETTO_DFATAL_OR_ELOG("Invalid mapping.");
return false;
}
const ResolvedMapping& mapping = it->second;
const std::vector<uint64_t>& frame_iids = mapping_and_frame_iids.second;
std::vector<uint64_t> rel_pcs;
for (uint64_t frame_iid : frame_iids)
rel_pcs.emplace_back(rel_pc_for_frame_[frame_iid]);
auto result =
symbolizer_->Symbolize(mapping.mapping_name, mapping.build_id, rel_pcs);
if (result.empty())
continue;
if (result.size() != frame_iids.size()) {
PERFETTO_DFATAL_OR_ELOG("Invalid response from symbolizer.");
return false;
}
for (size_t i = 0; i < frame_iids.size(); ++i) {
if (!result.empty())
symbols_for_frame_[frame_iids[i]] = std::move(result[i]);
}
}
return true;
}
const std::vector<SymbolizedFrame>* TraceSymbolTable::Get(
uint64_t frame_iid) const {
auto it = symbols_for_frame_.find(frame_iid);
if (it == symbols_for_frame_.end())
return nullptr;
return &it->second;
}
const std::string& TraceSymbolTable::ResolveString(uint64_t iid) {
auto it = interned_strings_.find(iid);
if (it == interned_strings_.end())
return kEmptyString;
return it->second;
}
TraceSymbolTable::ResolvedMapping TraceSymbolTable::ResolveMapping(
const Mapping& mapping) {
std::string path;
for (uint64_t iid : mapping.path_string_ids()) {
path += "/";
path += ResolveString(iid);
}
return {std::move(path), ResolveString(mapping.build_id())};
}
} // namespace trace_to_text
} // namespace perfetto