Commit 2b69157b authored by Chris Hamilton's avatar Chris Hamilton Committed by Commit Bot

[PM] Create FrameNodeImplDescriber.

This introduces a describer that allows annotating FrameNodes with their
full internal state in the chrome://discards/graph view.

BUG=1077305

Change-Id: Ibad88a2a4c4961a445c02e96c8c186f63831fd81
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2176285
Commit-Queue: Chris Hamilton <chrisha@chromium.org>
Reviewed-by: default avatardpapad <dpapad@chromium.org>
Reviewed-by: default avatarJoe Mason <joenotcharles@chromium.org>
Cr-Commit-Position: refs/heads/master@{#765413}
parent f4363929
......@@ -94,6 +94,71 @@ class ToolTip {
return;
}
/**
* Helper for recursively flattening an Object.
*
* @param {!Set} visited The set of visited objects, excluding
* {@code object}.
* @param {!Object<?,?>} flattened The flattened object being built.
* @param {string} path The current flattened path.
* @param {!Object<?,?>} object The nested dict to be flattened.
*/
function flattenObjectRec(visited, flattened, path, object) {
if (typeof object !== 'object' || visited.has(object)) {
return;
}
visited.add(object);
for (const [key, value] of Object.entries(object)) {
const fullPath = path ? `${path}.${key}` : key;
// Recurse on non-null objects.
if (!!value && typeof value === 'object') {
flattenObjectRec(
visited, flattened, fullPath,
/** @type {!Object<?,?>} */ (value));
} else {
// Everything else is considered a leaf value.
flattened[fullPath] = value;
}
}
}
/**
* Recursively flattens an Object of key/value pairs. Nested objects will be
* flattened to paths with a . separator between each key. If there are
* circular dependencies, they will not be expanded.
*
* For example, converting:
*
* {
* 'foo': 'hello',
* 'bar': 1,
* 'baz': {
* 'x': 43.5,
* 'y': 'fox'
* 'z': [1, 2]
* },
* 'self': (reference to self)
* }
*
* will yield:
*
* {
* 'foo': 'hello',
* 'bar': 1,
* 'baz.x': 43.5,
* 'baz.y': 'fox',
* 'baz.z.0': '1',
* 'baz.y.1': '2'
* }
* @param {!Object<?,?>} object The object to be flattened.
* @return {!Object<?,?>} the flattened object.
*/
function flattenObject(object) {
const flattened = {};
flattenObjectRec(new Set(), flattened, '', object);
return flattened;
}
// The JSON is a dictionary of data describer name to their data. Assuming a
// convention that describers emit a dictionary from string->string, this is
// flattened to an array. Each top-level dictionary entry is flattened to a
......@@ -104,7 +169,15 @@ class ToolTip {
/** @type {!Object<?,?>} */ (JSON.parse(description_json));
const flattenedDescription = [];
for (const [title, value] of Object.entries(description)) {
flattenedDescription.push([title, null], ...Object.entries(value));
flattenedDescription.push([title, null]);
const flattenedValue = flattenObject(value);
for (const [propName, propValue] of Object.entries(flattenedValue)) {
let strValue = String(propValue);
if (strValue.length > 50) {
strValue = `${strValue.substring(0, 47)}...`;
}
flattenedDescription.push([propName, strValue]);
}
}
if (flattenedDescription.length === 0) {
flattenedDescription.push(['No Data', null]);
......
......@@ -26,6 +26,8 @@ static_library("performance_manager") {
"graph/frame_node.cc",
"graph/frame_node_impl.cc",
"graph/frame_node_impl.h",
"graph/frame_node_impl_describer.cc",
"graph/frame_node_impl_describer.h",
"graph/graph.cc",
"graph/graph_impl.cc",
"graph/graph_impl.h",
......
......@@ -20,7 +20,7 @@
namespace performance_manager {
class FrameNodeImpl;
class FrameNodeImplDescriber;
class PageNodeImpl;
class ProcessNodeImpl;
class WorkerNodeImpl;
......@@ -132,6 +132,7 @@ class FrameNodeImpl
}
private:
friend class FrameNodeImplDescriber;
friend class FramePriorityAccess;
friend class PageNodeImpl;
friend class ProcessNodeImpl;
......
// 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 "components/performance_manager/graph/frame_node_impl_describer.h"
#include <sstream>
#include "base/task/task_traits.h"
#include "base/values.h"
#include "components/performance_manager/graph/frame_node_impl.h"
#include "components/performance_manager/public/frame_priority/frame_priority.h"
#include "components/performance_manager/public/graph/node_data_describer_registry.h"
namespace performance_manager {
namespace {
const char kDescriberName[] = "FrameNodeImpl";
// TODO(1077305): Move the following to a public describer_utils helper.
// Mojo enums have std::stream support. This converts them to a std::string.
template <typename MojoEnum>
std::string MojoEnumToString(MojoEnum mojo_enum_value) {
std::stringstream ss;
ss << mojo_enum_value;
return ss.str();
}
// Converts a string to a base::Value, where null strings go to a null value
// instead of an empty string.
base::Value MaybeNullStringToValue(base::StringPiece str) {
if (str.data() == nullptr)
return base::Value();
return base::Value(str);
}
base::Value PriorityAndReasonToValue(
const frame_priority::PriorityAndReason& priority_and_reason) {
base::Value priority(base::Value::Type::DICTIONARY);
priority.SetStringKey(
"priority", base::TaskPriorityToString(priority_and_reason.priority()));
priority.SetPath("reason",
MaybeNullStringToValue(priority_and_reason.reason()));
return priority;
}
} // namespace
FrameNodeImplDescriber::~FrameNodeImplDescriber() = default;
void FrameNodeImplDescriber::OnPassedToGraph(Graph* graph) {
graph->GetNodeDataDescriberRegistry()->RegisterDescriber(this,
kDescriberName);
}
void FrameNodeImplDescriber::OnTakenFromGraph(Graph* graph) {
graph->GetNodeDataDescriberRegistry()->UnregisterDescriber(this);
}
base::Value FrameNodeImplDescriber::DescribeFrameNodeData(
const FrameNode* node) const {
const FrameNodeImpl* impl = FrameNodeImpl::FromNode(node);
base::Value ret(base::Value::Type::DICTIONARY);
// Document specific properties. These are emitted in a nested dictionary, as
// a frame node can be reused for different documents.
base::Value doc(base::Value::Type::DICTIONARY);
doc.SetStringKey("url", impl->document_.url.value().possibly_invalid_spec());
doc.SetBoolKey("has_nonempty_beforeunload",
impl->document_.has_nonempty_beforeunload);
doc.SetBoolKey("network_almost_idle",
impl->document_.network_almost_idle.value());
doc.SetStringKey(
"origin_trial_freeze_policy",
MojoEnumToString(impl->document_.origin_trial_freeze_policy.value()));
doc.SetBoolKey("had_form_interaction",
impl->document_.had_form_interaction.value());
ret.SetKey("document", std::move(doc));
// Frame node properties.
ret.SetIntKey("frame_tree_node_id", impl->frame_tree_node_id_);
ret.SetIntKey("render_frame_id", impl->render_frame_id_);
ret.SetStringKey("dev_tools_token", impl->dev_tools_token_.ToString());
ret.SetIntKey("browsing_instance_id", impl->browsing_instance_id_);
ret.SetIntKey("site_instance_id", impl->site_instance_id_);
ret.SetStringKey("lifecycle_state",
MojoEnumToString(impl->lifecycle_state_.value()));
ret.SetBoolKey("is_ad_frame", impl->is_ad_frame_.value());
ret.SetBoolKey("is_holding_weblock", impl->is_holding_weblock_.value());
ret.SetBoolKey("is_holding_indexeddb_lock",
impl->is_holding_indexeddb_lock_.value());
ret.SetBoolKey("is_current", impl->is_current_.value());
ret.SetKey("priority",
PriorityAndReasonToValue(impl->priority_and_reason_.value()));
return ret;
}
} // namespace performance_manager
// 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 COMPONENTS_PERFORMANCE_MANAGER_GRAPH_FRAME_NODE_IMPL_DESCRIBER_H_
#define COMPONENTS_PERFORMANCE_MANAGER_GRAPH_FRAME_NODE_IMPL_DESCRIBER_H_
#include "components/performance_manager/public/graph/graph.h"
#include "components/performance_manager/public/graph/node_data_describer.h"
namespace performance_manager {
class FrameNodeImplDescriber : public GraphOwned,
public NodeDataDescriberDefaultImpl {
public:
FrameNodeImplDescriber() = default;
FrameNodeImplDescriber(const FrameNodeImplDescriber&) = delete;
FrameNodeImplDescriber& operator=(const FrameNodeImplDescriber&) = delete;
~FrameNodeImplDescriber() override;
// GraphOwned impl:
void OnPassedToGraph(Graph* graph) override;
void OnTakenFromGraph(Graph* graph) override;
// NodeDataDescriberDefaultImpl impl:
base::Value DescribeFrameNodeData(const FrameNode* node) const override;
};
} // namespace performance_manager
#endif // COMPONENTS_PERFORMANCE_MANAGER_GRAPH_FRAME_NODE_IMPL_DESCRIBER_H_
\ No newline at end of file
......@@ -41,23 +41,29 @@ base::Value GetProcessValueDict(const base::Process& process) {
ret.SetBoolKey("is_current", true);
}
#if !defined(OS_ANDROID)
ret.SetStringKey("creation_time", base::TimeFormatTimeOfDayWithMilliseconds(
process.CreationTime()));
#endif
#if defined(OS_CHROMEOS)
if (process.GetPidInNamespace() != base::kNullProcessId) {
ret.SetIntKey("pid_in_namespace", process.GetPidInNamespace());
}
#endif
#if defined(OS_WIN)
// Creation time is always available on Windows, even for dead processes.
// On other platforms it is available only for valid processes (see below).
ret.SetStringKey("creation_time", base::TimeFormatTimeOfDayWithMilliseconds(
process.CreationTime()));
#endif
if (process.IsValid()) {
// These properties can only be accessed for valid processes.
ret.SetIntKey("os_priority", process.GetPriority());
#if !defined(OS_MACOSX)
ret.SetBoolKey("is_backgrounded", process.IsProcessBackgrounded());
#endif
#if !defined(OS_ANDROID) && !defined(OS_WIN)
ret.SetStringKey("creation_time", base::TimeFormatTimeOfDayWithMilliseconds(
process.CreationTime()));
#endif
#if defined(OS_WIN)
// Most processes are running, so only show the outliers.
if (!process.IsRunning()) {
......
......@@ -6,6 +6,7 @@
#include "base/bind.h"
#include "components/performance_manager/decorators/page_load_tracker_decorator.h"
#include "components/performance_manager/graph/frame_node_impl_describer.h"
#include "components/performance_manager/graph/process_node_impl_describer.h"
#include "components/performance_manager/graph/worker_node_impl_describer.h"
#include "components/performance_manager/performance_manager_impl.h"
......@@ -20,6 +21,7 @@ namespace {
void DefaultGraphCreatedCallback(
GraphCreatedCallback external_graph_created_callback,
GraphImpl* graph) {
graph->PassToGraph(std::make_unique<FrameNodeImplDescriber>());
graph->PassToGraph(std::make_unique<PageLiveStateDecorator>());
graph->PassToGraph(std::make_unique<PageLoadTrackerDecorator>());
graph->PassToGraph(std::make_unique<ProcessNodeImplDescriber>());
......
......@@ -17,6 +17,24 @@ class WorkerNode;
// An interface for decoding node-private data for ultimate display as
// human-comprehensible text to allow diagnosis of node private data.
//
// Typically describers should be returning a dictionary, as the keys are the
// labels that will be attached to the data in the display. Care should be
// taken to make a difference between "has no data" and "has empty data". In the
// case where the describer truly has no data regarding a node, it should return
// a base::Value() (null value); in the case where the object being described
// knows about the node and has some data structure allocated relative to that
// node, but which is presently empty, it makes more sense to return an empty
// base::DictionaryValue. A null value will result in no data being displayed
// on the graph UI, while an empty dictionary will be displayed. Similarly, if
// the value being presented is a potentially null string, making that
// distinction by returning a null value instead of an empty string is
// worthwhile.
//
// In general, describers should return values using the base::Value type that
// is most appropriate. If no appropriate type exists (ie, a 64-bit integer
// value, or a time value, etc), prefer to use a human-readable string. See
// node_data_describer_util.h for helper functions for common value types.
class NodeDataDescriber {
public:
virtual ~NodeDataDescriber() = default;
......
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