Commit 2c02957d authored by Maciej Malinowski's avatar Maciej Malinowski Committed by Commit Bot

Add converter to Perfetto RawProcessMemoryNode

The MemoryDumpMapConverter is converting between the Chromium type
MemoryDumpMap and Perfetto RawProcessMemoryNode. Using this class it
is possible to process produced in Chromium MemoryDumpMap by Perfetto
GraphProcessor. See crbug.com/1095982 for more details.

Bug: 1095982
Change-Id: If7e852017c8efa51cd99b4f648c4a32e8fe0b8fe
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2299223
Commit-Queue: ssid <ssid@chromium.org>
Reviewed-by: default avatarEric Seckler <eseckler@chromium.org>
Reviewed-by: default avatarssid <ssid@chromium.org>
Reviewed-by: default avatarChris Hamilton <chrisha@chromium.org>
Cr-Commit-Position: refs/heads/master@{#797075}
parent b5ab8f00
......@@ -15,6 +15,8 @@ source_set("lib") {
"memory_instrumentation/graph.h",
"memory_instrumentation/graph_processor.cc",
"memory_instrumentation/graph_processor.h",
"memory_instrumentation/memory_dump_map_converter.cc",
"memory_instrumentation/memory_dump_map_converter.h",
"memory_instrumentation/queued_request.cc",
"memory_instrumentation/queued_request.h",
"memory_instrumentation/queued_request_dispatcher.cc",
......@@ -44,6 +46,7 @@ source_set("tests") {
"memory_instrumentation/coordinator_impl_unittest.cc",
"memory_instrumentation/graph_processor_unittest.cc",
"memory_instrumentation/graph_unittest.cc",
"memory_instrumentation/memory_dump_map_converter_unittest.cc",
"public/cpp/memory_instrumentation/memory_instrumentation_mojom_traits_unittest.cc",
"public/cpp/memory_instrumentation/os_metrics_unittest.cc",
"public/cpp/memory_instrumentation/tracing_integration_unittest.cc",
......
include_rules = [
"+components/ukm/test_ukm_recorder.h",
"+third_party/perfetto",
"+third_party/smhasher",
"+services/metrics/public",
]
// 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/memory_dump_map_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 {
MemoryDumpMapConverter::MemoryDumpMapConverter() = default;
MemoryDumpMapConverter::~MemoryDumpMapConverter() = default;
perfetto::trace_processor::GraphProcessor::RawMemoryNodeMap
MemoryDumpMapConverter::Convert(const MemoryDumpMap& input) {
perfetto::trace_processor::GraphProcessor::RawMemoryNodeMap output;
for (const auto& entry : input) {
const base::ProcessId process_id = entry.first;
const base::trace_event::ProcessMemoryDump* process_dump = entry.second;
if (process_dump == nullptr)
continue;
output.emplace(process_id, ConvertProcessMemoryDump(*process_dump));
}
return output;
}
std::unique_ptr<MemoryDumpMapConverter::PerfettoProcessMemoryNode>
MemoryDumpMapConverter::ConvertProcessMemoryDump(
const base::trace_event::ProcessMemoryDump& input) {
const perfetto::trace_processor::LevelOfDetail level_of_detail =
ConvertLevelOfDetail(input.dump_args().level_of_detail);
MemoryDumpMapConverter::PerfettoProcessMemoryNode::MemoryNodesMap nodes_map =
ConvertAllocatorDumps(input);
MemoryDumpMapConverter::PerfettoProcessMemoryNode::AllocatorNodeEdgesMap
edges_map = ConvertAllocatorDumpEdges(input);
return std::make_unique<PerfettoProcessMemoryNode>(
level_of_detail, std::move(edges_map), std::move(nodes_map));
}
perfetto::trace_processor::LevelOfDetail
MemoryDumpMapConverter::ConvertLevelOfDetail(
const base::trace_event::MemoryDumpLevelOfDetail& input) const {
switch (input) {
case base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND:
return perfetto::trace_processor::LevelOfDetail::kBackground;
case base::trace_event::MemoryDumpLevelOfDetail::LIGHT:
return perfetto::trace_processor::LevelOfDetail::kLight;
case base::trace_event::MemoryDumpLevelOfDetail::DETAILED:
return perfetto::trace_processor::LevelOfDetail::kDetailed;
}
return perfetto::trace_processor::LevelOfDetail::kDetailed;
}
std::vector<perfetto::trace_processor::RawMemoryGraphNode::MemoryNodeEntry>
MemoryDumpMapConverter::ConvertAllocatorDumpEntries(
const base::trace_event::MemoryAllocatorDump& input) const {
std::vector<perfetto::trace_processor::RawMemoryGraphNode::MemoryNodeEntry>
output;
for (const auto& entry : input.entries()) {
if (entry.entry_type ==
base::trace_event::MemoryAllocatorDump::Entry::kUint64) {
output.emplace_back(entry.name, entry.units, entry.value_uint64);
} else {
output.emplace_back(entry.name, entry.units, entry.value_string);
}
}
return output;
}
std::unique_ptr<perfetto::trace_processor::RawMemoryGraphNode>
MemoryDumpMapConverter::ConvertMemoryAllocatorDump(
const base::trace_event::MemoryAllocatorDump& input) const {
auto output = std::make_unique<perfetto::trace_processor::RawMemoryGraphNode>(
input.absolute_name(), ConvertLevelOfDetail(input.level_of_detail()),
ConvertMemoryAllocatorDumpGuid(input.guid()),
ConvertAllocatorDumpEntries(input));
CopyAndConvertAllocatorDumpFlags(input, output.get());
return output;
}
void MemoryDumpMapConverter::CopyAndConvertAllocatorDumpFlags(
const base::trace_event::MemoryAllocatorDump& input,
perfetto::trace_processor::RawMemoryGraphNode* output) const {
output->clear_flags(output->flags());
output->set_flags(
input.flags() & base::trace_event::MemoryAllocatorDump::WEAK
? perfetto::trace_processor::RawMemoryGraphNode::kWeak
: perfetto::trace_processor::RawMemoryGraphNode::kDefault);
}
MemoryDumpMapConverter::PerfettoProcessMemoryNode::MemoryNodesMap
MemoryDumpMapConverter::ConvertAllocatorDumps(
const base::trace_event::ProcessMemoryDump& input) const {
MemoryDumpMapConverter::PerfettoProcessMemoryNode::MemoryNodesMap output;
for (const auto& entry : input.allocator_dumps()) {
const std::unique_ptr<base::trace_event::MemoryAllocatorDump>& dump =
entry.second;
output.emplace(entry.first, ConvertMemoryAllocatorDump(*dump));
}
return output;
}
MemoryDumpMapConverter::PerfettoProcessMemoryNode::AllocatorNodeEdgesMap
MemoryDumpMapConverter::ConvertAllocatorDumpEdges(
const base::trace_event::ProcessMemoryDump& input) const {
MemoryDumpMapConverter::PerfettoProcessMemoryNode::AllocatorNodeEdgesMap
output;
for (const auto& entry : input.allocator_dumps_edges()) {
std::unique_ptr<perfetto::trace_processor::MemoryGraphEdge> edge =
ConvertAllocatorDumpEdge(entry.second);
const perfetto::trace_processor::MemoryAllocatorNodeId source =
edge->source;
output.emplace(source, std::move(edge));
}
return output;
}
std::unique_ptr<perfetto::trace_processor::MemoryGraphEdge>
MemoryDumpMapConverter::ConvertAllocatorDumpEdge(
const base::trace_event::ProcessMemoryDump::MemoryAllocatorDumpEdge& input)
const {
return std::make_unique<perfetto::trace_processor::MemoryGraphEdge>(
ConvertMemoryAllocatorDumpGuid(input.source),
ConvertMemoryAllocatorDumpGuid(input.target), input.importance,
input.overridable);
}
perfetto::trace_processor::MemoryAllocatorNodeId
MemoryDumpMapConverter::ConvertMemoryAllocatorDumpGuid(
const base::trace_event::MemoryAllocatorDumpGuid& input) const {
return perfetto::trace_processor::MemoryAllocatorNodeId(input.ToUint64());
}
} // 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_MEMORY_DUMP_MAP_CONVERTER_H_
#define SERVICES_RESOURCE_COORDINATOR_MEMORY_INSTRUMENTATION_MEMORY_DUMP_MAP_CONVERTER_H_
#include <list>
#include <map>
#include <memory>
#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 {
// Converts the Chromium MemoryDumpMap to the corresponding defined in Perfetto
// type RawMemoryNodeMap.
//
// Example usage:
//
// {
// base::trace_event::ProcessMemoryDump pmd;
//
// MemoryDumpMapConverter converter;
// perfetto::trace_processor::GraphProcessor::RawMemoryNodeMap
// perfettoNodeMap = converter.Convert(pmd);
// }
class MemoryDumpMapConverter {
public:
using MemoryDumpMap =
std::map<base::ProcessId, const base::trace_event::ProcessMemoryDump*>;
using PerfettoProcessMemoryNode =
perfetto::trace_processor::RawProcessMemoryNode;
MemoryDumpMapConverter();
~MemoryDumpMapConverter();
MemoryDumpMapConverter(const MemoryDumpMapConverter&) = delete;
void operator=(const MemoryDumpMapConverter&) = delete;
perfetto::trace_processor::GraphProcessor::RawMemoryNodeMap Convert(
const MemoryDumpMap& input);
private:
std::unique_ptr<MemoryDumpMapConverter::PerfettoProcessMemoryNode>
ConvertProcessMemoryDump(const base::trace_event::ProcessMemoryDump& input);
perfetto::trace_processor::LevelOfDetail ConvertLevelOfDetail(
const base::trace_event::MemoryDumpLevelOfDetail& input) const;
PerfettoProcessMemoryNode::MemoryNodesMap ConvertAllocatorDumps(
const base::trace_event::ProcessMemoryDump& input) const;
std::unique_ptr<perfetto::trace_processor::RawMemoryGraphNode>
ConvertMemoryAllocatorDump(
const base::trace_event::MemoryAllocatorDump& input) const;
void CopyAndConvertAllocatorDumpFlags(
const base::trace_event::MemoryAllocatorDump& input,
perfetto::trace_processor::RawMemoryGraphNode* output) const;
std::vector<perfetto::trace_processor::RawMemoryGraphNode::MemoryNodeEntry>
ConvertAllocatorDumpEntries(
const base::trace_event::MemoryAllocatorDump& input) const;
PerfettoProcessMemoryNode::AllocatorNodeEdgesMap ConvertAllocatorDumpEdges(
const base::trace_event::ProcessMemoryDump& input) const;
std::unique_ptr<perfetto::trace_processor::MemoryGraphEdge>
ConvertAllocatorDumpEdge(
const base::trace_event::ProcessMemoryDump::MemoryAllocatorDumpEdge&
input) const;
perfetto::trace_processor::MemoryAllocatorNodeId
ConvertMemoryAllocatorDumpGuid(
const base::trace_event::MemoryAllocatorDumpGuid& input) const;
};
} // namespace memory_instrumentation
#endif // SERVICES_RESOURCE_COORDINATOR_MEMORY_INSTRUMENTATION_MEMORY_DUMP_MAP_CONVERTER_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/memory_dump_map_converter.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace memory_instrumentation {
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;
TEST(MemoryDumpMapConverter, Convert) {
MemoryDumpMapConverter::MemoryDumpMap process_dumps;
MemoryDumpArgs dump_args1 = {MemoryDumpLevelOfDetail::DETAILED};
ProcessMemoryDump pmd1(dump_args1);
auto* source1 = pmd1.CreateAllocatorDump("test1/test2/test3");
source1->AddScalar(MemoryAllocatorDump::kNameSize,
MemoryAllocatorDump::kUnitsBytes, 10);
auto* target1 = pmd1.CreateAllocatorDump("target1");
pmd1.AddOwnershipEdge(source1->guid(), target1->guid(), 10);
pmd1.CreateWeakSharedGlobalAllocatorDump(MemoryAllocatorDumpGuid(1));
process_dumps.emplace(1, &pmd1);
MemoryDumpArgs dump_args2 = {MemoryDumpLevelOfDetail::LIGHT};
ProcessMemoryDump pmd2(dump_args2);
auto* source2 = pmd2.CreateAllocatorDump("test1/test4/test5");
source2->AddScalar(MemoryAllocatorDump::kNameSize,
MemoryAllocatorDump::kUnitsObjects, 1);
process_dumps.emplace(2, &pmd2);
MemoryDumpMapConverter converter;
auto output_dump_map = converter.Convert(process_dumps);
ASSERT_EQ(output_dump_map.size(), 2uL);
ASSERT_NE(output_dump_map.find(1), output_dump_map.end());
ASSERT_EQ(output_dump_map[1]->allocator_nodes().size(), 3uL);
ASSERT_NE(output_dump_map[1]->allocator_nodes().find("test1/test2/test3"),
output_dump_map[1]->allocator_nodes().end());
{
const auto& dump =
*output_dump_map[1]->allocator_nodes().at("test1/test2/test3");
ASSERT_EQ(dump.entries().size(), 1uL);
const auto& entry = dump.entries()[0];
ASSERT_EQ(entry.entry_type, perfetto::trace_processor::RawMemoryGraphNode::
MemoryNodeEntry::kUint64);
ASSERT_EQ(entry.units, "bytes");
ASSERT_EQ(entry.value_uint64, 10uL);
}
ASSERT_NE(output_dump_map[1]->allocator_nodes().find("target1"),
output_dump_map[1]->allocator_nodes().end());
ASSERT_NE(output_dump_map[1]->allocator_nodes().find("global/1"),
output_dump_map[1]->allocator_nodes().end());
ASSERT_NE(output_dump_map.find(2), output_dump_map.end());
ASSERT_EQ(output_dump_map[2]->allocator_nodes().size(), 1uL);
ASSERT_NE(output_dump_map[2]->allocator_nodes().find("test1/test4/test5"),
output_dump_map[2]->allocator_nodes().end());
{
const auto& dump =
*output_dump_map[2]->allocator_nodes().at("test1/test4/test5");
ASSERT_EQ(dump.entries().size(), 1uL);
const auto& entry = dump.entries()[0];
ASSERT_EQ(entry.entry_type, perfetto::trace_processor::RawMemoryGraphNode::
MemoryNodeEntry::kUint64);
ASSERT_EQ(entry.units, "objects");
ASSERT_EQ(entry.value_uint64, 1uL);
}
}
} // 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