forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add converter to Chromium GlobalDumpGraph
The GlobalDumpGraphConverter is converting between the Perfetto type GlobalNodeGraph and Chromium GlobalDumpGraph. Using this class it is possible to use produced by Perfetto GraphProcessor graphs in Chromium. See crbug.com/1095982 for more details. Bug: 1095982 Change-Id: I8bda59287a07d3759ca6da1037ecb6bfbad6439f Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2299745 Commit-Queue: ssid <ssid@chromium.org> Reviewed-by: Eric Seckler <eseckler@chromium.org> Reviewed-by: ssid <ssid@chromium.org> Cr-Commit-Position: refs/heads/master@{#805239}
- Loading branch information
Maciej Malinowski
authored and
Commit Bot
committed
Sep 9, 2020
1 parent
70b84c7
commit 4217726
Showing
4 changed files
with
363 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
158 changes: 158 additions & 0 deletions
158
services/resource_coordinator/memory_instrumentation/global_dump_graph_converter.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
// 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/global_dump_graph_converter.h" | ||
|
||
#include <list> | ||
#include <memory> | ||
#include <string> | ||
#include <utility> | ||
#include <vector> | ||
|
||
#include "base/trace_event/process_memory_dump.h" | ||
#include "third_party/perfetto/include/perfetto/ext/trace_processor/importers/memory_tracker/graph_processor.h" | ||
|
||
namespace memory_instrumentation { | ||
|
||
using perfetto::trace_processor::GlobalNodeGraph; | ||
|
||
GlobalDumpGraphConverter::GlobalDumpGraphConverter() = default; | ||
GlobalDumpGraphConverter::~GlobalDumpGraphConverter() = default; | ||
|
||
std::unique_ptr<GlobalDumpGraph> GlobalDumpGraphConverter::Convert( | ||
const GlobalNodeGraph& input) const { | ||
NodePointerPerfettoToChromeMap pointer_map; | ||
auto output = std::make_unique<GlobalDumpGraph>(); | ||
|
||
CopyAndConvertProcessDumps(input, output.get(), &pointer_map); | ||
|
||
CopyAndConvertSharedMemoryGraph(input, output.get(), &pointer_map); | ||
|
||
CopyAndConvertEdges(input, output.get(), &pointer_map); | ||
|
||
return output; | ||
} | ||
|
||
void GlobalDumpGraphConverter::CopyAndConvertProcessDumps( | ||
const GlobalNodeGraph& input, | ||
GlobalDumpGraph* output, | ||
NodePointerPerfettoToChromeMap* pointer_map) const { | ||
for (const auto& input_entry : input.process_node_graphs()) { | ||
const base::ProcessId process_id = input_entry.first; | ||
const GlobalNodeGraph::Process* input_process = input_entry.second.get(); | ||
if (input_process == nullptr) | ||
continue; | ||
|
||
GlobalDumpGraph::Process* output_process = | ||
output->CreateGraphForProcess(process_id); | ||
|
||
CopyAndConvertNodeTree(input_process->root(), output_process, {}, | ||
pointer_map); | ||
} | ||
} | ||
|
||
void GlobalDumpGraphConverter::CopyAndConvertSharedMemoryGraph( | ||
const GlobalNodeGraph& input, | ||
GlobalDumpGraph* output, | ||
NodePointerPerfettoToChromeMap* pointer_map) const { | ||
CopyAndConvertNodeTree(input.shared_memory_graph()->root(), | ||
output->shared_memory_graph(), {}, pointer_map); | ||
} | ||
|
||
void GlobalDumpGraphConverter::CopyAndConvertNodeTree( | ||
const GlobalNodeGraph::Node* input, | ||
GlobalDumpGraph::Process* output, | ||
const std::string& node_path, | ||
NodePointerPerfettoToChromeMap* pointer_map) const { | ||
DCHECK(input); | ||
|
||
for (const auto& entry : input->const_children()) { | ||
const std::string path = node_path + "/" + entry.first; | ||
const GlobalNodeGraph::Node* raw_child = entry.second; | ||
|
||
GlobalDumpGraph::Node* child = | ||
output->CreateNode(ConvertMemoryAllocatorDumpGuid(raw_child->id()), | ||
path, raw_child->is_weak()); | ||
|
||
CopyNodeMembers(*raw_child, child); | ||
|
||
CopyAndConvertNodeTree(raw_child, output, path, pointer_map); | ||
pointer_map->emplace(raw_child, child); | ||
} | ||
} | ||
|
||
base::trace_event::MemoryAllocatorDumpGuid | ||
GlobalDumpGraphConverter::ConvertMemoryAllocatorDumpGuid( | ||
const perfetto::trace_processor::MemoryAllocatorNodeId& input) const { | ||
return base::trace_event::MemoryAllocatorDumpGuid(input.ToUint64()); | ||
} | ||
|
||
void GlobalDumpGraphConverter::CopyNodeMembers( | ||
const GlobalNodeGraph::Node& input, | ||
GlobalDumpGraph::Node* output) const { | ||
for (const auto& item : input.const_entries()) { | ||
const std::string& name = item.first; | ||
const GlobalNodeGraph::Node::Entry& entry = item.second; | ||
|
||
if (entry.type == GlobalNodeGraph::Node::Entry::kUInt64) { | ||
output->AddEntry(name, ConvertScalarUnits(entry.units), | ||
entry.value_uint64); | ||
} else { | ||
output->AddEntry(name, entry.value_string); | ||
} | ||
} | ||
|
||
output->set_weak(input.is_weak()); | ||
output->set_explicit(input.is_explicit()); | ||
output->add_not_owned_sub_size(input.not_owned_sub_size()); | ||
output->add_not_owning_sub_size(input.not_owning_sub_size()); | ||
output->set_owned_coefficient(input.owned_coefficient()); | ||
output->set_owning_coefficient(input.owning_coefficient()); | ||
output->set_cumulative_owned_coefficient( | ||
input.cumulative_owned_coefficient()); | ||
output->set_cumulative_owning_coefficient( | ||
input.cumulative_owning_coefficient()); | ||
} | ||
|
||
void GlobalDumpGraphConverter::CopyAndConvertEdges( | ||
const GlobalNodeGraph& input, | ||
GlobalDumpGraph* output, | ||
NodePointerPerfettoToChromeMap* pointer_map) const { | ||
for (const auto& input_edge : input.edges()) { | ||
CopyAndConvertEdge(input_edge, output, pointer_map); | ||
} | ||
} | ||
|
||
void GlobalDumpGraphConverter::CopyAndConvertEdge( | ||
const GlobalNodeGraph::Edge& input, | ||
GlobalDumpGraph* output, | ||
const NodePointerPerfettoToChromeMap* pointer_map) const { | ||
DCHECK(input.source()); | ||
DCHECK(input.target()); | ||
|
||
GlobalDumpGraph::Node* source = pointer_map->at(input.source()); | ||
GlobalDumpGraph::Node* target = pointer_map->at(input.target()); | ||
|
||
DCHECK(source); | ||
DCHECK(target); | ||
|
||
output->AddNodeOwnershipEdge(source, target, input.priority()); | ||
} | ||
|
||
GlobalDumpGraph::Node::Entry::ScalarUnits | ||
GlobalDumpGraphConverter::ConvertScalarUnits( | ||
GlobalNodeGraph::Node::Entry::ScalarUnits input) const { | ||
using PerfettoScalarUnits = GlobalNodeGraph::Node::Entry::ScalarUnits; | ||
using ChromeScalarUnits = GlobalDumpGraph::Node::Entry::ScalarUnits; | ||
switch (input) { | ||
case PerfettoScalarUnits::kObjects: | ||
return ChromeScalarUnits::kObjects; | ||
case PerfettoScalarUnits::kBytes: | ||
return ChromeScalarUnits::kBytes; | ||
} | ||
|
||
NOTREACHED(); | ||
} | ||
|
||
} // namespace memory_instrumentation |
87 changes: 87 additions & 0 deletions
87
services/resource_coordinator/memory_instrumentation/global_dump_graph_converter.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
// 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_GLOBAL_DUMP_GRAPH_CONVERTER_H_ | ||
#define SERVICES_RESOURCE_COORDINATOR_MEMORY_INSTRUMENTATION_GLOBAL_DUMP_GRAPH_CONVERTER_H_ | ||
|
||
#include <list> | ||
#include <map> | ||
#include <memory> | ||
#include <vector> | ||
|
||
#include "services/resource_coordinator/memory_instrumentation/graph.h" | ||
#include "third_party/perfetto/include/perfetto/ext/trace_processor/importers/memory_tracker/graph_processor.h" | ||
|
||
namespace memory_instrumentation { | ||
|
||
// Converts the Perfetto GlobalNodeGraph to the corresponding defined in | ||
// Chromium type GlobalDumpGraph. | ||
// | ||
// Example usage: | ||
// | ||
// { | ||
// perfetto::trace_processor::GlobalNodeGraph graph; | ||
// | ||
// GlobalDumpGraphConverter converter; | ||
// std::unique_ptr<GlobalDumpGraph> dumpGraph = converter.Convert(graph); | ||
// } | ||
class GlobalDumpGraphConverter { | ||
public: | ||
GlobalDumpGraphConverter(); | ||
~GlobalDumpGraphConverter(); | ||
GlobalDumpGraphConverter(const GlobalDumpGraphConverter&) = delete; | ||
void operator=(const GlobalDumpGraphConverter&) = delete; | ||
|
||
std::unique_ptr<GlobalDumpGraph> Convert( | ||
const perfetto::trace_processor::GlobalNodeGraph& input) const; | ||
|
||
private: | ||
// Map is used during conversion from Perfetto GlobalNodeGraph to Chromium | ||
// GlobalDumpGraph. It simplifies finding matching nodes during conversion of | ||
// graph edges. | ||
using NodePointerPerfettoToChromeMap = | ||
std::map<const perfetto::trace_processor::GlobalNodeGraph::Node*, | ||
GlobalDumpGraph::Node*>; | ||
|
||
void CopyAndConvertProcessDumps( | ||
const perfetto::trace_processor::GlobalNodeGraph& input, | ||
GlobalDumpGraph* output, | ||
NodePointerPerfettoToChromeMap* pointer_map) const; | ||
|
||
void CopyAndConvertSharedMemoryGraph( | ||
const perfetto::trace_processor::GlobalNodeGraph& input, | ||
GlobalDumpGraph* output, | ||
NodePointerPerfettoToChromeMap* pointer_map) const; | ||
|
||
void CopyAndConvertNodeTree( | ||
const perfetto::trace_processor::GlobalNodeGraph::Node* input, | ||
GlobalDumpGraph::Process* output, | ||
const std::string& node_path, | ||
NodePointerPerfettoToChromeMap* pointer_map) const; | ||
|
||
base::trace_event::MemoryAllocatorDumpGuid ConvertMemoryAllocatorDumpGuid( | ||
const perfetto::trace_processor::MemoryAllocatorNodeId& input) const; | ||
|
||
void CopyNodeMembers( | ||
const perfetto::trace_processor::GlobalNodeGraph::Node& input, | ||
GlobalDumpGraph::Node* output) const; | ||
|
||
void CopyAndConvertEdges( | ||
const perfetto::trace_processor::GlobalNodeGraph& input, | ||
GlobalDumpGraph* output, | ||
NodePointerPerfettoToChromeMap* pointer_map) const; | ||
|
||
void CopyAndConvertEdge( | ||
const perfetto::trace_processor::GlobalNodeGraph::Edge& input, | ||
GlobalDumpGraph* output, | ||
const NodePointerPerfettoToChromeMap* pointer_map) const; | ||
|
||
GlobalDumpGraph::Node::Entry::ScalarUnits ConvertScalarUnits( | ||
const perfetto::trace_processor::GlobalNodeGraph::Node::Entry::ScalarUnits | ||
input) const; | ||
}; | ||
|
||
} // namespace memory_instrumentation | ||
|
||
#endif // SERVICES_RESOURCE_COORDINATOR_MEMORY_INSTRUMENTATION_GLOBAL_DUMP_GRAPH_CONVERTER_H_ |
114 changes: 114 additions & 0 deletions
114
services/resource_coordinator/memory_instrumentation/global_dump_graph_converter_unittest.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
// 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/global_dump_graph_converter.h" | ||
|
||
#include "testing/gtest/include/gtest/gtest.h" | ||
|
||
namespace memory_instrumentation { | ||
|
||
using base::trace_event::MemoryAllocatorDumpGuid; | ||
using perfetto::trace_processor::GlobalNodeGraph; | ||
|
||
TEST(GlobalDumpGraphConverterTest, Convert) { | ||
auto input = std::make_unique<perfetto::trace_processor::GlobalNodeGraph>(); | ||
perfetto::trace_processor::GlobalNodeGraph::Node* owner = nullptr; | ||
perfetto::trace_processor::GlobalNodeGraph::Node* owned = nullptr; | ||
|
||
// Adding first process with one node | ||
{ | ||
GlobalNodeGraph::Process* process = input->CreateGraphForProcess(1); | ||
perfetto::trace_processor::MemoryAllocatorNodeId node_id{1}; | ||
owner = process->CreateNode(node_id, "test1", false); | ||
owner->AddEntry( | ||
"first", | ||
perfetto::trace_processor::GlobalNodeGraph::Node::Entry::kObjects, 123); | ||
owner->AddEntry("second", "string"); | ||
owner->set_weak(true); | ||
owner->set_explicit(true); | ||
owner->add_not_owned_sub_size(100); | ||
owner->add_not_owning_sub_size(200); | ||
owner->set_owned_coefficient(3); | ||
owner->set_owning_coefficient(5); | ||
owner->set_cumulative_owned_coefficient(7); | ||
owner->set_cumulative_owning_coefficient(9); | ||
} | ||
|
||
// Adding second process with one node | ||
{ | ||
GlobalNodeGraph::Process* process = input->CreateGraphForProcess(2); | ||
perfetto::trace_processor::MemoryAllocatorNodeId node_id{2}; | ||
owned = process->CreateNode(node_id, "test2", false); | ||
} | ||
|
||
// Creating Edge between two previously created nodes | ||
input->AddNodeOwnershipEdge(owner, owned, 99); | ||
|
||
// Create shared memory graph | ||
{ | ||
GlobalNodeGraph::Process* process = input->shared_memory_graph(); | ||
perfetto::trace_processor::MemoryAllocatorNodeId node_id{3}; | ||
process->CreateNode(node_id, "test3", false); | ||
} | ||
|
||
// Convert GlobalDumpGraph to Perfetto GlobalDumpGrap. | ||
GlobalDumpGraphConverter converter; | ||
const std::unique_ptr<GlobalDumpGraph> output = converter.Convert(*input); | ||
|
||
ASSERT_EQ(output->process_dump_graphs().size(), 2uL); | ||
ASSERT_NE(output->process_dump_graphs().find(1), | ||
output->process_dump_graphs().end()); | ||
|
||
{ | ||
const GlobalDumpGraph::Process& process1 = | ||
*output->process_dump_graphs().at(1); | ||
ASSERT_EQ(process1.root()->const_children().size(), 1uL); | ||
const GlobalDumpGraph::Node* node1 = process1.root()->GetChild("test1"); | ||
ASSERT_NE(node1, nullptr); | ||
ASSERT_EQ(node1->is_weak(), true); | ||
ASSERT_EQ(node1->is_explicit(), true); | ||
ASSERT_EQ(node1->not_owned_sub_size(), 100uL); | ||
ASSERT_EQ(node1->not_owning_sub_size(), 200uL); | ||
ASSERT_EQ(node1->owned_coefficient(), 3); | ||
ASSERT_EQ(node1->owning_coefficient(), 5); | ||
ASSERT_EQ(node1->cumulative_owned_coefficient(), 7); | ||
ASSERT_EQ(node1->cumulative_owning_coefficient(), 9); | ||
ASSERT_EQ(node1->const_entries().size(), 2uL); | ||
ASSERT_NE(node1->const_entries().find("first"), | ||
node1->const_entries().end()); | ||
ASSERT_EQ(node1->const_entries().at("first").type, | ||
GlobalDumpGraph::Node::Entry::kUInt64); | ||
ASSERT_EQ(node1->const_entries().at("first").units, | ||
GlobalDumpGraph::Node::Entry::kObjects); | ||
ASSERT_EQ(node1->const_entries().at("first").value_uint64, 123uL); | ||
ASSERT_NE(node1->const_entries().find("second"), | ||
node1->const_entries().end()); | ||
ASSERT_EQ(node1->const_entries().at("second").type, | ||
GlobalDumpGraph::Node::Entry::kString); | ||
ASSERT_EQ(node1->const_entries().at("second").value_string, "string"); | ||
|
||
const GlobalDumpGraph::Process& process2 = | ||
*output->process_dump_graphs().at(2); | ||
ASSERT_EQ(process2.root()->const_children().size(), 1uL); | ||
const GlobalDumpGraph::Node* node2 = process2.root()->GetChild("test2"); | ||
ASSERT_NE(node2, nullptr); | ||
|
||
const auto edges_count = | ||
std::distance(std::begin(output->edges()), std::end(output->edges())); | ||
ASSERT_EQ(edges_count, 1L); | ||
|
||
const GlobalDumpGraph::Edge edge = *output->edges().begin(); | ||
ASSERT_EQ(edge.source(), node1); | ||
ASSERT_EQ(edge.target(), node2); | ||
ASSERT_EQ(edge.priority(), 99); | ||
|
||
ASSERT_EQ(output->shared_memory_graph()->root()->const_children().size(), | ||
1uL); | ||
const GlobalDumpGraph::Node* node3 = | ||
output->shared_memory_graph()->root()->GetChild("test3"); | ||
ASSERT_NE(node3, nullptr); | ||
} | ||
} | ||
|
||
} // namespace memory_instrumentation |