blob: 9849090e35a3adb96dc3643a15c19967a8d0c872 [file] [log] [blame]
/*
* Copyright (C) 2018 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_to_profile.h"
#include <inttypes.h>
#include <algorithm>
#include <map>
#include <vector>
#include "tools/trace_to_text/utils.h"
#include "perfetto/base/file_utils.h"
#include "perfetto/base/logging.h"
#include "perfetto/base/temp_file.h"
#include "perfetto/trace/profiling/profile_packet.pb.h"
#include "perfetto/trace/trace.pb.h"
#include "perfetto/trace/trace_packet.pb.h"
#include "third_party/pprof/profile.pb.h"
namespace perfetto {
namespace trace_to_text {
namespace {
using ::perfetto::protos::ProfilePacket;
using GLine = ::perftools::profiles::Line;
using GMapping = ::perftools::profiles::Mapping;
using GLocation = ::perftools::profiles::Location;
using GProfile = ::perftools::profiles::Profile;
using GValueType = ::perftools::profiles::ValueType;
using GFunction = ::perftools::profiles::Function;
using GSample = ::perftools::profiles::Sample;
void DumpProfilePacket(const ProfilePacket& packet,
const std::string& file_prefix) {
std::map<uint64_t, std::string> string_lookup;
for (const ProfilePacket::InternedString& interned_string : packet.strings())
string_lookup.emplace(interned_string.id(), interned_string.str());
std::map<uint64_t, const std::vector<uint64_t>> callstack_lookup;
for (const ProfilePacket::Callstack& callstack : packet.callstacks()) {
std::vector<uint64_t> frame_ids(
static_cast<size_t>(callstack.frame_ids().size()));
std::reverse_copy(callstack.frame_ids().cbegin(),
callstack.frame_ids().cend(), frame_ids.begin());
callstack_lookup.emplace(callstack.id(), std::move(frame_ids));
}
std::map<std::string, uint64_t> string_table;
string_table[""] = 0;
string_table["space"] = 1;
string_table["bytes"] = 2;
GProfile profile;
GValueType* value_type = profile.add_sample_type();
// ["space", "bytes"];
value_type->set_type(1);
value_type->set_type(2);
for (const ProfilePacket::Mapping& mapping : packet.mappings()) {
GMapping* gmapping = profile.add_mapping();
gmapping->set_id(mapping.id());
gmapping->set_memory_start(mapping.start());
gmapping->set_memory_limit(mapping.end());
gmapping->set_file_offset(mapping.offset());
std::string filename;
for (uint64_t str_id : mapping.path_string_ids()) {
auto it = string_lookup.find(str_id);
if (it == string_lookup.end()) {
PERFETTO_ELOG("Mapping %" PRIu64
" referring to invalid string_id %" PRIu64 ".",
static_cast<uint64_t>(mapping.id()), str_id);
continue;
}
filename += "/" + it->second;
}
decltype(string_table)::iterator it;
std::tie(it, std::ignore) =
string_table.emplace(filename, string_table.size());
gmapping->set_filename(static_cast<int64_t>(it->second));
}
std::set<uint64_t> functions_to_dump;
for (const ProfilePacket::Frame& frame : packet.frames()) {
GLocation* glocation = profile.add_location();
glocation->set_id(frame.id());
glocation->set_mapping_id(frame.mapping_id());
// TODO(fmayer): This is probably incorrect. Probably should be abs pc.
glocation->set_address(frame.rel_pc());
GLine* gline = glocation->add_line();
gline->set_function_id(frame.function_name_id());
functions_to_dump.emplace(frame.function_name_id());
}
for (uint64_t function_name_id : functions_to_dump) {
auto str_it = string_lookup.find(function_name_id);
if (str_it == string_lookup.end()) {
PERFETTO_ELOG("Function referring to invalid string id %" PRIu64,
function_name_id);
continue;
}
decltype(string_table)::iterator it;
std::tie(it, std::ignore) =
string_table.emplace(str_it->second, string_table.size());
GFunction* gfunction = profile.add_function();
gfunction->set_id(function_name_id);
gfunction->set_name(static_cast<int64_t>(it->second));
}
// We keep the interning table as string -> uint64_t for fast and easy
// lookup. When dumping, we need to turn it into a uint64_t -> string
// table so we get it sorted by key order.
std::map<uint64_t, std::string> inverted_string_table;
for (const auto& p : string_table)
inverted_string_table[p.second] = p.first;
for (const auto& p : inverted_string_table)
profile.add_string_table(p.second);
for (const ProfilePacket::ProcessHeapSamples& samples :
packet.process_dumps()) {
GProfile cur_profile = profile;
for (const ProfilePacket::HeapSample& sample : samples.samples()) {
GSample* gsample = cur_profile.add_sample();
auto it = callstack_lookup.find(sample.callstack_id());
if (it == callstack_lookup.end()) {
PERFETTO_ELOG("Callstack referring to invalid callstack id %" PRIu64,
static_cast<uint64_t>(sample.callstack_id()));
continue;
}
for (uint64_t frame_id : it->second)
gsample->add_location_id(frame_id);
gsample->add_value(static_cast<int64_t>(sample.cumulative_allocated() -
sample.cumulative_freed()));
}
std::string filename = file_prefix + std::to_string(samples.pid()) + ".pb";
base::ScopedFile fd(base::OpenFile(filename, O_CREAT | O_WRONLY, 0700));
if (!fd)
PERFETTO_FATAL("Failed to open %s", filename.c_str());
std::string serialized = cur_profile.SerializeAsString();
PERFETTO_CHECK(base::WriteAll(*fd, serialized.c_str(), serialized.size()) ==
static_cast<ssize_t>(serialized.size()));
}
}
} // namespace
int TraceToProfile(std::istream* input, std::ostream* output) {
std::string temp_dir = "/tmp/heap_profile-XXXXXXX";
size_t itr = 0;
PERFETTO_CHECK(mkdtemp(&temp_dir[0]));
ForEachPacketInTrace(input, [&temp_dir,
&itr](const protos::TracePacket& packet) {
if (!packet.has_profile_packet())
return;
DumpProfilePacket(packet.profile_packet(),
temp_dir + "/heap_dump." + std::to_string(++itr) + ".");
});
*output << "Wrote profiles to " << temp_dir << std::endl;
return 0;
}
} // namespace trace_to_text
} // namespace perfetto