blob: 5dd2714907d554b5ebe615813641f40c075f2206 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/resource_coordinator/memory_instrumentation/graph_processor.h"
#include "base/memory/shared_memory_tracker.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace memory_instrumentation {
using base::ProcessId;
using base::trace_event::MemoryAllocatorDump;
using base::trace_event::MemoryAllocatorDumpGuid;
using base::trace_event::MemoryDumpArgs;
using base::trace_event::MemoryDumpLevelOfDetail;
using base::trace_event::ProcessMemoryDump;
using Edge = GlobalDumpGraph::Edge;
using Node = GlobalDumpGraph::Node;
using Process = GlobalDumpGraph::Process;
namespace {
const MemoryAllocatorDumpGuid kEmptyGuid;
} // namespace
class GraphProcessorTest : public testing::Test {
public:
GraphProcessorTest() {}
void MarkImplicitWeakParentsRecursively(Node* node) {
GraphProcessor::MarkImplicitWeakParentsRecursively(node);
}
void MarkWeakOwnersAndChildrenRecursively(Node* node) {
std::set<const Node*> visited;
GraphProcessor::MarkWeakOwnersAndChildrenRecursively(node, &visited);
}
void RemoveWeakNodesRecursively(Node* node) {
GraphProcessor::RemoveWeakNodesRecursively(node);
}
void AssignTracingOverhead(base::StringPiece allocator,
GlobalDumpGraph* global_graph,
Process* process) {
GraphProcessor::AssignTracingOverhead(allocator, global_graph, process);
}
GlobalDumpGraph::Node::Entry AggregateNumericWithNameForNode(
Node* node,
base::StringPiece name) {
return GraphProcessor::AggregateNumericWithNameForNode(node, name);
}
void AggregateNumericsRecursively(Node* node) {
GraphProcessor::AggregateNumericsRecursively(node);
}
void PropagateNumericsAndDiagnosticsRecursively(Node* node) {
GraphProcessor::PropagateNumericsAndDiagnosticsRecursively(node);
}
base::Optional<uint64_t> AggregateSizeForDescendantNode(Node* root,
Node* descendant) {
return GraphProcessor::AggregateSizeForDescendantNode(root, descendant);
}
void CalculateSizeForNode(Node* node) {
GraphProcessor::CalculateSizeForNode(node);
}
void CalculateDumpSubSizes(Node* node) {
GraphProcessor::CalculateDumpSubSizes(node);
}
void CalculateDumpOwnershipCoefficient(Node* node) {
GraphProcessor::CalculateDumpOwnershipCoefficient(node);
}
void CalculateDumpCumulativeOwnershipCoefficient(Node* node) {
GraphProcessor::CalculateDumpCumulativeOwnershipCoefficient(node);
}
void CalculateDumpEffectiveSize(Node* node) {
GraphProcessor::CalculateDumpEffectiveSize(node);
}
protected:
GlobalDumpGraph graph;
};
TEST_F(GraphProcessorTest, SmokeComputeMemoryGraph) {
std::map<ProcessId, const ProcessMemoryDump*> process_dumps;
MemoryDumpArgs dump_args = {MemoryDumpLevelOfDetail::DETAILED};
ProcessMemoryDump pmd(dump_args);
auto* source = pmd.CreateAllocatorDump("test1/test2/test3");
source->AddScalar(MemoryAllocatorDump::kNameSize,
MemoryAllocatorDump::kUnitsBytes, 10);
auto* target = pmd.CreateAllocatorDump("target");
pmd.AddOwnershipEdge(source->guid(), target->guid(), 10);
auto* weak =
pmd.CreateWeakSharedGlobalAllocatorDump(MemoryAllocatorDumpGuid(1));
process_dumps.emplace(1, &pmd);
auto global_dump = GraphProcessor::CreateMemoryGraph(process_dumps);
ASSERT_EQ(1u, global_dump->process_dump_graphs().size());
auto id_to_dump_it = global_dump->process_dump_graphs().find(1);
auto* first_child = id_to_dump_it->second->FindNode("test1");
ASSERT_NE(first_child, nullptr);
ASSERT_EQ(first_child->parent(), id_to_dump_it->second->root());
auto* second_child = first_child->GetChild("test2");
ASSERT_NE(second_child, nullptr);
ASSERT_EQ(second_child->parent(), first_child);
auto* third_child = second_child->GetChild("test3");
ASSERT_NE(third_child, nullptr);
ASSERT_EQ(third_child->parent(), second_child);
auto* direct = id_to_dump_it->second->FindNode("test1/test2/test3");
ASSERT_EQ(third_child, direct);
ASSERT_EQ(third_child->entries()->size(), 1ul);
auto size = third_child->entries()->find(MemoryAllocatorDump::kNameSize);
ASSERT_EQ(10ul, size->second.value_uint64);
ASSERT_TRUE(weak->flags() & MemoryAllocatorDump::Flags::WEAK);
auto& edges = global_dump->edges();
auto edge_it = edges.begin();
ASSERT_EQ(std::distance(edges.begin(), edges.end()), 1l);
ASSERT_EQ(edge_it->source(), direct);
ASSERT_EQ(edge_it->target(), id_to_dump_it->second->FindNode("target"));
ASSERT_EQ(edge_it->priority(), 10);
}
TEST_F(GraphProcessorTest, SmokeComputeSharedFootprint) {
std::map<ProcessId, const ProcessMemoryDump*> process_dumps;
MemoryDumpArgs dump_args = {MemoryDumpLevelOfDetail::DETAILED};
ProcessMemoryDump pmd1(dump_args);
ProcessMemoryDump pmd2(dump_args);
auto* p1_d1 = pmd1.CreateAllocatorDump("process1/dump1");
auto* p1_d2 = pmd1.CreateAllocatorDump("process1/dump2");
auto* p2_d1 = pmd2.CreateAllocatorDump("process2/dump1");
base::UnguessableToken token = base::UnguessableToken::Create();
// Done by SharedMemoryTracker.
size_t size = 256;
auto global_dump_guid =
base::SharedMemoryTracker::GetGlobalDumpIdForTracing(token);
auto local_dump_name =
base::SharedMemoryTracker::GetDumpNameForTracing(token);
MemoryAllocatorDump* local_dump_1 =
pmd1.CreateAllocatorDump(local_dump_name, MemoryAllocatorDumpGuid(1));
local_dump_1->AddScalar("virtual_size", MemoryAllocatorDump::kUnitsBytes,
size);
local_dump_1->AddScalar(MemoryAllocatorDump::kNameSize,
MemoryAllocatorDump::kUnitsBytes, size);
MemoryAllocatorDump* global_dump_1 =
pmd1.CreateSharedGlobalAllocatorDump(global_dump_guid);
global_dump_1->AddScalar(MemoryAllocatorDump::kNameSize,
MemoryAllocatorDump::kUnitsBytes, size);
pmd1.AddOverridableOwnershipEdge(local_dump_1->guid(), global_dump_1->guid(),
0 /* importance */);
MemoryAllocatorDump* local_dump_2 =
pmd2.CreateAllocatorDump(local_dump_name, MemoryAllocatorDumpGuid(2));
local_dump_2->AddScalar("virtual_size", MemoryAllocatorDump::kUnitsBytes,
size);
local_dump_2->AddScalar(MemoryAllocatorDump::kNameSize,
MemoryAllocatorDump::kUnitsBytes, size);
MemoryAllocatorDump* global_dump_2 =
pmd2.CreateSharedGlobalAllocatorDump(global_dump_guid);
pmd2.AddOverridableOwnershipEdge(local_dump_2->guid(), global_dump_2->guid(),
0 /* importance */);
// Done by each consumer of the shared memory.
pmd1.CreateSharedMemoryOwnershipEdge(p1_d1->guid(), token, 2);
pmd1.CreateSharedMemoryOwnershipEdge(p1_d2->guid(), token, 1);
pmd2.CreateSharedMemoryOwnershipEdge(p2_d1->guid(), token, 2);
process_dumps.emplace(1, &pmd1);
process_dumps.emplace(2, &pmd2);
auto graph = GraphProcessor::CreateMemoryGraph(process_dumps);
auto pid_to_sizes = GraphProcessor::ComputeSharedFootprintFromGraph(*graph);
ASSERT_EQ(pid_to_sizes[1], 128ul);
ASSERT_EQ(pid_to_sizes[2], 128ul);
}
TEST_F(GraphProcessorTest, ComputeSharedFootprintFromGraphSameImportance) {
Process* global_process = graph.shared_memory_graph();
Node* global_node = global_process->CreateNode(kEmptyGuid, "global/1", false);
global_node->AddEntry("size", Node::Entry::ScalarUnits::kBytes, 100);
Process* first = graph.CreateGraphForProcess(1);
Node* shared_1 = first->CreateNode(kEmptyGuid, "shared_memory/1", false);
Process* second = graph.CreateGraphForProcess(2);
Node* shared_2 = second->CreateNode(kEmptyGuid, "shared_memory/2", false);
graph.AddNodeOwnershipEdge(shared_1, global_node, 1);
graph.AddNodeOwnershipEdge(shared_2, global_node, 1);
auto pid_to_sizes = GraphProcessor::ComputeSharedFootprintFromGraph(graph);
ASSERT_EQ(pid_to_sizes[1], 50ul);
ASSERT_EQ(pid_to_sizes[2], 50ul);
}
TEST_F(GraphProcessorTest, ComputeSharedFootprintFromGraphSomeDiffImportance) {
Process* global_process = graph.shared_memory_graph();
Node* global_node = global_process->CreateNode(kEmptyGuid, "global/1", false);
global_node->AddEntry("size", Node::Entry::ScalarUnits::kBytes, 100);
Process* first = graph.CreateGraphForProcess(1);
Node* shared_1 = first->CreateNode(kEmptyGuid, "shared_memory/1", false);
Process* second = graph.CreateGraphForProcess(2);
Node* shared_2 = second->CreateNode(kEmptyGuid, "shared_memory/2", false);
Process* third = graph.CreateGraphForProcess(3);
Node* shared_3 = third->CreateNode(kEmptyGuid, "shared_memory/3", false);
Process* fourth = graph.CreateGraphForProcess(4);
Node* shared_4 = fourth->CreateNode(kEmptyGuid, "shared_memory/4", false);
Process* fifth = graph.CreateGraphForProcess(5);
Node* shared_5 = fifth->CreateNode(kEmptyGuid, "shared_memory/5", false);
graph.AddNodeOwnershipEdge(shared_1, global_node, 1);
graph.AddNodeOwnershipEdge(shared_2, global_node, 2);
graph.AddNodeOwnershipEdge(shared_3, global_node, 3);
graph.AddNodeOwnershipEdge(shared_4, global_node, 3);
graph.AddNodeOwnershipEdge(shared_5, global_node, 3);
auto pid_to_sizes = GraphProcessor::ComputeSharedFootprintFromGraph(graph);
ASSERT_EQ(pid_to_sizes[1], 0ul);
ASSERT_EQ(pid_to_sizes[2], 0ul);
ASSERT_EQ(pid_to_sizes[3], 33ul);
ASSERT_EQ(pid_to_sizes[4], 33ul);
ASSERT_EQ(pid_to_sizes[5], 33ul);
}
TEST_F(GraphProcessorTest, MarkWeakParentsSimple) {
Process* process = graph.CreateGraphForProcess(1);
Node* parent = process->CreateNode(kEmptyGuid, "parent", false);
Node* first = process->CreateNode(kEmptyGuid, "parent/first", true);
Node* second = process->CreateNode(kEmptyGuid, "parent/second", false);
// Case where one child is not weak.
parent->set_explicit(false);
first->set_explicit(true);
second->set_explicit(true);
// The function should be a no-op.
MarkImplicitWeakParentsRecursively(parent);
ASSERT_FALSE(parent->is_weak());
ASSERT_TRUE(first->is_weak());
ASSERT_FALSE(second->is_weak());
// Case where all children is weak.
second->set_weak(true);
// The function should mark parent as weak.
MarkImplicitWeakParentsRecursively(parent);
ASSERT_TRUE(parent->is_weak());
ASSERT_TRUE(first->is_weak());
ASSERT_TRUE(second->is_weak());
}
TEST_F(GraphProcessorTest, MarkWeakParentsComplex) {
Process* process = graph.CreateGraphForProcess(1);
// |first| is explicitly strong but |first_child| is implicitly so.
Node* parent = process->CreateNode(kEmptyGuid, "parent", false);
Node* first = process->CreateNode(kEmptyGuid, "parent/f", false);
Node* first_child = process->CreateNode(kEmptyGuid, "parent/f/c", false);
Node* first_gchild = process->CreateNode(kEmptyGuid, "parent/f/c/c", true);
parent->set_explicit(false);
first->set_explicit(true);
first_child->set_explicit(false);
first_gchild->set_explicit(true);
// That should lead to |first_child| marked implicitly weak.
MarkImplicitWeakParentsRecursively(parent);
ASSERT_FALSE(parent->is_weak());
ASSERT_FALSE(first->is_weak());
ASSERT_TRUE(first_child->is_weak());
ASSERT_TRUE(first_gchild->is_weak());
// Reset and change so that first is now only implicitly strong.
first->set_explicit(false);
first_child->set_weak(false);
// The whole chain should now be weak.
MarkImplicitWeakParentsRecursively(parent);
ASSERT_TRUE(parent->is_weak());
ASSERT_TRUE(first->is_weak());
ASSERT_TRUE(first_child->is_weak());
ASSERT_TRUE(first_gchild->is_weak());
}
TEST_F(GraphProcessorTest, MarkWeakOwners) {
Process* process = graph.CreateGraphForProcess(1);
// Make only the ultimate owned node weak.
Node* owner = process->CreateNode(kEmptyGuid, "owner", false);
Node* owned = process->CreateNode(kEmptyGuid, "owned", false);
Node* owned_2 = process->CreateNode(kEmptyGuid, "owned2", true);
graph.AddNodeOwnershipEdge(owner, owned, 0);
graph.AddNodeOwnershipEdge(owned, owned_2, 0);
// Starting from leaf node should lead to everything being weak.
MarkWeakOwnersAndChildrenRecursively(process->root());
ASSERT_TRUE(owner->is_weak());
ASSERT_TRUE(owned->is_weak());
ASSERT_TRUE(owned_2->is_weak());
}
TEST_F(GraphProcessorTest, MarkWeakParent) {
Process* process = graph.CreateGraphForProcess(1);
Node* parent = process->CreateNode(kEmptyGuid, "parent", true);
Node* child = process->CreateNode(kEmptyGuid, "parent/c", false);
Node* child_2 = process->CreateNode(kEmptyGuid, "parent/c/c", false);
// Starting from parent node should lead to everything being weak.
MarkWeakOwnersAndChildrenRecursively(process->root());
ASSERT_TRUE(parent->is_weak());
ASSERT_TRUE(child->is_weak());
ASSERT_TRUE(child_2->is_weak());
}
TEST_F(GraphProcessorTest, MarkWeakParentOwner) {
Process* process = graph.CreateGraphForProcess(1);
// Make only the parent node weak.
Node* parent = process->CreateNode(kEmptyGuid, "parent", true);
Node* child = process->CreateNode(kEmptyGuid, "parent/c", false);
Node* child_2 = process->CreateNode(kEmptyGuid, "parent/c/c", false);
Node* owner = process->CreateNode(kEmptyGuid, "owner", false);
graph.AddNodeOwnershipEdge(owner, parent, 0);
// Starting from parent node should lead to everything being weak.
MarkWeakOwnersAndChildrenRecursively(process->root());
ASSERT_TRUE(parent->is_weak());
ASSERT_TRUE(child->is_weak());
ASSERT_TRUE(child_2->is_weak());
ASSERT_TRUE(owner->is_weak());
}
TEST_F(GraphProcessorTest, RemoveWeakNodesRecursively) {
Process* process = graph.CreateGraphForProcess(1);
// Make only the child node weak.
Node* parent = process->CreateNode(kEmptyGuid, "parent", false);
Node* child = process->CreateNode(kEmptyGuid, "parent/c", true);
process->CreateNode(kEmptyGuid, "parent/c/c", false);
Node* owned = process->CreateNode(kEmptyGuid, "parent/owned", false);
graph.AddNodeOwnershipEdge(child, owned, 0);
// Starting from parent node should lead child and child_2 being
// removed and owned to have the edge from it removed.
RemoveWeakNodesRecursively(parent);
ASSERT_EQ(parent->children()->size(), 1ul);
ASSERT_EQ(parent->children()->begin()->second, owned);
ASSERT_TRUE(owned->owned_by_edges()->empty());
}
TEST_F(GraphProcessorTest, RemoveWeakNodesRecursivelyBetweenGraphs) {
Process* f_process = graph.CreateGraphForProcess(1);
Process* s_process = graph.CreateGraphForProcess(2);
// Make only the child node weak.
Node* child = f_process->CreateNode(kEmptyGuid, "c", true);
f_process->CreateNode(kEmptyGuid, "c/c", false);
Node* owned = s_process->CreateNode(kEmptyGuid, "owned", false);
graph.AddNodeOwnershipEdge(child, owned, 0);
// Starting from root node should lead child and child_2 being
// removed.
RemoveWeakNodesRecursively(f_process->root());
ASSERT_EQ(f_process->root()->children()->size(), 0ul);
ASSERT_EQ(s_process->root()->children()->size(), 1ul);
// This should be false until our next pass.
ASSERT_FALSE(owned->owned_by_edges()->empty());
RemoveWeakNodesRecursively(s_process->root());
// We should now have cleaned up the owned node's edges.
ASSERT_TRUE(owned->owned_by_edges()->empty());
}
TEST_F(GraphProcessorTest, AssignTracingOverhead) {
Process* process = graph.CreateGraphForProcess(1);
// Now add an allocator node.
process->CreateNode(kEmptyGuid, "malloc", false);
// If the tracing node does not exist, this should do nothing.
AssignTracingOverhead("malloc", &graph, process);
ASSERT_TRUE(process->root()->GetChild("malloc")->children()->empty());
// Now add a tracing node.
process->CreateNode(kEmptyGuid, "tracing", false);
// This should now add a node with the allocator.
AssignTracingOverhead("malloc", &graph, process);
ASSERT_NE(process->FindNode("malloc/allocated_objects/tracing_overhead"),
nullptr);
}
TEST_F(GraphProcessorTest, AggregateNumericWithNameForNode) {
Process* process = graph.CreateGraphForProcess(1);
Node* c1 = process->CreateNode(kEmptyGuid, "c1", false);
Node* c2 = process->CreateNode(kEmptyGuid, "c2", false);
Node* c3 = process->CreateNode(kEmptyGuid, "c3", false);
c1->AddEntry("random_numeric", Node::Entry::ScalarUnits::kBytes, 100);
c2->AddEntry("random_numeric", Node::Entry::ScalarUnits::kBytes, 256);
c3->AddEntry("other_numeric", Node::Entry::ScalarUnits::kBytes, 1000);
Node* root = process->root();
Node::Entry entry = AggregateNumericWithNameForNode(root, "random_numeric");
ASSERT_EQ(entry.value_uint64, 356ul);
ASSERT_EQ(entry.units, Node::Entry::ScalarUnits::kBytes);
}
TEST_F(GraphProcessorTest, AggregateNumericsRecursively) {
Process* process = graph.CreateGraphForProcess(1);
Node* c1 = process->CreateNode(kEmptyGuid, "c1", false);
Node* c2 = process->CreateNode(kEmptyGuid, "c2", false);
Node* c2_c1 = process->CreateNode(kEmptyGuid, "c2/c1", false);
Node* c2_c2 = process->CreateNode(kEmptyGuid, "c2/c2", false);
Node* c3_c1 = process->CreateNode(kEmptyGuid, "c3/c1", false);
Node* c3_c2 = process->CreateNode(kEmptyGuid, "c3/c2", false);
// If an entry already exists in the parent, the child should not
// ovewrite it. If nothing exists, then the child can aggregrate.
c1->AddEntry("random_numeric", Node::Entry::ScalarUnits::kBytes, 100);
c2->AddEntry("random_numeric", Node::Entry::ScalarUnits::kBytes, 256);
c2_c1->AddEntry("random_numeric", Node::Entry::ScalarUnits::kBytes, 256);
c2_c2->AddEntry("random_numeric", Node::Entry::ScalarUnits::kBytes, 256);
c3_c1->AddEntry("random_numeric", Node::Entry::ScalarUnits::kBytes, 10);
c3_c2->AddEntry("random_numeric", Node::Entry::ScalarUnits::kBytes, 10);
Node* root = process->root();
AggregateNumericsRecursively(root);
ASSERT_EQ(root->entries()->size(), 1ul);
auto entry = root->entries()->begin()->second;
ASSERT_EQ(entry.value_uint64, 376ul);
ASSERT_EQ(entry.units, Node::Entry::ScalarUnits::kBytes);
}
TEST_F(GraphProcessorTest, AggregateSizeForDescendantNode) {
Process* process = graph.CreateGraphForProcess(1);
Node* c1 = process->CreateNode(kEmptyGuid, "c1", false);
Node* c2 = process->CreateNode(kEmptyGuid, "c2", false);
Node* c2_c1 = process->CreateNode(kEmptyGuid, "c2/c1", false);
Node* c2_c2 = process->CreateNode(kEmptyGuid, "c2/c2", false);
Node* c3_c1 = process->CreateNode(kEmptyGuid, "c3/c1", false);
Node* c3_c2 = process->CreateNode(kEmptyGuid, "c3/c2", false);
c1->AddEntry("size", Node::Entry::ScalarUnits::kBytes, 100);
c2_c1->AddEntry("size", Node::Entry::ScalarUnits::kBytes, 256);
c2_c2->AddEntry("size", Node::Entry::ScalarUnits::kBytes, 256);
c3_c1->AddEntry("size", Node::Entry::ScalarUnits::kBytes, 10);
c3_c2->AddEntry("size", Node::Entry::ScalarUnits::kBytes, 10);
graph.AddNodeOwnershipEdge(c2_c2, c3_c2, 0);
// Aggregating root should give size of (100 + 256 + 10 * 2) = 376.
// |c2_c2| is not counted because it is owns by |c3_c2|.
Node* root = process->root();
ASSERT_EQ(376ul, *AggregateSizeForDescendantNode(root, root));
// Aggregating c2 should give size of (256 * 2) = 512. |c2_c2| is counted
// because |c3_c2| is not a child of |c2|.
ASSERT_EQ(512ul, *AggregateSizeForDescendantNode(c2, c2));
}
TEST_F(GraphProcessorTest, CalculateSizeForNode) {
Process* process = graph.CreateGraphForProcess(1);
Node* c1 = process->CreateNode(kEmptyGuid, "c1", false);
Node* c2 = process->CreateNode(kEmptyGuid, "c2", false);
Node* c2_c1 = process->CreateNode(kEmptyGuid, "c2/c1", false);
Node* c2_c2 = process->CreateNode(kEmptyGuid, "c2/c2", false);
Node* c3 = process->CreateNode(kEmptyGuid, "c3", false);
Node* c3_c1 = process->CreateNode(kEmptyGuid, "c3/c1", false);
Node* c3_c2 = process->CreateNode(kEmptyGuid, "c3/c2", false);
c1->AddEntry("size", Node::Entry::ScalarUnits::kBytes, 600);
c2_c1->AddEntry("size", Node::Entry::ScalarUnits::kBytes, 10);
c2_c2->AddEntry("size", Node::Entry::ScalarUnits::kBytes, 10);
c3->AddEntry("size", Node::Entry::ScalarUnits::kBytes, 600);
c3_c1->AddEntry("size", Node::Entry::ScalarUnits::kBytes, 256);
c3_c2->AddEntry("size", Node::Entry::ScalarUnits::kBytes, 256);
graph.AddNodeOwnershipEdge(c2_c2, c3_c2, 0);
// Compute size entry for |c2| since computations for |c2_c1| and |c2_c2|
// are already complete.
CalculateSizeForNode(c2);
// Check that |c2| now has a size entry of 20 (sum of children).
auto c2_entry = c2->entries()->begin()->second;
ASSERT_EQ(c2_entry.value_uint64, 20ul);
ASSERT_EQ(c2_entry.units, Node::Entry::ScalarUnits::kBytes);
// Compute size entry for |c3_c2| which should not change in size.
CalculateSizeForNode(c3_c2);
// Check that |c3_c2| now has unchanged size.
auto c3_c2_entry = c3_c2->entries()->begin()->second;
ASSERT_EQ(c3_c2_entry.value_uint64, 256ul);
ASSERT_EQ(c3_c2_entry.units, Node::Entry::ScalarUnits::kBytes);
// Compute size entry for |c3| which should add an unspecified node.
CalculateSizeForNode(c3);
// Check that |c3| has unchanged size.
auto c3_entry = c3->entries()->begin()->second;
ASSERT_EQ(c3_entry.value_uint64, 600ul);
ASSERT_EQ(c3_entry.units, Node::Entry::ScalarUnits::kBytes);
// Check that the unspecified node is a child of |c3| and has size
// 600 - 512 = 88.
Node* c3_child = c3->children()->find("<unspecified>")->second;
auto c3_child_entry = c3_child->entries()->begin()->second;
ASSERT_EQ(c3_child_entry.value_uint64, 88ul);
ASSERT_EQ(c3_child_entry.units, Node::Entry::ScalarUnits::kBytes);
// Compute size entry for |root| which should aggregate children sizes.
CalculateSizeForNode(process->root());
// Check that |root| has been assigned a size of 600 + 10 + 600 = 1210.
// Note that |c2_c2| is not counted because it ows |c3_c2| which is a
// descendant of |root|.
auto root_entry = process->root()->entries()->begin()->second;
ASSERT_EQ(root_entry.value_uint64, 1210ul);
ASSERT_EQ(root_entry.units, Node::Entry::ScalarUnits::kBytes);
}
TEST_F(GraphProcessorTest, CalculateDumpSubSizes) {
Process* process_1 = graph.CreateGraphForProcess(1);
Process* process_2 = graph.CreateGraphForProcess(2);
Node* parent_1 = process_1->CreateNode(kEmptyGuid, "parent", false);
Node* child_1 = process_1->CreateNode(kEmptyGuid, "parent/child", false);
Node* parent_2 = process_2->CreateNode(kEmptyGuid, "parent", false);
Node* child_2 = process_2->CreateNode(kEmptyGuid, "parent/child", false);
graph.AddNodeOwnershipEdge(parent_1, parent_2, 0);
process_1->root()->AddEntry("size", Node::Entry::ScalarUnits::kBytes, 4);
parent_1->AddEntry("size", Node::Entry::ScalarUnits::kBytes, 4);
child_1->AddEntry("size", Node::Entry::ScalarUnits::kBytes, 4);
process_2->root()->AddEntry("size", Node::Entry::ScalarUnits::kBytes, 5);
parent_2->AddEntry("size", Node::Entry::ScalarUnits::kBytes, 5);
child_2->AddEntry("size", Node::Entry::ScalarUnits::kBytes, 5);
// Each of these nodes should have owner/owned same as size itself.
CalculateDumpSubSizes(child_1);
ASSERT_EQ(child_1->not_owned_sub_size(), 4ul);
ASSERT_EQ(child_1->not_owning_sub_size(), 4ul);
CalculateDumpSubSizes(child_2);
ASSERT_EQ(child_2->not_owned_sub_size(), 5ul);
ASSERT_EQ(child_2->not_owning_sub_size(), 5ul);
// These nodes should also have size of children.
CalculateDumpSubSizes(parent_1);
ASSERT_EQ(parent_1->not_owned_sub_size(), 4ul);
ASSERT_EQ(parent_1->not_owning_sub_size(), 4ul);
CalculateDumpSubSizes(parent_2);
ASSERT_EQ(parent_2->not_owned_sub_size(), 5ul);
ASSERT_EQ(parent_2->not_owning_sub_size(), 5ul);
// These nodes should account for edge between parents.
CalculateDumpSubSizes(process_1->root());
ASSERT_EQ(process_1->root()->not_owned_sub_size(), 4ul);
ASSERT_EQ(process_1->root()->not_owning_sub_size(), 0ul);
CalculateDumpSubSizes(process_2->root());
ASSERT_EQ(process_2->root()->not_owned_sub_size(), 1ul);
ASSERT_EQ(process_2->root()->not_owning_sub_size(), 5ul);
}
TEST_F(GraphProcessorTest, CalculateDumpOwnershipCoefficient) {
Process* process = graph.CreateGraphForProcess(1);
Node* owned = process->CreateNode(kEmptyGuid, "owned", false);
Node* owner_1 = process->CreateNode(kEmptyGuid, "owner1", false);
Node* owner_2 = process->CreateNode(kEmptyGuid, "owner2", false);
Node* owner_3 = process->CreateNode(kEmptyGuid, "owner3", false);
Node* owner_4 = process->CreateNode(kEmptyGuid, "owner4", false);
graph.AddNodeOwnershipEdge(owner_1, owned, 2);
graph.AddNodeOwnershipEdge(owner_2, owned, 2);
graph.AddNodeOwnershipEdge(owner_3, owned, 1);
graph.AddNodeOwnershipEdge(owner_4, owned, 0);
// Ensure the owned node has a size otherwise calculations will not happen.
owned->AddEntry("size", Node::Entry::kBytes, 10);
// Setup the owned/owning sub sizes.
owned->add_not_owned_sub_size(10);
owner_1->add_not_owning_sub_size(6);
owner_2->add_not_owning_sub_size(7);
owner_3->add_not_owning_sub_size(5);
owner_4->add_not_owning_sub_size(8);
// Perform the computation.
CalculateDumpOwnershipCoefficient(owned);
// Ensure that the coefficients are correct.
ASSERT_DOUBLE_EQ(owned->owned_coefficient(), 2.0 / 10.0);
ASSERT_DOUBLE_EQ(owner_1->owning_coefficient(), 3.0 / 6.0);
ASSERT_DOUBLE_EQ(owner_2->owning_coefficient(), 4.0 / 7.0);
ASSERT_DOUBLE_EQ(owner_3->owning_coefficient(), 0.0 / 5.0);
ASSERT_DOUBLE_EQ(owner_4->owning_coefficient(), 1.0 / 8.0);
}
TEST_F(GraphProcessorTest, CalculateDumpCumulativeOwnershipCoefficient) {
Process* process = graph.CreateGraphForProcess(1);
Node* c1 = process->CreateNode(kEmptyGuid, "c1", false);
Node* c1_c1 = process->CreateNode(kEmptyGuid, "c1/c1", false);
Node* c1_c2 = process->CreateNode(kEmptyGuid, "c1/c2", false);
Node* owned = process->CreateNode(kEmptyGuid, "owned", false);
graph.AddNodeOwnershipEdge(c1_c2, owned, 2);
// Ensure all nodes have sizes otherwise calculations will not happen.
c1_c1->AddEntry("size", Node::Entry::kBytes, 10);
c1_c2->AddEntry("size", Node::Entry::kBytes, 10);
owned->AddEntry("size", Node::Entry::kBytes, 10);
// Setup the owned/owning cummulative coefficients.
c1->set_cumulative_owning_coefficient(0.123);
c1->set_cumulative_owned_coefficient(0.456);
owned->set_cumulative_owning_coefficient(0.789);
owned->set_cumulative_owned_coefficient(0.987);
// Set owning and owned for the children.
c1_c1->set_owning_coefficient(0.654);
c1_c1->set_owned_coefficient(0.321);
c1_c2->set_owning_coefficient(0.135);
c1_c2->set_owned_coefficient(0.246);
// Perform the computation and check our answers.
CalculateDumpCumulativeOwnershipCoefficient(c1_c1);
ASSERT_DOUBLE_EQ(c1_c1->cumulative_owning_coefficient(), 0.123);
ASSERT_DOUBLE_EQ(c1_c1->cumulative_owned_coefficient(), 0.456 * 0.321);
CalculateDumpCumulativeOwnershipCoefficient(c1_c2);
ASSERT_DOUBLE_EQ(c1_c2->cumulative_owning_coefficient(), 0.135 * 0.789);
ASSERT_DOUBLE_EQ(c1_c2->cumulative_owned_coefficient(), 0.456 * 0.246);
}
TEST_F(GraphProcessorTest, CalculateDumpEffectiveSize) {
Process* process = graph.CreateGraphForProcess(1);
Node* c1 = process->CreateNode(kEmptyGuid, "c1", false);
Node* c1_c1 = process->CreateNode(kEmptyGuid, "c1/c1", false);
Node* c1_c2 = process->CreateNode(kEmptyGuid, "c1/c2", false);
// Ensure all nodes have sizes otherwise calculations will not happen.
c1->AddEntry("size", Node::Entry::kBytes, 200);
c1_c1->AddEntry("size", Node::Entry::kBytes, 32);
c1_c2->AddEntry("size", Node::Entry::kBytes, 20);
// Setup the owned/owning cummulative coefficients.
c1_c1->set_cumulative_owning_coefficient(0.123);
c1_c1->set_cumulative_owned_coefficient(0.456);
c1_c2->set_cumulative_owning_coefficient(0.789);
c1_c2->set_cumulative_owned_coefficient(0.987);
// Perform the computation and check our answers.
CalculateDumpEffectiveSize(c1_c1);
const Node::Entry& entry_c1_c1 =
c1_c1->entries()->find("effective_size")->second;
uint64_t expected_c1_c1 = static_cast<int>(0.123 * 0.456 * 32);
ASSERT_EQ(entry_c1_c1.value_uint64, expected_c1_c1);
CalculateDumpEffectiveSize(c1_c2);
const Node::Entry& entry_c1_c2 =
c1_c2->entries()->find("effective_size")->second;
uint64_t expected_c1_c2 = static_cast<int>(0.789 * 0.987 * 20);
ASSERT_EQ(entry_c1_c2.value_uint64, expected_c1_c2);
CalculateDumpEffectiveSize(c1);
const Node::Entry& entry_c1 = c1->entries()->find("effective_size")->second;
ASSERT_EQ(entry_c1.value_uint64, expected_c1_c1 + expected_c1_c2);
}
} // namespace memory_instrumentation