blob: ffb970d6dc8356408b73b8ee9532cd9087a63470 [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 "src/trace_processor/heap_profile_tracker.h"
#include "src/trace_processor/process_tracker.h"
#include "src/trace_processor/trace_processor_context.h"
#include "perfetto/base/logging.h"
namespace perfetto {
namespace trace_processor {
namespace {
std::string ToHex(const char* build_id, size_t size) {
std::string hex_build_id(2 * size + 1, 'x');
for (size_t i = 0; i < size; ++i) {
// snprintf prints 3 characters, the two hex digits and a null byte. As we
// write left to write, we keep overwriting the nullbytes, except for the
// last call to snprintf.
snprintf(&(hex_build_id[2 * i]), 3, "%02hhx", build_id[i]);
}
// Remove the trailing nullbyte produced by the last snprintf.
hex_build_id.resize(2 * size);
return hex_build_id;
}
} // namespace
HeapProfileTracker::HeapProfileTracker(TraceProcessorContext* context)
: context_(context), empty_(context_->storage->InternString({"", 0})) {}
HeapProfileTracker::~HeapProfileTracker() = default;
void HeapProfileTracker::AddString(ProfileIndex pidx,
SourceStringId id,
StringId str) {
string_map_.emplace(std::make_pair(pidx, id), str);
}
void HeapProfileTracker::AddMapping(ProfileIndex pidx,
SourceMappingId id,
const SourceMapping& mapping) {
auto opt_name_id = FindString(pidx, mapping.name_id);
if (!opt_name_id) {
context_->storage->IncrementStats(stats::heapprofd_invalid_string_id);
PERFETTO_DFATAL("Invalid string.");
return;
}
const StringId name_id = opt_name_id.value();
auto opt_build_id = FindString(pidx, mapping.build_id);
if (!opt_build_id) {
context_->storage->IncrementStats(stats::heapprofd_invalid_string_id);
PERFETTO_DFATAL("Invalid string.");
return;
}
const StringId raw_build_id = opt_build_id.value();
NullTermStringView raw_build_id_str =
context_->storage->GetString(raw_build_id);
StringId build_id = empty_;
if (raw_build_id_str.size() > 0) {
std::string hex_build_id =
ToHex(raw_build_id_str.c_str(), raw_build_id_str.size());
build_id = context_->storage->InternString(base::StringView(hex_build_id));
}
TraceStorage::HeapProfileMappings::Row row{
build_id,
static_cast<int64_t>(mapping.offset),
static_cast<int64_t>(mapping.start),
static_cast<int64_t>(mapping.end),
static_cast<int64_t>(mapping.load_bias),
name_id};
int64_t cur_row;
auto it = mapping_idx_.find(row);
if (it != mapping_idx_.end()) {
cur_row = it->second;
} else {
cur_row = context_->storage->mutable_heap_profile_mappings()->Insert(row);
mapping_idx_.emplace(row, cur_row);
}
mappings_.emplace(std::make_pair(pidx, id), cur_row);
}
void HeapProfileTracker::AddFrame(ProfileIndex pidx,
SourceFrameId id,
const SourceFrame& frame) {
auto opt_str_id = FindString(pidx, frame.name_id);
if (!opt_str_id) {
context_->storage->IncrementStats(stats::heapprofd_invalid_string_id);
PERFETTO_DFATAL("Invalid string.");
return;
}
const StringId& str_id = opt_str_id.value();
auto mapping_it = mappings_.find({pidx, frame.mapping_id});
if (mapping_it == mappings_.end()) {
context_->storage->IncrementStats(stats::heapprofd_invalid_mapping_id);
PERFETTO_DFATAL("Invalid mapping.");
return;
}
int64_t mapping_row = mapping_it->second;
TraceStorage::HeapProfileFrames::Row row{str_id, mapping_row,
static_cast<int64_t>(frame.rel_pc)};
int64_t cur_row;
auto it = frame_idx_.find(row);
if (it != frame_idx_.end()) {
cur_row = it->second;
} else {
cur_row = context_->storage->mutable_heap_profile_frames()->Insert(row);
frame_idx_.emplace(row, cur_row);
}
frames_.emplace(std::make_pair(pidx, id), cur_row);
}
void HeapProfileTracker::AddCallstack(ProfileIndex pidx,
SourceCallstackId id,
const SourceCallstack& frame_ids) {
// TODO(fmayer): This should be NULL.
int64_t parent_id = -1;
for (size_t depth = 0; depth < frame_ids.size(); ++depth) {
std::vector<uint64_t> frame_subset = frame_ids;
frame_subset.resize(depth + 1);
auto self_it = callstacks_from_frames_.find({pidx, frame_subset});
if (self_it != callstacks_from_frames_.end()) {
parent_id = self_it->second;
continue;
}
uint64_t frame_id = frame_ids[depth];
auto it = frames_.find({pidx, frame_id});
if (it == frames_.end()) {
context_->storage->IncrementStats(stats::heapprofd_invalid_frame_id);
PERFETTO_DFATAL("Unknown frames.");
return;
}
int64_t frame_row = it->second;
TraceStorage::HeapProfileCallsites::Row row{static_cast<int64_t>(depth),
parent_id, frame_row};
int64_t self_id;
auto callsite_it = callsite_idx_.find(row);
if (callsite_it != callsite_idx_.end()) {
self_id = callsite_it->second;
} else {
self_id =
context_->storage->mutable_heap_profile_callsites()->Insert(row);
callsite_idx_.emplace(row, self_id);
}
parent_id = self_id;
}
callstacks_.emplace(std::make_pair(pidx, id), parent_id);
}
void HeapProfileTracker::AddAllocation(ProfileIndex pidx,
const SourceAllocation& alloc) {
auto it = callstacks_.find({pidx, alloc.callstack_id});
if (it == callstacks_.end()) {
context_->storage->IncrementStats(stats::heapprofd_invalid_callstack_id);
PERFETTO_DFATAL("Unknown callstack %" PRIu64 " : %zu", alloc.callstack_id,
callstacks_.size());
return;
}
int64_t callstack_id = static_cast<int64_t>(it->second);
UniquePid upid = context_->process_tracker->GetOrCreateProcess(
static_cast<uint32_t>(alloc.pid));
TraceStorage::HeapProfileAllocations::Row alloc_row{
alloc.timestamp, upid, callstack_id,
static_cast<int64_t>(alloc.alloc_count),
static_cast<int64_t>(alloc.self_allocated)};
TraceStorage::HeapProfileAllocations::Row free_row{
alloc.timestamp, upid, callstack_id,
-static_cast<int64_t>(alloc.free_count),
-static_cast<int64_t>(alloc.self_freed)};
TraceStorage::HeapProfileAllocations::Row alloc_delta = alloc_row;
TraceStorage::HeapProfileAllocations::Row free_delta = free_row;
auto prev_alloc_it = prev_alloc_.find({upid, callstack_id});
if (prev_alloc_it == prev_alloc_.end()) {
std::tie(prev_alloc_it, std::ignore) =
prev_alloc_.emplace(std::make_pair(upid, callstack_id),
TraceStorage::HeapProfileAllocations::Row{});
}
TraceStorage::HeapProfileAllocations::Row& prev_alloc = prev_alloc_it->second;
alloc_delta.count -= prev_alloc.count;
alloc_delta.size -= prev_alloc.size;
auto prev_free_it = prev_free_.find({upid, callstack_id});
if (prev_free_it == prev_free_.end()) {
std::tie(prev_free_it, std::ignore) =
prev_free_.emplace(std::make_pair(upid, callstack_id),
TraceStorage::HeapProfileAllocations::Row{});
}
TraceStorage::HeapProfileAllocations::Row& prev_free = prev_free_it->second;
free_delta.count -= prev_free.count;
free_delta.size -= prev_free.size;
if (alloc_delta.count)
context_->storage->mutable_heap_profile_allocations()->Insert(alloc_delta);
if (free_delta.count)
context_->storage->mutable_heap_profile_allocations()->Insert(free_delta);
prev_alloc = alloc_row;
prev_free = free_row;
}
void HeapProfileTracker::StoreAllocation(ProfileIndex pidx,
SourceAllocation alloc) {
pending_allocs_.emplace_back(pidx, std::move(alloc));
}
void HeapProfileTracker::ApplyAllAllocations() {
for (const auto& p : pending_allocs_)
AddAllocation(p.first, p.second);
pending_allocs_.clear();
}
int64_t HeapProfileTracker::GetDatabaseFrameIdForTesting(
ProfileIndex pidx,
SourceFrameId frame_id) {
auto it = frames_.find({pidx, frame_id});
if (it == frames_.end()) {
PERFETTO_DFATAL("Invalid frame.");
return -1;
}
return it->second;
}
base::Optional<StringId> HeapProfileTracker::FindString(ProfileIndex pidx,
SourceStringId id) {
base::Optional<StringId> res;
if (id == 0) {
res = empty_;
return res;
}
auto it = string_map_.find({pidx, id});
if (it == string_map_.end()) {
context_->storage->IncrementStats(stats::heapprofd_invalid_string_id);
PERFETTO_DFATAL("Invalid string.");
return res;
}
res = it->second;
return res;
}
} // namespace trace_processor
} // namespace perfetto