/*
* 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/trace_processor_context.h"
#include "perfetto/base/logging.h"
namespace perfetto {
namespace trace_processor {
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)
return;
const StringId name_id = opt_name_id.value();
auto opt_build_id = FindString(pidx, mapping.build_id);
if (!opt_build_id)
return;
const StringId build_id = opt_build_id.value();
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)
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) {
int64_t parent_id = 0;
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;
}
TraceStorage::HeapProfileAllocations::Row alloc_row{
static_cast<int64_t>(alloc.timestamp), static_cast<int64_t>(alloc.pid),
static_cast<int64_t>(it->second), static_cast<int64_t>(alloc.alloc_count),
static_cast<int64_t>(alloc.self_allocated)};
TraceStorage::HeapProfileAllocations::Row free_row{
static_cast<int64_t>(alloc.timestamp), static_cast<int64_t>(alloc.pid),
static_cast<int64_t>(it->second), -static_cast<int64_t>(alloc.free_count),
-static_cast<int64_t>(alloc.self_freed)};
context_->storage->mutable_heap_profile_allocations()->Insert(alloc_row);
context_->storage->mutable_heap_profile_allocations()->Insert(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);
}
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