blob: ea2010869291faec3c8c18ee0b0f37e4bce7396b [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/importers/proto/heap_profile_tracker.h"
#include "perfetto/base/logging.h"
#include "src/trace_processor/importers/common/process_tracker.h"
#include "src/trace_processor/types/trace_processor_context.h"
#include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
#include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
namespace perfetto {
namespace trace_processor {
HeapProfileTracker::HeapProfileTracker(TraceProcessorContext* context)
: context_(context),
empty_(context_->storage->InternString({"", 0})),
art_heap_(context_->storage->InternString("com.android.art")) {}
HeapProfileTracker::~HeapProfileTracker() = default;
void HeapProfileTracker::SetProfilePacketIndex(uint32_t seq_id,
uint64_t index) {
SequenceState& sequence_state = sequence_state_[seq_id];
bool dropped_packet = false;
// heapprofd starts counting at index = 0.
if (!sequence_state.prev_index && index != 0) {
dropped_packet = true;
}
if (sequence_state.prev_index && *sequence_state.prev_index + 1 != index) {
dropped_packet = true;
}
if (dropped_packet) {
if (sequence_state.prev_index) {
PERFETTO_ELOG("Missing packets between %" PRIu64 " and %" PRIu64,
*sequence_state.prev_index, index);
} else {
PERFETTO_ELOG("Invalid first packet index %" PRIu64 " (!= 0)", index);
}
context_->storage->IncrementStats(stats::heapprofd_missing_packet);
}
sequence_state.prev_index = index;
}
void HeapProfileTracker::AddAllocation(
uint32_t seq_id,
SequenceStackProfileTracker* sequence_stack_profile_tracker,
const SourceAllocation& alloc,
const SequenceStackProfileTracker::InternLookup* intern_lookup) {
SequenceState& sequence_state = sequence_state_[seq_id];
auto opt_callstack_id = sequence_stack_profile_tracker->FindOrInsertCallstack(
alloc.callstack_id, intern_lookup);
if (!opt_callstack_id)
return;
CallsiteId callstack_id = *opt_callstack_id;
UniquePid upid = context_->process_tracker->GetOrCreateProcess(
static_cast<uint32_t>(alloc.pid));
tables::HeapProfileAllocationTable::Row alloc_row{
alloc.timestamp,
upid,
alloc.heap_name,
callstack_id,
static_cast<int64_t>(alloc.alloc_count),
static_cast<int64_t>(alloc.self_allocated)};
tables::HeapProfileAllocationTable::Row free_row{
alloc.timestamp,
upid,
alloc.heap_name,
callstack_id,
-static_cast<int64_t>(alloc.free_count),
-static_cast<int64_t>(alloc.self_freed)};
auto prev_alloc_it = sequence_state.prev_alloc.find({upid, callstack_id});
if (prev_alloc_it == sequence_state.prev_alloc.end()) {
std::tie(prev_alloc_it, std::ignore) = sequence_state.prev_alloc.emplace(
std::make_pair(upid, callstack_id),
tables::HeapProfileAllocationTable::Row{});
}
tables::HeapProfileAllocationTable::Row& prev_alloc = prev_alloc_it->second;
auto prev_free_it = sequence_state.prev_free.find({upid, callstack_id});
if (prev_free_it == sequence_state.prev_free.end()) {
std::tie(prev_free_it, std::ignore) = sequence_state.prev_free.emplace(
std::make_pair(upid, callstack_id),
tables::HeapProfileAllocationTable::Row{});
}
tables::HeapProfileAllocationTable::Row& prev_free = prev_free_it->second;
std::set<CallsiteId>& callstacks_for_source_callstack_id =
sequence_state.seen_callstacks[SourceAllocationIndex{
upid, alloc.callstack_id, alloc.heap_name}];
bool new_callstack;
std::tie(std::ignore, new_callstack) =
callstacks_for_source_callstack_id.emplace(callstack_id);
if (new_callstack) {
sequence_state.alloc_correction[alloc.callstack_id] = prev_alloc;
sequence_state.free_correction[alloc.callstack_id] = prev_free;
}
auto alloc_correction_it =
sequence_state.alloc_correction.find(alloc.callstack_id);
if (alloc_correction_it != sequence_state.alloc_correction.end()) {
const auto& alloc_correction = alloc_correction_it->second;
alloc_row.count += alloc_correction.count;
alloc_row.size += alloc_correction.size;
}
auto free_correction_it =
sequence_state.free_correction.find(alloc.callstack_id);
if (free_correction_it != sequence_state.free_correction.end()) {
const auto& free_correction = free_correction_it->second;
free_row.count += free_correction.count;
free_row.size += free_correction.size;
}
tables::HeapProfileAllocationTable::Row alloc_delta = alloc_row;
tables::HeapProfileAllocationTable::Row free_delta = free_row;
alloc_delta.count -= prev_alloc.count;
alloc_delta.size -= prev_alloc.size;
free_delta.count -= prev_free.count;
free_delta.size -= prev_free.size;
if (alloc_delta.count < 0 || alloc_delta.size < 0 || free_delta.count > 0 ||
free_delta.size > 0) {
PERFETTO_DLOG("Non-monotonous allocation.");
context_->storage->IncrementIndexedStats(stats::heapprofd_malformed_packet,
static_cast<int>(upid));
return;
}
// Dump at max profiles do not have .count set.
if (alloc_delta.count || alloc_delta.size) {
context_->storage->mutable_heap_profile_allocation_table()->Insert(
alloc_delta);
}
// ART only reports allocations, and not frees. This throws off our logic
// that assumes that if a new object was allocated with the same address,
// the old one has to have been freed in the meantime.
// See HeapTracker::RecordMalloc in bookkeeping.cc.
if (alloc.heap_name != art_heap_ && (free_delta.count || free_delta.size)) {
context_->storage->mutable_heap_profile_allocation_table()->Insert(
free_delta);
}
prev_alloc = alloc_row;
prev_free = free_row;
}
void HeapProfileTracker::StoreAllocation(uint32_t seq_id,
SourceAllocation alloc) {
SequenceState& sequence_state = sequence_state_[seq_id];
sequence_state.pending_allocs.emplace_back(std::move(alloc));
}
void HeapProfileTracker::CommitAllocations(
uint32_t seq_id,
SequenceStackProfileTracker* sequence_stack_profile_tracker,
const SequenceStackProfileTracker::InternLookup* intern_lookup) {
SequenceState& sequence_state = sequence_state_[seq_id];
for (const auto& p : sequence_state.pending_allocs)
AddAllocation(seq_id, sequence_stack_profile_tracker, p, intern_lookup);
sequence_state.pending_allocs.clear();
}
void HeapProfileTracker::FinalizeProfile(
uint32_t seq_id,
SequenceStackProfileTracker* sequence_stack_profile_tracker,
const SequenceStackProfileTracker::InternLookup* intern_lookup) {
CommitAllocations(seq_id, sequence_stack_profile_tracker, intern_lookup);
sequence_stack_profile_tracker->ClearIndices();
}
void HeapProfileTracker::NotifyEndOfFile() {
for (const auto& key_and_sequence_state : sequence_state_) {
const SequenceState& sequence_state = key_and_sequence_state.second;
if (!sequence_state.pending_allocs.empty()) {
context_->storage->IncrementStats(stats::heapprofd_non_finalized_profile);
}
}
}
} // namespace trace_processor
} // namespace perfetto