Commit e9a081ab authored by kouhei@chromium.org's avatar kouhei@chromium.org

[Invalidation Tracking] Trace StyleInvalidator setNeedsStyleRecalc

This patch adds a set of trace events to track reason when
StyleInvalidator invokes Node::setNeedsStyleRecalc. One of the trace events is guaranteed to be issued before StyleInvalidator issues setNeedsStyleRecalc on the node. The trace events come with InspectorStyleInvalidatorInvalidateEvent, which contains inspector node id, human readable reason string, and optionally the tagName/id/class/attr/invalidationList involved.

This is to be used from devtools so we post process and find the StyleInvalidator trace event on the given node to see the detailed reason for why StyleInvalidator decided issue setNeedsStyleRecalc on the node.

BUG=410701

Committed: https://src.chromium.org/viewvc/blink?view=rev&revision=183177

Review URL: https://codereview.chromium.org/580373002

git-svn-id: svn://svn.chromium.org/blink/trunk@183643 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent bda3b72d
...@@ -33,9 +33,24 @@ ...@@ -33,9 +33,24 @@
#include "core/css/resolver/StyleResolver.h" #include "core/css/resolver/StyleResolver.h"
#include "core/dom/Element.h" #include "core/dom/Element.h"
#include "core/inspector/InspectorTraceEvents.h"
#include "platform/TracedValue.h"
#include "wtf/Compiler.h"
#include "wtf/text/StringBuilder.h"
namespace blink { namespace blink {
static const unsigned char* s_tracingEnabled = nullptr;
#define TRACE_STYLE_INVALIDATOR_INVALIDATION_SELECTORPART_IF_ENABLED(element, reason, singleSelectorPart) \
if (UNLIKELY(*s_tracingEnabled)) \
TRACE_STYLE_INVALIDATOR_INVALIDATION_SELECTORPART(element, reason, singleSelectorPart);
void DescendantInvalidationSet::cacheTracingFlag()
{
s_tracingEnabled = TRACE_EVENT_API_GET_CATEGORY_ENABLED(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.invalidationTracking"));
}
DescendantInvalidationSet::DescendantInvalidationSet() DescendantInvalidationSet::DescendantInvalidationSet()
: m_allDescendantsMightBeInvalid(false) : m_allDescendantsMightBeInvalid(false)
, m_customPseudoInvalid(false) , m_customPseudoInvalid(false)
...@@ -48,24 +63,32 @@ bool DescendantInvalidationSet::invalidatesElement(Element& element) const ...@@ -48,24 +63,32 @@ bool DescendantInvalidationSet::invalidatesElement(Element& element) const
if (m_allDescendantsMightBeInvalid) if (m_allDescendantsMightBeInvalid)
return true; return true;
if (m_tagNames && m_tagNames->contains(element.tagQName().localName())) if (m_tagNames && m_tagNames->contains(element.tagQName().localName())) {
TRACE_STYLE_INVALIDATOR_INVALIDATION_SELECTORPART_IF_ENABLED(element, InvalidationSetMatchedTagName, element.tagQName().localName());
return true; return true;
}
if (element.hasID() && m_ids && m_ids->contains(element.idForStyleResolution())) if (element.hasID() && m_ids && m_ids->contains(element.idForStyleResolution())) {
TRACE_STYLE_INVALIDATOR_INVALIDATION_SELECTORPART_IF_ENABLED(element, InvalidationSetMatchedId, element.idForStyleResolution());
return true; return true;
}
if (element.hasClass() && m_classes) { if (element.hasClass() && m_classes) {
const SpaceSplitString& classNames = element.classNames(); const SpaceSplitString& classNames = element.classNames();
for (const auto& className : *m_classes) { for (const auto& className : *m_classes) {
if (classNames.contains(className)) if (classNames.contains(className)) {
TRACE_STYLE_INVALIDATOR_INVALIDATION_SELECTORPART_IF_ENABLED(element, InvalidationSetMatchedClass, className);
return true; return true;
}
} }
} }
if (element.hasAttributes() && m_attributes) { if (element.hasAttributes() && m_attributes) {
for (const auto& attribute : *m_attributes) { for (const auto& attribute : *m_attributes) {
if (element.hasAttribute(attribute)) if (element.hasAttribute(attribute)) {
TRACE_STYLE_INVALIDATOR_INVALIDATION_SELECTORPART_IF_ENABLED(element, InvalidationSetMatchedAttribute, attribute);
return true; return true;
}
} }
} }
...@@ -189,33 +212,54 @@ void DescendantInvalidationSet::trace(Visitor* visitor) ...@@ -189,33 +212,54 @@ void DescendantInvalidationSet::trace(Visitor* visitor)
#endif #endif
} }
#ifndef NDEBUG void DescendantInvalidationSet::toTracedValue(TracedValue* value) const
void DescendantInvalidationSet::show() const
{ {
fprintf(stderr, "DescendantInvalidationSet { "); value->beginDictionary();
if (m_allDescendantsMightBeInvalid) if (m_allDescendantsMightBeInvalid)
fprintf(stderr, "* "); value->setBoolean("allDescendantsMightBeInvalid", true);
if (m_customPseudoInvalid) if (m_customPseudoInvalid)
fprintf(stderr, "::custom "); value->setBoolean("customPseudoInvalid", true);
if (m_treeBoundaryCrossing) if (m_treeBoundaryCrossing)
fprintf(stderr, "::shadow/deep/ "); value->setBoolean("treeBoundaryCrossing", true);
if (m_ids) { if (m_ids) {
value->beginArray("ids");
for (const auto& id : *m_ids) for (const auto& id : *m_ids)
fprintf(stderr, "#%s ", id.ascii().data()); value->pushString(id);
value->endArray();
} }
if (m_classes) { if (m_classes) {
value->beginArray("classes");
for (const auto& className : *m_classes) for (const auto& className : *m_classes)
fprintf(stderr, ".%s ", className.ascii().data()); value->pushString(className);
value->endArray();
} }
if (m_tagNames) { if (m_tagNames) {
value->beginArray("tagNames");
for (const auto& tagName : *m_tagNames) for (const auto& tagName : *m_tagNames)
fprintf(stderr, "<%s> ", tagName.ascii().data()); value->pushString(tagName);
value->endArray();
} }
if (m_attributes) { if (m_attributes) {
value->beginArray("attributes");
for (const auto& attribute : *m_attributes) for (const auto& attribute : *m_attributes)
fprintf(stderr, "[%s] ", attribute.ascii().data()); value->pushString(attribute);
value->endArray();
} }
fprintf(stderr, "}\n");
value->endDictionary();
}
#ifndef NDEBUG
void DescendantInvalidationSet::show() const
{
RefPtr<TracedValue> value = TracedValue::create();
toTracedValue(value.get());
fprintf(stderr, "%s\n", value->asTraceFormat().ascii().data());
} }
#endif // NDEBUG #endif // NDEBUG
......
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
namespace blink { namespace blink {
class Element; class Element;
class TracedValue;
// Tracks data to determine which elements of a DOM subtree need to have style // Tracks data to determine which elements of a DOM subtree need to have style
// recalculated. // recalculated.
...@@ -52,6 +53,8 @@ public: ...@@ -52,6 +53,8 @@ public:
return adoptRefWillBeNoop(new DescendantInvalidationSet); return adoptRefWillBeNoop(new DescendantInvalidationSet);
} }
static void cacheTracingFlag();
bool invalidatesElement(Element&) const; bool invalidatesElement(Element&) const;
void combine(const DescendantInvalidationSet& other); void combine(const DescendantInvalidationSet& other);
...@@ -74,6 +77,8 @@ public: ...@@ -74,6 +77,8 @@ public:
void trace(Visitor*); void trace(Visitor*);
void toTracedValue(TracedValue*) const;
#ifndef NDEBUG #ifndef NDEBUG
void show() const; void show() const;
#endif #endif
......
...@@ -13,10 +13,17 @@ ...@@ -13,10 +13,17 @@
#include "core/dom/ElementTraversal.h" #include "core/dom/ElementTraversal.h"
#include "core/dom/shadow/ElementShadow.h" #include "core/dom/shadow/ElementShadow.h"
#include "core/dom/shadow/ShadowRoot.h" #include "core/dom/shadow/ShadowRoot.h"
#include "core/inspector/InspectorTraceEvents.h"
#include "core/rendering/RenderObject.h" #include "core/rendering/RenderObject.h"
namespace blink { namespace blink {
static const unsigned char* s_tracingEnabled = nullptr;
#define TRACE_STYLE_INVALIDATOR_INVALIDATION_IF_ENABLED(element, reason) \
if (UNLIKELY(*s_tracingEnabled)) \
TRACE_STYLE_INVALIDATOR_INVALIDATION(element, reason);
void StyleInvalidator::invalidate(Document& document) void StyleInvalidator::invalidate(Document& document)
{ {
RecursionData recursionData; RecursionData recursionData;
...@@ -63,6 +70,8 @@ void StyleInvalidator::clearPendingInvalidations() ...@@ -63,6 +70,8 @@ void StyleInvalidator::clearPendingInvalidations()
StyleInvalidator::StyleInvalidator() StyleInvalidator::StyleInvalidator()
{ {
s_tracingEnabled = TRACE_EVENT_API_GET_CATEGORY_ENABLED(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.invalidationTracking"));
DescendantInvalidationSet::cacheTracingFlag();
} }
StyleInvalidator::~StyleInvalidator() StyleInvalidator::~StyleInvalidator()
...@@ -82,12 +91,14 @@ void StyleInvalidator::RecursionData::pushInvalidationSet(const DescendantInvali ...@@ -82,12 +91,14 @@ void StyleInvalidator::RecursionData::pushInvalidationSet(const DescendantInvali
m_invalidateCustomPseudo = invalidationSet.customPseudoInvalid(); m_invalidateCustomPseudo = invalidationSet.customPseudoInvalid();
} }
bool StyleInvalidator::RecursionData::matchesCurrentInvalidationSets(Element& element) ALWAYS_INLINE bool StyleInvalidator::RecursionData::matchesCurrentInvalidationSets(Element& element)
{ {
ASSERT(!m_wholeSubtreeInvalid); ASSERT(!m_wholeSubtreeInvalid);
if (m_invalidateCustomPseudo && element.shadowPseudoId() != nullAtom) if (m_invalidateCustomPseudo && element.shadowPseudoId() != nullAtom) {
TRACE_STYLE_INVALIDATOR_INVALIDATION_IF_ENABLED(element, InvalidateCustomPseudo);
return true; return true;
}
for (const auto& invalidationSet : m_invalidationSets) { for (const auto& invalidationSet : m_invalidationSets) {
if (invalidationSet->invalidatesElement(element)) if (invalidationSet->invalidatesElement(element))
...@@ -97,7 +108,7 @@ bool StyleInvalidator::RecursionData::matchesCurrentInvalidationSets(Element& el ...@@ -97,7 +108,7 @@ bool StyleInvalidator::RecursionData::matchesCurrentInvalidationSets(Element& el
return false; return false;
} }
bool StyleInvalidator::checkInvalidationSetsAgainstElement(Element& element, StyleInvalidator::RecursionData& recursionData) ALWAYS_INLINE bool StyleInvalidator::checkInvalidationSetsAgainstElement(Element& element, StyleInvalidator::RecursionData& recursionData)
{ {
if (element.styleChangeType() >= SubtreeStyleChange || recursionData.wholeSubtreeInvalid()) { if (element.styleChangeType() >= SubtreeStyleChange || recursionData.wholeSubtreeInvalid()) {
recursionData.setWholeSubtreeInvalid(); recursionData.setWholeSubtreeInvalid();
...@@ -108,9 +119,15 @@ bool StyleInvalidator::checkInvalidationSetsAgainstElement(Element& element, Sty ...@@ -108,9 +119,15 @@ bool StyleInvalidator::checkInvalidationSetsAgainstElement(Element& element, Sty
for (const auto& invalidationSet : *invalidationList) for (const auto& invalidationSet : *invalidationList)
recursionData.pushInvalidationSet(*invalidationSet); recursionData.pushInvalidationSet(*invalidationSet);
// FIXME: It's really only necessary to clone the render style for this element, not full style recalc. // FIXME: It's really only necessary to clone the render style for this element, not full style recalc.
if (UNLIKELY(*s_tracingEnabled)) {
TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.invalidationTracking"),
"StyleInvalidatorInvalidationTracking",
"data", InspectorStyleInvalidatorInvalidateEvent::invalidationList(element, *invalidationList));
}
return true; return true;
} }
} }
return recursionData.matchesCurrentInvalidationSets(element); return recursionData.matchesCurrentInvalidationSets(element);
} }
...@@ -148,10 +165,12 @@ bool StyleInvalidator::invalidate(Element& element, StyleInvalidator::RecursionD ...@@ -148,10 +165,12 @@ bool StyleInvalidator::invalidate(Element& element, StyleInvalidator::RecursionD
element.setNeedsStyleRecalc(recursionData.wholeSubtreeInvalid() ? SubtreeStyleChange : LocalStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::StyleInvalidator)); element.setNeedsStyleRecalc(recursionData.wholeSubtreeInvalid() ? SubtreeStyleChange : LocalStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::StyleInvalidator));
} else if (recursionData.hasInvalidationSets() && someChildrenNeedStyleRecalc) { } else if (recursionData.hasInvalidationSets() && someChildrenNeedStyleRecalc) {
// Clone the RenderStyle in order to preserve correct style sharing, if possible. Otherwise recalc style. // Clone the RenderStyle in order to preserve correct style sharing, if possible. Otherwise recalc style.
if (RenderObject* renderer = element.renderer()) if (RenderObject* renderer = element.renderer()) {
renderer->setStyleInternal(RenderStyle::clone(renderer->style())); renderer->setStyleInternal(RenderStyle::clone(renderer->style()));
else } else {
TRACE_STYLE_INVALIDATOR_INVALIDATION_IF_ENABLED(element, PreventStyleSharingForParent);
element.setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::StyleInvalidator)); element.setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::StyleInvalidator));
}
} }
element.clearChildNeedsStyleInvalidation(); element.clearChildNeedsStyleInvalidation();
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "bindings/core/v8/ScriptCallStackFactory.h" #include "bindings/core/v8/ScriptCallStackFactory.h"
#include "bindings/core/v8/ScriptGCEvent.h" #include "bindings/core/v8/ScriptGCEvent.h"
#include "bindings/core/v8/ScriptSourceCode.h" #include "bindings/core/v8/ScriptSourceCode.h"
#include "core/css/invalidation/DescendantInvalidationSet.h"
#include "core/dom/StyleChangeReason.h" #include "core/dom/StyleChangeReason.h"
#include "core/events/Event.h" #include "core/events/Event.h"
#include "core/frame/FrameView.h" #include "core/frame/FrameView.h"
...@@ -66,6 +67,45 @@ void setNodeInfo(TracedValue* value, Node* node, const char* idFieldName, const ...@@ -66,6 +67,45 @@ void setNodeInfo(TracedValue* value, Node* node, const char* idFieldName, const
} }
const char InspectorStyleInvalidatorInvalidateEvent::ElementHasPendingInvalidationList[] = "Element has pending invalidation list";
const char InspectorStyleInvalidatorInvalidateEvent::InvalidateCustomPseudo[] = "Invalidate custom pseudo element.";
const char InspectorStyleInvalidatorInvalidateEvent::InvalidationSetMatchedAttribute[] = "Invalidation set matched attribute.";
const char InspectorStyleInvalidatorInvalidateEvent::InvalidationSetMatchedClass[] = "Invalidation set matched class.";
const char InspectorStyleInvalidatorInvalidateEvent::InvalidationSetMatchedId[] = "Invalidation set matched id.";
const char InspectorStyleInvalidatorInvalidateEvent::InvalidationSetMatchedTagName[] = "Invalidation set matched tagName.";
const char InspectorStyleInvalidatorInvalidateEvent::PreventStyleSharingForParent[] = "Prevent style sharing for parent.";
PassRefPtr<TracedValue> InspectorStyleInvalidatorInvalidateEvent::fillCommonPart(Element& element, const char* reason)
{
RefPtr<TracedValue> value = TracedValue::create();
value->setString("frame", toHexString(element.document().frame()));
setNodeInfo(value.get(), &element, "nodeId", "nodeName");
value->setString("reason", reason);
return value.release();
}
PassRefPtr<TraceEvent::ConvertableToTraceFormat> InspectorStyleInvalidatorInvalidateEvent::data(Element& element, const char* reason)
{
return fillCommonPart(element, reason);
}
PassRefPtr<TraceEvent::ConvertableToTraceFormat> InspectorStyleInvalidatorInvalidateEvent::selectorPart(Element& element, const char* reason, const String& selectorPart)
{
RefPtr<TracedValue> value = fillCommonPart(element, reason);
value->setString("selectorPart", selectorPart);
return value.release();
}
PassRefPtr<TraceEvent::ConvertableToTraceFormat> InspectorStyleInvalidatorInvalidateEvent::invalidationList(Element& element, const WillBeHeapVector<RefPtrWillBeMember<DescendantInvalidationSet> >& invalidationList)
{
RefPtr<TracedValue> value = fillCommonPart(element, ElementHasPendingInvalidationList);
value->beginArray("invalidationList");
for (const auto& invalidationSet : invalidationList)
invalidationSet->toTracedValue(value.get());
value->endArray();
return value.release();
}
PassRefPtr<TraceEvent::ConvertableToTraceFormat> InspectorStyleRecalcInvalidationTrackingEvent::data(Node* node, const StyleChangeReasonForTracing& reason) PassRefPtr<TraceEvent::ConvertableToTraceFormat> InspectorStyleRecalcInvalidationTrackingEvent::data(Node* node, const StyleChangeReasonForTracing& reason)
{ {
ASSERT(node); ASSERT(node);
......
...@@ -7,11 +7,15 @@ ...@@ -7,11 +7,15 @@
#include "platform/EventTracer.h" #include "platform/EventTracer.h"
#include "platform/TraceEvent.h" #include "platform/TraceEvent.h"
#include "platform/heap/Handle.h"
#include "wtf/Forward.h" #include "wtf/Forward.h"
#include "wtf/Functional.h"
namespace blink { namespace blink {
class DescendantInvalidationSet;
class Document; class Document;
class Element;
class Event; class Event;
class ExecutionContext; class ExecutionContext;
class FrameView; class FrameView;
...@@ -29,6 +33,7 @@ class ResourceResponse; ...@@ -29,6 +33,7 @@ class ResourceResponse;
class ScriptCallStack; class ScriptCallStack;
class ScriptSourceCode; class ScriptSourceCode;
class StyleChangeReasonForTracing; class StyleChangeReasonForTracing;
class TracedValue;
class WorkerThread; class WorkerThread;
class XMLHttpRequest; class XMLHttpRequest;
...@@ -43,6 +48,38 @@ public: ...@@ -43,6 +48,38 @@ public:
static PassRefPtr<TraceEvent::ConvertableToTraceFormat> data(Node*, const StyleChangeReasonForTracing&); static PassRefPtr<TraceEvent::ConvertableToTraceFormat> data(Node*, const StyleChangeReasonForTracing&);
}; };
class InspectorStyleInvalidatorInvalidateEvent {
public:
static const char ElementHasPendingInvalidationList[];
static const char InvalidateCustomPseudo[];
static const char InvalidationSetMatchedAttribute[];
static const char InvalidationSetMatchedClass[];
static const char InvalidationSetMatchedId[];
static const char InvalidationSetMatchedTagName[];
static const char PreventStyleSharingForParent[];
static PassRefPtr<TraceEvent::ConvertableToTraceFormat> data(Element&, const char* reason);
static PassRefPtr<TraceEvent::ConvertableToTraceFormat> selectorPart(Element&, const char* reason, const String&);
static PassRefPtr<TraceEvent::ConvertableToTraceFormat> invalidationList(Element&, const WillBeHeapVector<RefPtrWillBeMember<DescendantInvalidationSet> >&);
private:
static PassRefPtr<TracedValue> fillCommonPart(Element&, const char* reason);
};
#define TRACE_STYLE_INVALIDATOR_INVALIDATION(element, reason) \
TRACE_EVENT_INSTANT1( \
TRACE_DISABLED_BY_DEFAULT("devtools.timeline.invalidationTracking"), \
"StyleInvalidatorInvalidationTracking", \
"data", \
InspectorStyleInvalidatorInvalidateEvent::data((element), (InspectorStyleInvalidatorInvalidateEvent::reason)))
#define TRACE_STYLE_INVALIDATOR_INVALIDATION_SELECTORPART(element, reason, singleSelectorPart) \
TRACE_EVENT_INSTANT1( \
TRACE_DISABLED_BY_DEFAULT("devtools.timeline.invalidationTracking"), \
"StyleInvalidatorInvalidationTracking", \
"data", \
InspectorStyleInvalidatorInvalidateEvent::selectorPart((element), (InspectorStyleInvalidatorInvalidateEvent::reason), (singleSelectorPart)))
class InspectorLayoutInvalidationTrackingEvent { class InspectorLayoutInvalidationTrackingEvent {
public: public:
static PassRefPtr<TraceEvent::ConvertableToTraceFormat> data(const RenderObject*); static PassRefPtr<TraceEvent::ConvertableToTraceFormat> data(const RenderObject*);
......
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