LCOV - code coverage report
Current view: top level - tools/profiler/core - ProfileBufferEntry.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 412 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 54 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include <ostream>
       8             : #include "platform.h"
       9             : #include "mozilla/HashFunctions.h"
      10             : #include "mozilla/Sprintf.h"
      11             : 
      12             : #include "nsThreadUtils.h"
      13             : #include "nsXULAppAPI.h"
      14             : 
      15             : // JS
      16             : #include "jsapi.h"
      17             : #include "jsfriendapi.h"
      18             : #include "js/TrackedOptimizationInfo.h"
      19             : 
      20             : // Self
      21             : #include "ProfileBufferEntry.h"
      22             : 
      23             : using mozilla::JSONWriter;
      24             : using mozilla::MakeUnique;
      25             : using mozilla::Maybe;
      26             : using mozilla::Nothing;
      27             : using mozilla::Some;
      28             : using mozilla::TimeStamp;
      29             : using mozilla::UniquePtr;
      30             : 
      31             : ////////////////////////////////////////////////////////////////////////
      32             : // BEGIN ProfileBufferEntry
      33             : 
      34           0 : ProfileBufferEntry::ProfileBufferEntry()
      35           0 :   : mKind(Kind::INVALID)
      36             : {
      37           0 :   u.mString = nullptr;
      38           0 : }
      39             : 
      40             : // aString must be a static string.
      41           0 : ProfileBufferEntry::ProfileBufferEntry(Kind aKind, const char *aString)
      42           0 :   : mKind(aKind)
      43             : {
      44           0 :   u.mString = aString;
      45           0 : }
      46             : 
      47           0 : ProfileBufferEntry::ProfileBufferEntry(Kind aKind, char aChars[kNumChars])
      48           0 :   : mKind(aKind)
      49             : {
      50           0 :   memcpy(u.mChars, aChars, kNumChars);
      51           0 : }
      52             : 
      53           0 : ProfileBufferEntry::ProfileBufferEntry(Kind aKind, void* aPtr)
      54           0 :   : mKind(aKind)
      55             : {
      56           0 :   u.mPtr = aPtr;
      57           0 : }
      58             : 
      59           0 : ProfileBufferEntry::ProfileBufferEntry(Kind aKind, ProfilerMarker* aMarker)
      60           0 :   : mKind(aKind)
      61             : {
      62           0 :   u.mMarker = aMarker;
      63           0 : }
      64             : 
      65           0 : ProfileBufferEntry::ProfileBufferEntry(Kind aKind, double aDouble)
      66           0 :   : mKind(aKind)
      67             : {
      68           0 :   u.mDouble = aDouble;
      69           0 : }
      70             : 
      71           0 : ProfileBufferEntry::ProfileBufferEntry(Kind aKind, int aInt)
      72           0 :   : mKind(aKind)
      73             : {
      74           0 :   u.mInt = aInt;
      75           0 : }
      76             : 
      77             : // END ProfileBufferEntry
      78             : ////////////////////////////////////////////////////////////////////////
      79             : 
      80             : class JSONSchemaWriter
      81             : {
      82             :   JSONWriter& mWriter;
      83             :   uint32_t mIndex;
      84             : 
      85             : public:
      86           0 :   explicit JSONSchemaWriter(JSONWriter& aWriter)
      87           0 :    : mWriter(aWriter)
      88           0 :    , mIndex(0)
      89             :   {
      90           0 :     aWriter.StartObjectProperty("schema");
      91           0 :   }
      92             : 
      93           0 :   void WriteField(const char* aName) {
      94           0 :     mWriter.IntProperty(aName, mIndex++);
      95           0 :   }
      96             : 
      97           0 :   ~JSONSchemaWriter() {
      98           0 :     mWriter.EndObject();
      99           0 :   }
     100             : };
     101             : 
     102             : class StreamOptimizationTypeInfoOp : public JS::ForEachTrackedOptimizationTypeInfoOp
     103             : {
     104             :   JSONWriter& mWriter;
     105             :   UniqueJSONStrings& mUniqueStrings;
     106             :   bool mStartedTypeList;
     107             : 
     108             : public:
     109           0 :   StreamOptimizationTypeInfoOp(JSONWriter& aWriter, UniqueJSONStrings& aUniqueStrings)
     110           0 :     : mWriter(aWriter)
     111             :     , mUniqueStrings(aUniqueStrings)
     112           0 :     , mStartedTypeList(false)
     113           0 :   { }
     114             : 
     115           0 :   void readType(const char* keyedBy, const char* name,
     116             :                 const char* location, const Maybe<unsigned>& lineno) override {
     117           0 :     if (!mStartedTypeList) {
     118           0 :       mStartedTypeList = true;
     119           0 :       mWriter.StartObjectElement();
     120           0 :       mWriter.StartArrayProperty("typeset");
     121             :     }
     122             : 
     123           0 :     mWriter.StartObjectElement();
     124             :     {
     125           0 :       mUniqueStrings.WriteProperty(mWriter, "keyedBy", keyedBy);
     126           0 :       if (name) {
     127           0 :         mUniqueStrings.WriteProperty(mWriter, "name", name);
     128             :       }
     129           0 :       if (location) {
     130           0 :         mUniqueStrings.WriteProperty(mWriter, "location", location);
     131             :       }
     132           0 :       if (lineno.isSome()) {
     133           0 :         mWriter.IntProperty("line", *lineno);
     134             :       }
     135             :     }
     136           0 :     mWriter.EndObject();
     137           0 :   }
     138             : 
     139           0 :   void operator()(JS::TrackedTypeSite site, const char* mirType) override {
     140           0 :     if (mStartedTypeList) {
     141           0 :       mWriter.EndArray();
     142           0 :       mStartedTypeList = false;
     143             :     } else {
     144           0 :       mWriter.StartObjectElement();
     145             :     }
     146             : 
     147             :     {
     148           0 :       mUniqueStrings.WriteProperty(mWriter, "site", JS::TrackedTypeSiteString(site));
     149           0 :       mUniqueStrings.WriteProperty(mWriter, "mirType", mirType);
     150             :     }
     151           0 :     mWriter.EndObject();
     152           0 :   }
     153             : };
     154             : 
     155             : // As mentioned in ProfileBufferEntry.h, the JSON format contains many
     156             : // arrays whose elements are laid out according to various schemas to help
     157             : // de-duplication. This RAII class helps write these arrays by keeping track of
     158             : // the last non-null element written and adding the appropriate number of null
     159             : // elements when writing new non-null elements. It also automatically opens and
     160             : // closes an array element on the given JSON writer.
     161             : //
     162             : // Example usage:
     163             : //
     164             : //     // Define the schema of elements in this type of array: [FOO, BAR, BAZ]
     165             : //     enum Schema : uint32_t {
     166             : //       FOO = 0,
     167             : //       BAR = 1,
     168             : //       BAZ = 2
     169             : //     };
     170             : //
     171             : //     AutoArraySchemaWriter writer(someJsonWriter, someUniqueStrings);
     172             : //     if (shouldWriteFoo) {
     173             : //       writer.IntElement(FOO, getFoo());
     174             : //     }
     175             : //     ... etc ...
     176             : class MOZ_RAII AutoArraySchemaWriter
     177             : {
     178             :   friend class AutoObjectWriter;
     179             : 
     180             :   SpliceableJSONWriter& mJSONWriter;
     181             :   UniqueJSONStrings*    mStrings;
     182             :   uint32_t              mNextFreeIndex;
     183             : 
     184             : public:
     185           0 :   AutoArraySchemaWriter(SpliceableJSONWriter& aWriter, UniqueJSONStrings& aStrings)
     186           0 :     : mJSONWriter(aWriter)
     187             :     , mStrings(&aStrings)
     188           0 :     , mNextFreeIndex(0)
     189             :   {
     190           0 :     mJSONWriter.StartArrayElement();
     191           0 :   }
     192             : 
     193             :   // If you don't have access to a UniqueStrings, you had better not try and
     194             :   // write a string element down the line!
     195           0 :   explicit AutoArraySchemaWriter(SpliceableJSONWriter& aWriter)
     196           0 :     : mJSONWriter(aWriter)
     197             :     , mStrings(nullptr)
     198           0 :     , mNextFreeIndex(0)
     199             :   {
     200           0 :     mJSONWriter.StartArrayElement();
     201           0 :   }
     202             : 
     203           0 :   ~AutoArraySchemaWriter() {
     204           0 :     mJSONWriter.EndArray();
     205           0 :   }
     206             : 
     207           0 :   void FillUpTo(uint32_t aIndex) {
     208           0 :     MOZ_ASSERT(aIndex >= mNextFreeIndex);
     209           0 :     mJSONWriter.NullElements(aIndex - mNextFreeIndex);
     210           0 :     mNextFreeIndex = aIndex + 1;
     211           0 :   }
     212             : 
     213           0 :   void IntElement(uint32_t aIndex, uint32_t aValue) {
     214           0 :     FillUpTo(aIndex);
     215           0 :     mJSONWriter.IntElement(aValue);
     216           0 :   }
     217             : 
     218           0 :   void DoubleElement(uint32_t aIndex, double aValue) {
     219           0 :     FillUpTo(aIndex);
     220           0 :     mJSONWriter.DoubleElement(aValue);
     221           0 :   }
     222             : 
     223           0 :   void StringElement(uint32_t aIndex, const char* aValue) {
     224           0 :     MOZ_RELEASE_ASSERT(mStrings);
     225           0 :     FillUpTo(aIndex);
     226           0 :     mStrings->WriteElement(mJSONWriter, aValue);
     227           0 :   }
     228             : };
     229             : 
     230             : class StreamOptimizationAttemptsOp : public JS::ForEachTrackedOptimizationAttemptOp
     231             : {
     232             :   SpliceableJSONWriter& mWriter;
     233             :   UniqueJSONStrings& mUniqueStrings;
     234             : 
     235             : public:
     236           0 :   StreamOptimizationAttemptsOp(SpliceableJSONWriter& aWriter, UniqueJSONStrings& aUniqueStrings)
     237           0 :     : mWriter(aWriter),
     238           0 :       mUniqueStrings(aUniqueStrings)
     239           0 :   { }
     240             : 
     241           0 :   void operator()(JS::TrackedStrategy strategy, JS::TrackedOutcome outcome) override {
     242             :     enum Schema : uint32_t {
     243             :       STRATEGY = 0,
     244             :       OUTCOME = 1
     245             :     };
     246             : 
     247           0 :     AutoArraySchemaWriter writer(mWriter, mUniqueStrings);
     248           0 :     writer.StringElement(STRATEGY, JS::TrackedStrategyString(strategy));
     249           0 :     writer.StringElement(OUTCOME, JS::TrackedOutcomeString(outcome));
     250           0 :   }
     251             : };
     252             : 
     253             : class StreamJSFramesOp : public JS::ForEachProfiledFrameOp
     254             : {
     255             :   void* mReturnAddress;
     256             :   UniqueStacks::Stack& mStack;
     257             :   unsigned mDepth;
     258             : 
     259             : public:
     260           0 :   StreamJSFramesOp(void* aReturnAddr, UniqueStacks::Stack& aStack)
     261           0 :    : mReturnAddress(aReturnAddr)
     262             :    , mStack(aStack)
     263           0 :    , mDepth(0)
     264           0 :   { }
     265             : 
     266           0 :   unsigned depth() const {
     267           0 :     MOZ_ASSERT(mDepth > 0);
     268           0 :     return mDepth;
     269             :   }
     270             : 
     271           0 :   void operator()(const JS::ForEachProfiledFrameOp::FrameHandle& aFrameHandle) override {
     272           0 :     UniqueStacks::OnStackFrameKey frameKey(mReturnAddress, mDepth, aFrameHandle);
     273           0 :     mStack.AppendFrame(frameKey);
     274           0 :     mDepth++;
     275           0 :   }
     276             : };
     277             : 
     278           0 : uint32_t UniqueJSONStrings::GetOrAddIndex(const char* aStr)
     279             : {
     280             :   uint32_t index;
     281           0 :   StringKey key(aStr);
     282             : 
     283           0 :   auto it = mStringToIndexMap.find(key);
     284             : 
     285           0 :   if (it != mStringToIndexMap.end()) {
     286           0 :     return it->second;
     287             :   }
     288           0 :   index = mStringToIndexMap.size();
     289           0 :   mStringToIndexMap[key] = index;
     290           0 :   mStringTableWriter.StringElement(aStr);
     291           0 :   return index;
     292             : }
     293             : 
     294           0 : bool UniqueStacks::FrameKey::operator==(const FrameKey& aOther) const
     295             : {
     296           0 :   return mLocation == aOther.mLocation &&
     297           0 :          mLine == aOther.mLine &&
     298           0 :          mCategory == aOther.mCategory &&
     299           0 :          mJITAddress == aOther.mJITAddress &&
     300           0 :          mJITDepth == aOther.mJITDepth;
     301             : }
     302             : 
     303           0 : bool UniqueStacks::StackKey::operator==(const StackKey& aOther) const
     304             : {
     305           0 :   MOZ_ASSERT_IF(mPrefix == aOther.mPrefix, mPrefixHash == aOther.mPrefixHash);
     306           0 :   return mPrefix == aOther.mPrefix && mFrame == aOther.mFrame;
     307             : }
     308             : 
     309           0 : UniqueStacks::Stack::Stack(UniqueStacks& aUniqueStacks, const OnStackFrameKey& aRoot)
     310             :  : mUniqueStacks(aUniqueStacks)
     311           0 :  , mStack(aUniqueStacks.GetOrAddFrameIndex(aRoot))
     312             : {
     313           0 : }
     314             : 
     315           0 : void UniqueStacks::Stack::AppendFrame(const OnStackFrameKey& aFrame)
     316             : {
     317             :   // Compute the prefix hash and index before mutating mStack.
     318           0 :   uint32_t prefixHash = mStack.Hash();
     319           0 :   uint32_t prefix = mUniqueStacks.GetOrAddStackIndex(mStack);
     320           0 :   mStack.UpdateHash(prefixHash, prefix, mUniqueStacks.GetOrAddFrameIndex(aFrame));
     321           0 : }
     322             : 
     323           0 : uint32_t UniqueStacks::Stack::GetOrAddIndex() const
     324             : {
     325           0 :   return mUniqueStacks.GetOrAddStackIndex(mStack);
     326             : }
     327             : 
     328           0 : uint32_t UniqueStacks::FrameKey::Hash() const
     329             : {
     330           0 :   uint32_t hash = 0;
     331           0 :   if (!mLocation.IsEmpty()) {
     332           0 :     hash = mozilla::HashString(mLocation.get());
     333             :   }
     334           0 :   if (mLine.isSome()) {
     335           0 :     hash = mozilla::AddToHash(hash, *mLine);
     336             :   }
     337           0 :   if (mCategory.isSome()) {
     338           0 :     hash = mozilla::AddToHash(hash, *mCategory);
     339             :   }
     340           0 :   if (mJITAddress.isSome()) {
     341           0 :     hash = mozilla::AddToHash(hash, *mJITAddress);
     342           0 :     if (mJITDepth.isSome()) {
     343           0 :       hash = mozilla::AddToHash(hash, *mJITDepth);
     344             :     }
     345             :   }
     346           0 :   return hash;
     347             : }
     348             : 
     349           0 : uint32_t UniqueStacks::StackKey::Hash() const
     350             : {
     351           0 :   if (mPrefix.isNothing()) {
     352           0 :     return mozilla::HashGeneric(mFrame);
     353             :   }
     354           0 :   return mozilla::AddToHash(*mPrefixHash, mFrame);
     355             : }
     356             : 
     357           0 : UniqueStacks::Stack UniqueStacks::BeginStack(const OnStackFrameKey& aRoot)
     358             : {
     359           0 :   return Stack(*this, aRoot);
     360             : }
     361             : 
     362           0 : UniqueStacks::UniqueStacks(JSContext* aContext)
     363             :  : mContext(aContext)
     364           0 :  , mFrameCount(0)
     365             : {
     366           0 :   mFrameTableWriter.StartBareList();
     367           0 :   mStackTableWriter.StartBareList();
     368           0 : }
     369             : 
     370           0 : uint32_t UniqueStacks::GetOrAddStackIndex(const StackKey& aStack)
     371             : {
     372             :   uint32_t index;
     373           0 :   if (mStackToIndexMap.Get(aStack, &index)) {
     374           0 :     MOZ_ASSERT(index < mStackToIndexMap.Count());
     375           0 :     return index;
     376             :   }
     377             : 
     378           0 :   index = mStackToIndexMap.Count();
     379           0 :   mStackToIndexMap.Put(aStack, index);
     380           0 :   StreamStack(aStack);
     381           0 :   return index;
     382             : }
     383             : 
     384           0 : uint32_t UniqueStacks::GetOrAddFrameIndex(const OnStackFrameKey& aFrame)
     385             : {
     386             :   uint32_t index;
     387           0 :   if (mFrameToIndexMap.Get(aFrame, &index)) {
     388           0 :     MOZ_ASSERT(index < mFrameCount);
     389           0 :     return index;
     390             :   }
     391             : 
     392             :   // If aFrame isn't canonical, forward it to the canonical frame's index.
     393           0 :   if (aFrame.mJITFrameHandle) {
     394           0 :     void* canonicalAddr = aFrame.mJITFrameHandle->canonicalAddress();
     395           0 :     if (canonicalAddr != *aFrame.mJITAddress) {
     396           0 :       OnStackFrameKey canonicalKey(canonicalAddr, *aFrame.mJITDepth, *aFrame.mJITFrameHandle);
     397           0 :       uint32_t canonicalIndex = GetOrAddFrameIndex(canonicalKey);
     398           0 :       mFrameToIndexMap.Put(aFrame, canonicalIndex);
     399           0 :       return canonicalIndex;
     400             :     }
     401             :   }
     402             : 
     403             :   // A manual count is used instead of mFrameToIndexMap.Count() due to
     404             :   // forwarding of canonical JIT frames above.
     405           0 :   index = mFrameCount++;
     406           0 :   mFrameToIndexMap.Put(aFrame, index);
     407           0 :   StreamFrame(aFrame);
     408           0 :   return index;
     409             : }
     410             : 
     411           0 : uint32_t UniqueStacks::LookupJITFrameDepth(void* aAddr)
     412             : {
     413             :   uint32_t depth;
     414             : 
     415           0 :   auto it = mJITFrameDepthMap.find(aAddr);
     416           0 :   if (it != mJITFrameDepthMap.end()) {
     417           0 :     depth = it->second;
     418           0 :     MOZ_ASSERT(depth > 0);
     419           0 :     return depth;
     420             :   }
     421           0 :   return 0;
     422             : }
     423             : 
     424           0 : void UniqueStacks::AddJITFrameDepth(void* aAddr, unsigned depth)
     425             : {
     426           0 :   mJITFrameDepthMap[aAddr] = depth;
     427           0 : }
     428             : 
     429           0 : void UniqueStacks::SpliceFrameTableElements(SpliceableJSONWriter& aWriter)
     430             : {
     431           0 :   mFrameTableWriter.EndBareList();
     432           0 :   aWriter.TakeAndSplice(mFrameTableWriter.WriteFunc());
     433           0 : }
     434             : 
     435           0 : void UniqueStacks::SpliceStackTableElements(SpliceableJSONWriter& aWriter)
     436             : {
     437           0 :   mStackTableWriter.EndBareList();
     438           0 :   aWriter.TakeAndSplice(mStackTableWriter.WriteFunc());
     439           0 : }
     440             : 
     441           0 : void UniqueStacks::StreamStack(const StackKey& aStack)
     442             : {
     443             :   enum Schema : uint32_t {
     444             :     PREFIX = 0,
     445             :     FRAME = 1
     446             :   };
     447             : 
     448           0 :   AutoArraySchemaWriter writer(mStackTableWriter, mUniqueStrings);
     449           0 :   if (aStack.mPrefix.isSome()) {
     450           0 :     writer.IntElement(PREFIX, *aStack.mPrefix);
     451             :   }
     452           0 :   writer.IntElement(FRAME, aStack.mFrame);
     453           0 : }
     454             : 
     455           0 : void UniqueStacks::StreamFrame(const OnStackFrameKey& aFrame)
     456             : {
     457             :   enum Schema : uint32_t {
     458             :     LOCATION = 0,
     459             :     IMPLEMENTATION = 1,
     460             :     OPTIMIZATIONS = 2,
     461             :     LINE = 3,
     462             :     CATEGORY = 4
     463             :   };
     464             : 
     465           0 :   AutoArraySchemaWriter writer(mFrameTableWriter, mUniqueStrings);
     466             : 
     467           0 :   if (!aFrame.mJITFrameHandle) {
     468           0 :     writer.StringElement(LOCATION, aFrame.mLocation.get());
     469           0 :     if (aFrame.mLine.isSome()) {
     470           0 :       writer.IntElement(LINE, *aFrame.mLine);
     471             :     }
     472           0 :     if (aFrame.mCategory.isSome()) {
     473           0 :       writer.IntElement(CATEGORY, *aFrame.mCategory);
     474             :     }
     475             :   } else {
     476           0 :     const JS::ForEachProfiledFrameOp::FrameHandle& jitFrame = *aFrame.mJITFrameHandle;
     477             : 
     478           0 :     writer.StringElement(LOCATION, jitFrame.label());
     479             : 
     480           0 :     JS::ProfilingFrameIterator::FrameKind frameKind = jitFrame.frameKind();
     481           0 :     MOZ_ASSERT(frameKind == JS::ProfilingFrameIterator::Frame_Ion ||
     482             :                frameKind == JS::ProfilingFrameIterator::Frame_Baseline);
     483           0 :     writer.StringElement(IMPLEMENTATION,
     484             :                          frameKind == JS::ProfilingFrameIterator::Frame_Ion
     485             :                          ? "ion"
     486           0 :                          : "baseline");
     487             : 
     488           0 :     if (jitFrame.hasTrackedOptimizations()) {
     489           0 :       writer.FillUpTo(OPTIMIZATIONS);
     490           0 :       mFrameTableWriter.StartObjectElement();
     491             :       {
     492           0 :         mFrameTableWriter.StartArrayProperty("types");
     493             :         {
     494           0 :           StreamOptimizationTypeInfoOp typeInfoOp(mFrameTableWriter, mUniqueStrings);
     495           0 :           jitFrame.forEachOptimizationTypeInfo(typeInfoOp);
     496             :         }
     497           0 :         mFrameTableWriter.EndArray();
     498             : 
     499           0 :         JS::Rooted<JSScript*> script(mContext);
     500             :         jsbytecode* pc;
     501           0 :         mFrameTableWriter.StartObjectProperty("attempts");
     502             :         {
     503             :           {
     504           0 :             JSONSchemaWriter schema(mFrameTableWriter);
     505           0 :             schema.WriteField("strategy");
     506           0 :             schema.WriteField("outcome");
     507             :           }
     508             : 
     509           0 :           mFrameTableWriter.StartArrayProperty("data");
     510             :           {
     511           0 :             StreamOptimizationAttemptsOp attemptOp(mFrameTableWriter, mUniqueStrings);
     512           0 :             jitFrame.forEachOptimizationAttempt(attemptOp, script.address(), &pc);
     513             :           }
     514           0 :           mFrameTableWriter.EndArray();
     515             :         }
     516           0 :         mFrameTableWriter.EndObject();
     517             : 
     518           0 :         if (JSAtom* name = js::GetPropertyNameFromPC(script, pc)) {
     519             :           char buf[512];
     520           0 :           JS_PutEscapedFlatString(buf, mozilla::ArrayLength(buf), js::AtomToFlatString(name), 0);
     521           0 :           mUniqueStrings.WriteProperty(mFrameTableWriter, "propertyName", buf);
     522             :         }
     523             : 
     524             :         unsigned line, column;
     525           0 :         line = JS_PCToLineNumber(script, pc, &column);
     526           0 :         mFrameTableWriter.IntProperty("line", line);
     527           0 :         mFrameTableWriter.IntProperty("column", column);
     528             :       }
     529           0 :       mFrameTableWriter.EndObject();
     530             :     }
     531             :   }
     532           0 : }
     533             : 
     534           0 : struct ProfileSample
     535             : {
     536             :   uint32_t mStack;
     537             :   double mTime;
     538             :   Maybe<double> mResponsiveness;
     539             :   Maybe<double> mRSS;
     540             :   Maybe<double> mUSS;
     541             : };
     542             : 
     543           0 : static void WriteSample(SpliceableJSONWriter& aWriter, ProfileSample& aSample)
     544             : {
     545             :   enum Schema : uint32_t {
     546             :     STACK = 0,
     547             :     TIME = 1,
     548             :     RESPONSIVENESS = 2,
     549             :     RSS = 3,
     550             :     USS = 4
     551             :   };
     552             : 
     553           0 :   AutoArraySchemaWriter writer(aWriter);
     554             : 
     555           0 :   writer.IntElement(STACK, aSample.mStack);
     556             : 
     557           0 :   writer.DoubleElement(TIME, aSample.mTime);
     558             : 
     559           0 :   if (aSample.mResponsiveness.isSome()) {
     560           0 :     writer.DoubleElement(RESPONSIVENESS, *aSample.mResponsiveness);
     561             :   }
     562             : 
     563           0 :   if (aSample.mRSS.isSome()) {
     564           0 :     writer.DoubleElement(RSS, *aSample.mRSS);
     565             :   }
     566             : 
     567           0 :   if (aSample.mUSS.isSome()) {
     568           0 :     writer.DoubleElement(USS, *aSample.mUSS);
     569             :   }
     570           0 : }
     571             : 
     572             : class EntryGetter
     573             : {
     574             : public:
     575           0 :   explicit EntryGetter(const ProfileBuffer& aBuffer)
     576           0 :     : mEntries(aBuffer.mEntries.get())
     577           0 :     , mReadPos(aBuffer.mReadPos)
     578           0 :     , mWritePos(aBuffer.mWritePos)
     579           0 :     , mEntrySize(aBuffer.mEntrySize)
     580           0 :   {}
     581             : 
     582           0 :   bool Has() const { return mReadPos != mWritePos; }
     583           0 :   const ProfileBufferEntry& Get() const { return mEntries[mReadPos]; }
     584           0 :   void Next() { mReadPos = (mReadPos + 1) % mEntrySize; }
     585             : 
     586             : private:
     587             :   const ProfileBufferEntry* const mEntries;
     588             :   int mReadPos;
     589             :   const int mWritePos;
     590             :   const int mEntrySize;
     591             : };
     592             : 
     593             : // Each sample is made up of multiple ProfileBuffer entries. The following
     594             : // grammar shows legal sequences.
     595             : //
     596             : // (
     597             : //   ThreadId
     598             : //   Time
     599             : //   ( NativeLeafAddr
     600             : //   | Label DynamicStringFragment* LineNumber? Category?
     601             : //   | JitReturnAddr
     602             : //   )+
     603             : //   Marker*
     604             : //   Responsiveness?
     605             : //   ResidentMemory?
     606             : //   UnsharedMemory?
     607             : // )*
     608             : //
     609             : // The most complicated part is the stack entry sequence that begins with
     610             : // Label. Here are some examples.
     611             : //
     612             : // - PseudoStack entries without a dynamic string:
     613             : //
     614             : //     Label("js::RunScript")
     615             : //     Category(ProfileEntry::Category::JS)
     616             : //
     617             : //     Label("XREMain::XRE_main")
     618             : //     LineNumber(4660)
     619             : //     Category(ProfileEntry::Category::OTHER)
     620             : //
     621             : //     Label("ElementRestyler::ComputeStyleChangeFor")
     622             : //     LineNumber(3003)
     623             : //     Category(ProfileEntry::Category::CSS)
     624             : //
     625             : // - PseudoStack entries with a dynamic string:
     626             : //
     627             : //     Label("nsObserverService::NotifyObservers")
     628             : //     DynamicStringFragment("domwindo")
     629             : //     DynamicStringFragment("wopened")
     630             : //     LineNumber(291)
     631             : //     Category(ProfileEntry::Category::OTHER)
     632             : //
     633             : //     Label("")
     634             : //     DynamicStringFragment("closeWin")
     635             : //     DynamicStringFragment("dow (chr")
     636             : //     DynamicStringFragment("ome://gl")
     637             : //     DynamicStringFragment("obal/con")
     638             : //     DynamicStringFragment("tent/glo")
     639             : //     DynamicStringFragment("balOverl")
     640             : //     DynamicStringFragment("ay.js:5)")
     641             : //     DynamicStringFragment("")          # this string holds the closing '\0'
     642             : //     LineNumber(25)
     643             : //     Category(ProfileEntry::Category::JS)
     644             : //
     645             : //     Label("")
     646             : //     DynamicStringFragment("bound (s")
     647             : //     DynamicStringFragment("elf-host")
     648             : //     DynamicStringFragment("ed:914)")
     649             : //     LineNumber(945)
     650             : //     Category(ProfileEntry::Category::JS)
     651             : //
     652             : // - A pseudoStack entry with a dynamic string, but with privacy enabled:
     653             : //
     654             : //     Label("nsObserverService::NotifyObservers")
     655             : //     DynamicStringFragment("(private")
     656             : //     DynamicStringFragment(")")
     657             : //     LineNumber(291)
     658             : //     Category(ProfileEntry::Category::OTHER)
     659             : //
     660             : // - A pseudoStack entry with an overly long dynamic string:
     661             : //
     662             : //     Label("")
     663             : //     DynamicStringFragment("(too lon")
     664             : //     DynamicStringFragment("g)")
     665             : //     LineNumber(100)
     666             : //     Category(ProfileEntry::Category::NETWORK)
     667             : //
     668             : // - A wasm JIT frame entry:
     669             : //
     670             : //     Label("")
     671             : //     DynamicStringFragment("wasm-fun")
     672             : //     DynamicStringFragment("ction[87")
     673             : //     DynamicStringFragment("36] (blo")
     674             : //     DynamicStringFragment("b:http:/")
     675             : //     DynamicStringFragment("/webasse")
     676             : //     DynamicStringFragment("mbly.org")
     677             : //     DynamicStringFragment("/3dc5759")
     678             : //     DynamicStringFragment("4-ce58-4")
     679             : //     DynamicStringFragment("626-975b")
     680             : //     DynamicStringFragment("-08ad116")
     681             : //     DynamicStringFragment("30bc1:38")
     682             : //     DynamicStringFragment("29856)")
     683             : //
     684             : // - A JS frame entry in a synchronous sample:
     685             : //
     686             : //     Label("")
     687             : //     DynamicStringFragment("u (https")
     688             : //     DynamicStringFragment("://perf-")
     689             : //     DynamicStringFragment("html.io/")
     690             : //     DynamicStringFragment("ac0da204")
     691             : //     DynamicStringFragment("aaa44d75")
     692             : //     DynamicStringFragment("a800.bun")
     693             : //     DynamicStringFragment("dle.js:2")
     694             : //     DynamicStringFragment("5)")
     695             : //
     696             : void
     697           0 : ProfileBuffer::StreamSamplesToJSON(SpliceableJSONWriter& aWriter, int aThreadId,
     698             :                                    double aSinceTime, JSContext* aContext,
     699             :                                    UniqueStacks& aUniqueStacks) const
     700             : {
     701           0 :   UniquePtr<char[]> strbuf = MakeUnique<char[]>(kMaxFrameKeyLength);
     702             : 
     703             :   // Because this is a format entirely internal to the Profiler, any parsing
     704             :   // error indicates a bug in the ProfileBuffer writing or the parser itself,
     705             :   // or possibly flaky hardware.
     706             :   #define ERROR_AND_SKIP_TO_NEXT_SAMPLE(msg) \
     707             :     do { \
     708             :       fprintf(stderr, "ProfileBuffer parse error: %s", msg); \
     709             :       MOZ_ASSERT(false, msg); \
     710             :       goto skip_to_next_sample; \
     711             :     } while (0)
     712             : 
     713           0 :   EntryGetter e(*this);
     714             : 
     715             :   // This block skips entries until we find the start of the next sample. This
     716             :   // is useful in two situations.
     717             :   //
     718             :   // - The circular buffer overwrites old entries, so when we start parsing we
     719             :   //   might be in the middle of a sample, and we must skip forward to the
     720             :   //   start of the next sample.
     721             :   //
     722             :   // - We skip samples that don't have an appropriate ThreadId or Time.
     723             :   //
     724             : skip_to_next_sample:
     725           0 :   while (e.Has()) {
     726           0 :     if (e.Get().IsThreadId()) {
     727           0 :       break;
     728             :     } else {
     729           0 :       e.Next();
     730             :     }
     731             :   }
     732             : 
     733           0 :   while (e.Has()) {
     734           0 :     if (e.Get().IsThreadId()) {
     735           0 :       int threadId = e.Get().u.mInt;
     736           0 :       e.Next();
     737             : 
     738             :       // Ignore samples that are for the wrong thread.
     739           0 :       if (threadId != aThreadId) {
     740           0 :         goto skip_to_next_sample;
     741             :       }
     742             :     } else {
     743             :       // Due to the skip_to_next_sample block above, if we have an entry here
     744             :       // it must be a ThreadId entry.
     745           0 :       MOZ_CRASH();
     746             :     }
     747             : 
     748           0 :     ProfileSample sample;
     749             : 
     750           0 :     if (e.Has() && e.Get().IsTime()) {
     751           0 :       sample.mTime = e.Get().u.mDouble;
     752           0 :       e.Next();
     753             : 
     754             :       // Ignore samples that are too old.
     755           0 :       if (sample.mTime < aSinceTime) {
     756           0 :         goto skip_to_next_sample;
     757             :       }
     758             :     } else {
     759           0 :       ERROR_AND_SKIP_TO_NEXT_SAMPLE("expected a Time entry");
     760             :     }
     761             : 
     762             :     UniqueStacks::Stack stack =
     763           0 :       aUniqueStacks.BeginStack(UniqueStacks::OnStackFrameKey("(root)"));
     764             : 
     765           0 :     int numFrames = 0;
     766           0 :     while (e.Has()) {
     767           0 :       if (e.Get().IsNativeLeafAddr()) {
     768           0 :         numFrames++;
     769             : 
     770             :         // Bug 753041: We need a double cast here to tell GCC that we don't
     771             :         // want to sign extend 32-bit addresses starting with 0xFXXXXXX.
     772           0 :         unsigned long long pc = (unsigned long long)(uintptr_t)e.Get().u.mPtr;
     773             :         char buf[20];
     774           0 :         SprintfLiteral(buf, "%#llx", pc);
     775           0 :         stack.AppendFrame(UniqueStacks::OnStackFrameKey(buf));
     776           0 :         e.Next();
     777             : 
     778           0 :       } else if (e.Get().IsLabel()) {
     779           0 :         numFrames++;
     780             : 
     781             :         // Copy the label into strbuf.
     782           0 :         const char* label = e.Get().u.mString;
     783           0 :         strncpy(strbuf.get(), label, kMaxFrameKeyLength);
     784           0 :         size_t i = strlen(label);
     785           0 :         e.Next();
     786             : 
     787           0 :         bool seenFirstDynamicStringFragment = false;
     788           0 :         while (e.Has()) {
     789           0 :           if (e.Get().IsDynamicStringFragment()) {
     790             :             // If this is the first dynamic string fragment and we have a
     791             :             // non-empty label, insert a ' ' after the label and before the
     792             :             // dynamic string.
     793           0 :             if (!seenFirstDynamicStringFragment) {
     794           0 :               if (i > 0 && i < kMaxFrameKeyLength) {
     795           0 :                 strbuf[i] = ' ';
     796           0 :                 i++;
     797             :               }
     798           0 :               seenFirstDynamicStringFragment = true;
     799             :             }
     800             : 
     801           0 :             for (size_t j = 0; j < ProfileBufferEntry::kNumChars; j++) {
     802           0 :               const char* chars = e.Get().u.mChars;
     803           0 :               if (i < kMaxFrameKeyLength) {
     804           0 :                 strbuf[i] = chars[j];
     805           0 :                 i++;
     806             :               }
     807             :             }
     808           0 :             e.Next();
     809             :           } else {
     810           0 :             break;
     811             :           }
     812             :         }
     813           0 :         strbuf[kMaxFrameKeyLength - 1] = '\0';
     814             : 
     815           0 :         UniqueStacks::OnStackFrameKey frameKey(strbuf.get());
     816             : 
     817           0 :         if (e.Has() && e.Get().IsLineNumber()) {
     818           0 :           frameKey.mLine = Some(unsigned(e.Get().u.mInt));
     819           0 :           e.Next();
     820             :         }
     821             : 
     822           0 :         if (e.Has() && e.Get().IsCategory()) {
     823           0 :           frameKey.mCategory = Some(unsigned(e.Get().u.mInt));
     824           0 :           e.Next();
     825             :         }
     826             : 
     827           0 :         stack.AppendFrame(frameKey);
     828             : 
     829           0 :       } else if (e.Get().IsJitReturnAddr()) {
     830           0 :         numFrames++;
     831             : 
     832             :         // A JIT frame may expand to multiple frames due to inlining.
     833           0 :         void* pc = e.Get().u.mPtr;
     834           0 :         unsigned depth = aUniqueStacks.LookupJITFrameDepth(pc);
     835           0 :         if (depth == 0) {
     836           0 :           StreamJSFramesOp framesOp(pc, stack);
     837           0 :           MOZ_RELEASE_ASSERT(aContext);
     838           0 :           JS::ForEachProfiledFrame(aContext, pc, framesOp);
     839           0 :           aUniqueStacks.AddJITFrameDepth(pc, framesOp.depth());
     840             :         } else {
     841           0 :           for (unsigned i = 0; i < depth; i++) {
     842           0 :             UniqueStacks::OnStackFrameKey inlineFrameKey(pc, i);
     843           0 :             stack.AppendFrame(inlineFrameKey);
     844             :           }
     845             :         }
     846             : 
     847           0 :         e.Next();
     848             : 
     849             :       } else {
     850           0 :         break;
     851             :       }
     852             :     }
     853             : 
     854           0 :     if (numFrames == 0) {
     855           0 :       ERROR_AND_SKIP_TO_NEXT_SAMPLE("expected one or more frame entries");
     856             :     }
     857             : 
     858           0 :     sample.mStack = stack.GetOrAddIndex();
     859             : 
     860             :     // Skip over the markers. We process them in StreamMarkersToJSON().
     861           0 :     while (e.Has()) {
     862           0 :       if (e.Get().IsMarker()) {
     863           0 :         e.Next();
     864             :       } else {
     865           0 :         break;
     866             :       }
     867             :     }
     868             : 
     869           0 :     if (e.Has() && e.Get().IsResponsiveness()) {
     870           0 :       sample.mResponsiveness = Some(e.Get().u.mDouble);
     871           0 :       e.Next();
     872             :     }
     873             : 
     874           0 :     if (e.Has() && e.Get().IsResidentMemory()) {
     875           0 :       sample.mRSS = Some(e.Get().u.mDouble);
     876           0 :       e.Next();
     877             :     }
     878             : 
     879           0 :     if (e.Has() && e.Get().IsUnsharedMemory()) {
     880           0 :       sample.mUSS = Some(e.Get().u.mDouble);
     881           0 :       e.Next();
     882             :     }
     883             : 
     884           0 :     WriteSample(aWriter, sample);
     885             :   }
     886             : 
     887             :   #undef ERROR_AND_SKIP_TO_NEXT_SAMPLE
     888           0 : }
     889             : 
     890             : void
     891           0 : ProfileBuffer::StreamMarkersToJSON(SpliceableJSONWriter& aWriter,
     892             :                                    int aThreadId,
     893             :                                    const TimeStamp& aProcessStartTime,
     894             :                                    double aSinceTime,
     895             :                                    UniqueStacks& aUniqueStacks) const
     896             : {
     897           0 :   EntryGetter e(*this);
     898             : 
     899           0 :   int currentThreadID = -1;
     900             : 
     901             :   // Stream all markers whose threadId matches aThreadId. All other entries are
     902             :   // skipped, because we process them in StreamSamplesToJSON().
     903           0 :   while (e.Has()) {
     904           0 :     if (e.Get().IsThreadId()) {
     905           0 :       currentThreadID = e.Get().u.mInt;
     906           0 :     } else if (currentThreadID == aThreadId && e.Get().IsMarker()) {
     907           0 :       const ProfilerMarker* marker = e.Get().u.mMarker;
     908           0 :       if (marker->GetTime() >= aSinceTime) {
     909           0 :         marker->StreamJSON(aWriter, aProcessStartTime, aUniqueStacks);
     910             :       }
     911             :     }
     912           0 :     e.Next();
     913             :   }
     914           0 : }
     915             : 
     916             : int
     917           0 : ProfileBuffer::FindLastSampleOfThread(int aThreadId, const LastSample& aLS)
     918             :   const
     919             : {
     920             :   // |aLS| has a valid generation number if either it matches the buffer's
     921             :   // generation, or is one behind the buffer's generation, since the buffer's
     922             :   // generation is incremented on wraparound.  There's no ambiguity relative to
     923             :   // ProfileBuffer::reset, since that increments mGeneration by two.
     924           0 :   if (aLS.mGeneration == mGeneration ||
     925           0 :       (mGeneration > 0 && aLS.mGeneration == mGeneration - 1)) {
     926           0 :     int ix = aLS.mPos;
     927             : 
     928           0 :     if (ix == -1) {
     929             :       // There's no record of |aLS|'s thread ever having recorded a sample in
     930             :       // the buffer.
     931           0 :       return -1;
     932             :     }
     933             : 
     934             :     // It might be that the sample has since been overwritten, so check that it
     935             :     // is still valid.
     936           0 :     MOZ_RELEASE_ASSERT(0 <= ix && ix < mEntrySize);
     937           0 :     ProfileBufferEntry& entry = mEntries[ix];
     938           0 :     bool isStillValid = entry.IsThreadId() && entry.u.mInt == aThreadId;
     939           0 :     return isStillValid ? ix : -1;
     940             :   }
     941             : 
     942             :   // |aLS| denotes a sample which is older than either two wraparounds or one
     943             :   // call to ProfileBuffer::reset.  In either case it is no longer valid.
     944           0 :   MOZ_ASSERT(aLS.mGeneration <= mGeneration - 2);
     945           0 :   return -1;
     946             : }
     947             : 
     948             : bool
     949           0 : ProfileBuffer::DuplicateLastSample(int aThreadId,
     950             :                                    const TimeStamp& aProcessStartTime,
     951             :                                    LastSample& aLS)
     952             : {
     953           0 :   int lastSampleStartPos = FindLastSampleOfThread(aThreadId, aLS);
     954           0 :   if (lastSampleStartPos == -1) {
     955           0 :     return false;
     956             :   }
     957             : 
     958           0 :   MOZ_ASSERT(mEntries[lastSampleStartPos].IsThreadId() &&
     959             :              mEntries[lastSampleStartPos].u.mInt == aThreadId);
     960             : 
     961           0 :   AddThreadIdEntry(aThreadId, &aLS);
     962             : 
     963             :   // Go through the whole entry and duplicate it, until we find the next one.
     964           0 :   for (int readPos = (lastSampleStartPos + 1) % mEntrySize;
     965           0 :        readPos != mWritePos;
     966           0 :        readPos = (readPos + 1) % mEntrySize) {
     967           0 :     switch (mEntries[readPos].GetKind()) {
     968             :       case ProfileBufferEntry::Kind::ThreadId:
     969             :         // We're done.
     970           0 :         return true;
     971             :       case ProfileBufferEntry::Kind::Time:
     972             :         // Copy with new time
     973           0 :         AddEntry(ProfileBufferEntry::Time(
     974           0 :           (TimeStamp::Now() - aProcessStartTime).ToMilliseconds()));
     975           0 :         break;
     976             :       case ProfileBufferEntry::Kind::Marker:
     977             :         // Don't copy markers
     978           0 :         break;
     979             :       default:
     980             :         // Copy anything else we don't know about.
     981           0 :         AddEntry(mEntries[readPos]);
     982           0 :         break;
     983             :     }
     984             :   }
     985           0 :   return true;
     986             : }
     987             : 
     988             : // END ProfileBuffer
     989             : ////////////////////////////////////////////////////////////////////////
     990             : 

Generated by: LCOV version 1.13