Bug 1072724 - Support showing more information about style structs in restyle logs. r=dbaron
authorCameron McCormack <cam@mcc.id.au>
Wed, 01 Oct 2014 09:13:57 +1000
changeset 208078 298b1f34d02a31392c1e725fbd68cd114c56df66
parent 208077 b09b8406f2c7c517879c8d514846da84092f9133
child 208079 1d4896122f46bb8d7d50b6d1ee083e9afaea0caa
push id27575
push usercbook@mozilla.com
push dateWed, 01 Oct 2014 12:52:17 +0000
treeherdermozilla-central@fe0afc101ad2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
bugs1072724
milestone35.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1072724 - Support showing more information about style structs in restyle logs. r=dbaron The MOZ_DEBUG_RESTYLE_STRUCTS environment variable can be set to a comma- separated list of style struct names. When restyle logging is enabled, this will cause the style context tree -- showing cached style struct pointers for those structs specified -- to be logged before each individual restyle is processed. It will also show the struct pointer values involved when swapping structs between style contexts. For example, set MOZ_DEBUG_RESTYLE_STRUCTS=Font,UserInterface to show the cached nsStyleFont and nsStyleUserInterface pointers on the style contexts involved in the restyle process.
layout/base/RestyleManager.cpp
layout/base/RestyleManager.h
layout/base/RestyleTracker.cpp
layout/style/nsStyleContext.cpp
layout/style/nsStyleContext.h
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -3064,16 +3064,26 @@ ElementRestyler::RestyleSelf(nsIFrame* a
         } else if (newContext->IsShared()) {
           LOG_RESTYLE("not swapping style structs, since the new context is "
                       "shared");
         } else {
           LOG_RESTYLE("swapping style structs between %p and %p",
                       oldContext.get(), newContext.get());
           oldContext->SwapStyleData(newContext, equalStructs);
           *aSwappedStructs |= equalStructs;
+#ifdef RESTYLE_LOGGING
+          uint32_t structs = RestyleManager::StructsToLog() & equalStructs;
+          if (structs) {
+            LOG_RESTYLE_INDENT();
+            LOG_RESTYLE("old style context now has: %s",
+                        oldContext->GetCachedStyleDataAsString(structs).get());
+            LOG_RESTYLE("new style context now has: %s",
+                        newContext->GetCachedStyleDataAsString(structs).get());
+          }
+#endif
         }
         LOG_RESTYLE("setting new style context");
         aSelf->SetStyleContext(newContext);
       }
     } else {
       LOG_RESTYLE("not setting new style context, since we'll reframe");
     }
   }
@@ -3631,16 +3641,49 @@ RestyleManager::ComputeStyleChangeFor(ns
         NS_ASSERTION(!cont->GetPrevContinuation(),
                      "continuing frame had more severe impact than first-in-flow");
         return;
       }
     }
   }
 }
 
+#ifdef RESTYLE_LOGGING
+uint32_t
+RestyleManager::StructsToLog()
+{
+  static bool initialized = false;
+  static uint32_t structs;
+  if (!initialized) {
+    structs = 0;
+    const char* value = getenv("MOZ_DEBUG_RESTYLE_STRUCTS");
+    if (value) {
+      nsCString s(value);
+      while (!s.IsEmpty()) {
+        int32_t index = s.FindChar(',');
+        nsStyleStructID sid;
+        bool found;
+        if (index == -1) {
+          found = nsStyleContext::LookupStruct(s, sid);
+          s.Truncate();
+        } else {
+          found = nsStyleContext::LookupStruct(Substring(s, 0, index), sid);
+          s = Substring(s, index + 1);
+        }
+        if (found) {
+          structs |= nsCachedStyleData::GetBitForSID(sid);
+        }
+      }
+    }
+    initialized = true;
+  }
+  return structs;
+}
+#endif
+
 #ifdef DEBUG
 /* static */ nsCString
 RestyleManager::RestyleHintToString(nsRestyleHint aHint)
 {
   nsCString result;
   bool any = false;
   const char* names[] = { "Self", "Subtree", "LaterSiblings", "CSSTransitions",
                           "CSSAnimations", "SVGAttrAnimations", "StyleAttribute",
--- a/layout/base/RestyleManager.h
+++ b/layout/base/RestyleManager.h
@@ -381,16 +381,23 @@ public:
     return enabled;
   }
 
   static bool AnimationRestyleLoggingEnabled() {
     static bool animations = getenv("MOZ_DEBUG_RESTYLE_ANIMATIONS") != 0;
     return animations;
   }
 
+  // Set MOZ_DEBUG_RESTYLE_STRUCTS to a comma-separated string of
+  // style struct names -- such as "Font,SVGReset" -- to log the style context
+  // tree and those cached struct pointers before each restyle.  This
+  // function returns a bitfield of the structs named in the
+  // environment variable.
+  static uint32_t StructsToLog();
+
   static nsCString StructNamesToString(uint32_t aSIDs);
   int32_t& LoggingDepth() { return mLoggingDepth; }
 #endif
 
 private:
   /* aMinHint is the minimal change that should be made to the element */
   // XXXbz do we really need the aPrimaryFrame argument here?
   void RestyleElement(Element*        aElement,
--- a/layout/base/RestyleTracker.cpp
+++ b/layout/base/RestyleTracker.cpp
@@ -157,17 +157,27 @@ RestyleTracker::ProcessOneRestyle(Elemen
   NS_PRECONDITION(aElement->GetCrossShadowCurrentDoc() == Document(),
                   "Element has unexpected document");
 
   LOG_RESTYLE("aRestyleHint = %s, aChangeHint = %s",
               RestyleManager::RestyleHintToString(aRestyleHint).get(),
               RestyleManager::ChangeHintToString(aChangeHint).get());
 
   nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
+
   if (aRestyleHint & ~eRestyle_LaterSiblings) {
+#ifdef RESTYLE_LOGGING
+    if (ShouldLogRestyle() && primaryFrame &&
+        RestyleManager::StructsToLog() != 0) {
+      LOG_RESTYLE("style context tree before restyle:");
+      LOG_RESTYLE_INDENT();
+      primaryFrame->StyleContext()->LogStyleContextTree(
+          LoggingDepth(), RestyleManager::StructsToLog());
+    }
+#endif
     mRestyleManager->RestyleElement(aElement, primaryFrame, aChangeHint,
                                     *this, aRestyleHint);
   } else if (aChangeHint &&
              (primaryFrame ||
               (aChangeHint & nsChangeHint_ReconstructFrame))) {
     // Don't need to recompute style; just apply the hint
     nsStyleChangeList changeList;
     changeList.AppendChange(primaryFrame, aElement, aChangeHint);
--- a/layout/style/nsStyleContext.cpp
+++ b/layout/style/nsStyleContext.cpp
@@ -1043,16 +1043,31 @@ nsStyleContext::StructName(nsStyleStruct
     case eStyleStruct_##name_:                                                \
       return #name_;
 #include "nsStyleStructList.h"
 #undef STYLE_STRUCT
     default:
       return "Unknown";
   }
 }
+
+/* static */ bool
+nsStyleContext::LookupStruct(const nsACString& aName, nsStyleStructID& aResult)
+{
+  if (false)
+    ;
+#define STYLE_STRUCT(name_, checkdata_cb_)                                    \
+  else if (aName.EqualsLiteral(#name_))                                       \
+    aResult = eStyleStruct_##name_;
+#include "nsStyleStructList.h"
+#undef STYLE_STRUCT
+  else
+    return false;
+  return true;
+}
 #endif
 
 bool
 nsStyleContext::HasSameCachedStyleData(nsStyleContext* aOther,
                                        nsStyleStructID aSID)
 {
   return GetCachedStyleData(aSID) == aOther->GetCachedStyleData(aSID);
 }
@@ -1157,8 +1172,101 @@ nsStyleContext::DoClearCachedInheritedSt
   }
 
   if (aStructs == 0) {
     return;
   }
 
   ClearCachedInheritedStyleDataOnDescendants(aStructs);
 }
+
+#ifdef RESTYLE_LOGGING
+nsCString
+nsStyleContext::GetCachedStyleDataAsString(uint32_t aStructs)
+{
+  nsCString structs;
+  for (nsStyleStructID i = nsStyleStructID(0);
+       i < nsStyleStructID_Length;
+       i = nsStyleStructID(i + 1)) {
+    if (aStructs & nsCachedStyleData::GetBitForSID(i)) {
+      const void* data = GetCachedStyleData(i);
+      if (!structs.IsEmpty()) {
+        structs.Append(' ');
+      }
+      structs.AppendPrintf("%s=%p", StructName(i), data);
+      if (HasCachedInheritedStyleData(i)) {
+        structs.AppendLiteral("(dependent)");
+      } else {
+        structs.AppendLiteral("(owned)");
+      }
+    }
+  }
+  return structs;
+}
+
+int32_t&
+nsStyleContext::LoggingDepth()
+{
+  static int32_t depth = 0;
+  return depth;
+}
+
+void
+nsStyleContext::LogStyleContextTree(int32_t aLoggingDepth, uint32_t aStructs)
+{
+  LoggingDepth() = aLoggingDepth;
+  LogStyleContextTree(true, aStructs);
+}
+
+void
+nsStyleContext::LogStyleContextTree(bool aFirst, uint32_t aStructs)
+{
+  nsCString structs = GetCachedStyleDataAsString(aStructs);
+  if (!structs.IsEmpty()) {
+    structs.Append(' ');
+  }
+
+  nsCString pseudo;
+  if (mPseudoTag) {
+    nsAutoString pseudoTag;
+    mPseudoTag->ToString(pseudoTag);
+    AppendUTF16toUTF8(pseudoTag, pseudo);
+    pseudo.Append(' ');
+  }
+
+  nsCString flags;
+  if (IsStyleIfVisited()) {
+    flags.AppendLiteral("IS_STYLE_IF_VISITED ");
+  }
+  if (UsesGrandancestorStyle()) {
+    flags.AppendLiteral("USES_GRANDANCESTOR_STYLE ");
+  }
+  if (IsShared()) {
+    flags.AppendLiteral("IS_SHARED ");
+  }
+
+  nsCString parent;
+  if (aFirst) {
+    parent.AppendPrintf("parent=%p ", mParent);
+  }
+
+  LOG_RESTYLE("%p(%d) %s%s%s%s",
+              this, mRefCnt,
+              structs.get(), pseudo.get(), flags.get(), parent.get());
+
+  LOG_RESTYLE_INDENT();
+
+  if (nullptr != mChild) {
+    nsStyleContext* child = mChild;
+    do {
+      child->LogStyleContextTree(false, aStructs);
+      child = child->mNextSibling;
+    } while (mChild != child);
+  }
+  if (nullptr != mEmptyChild) {
+    nsStyleContext* child = mEmptyChild;
+    do {
+      child->LogStyleContextTree(false, aStructs);
+      child = child->mNextSibling;
+    } while (mEmptyChild != child);
+  }
+}
+#endif
--- a/layout/style/nsStyleContext.h
+++ b/layout/style/nsStyleContext.h
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* the interface (to internal code) for retrieving computed style data */
 
 #ifndef _nsStyleContext_h_
 #define _nsStyleContext_h_
 
+#include "mozilla/RestyleLogging.h"
 #include "nsRuleNode.h"
 #include "nsCSSPseudoElements.h"
 
 class nsIAtom;
 class nsPresContext;
 
 /**
  * An nsStyleContext represents the computed style data for an element.
@@ -392,16 +393,23 @@ public:
    * structs indicated in aStructs.
    */
   void ClearCachedInheritedStyleDataOnDescendants(uint32_t aStructs);
 
 #ifdef DEBUG
   void List(FILE* out, int32_t aIndent);
   static void AssertStyleStructMaxDifferenceValid();
   static const char* StructName(nsStyleStructID aSID);
+  static bool LookupStruct(const nsACString& aName, nsStyleStructID& aResult);
+#endif
+
+#ifdef RESTYLE_LOGGING
+  nsCString GetCachedStyleDataAsString(uint32_t aStructs);
+  void LogStyleContextTree(int32_t aLoggingDepth, uint32_t aStructs);
+  int32_t& LoggingDepth();
 #endif
 
 private:
   // Private destructor, to discourage deletion outside of Release():
   ~nsStyleContext();
 
   void AddChild(nsStyleContext* aChild);
   void RemoveChild(nsStyleContext* aChild);
@@ -445,16 +453,25 @@ private:
   // Helper for ClearCachedInheritedStyleDataOnDescendants.
   void DoClearCachedInheritedStyleDataOnDescendants(uint32_t aStructs);
 
 #ifdef DEBUG
   void AssertStructsNotUsedElsewhere(nsStyleContext* aDestroyingContext,
                                      int32_t aLevels) const;
 #endif
 
+#ifdef RESTYLE_LOGGING
+  void LogStyleContextTree(bool aFirst, uint32_t aStructs);
+
+  // This only gets called under call trees where we've already checked
+  // that PresContext()->RestyleManager()->ShouldLogRestyle() returned true.
+  // It exists here just to satisfy LOG_RESTYLE's expectations.
+  bool ShouldLogRestyle() { return true; }
+#endif
+
   nsStyleContext* mParent; // STRONG
 
   // Children are kept in two circularly-linked lists.  The list anchor
   // is not part of the list (null for empty), and we point to the first
   // child.
   // mEmptyChild for children whose rule node is the root rule node, and
   // mChild for other children.  The order of children is not
   // meaningful.