Commit 0e94bdf3 authored by Maciej Malinowski's avatar Maciej Malinowski Committed by Commit Bot

Add Graph Processor wrapper

The Chrome Graph Processor is a wrapper for a graph processor
implementation from Perfetto. This class takes care of all required
input and output parameters conversion. See crbug.com/1095982 for
more details.

Bug: 1095982
Change-Id: I3c6a320fe354bcd4bbd93388dfc013244bc9f261
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2299348
Commit-Queue: ssid <ssid@chromium.org>
Reviewed-by: default avatarssid <ssid@chromium.org>
Reviewed-by: default avatarEric Seckler <eseckler@chromium.org>
Cr-Commit-Position: refs/heads/master@{#805315}
parent 23805107
......@@ -9,6 +9,8 @@ source_set("lib") {
sources = [
"memory_instrumentation/aggregate_metrics_processor.cc",
"memory_instrumentation/aggregate_metrics_processor.h",
"memory_instrumentation/chrome_graph_processor.cc",
"memory_instrumentation/chrome_graph_processor.h",
"memory_instrumentation/coordinator_impl.cc",
"memory_instrumentation/coordinator_impl.h",
"memory_instrumentation/global_dump_graph_converter.cc",
......@@ -46,6 +48,7 @@ source_set("tests") {
testonly = true
sources = [
"memory_instrumentation/chrome_graph_processor_unittest.cc",
"memory_instrumentation/coordinator_impl_unittest.cc",
"memory_instrumentation/global_dump_graph_converter_unittest.cc",
"memory_instrumentation/graph_processor_unittest.cc",
......
// Copyright 2020 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/chrome_graph_processor.h"
#include <list>
#include <memory>
#include <string>
#include "services/resource_coordinator/memory_instrumentation/global_dump_graph_converter.h"
#include "services/resource_coordinator/memory_instrumentation/memory_dump_map_converter.h"
namespace memory_instrumentation {
using perfetto::trace_processor::GraphProcessor;
// static
std::unique_ptr<GlobalNodeGraph> ChromeGraphProcessor::CreateMemoryGraph(
const MemoryDumpMap& process_dumps,
Operations operations) {
operations = static_cast<Operations>(
static_cast<std::underlying_type<Operations>::type>(operations) &
~static_cast<std::underlying_type<Operations>::type>(
Operations::kComputeSharedFootprintFromGraph));
return CreateMemoryGraph(process_dumps, operations, nullptr);
}
// static
std::unique_ptr<GlobalNodeGraph> ChromeGraphProcessor::CreateMemoryGraph(
const MemoryDumpMap& process_dumps,
Operations operations,
std::map<base::ProcessId, uint64_t>* shared_footprints) {
DCHECK(operations == Operations::kNoneOperation ||
operations == Operations::kGraphWithoutSharedFootprint ||
operations == Operations::kAllOperations);
MemoryDumpMapConverter input_converter;
GraphProcessor::RawMemoryNodeMap memory_node_map =
input_converter.Convert(process_dumps);
std::unique_ptr<GlobalNodeGraph> memory_graph =
GraphProcessor::CreateMemoryGraph(memory_node_map);
if (operations == Operations::kGraphWithoutSharedFootprint) {
GraphProcessor::RemoveWeakNodesFromGraph(memory_graph.get());
GraphProcessor::AddOverheadsAndPropagateEntries(memory_graph.get());
GraphProcessor::CalculateSizesForGraph(memory_graph.get());
} else if (operations == Operations::kAllOperations) {
GraphProcessor::RemoveWeakNodesFromGraph(memory_graph.get());
DCHECK(shared_footprints);
shared_footprints->clear();
auto original =
GraphProcessor::ComputeSharedFootprintFromGraph(*memory_graph);
for (const auto& item : original) {
shared_footprints->emplace(static_cast<base::ProcessId>(item.first),
item.second);
}
GraphProcessor::AddOverheadsAndPropagateEntries(memory_graph.get());
GraphProcessor::CalculateSizesForGraph(memory_graph.get());
}
return memory_graph;
}
} // namespace memory_instrumentation
// Copyright 2020 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.
#ifndef SERVICES_RESOURCE_COORDINATOR_MEMORY_INSTRUMENTATION_CHROME_GRAPH_PROCESSOR_H_
#define SERVICES_RESOURCE_COORDINATOR_MEMORY_INSTRUMENTATION_CHROME_GRAPH_PROCESSOR_H_
#include <list>
#include <map>
#include <memory>
#include "base/trace_event/process_memory_dump.h"
#include "third_party/perfetto/include/perfetto/ext/trace_processor/importers/memory_tracker/graph.h"
namespace memory_instrumentation {
// ChromeGraphProcessor is a wrapper for a GraphProcessor from Perfetto. This
// class takes care of all required input and output parameter conversions and
// could be used as a replacement for GraphProcessor class from Chromium.
//
// Example usage:
// {
// std::map<base::ProcessId, uint64_t> shared_footprints;
// std::unique_ptr<GlobalDumpGraph> global_graph =
// ChromeGraphProcessor::CreateMemoryGraph(
// &shared_footprints,
// pid_to_pmd ChromeGraphProcessor::Operation::kAllOperations);
// }
using perfetto::trace_processor::GlobalNodeGraph;
class ChromeGraphProcessor {
public:
using MemoryDumpMap =
std::map<base::ProcessId, const base::trace_event::ProcessMemoryDump*>;
ChromeGraphProcessor() = delete;
~ChromeGraphProcessor() = delete;
ChromeGraphProcessor(const ChromeGraphProcessor&) = delete;
void operator=(const ChromeGraphProcessor&) = delete;
enum class Operations : int {
kNoneOperation = 0,
kRemoveWeakNodesFromGraph = (1 << 1),
kComputeSharedFootprintFromGraph = (1 << 2),
kAddOverheadsAndPropagateEntries = (1 << 3),
kCalculateSizesForGraph = (1 << 4),
kGraphWithoutSharedFootprint =
(kRemoveWeakNodesFromGraph | kAddOverheadsAndPropagateEntries |
kCalculateSizesForGraph),
kAllOperations =
(kRemoveWeakNodesFromGraph | kComputeSharedFootprintFromGraph |
kAddOverheadsAndPropagateEntries | kCalculateSizesForGraph)
};
// This is a wrapper function for the Perfetto GraphProcessor member function
// perfetto::GraphProcessor::CreateMemoryGraph(). This function combines
// creation of node graph with some possible operations on that graph
// performed by other Perfetto GraphProcessor member functions like
// RemoveWeakNodesFromGraph() or AddOverheadsAndPropagateEntries().
//
// Shared footprints is an output parameter which will hold information
// about computed footprints by distributing the memory of the nodes among the
// processes which have edges left. This parameter will be filled when bit
// |kComputeSharedFootprintFromGraph| is set in operations parameter.
// The valid values of |operations| are |kNoneOperation|, |kAllOperations| and
// |kGraphWithoutSharedFootprint|
static std::unique_ptr<GlobalNodeGraph> CreateMemoryGraph(
const MemoryDumpMap& process_dumps,
Operations operations,
std::map<base::ProcessId, uint64_t>* shared_footprints);
// This is overloaded function to simplify usage if the shared footpints are
// not needed.
static std::unique_ptr<GlobalNodeGraph> CreateMemoryGraph(
const MemoryDumpMap& process_dumps,
Operations operations);
};
} // namespace memory_instrumentation
#endif // SERVICES_RESOURCE_COORDINATOR_MEMORY_INSTRUMENTATION_CHROME_GRAPH_PROCESSOR_H_
// Copyright 2020 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/chrome_graph_processor.h"
#include <iostream>
#include "base/memory/shared_memory_tracker.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/perfetto/include/perfetto/ext/trace_processor/importers/memory_tracker/raw_memory_graph_node.h"
namespace memory_instrumentation {
TEST(ChromeGraphProcessorTest, CreateMemoryGraphWithNoneOperation) {
ChromeGraphProcessor::MemoryDumpMap process_dumps;
base::trace_event::MemoryDumpArgs dump_args = {
.level_of_detail = base::trace_event::MemoryDumpLevelOfDetail::DETAILED};
base::trace_event::ProcessMemoryDump pmd(dump_args);
auto* source = pmd.CreateAllocatorDump("test1/test2/test3");
source->AddScalar(perfetto::trace_processor::RawMemoryGraphNode::kNameSize,
perfetto::trace_processor::RawMemoryGraphNode::kUnitsBytes,
10);
auto* target = pmd.CreateAllocatorDump("target");
pmd.AddOwnershipEdge(source->guid(), target->guid(), 10);
auto* weak = pmd.CreateWeakSharedGlobalAllocatorDump(
base::trace_event::MemoryAllocatorDumpGuid(1));
process_dumps.emplace(1, &pmd);
auto global_node = ChromeGraphProcessor::CreateMemoryGraph(
process_dumps, ChromeGraphProcessor::Operations::kNoneOperation);
ASSERT_EQ(1u, global_node->process_node_graphs().size());
auto id_to_dump_it = global_node->process_node_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(
perfetto::trace_processor::RawMemoryGraphNode::kNameSize);
ASSERT_EQ(10ul, size->second.value_uint64);
ASSERT_TRUE(weak->flags() &
perfetto::trace_processor::RawMemoryGraphNode::Flags::kWeak);
auto& edges = global_node->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(ChromeGraphProcessorTest, CreateMemoryGraphWithAllOperations) {
ChromeGraphProcessor::MemoryDumpMap process_dumps;
base::trace_event::MemoryDumpArgs dump_args = {
.level_of_detail = base::trace_event::MemoryDumpLevelOfDetail::DETAILED};
base::trace_event::ProcessMemoryDump pmd(dump_args);
auto* source = pmd.CreateAllocatorDump("test1/test2/test3");
source->AddScalar(perfetto::trace_processor::RawMemoryGraphNode::kNameSize,
perfetto::trace_processor::RawMemoryGraphNode::kUnitsBytes,
10);
auto* target = pmd.CreateAllocatorDump("target");
pmd.AddOwnershipEdge(source->guid(), target->guid(), 10);
auto* weak = pmd.CreateWeakSharedGlobalAllocatorDump(
base::trace_event::MemoryAllocatorDumpGuid(1));
process_dumps.emplace(1, &pmd);
std::map<base::ProcessId, uint64_t> shared_footprints;
auto global_node = ChromeGraphProcessor::CreateMemoryGraph(
process_dumps, ChromeGraphProcessor::Operations::kAllOperations,
&shared_footprints);
ASSERT_EQ(1u, global_node->process_node_graphs().size());
auto id_to_dump_it = global_node->process_node_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(), 2ul);
auto size = third_child->entries()->find(
perfetto::trace_processor::RawMemoryGraphNode::kNameSize);
ASSERT_EQ(10ul, size->second.value_uint64);
ASSERT_TRUE(weak->flags() &
perfetto::trace_processor::RawMemoryGraphNode::Flags::kWeak);
auto& edges = global_node->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);
}
} // namespace memory_instrumentation
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment