Files
palemoon27/tools/profiler/core/ProfileEntry.cpp
T
roytam1 54259209d7 import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 891551 - Part 7: Fix some intermittent failures in multi_tcp_socket_unittest. r=ekr (b0fb5f6167)
- Bug 891551 - Part 11: fix a couple of issues from part 5. r=bwc (1f3586e037)
- Bug 1131779: 403 on permission request no longer invalidates the whole TURN relay. r=bwc (d0c594e2c7)
- Bug 797262 - IPV6 support for webrtc. r=drno (91894f480c)
- Bug 1187775 - Plumb RTCConfiguration.iceTransportPolicy down to NrIceCtx. r=smaug, r=bwc (1edc8f89c7)
- Bug 1191258 - check for IFF_RUNNING to detect online interfaces. r=mcmanus (3882c4d36e)
- Bug 1191253 - start the link monitor, ignore link-local notifications. r=mcmanus (f26b864061)
- Bug 1203658 - mark Linux's nsNotifyAddrListener::mChildThreadShutdown member as Atomic; r=bagder (6459a89dc0)
- re-enable warnings (e132d2f5db)
- Bug 1205266 - log network-change events on Mac. r=mcmanus (4c50d25945)
- Bug 1163419 - Make nsQtNetworkLinkService destructor private. r=dougt (c6993deef9)
- Bug 1178091: Implement RTCDataChannel BufferedAmountLowThreshold and bufferedamountlow event r=smaug,drno (0536f587b4)
- Bug 1194817: disable PMTUD in DataChannels/SCTP, set initial MTU per spec r=tuexen (60db372861)
- Bug 1198730: fix simple bug in PMTUD disable that clears all other flags r=tuexen (aeac9858cf)
- Bug 1148231: fix wrong error message on DataChannel failed resend of packet r=khuey (c1d9d11e17)
- Bug 1152334 - StartWebsocketData null deref mSocketIn, r=bagder (e55850770e)
- Bug 1149872 - Add the missing format specifiers. r=jduell (ffef5c91c2)
- Bug 1182515 - WebSocketChannel leaked when WebSocketChannel::AsyncOpen fails, r=mcmanus (0b52e2eb82)
- Bug 1140765 - Build some network code in unified mode; r=mcmanus (a6b9c325bb)
- Bug 1147749 - View source should go through service worker interception, r=bz, r=jduell (8c5a3cab90)
- Bug 1137151: Marked destructors of ref-counted RTSP classes as protected, r=sworkman (a6873944ee)
- Bug 1117486 - [FFOS2.0][Woodduck] System crash by replaying the same RTSP clip several times. r=bechen (1401ee0f11)
- Bug 1133461 - [RTSP] Remove the extra PLAY request for seek operation. r=bechen (c603b06ac6)
- Bug 1138983 - RTSP not enabled in Lollipop. r=vchang (a42d0bcd30)
- Bug 1144513 - [gonk-lmr1] Adapt RTSPSource.cpp. r=ettseng (8e011ab7bf)
- Bug 1151760 - Separate timeout timer with end of stream. r=ettseng (60bb18556c)
- Bug 1208756 - Hoist shared CheckMayLoad logic into BasePrincipal. r=bz (0108fa0c7c)
- Bug 1208756 - Introduce URI_FETCHABLE_BY_ANYONE and use it for moz-extension. r=bz (04de31fcbd)
- Bug 1188421 - Properly null memory reporter's compressor reference. r=hurley (9275695d87)
- bug 1204614 - test for h2 per stream flow control r=hurley (a56d4aacff)
- Bug 1197847 - Disallow folded headers in h2. r=mcmanus (ec5febf843)
- Bug 1136361 - update node-http2 on ci. rs=mcmanus (a7e2de9e02)
- Bug 1197847 - make debugging h2 and spdy test servers easier. r=mcmanus (7dcc251b63)
- Missing bits of 1132357 (431f194ed7)
- Bug 1207519 - Prevent HashTable shrink from ignoring allocation failures that may have been reported r=Waldo (d9270ce946)
- Bug 920479 - Properly convert all arguments to the Function constructor to strings before parsing out an arguments list for the function to be created. r=arai (2ba9fb0af0)
- Bug 1206520: Create an option to throw on asm.js validation failures; r=luke (6a19a37db7)
- Bug 1210295 - Simplify TOK_DEFAULT case in Parser::exportDeclaration. r=Waldo (850e383381)
- code style (5356d4a123)
- Bug 589199 - Mark non-global lexical scopes are non-extensible. (r=efaust) (d0430a0f5e)
- Bug 589199 - Cleanup: remove unused DEPTH_SLOT from BlockObject. (r=efaust) (97206b2b00)
- Bug 1211100 - Add Vector::infallibleEmplaceBack and use it in JSScript::initScriptCounts() r=nbp (09705ff8db)
- Bug 1199203 - Add support for per-thread OOM testing. r=jonco (2a9919d659)
- Bug 1171226 - Fix mozilla::devtools::UniqueStringHashPolicy::match for strings with the same prefix; r=jimb (1843cec175)
- Bug 1206290 - Part 0: Move js/UbiNodeTraverse.h to js/UbiNodeBreadthFirst.h; r=sfink (c22e008995)
- Bug 1199843 - Part 1: Prefer T::traceChildren over tag dispatched TraceChildren; r=jonco (d935bb1c1c)
- Bug 1199843 - Part 2: Use js::TraceChildren everywhere we don't need external linkage; r=jonco (de8cd29a01)
- Bug 1199843 - Part 3: Strongly type JS::TraceChildren; r=jonco r=mccr8 (2cdd931159)
- Bug 1178961 - Restore the std::string fix from bug 1167230 r=BenWa (4251402fce)
- Bug 1206288 - Remove JS::ubi::SimpleEdge and make JS::ubi::Edge a concrete class; r=shu (d995efed2d)
- Bug 1182653 - Use JSRuntime* instead of JSContext* in ubi::Node infrastructure; r=sfink (e11e0bd0ba)
- Bug 1207236 - ARM64: Build errors with --enable-simulator=arm64. r=h4writer (cc76d43493)
- Bug 1207793 - Fix unified bustage from the TaggedPointer split; r=jolesen (e05b300406)
- Bug 1204866 - IonMonkey: Crash when failing to apand to parse finished list, r=bhackett (efab1b4a1f)
- Bug 1209911 - Use a Variant type to represent HelperThread data r=jandem (ea21131f21)
- Bug 1209911 - Make AutoEnterOOMUnsafeRegion ignore threads that are not simulating OOM r=terrence (769f06a4dc)
- Bug 1203814 - "Adding a testing function causes intermittent orange in oomInFormatStackDump.js on Win32 debug SM(p)." r=jcoppeard (da95054606)
- Bug 1209911 - Add oomThreadTypes() test function to report the number of thread types we can simulate OOM on r=terrence (1d56ca0b74)
- Bug 1209911 - Limit the number of helper threads of a particular type when simulating OOM r=jandem (ceafa83794)
- Bug 1209911 - Remove option to simulate OOM on all threads r=terrence (b43fcf3959)
- Bug 1201057 - Update shell args for OOM tests now we don't have to worry about unhandlable OOMs r=terrence (843c1fc6e3)
- Bug 1208994 - ToAtom<NoGC> should not throw an exception on failure r=terrence (ad57246c1e)
- Bug 1210760 - Don't simulate OOM in ExceptionHandlerBailout() r=terrence (89bab8d51b)
- Bug 1209911 - Enable threads for existing OOM tests r=terrence (a6f94e1f2f)
- Bug 1169974 - Tolerate inconsistent-missing-override warnings for MOCK_METHOD2 macro from gtests. r=fitzgen. (96d613239d)
- Bug 1206290 - Part 1: Implement a JS::ubi::PostOrder depth first traversal; r=sfink (d4de3aa8bc)
- Bug 1196461 - De-duplicate strings in heap snapshot core dumps; r=shu,jimb (24080971ae)
- missing bit of 1101561 (d4b61c9688)
- Bug 1199215 - Implement JS::ubi::Node::size for JS::Symbol referents;S::ubi::Node::size for JS::Symbol referents; r=sfink (929fceb199)
- Bug 1200345: Add comment to js/public/UbiNode.h warning about operating on graphs constructed by hostile code. DONTBUILD r=fitzgen (b83c2d0aad)
- Bug 1199216 - Implement JS::ubi::Node::size for JSScript referents; r=sfink (5a134ffa8e)
- Bug 1205744 - Removed redundant method IsContextRunningJS. r=h4writer (a7e90bbead)
- Bug 1203920 - Get rid of JS_SetStructuredCloneCallbacks, r=sfink (197118cf95)
- Bug 1204554 part 1 - Remove unused JSContext from LcovWriteScript. r=bhackett (12fa4dcc99)
- Bug 1209263 - Allow embedders to tell SpiderMonkey how to structured clone principals; r=bz (c4e929e50f)
- Bug 1191098 - Replace AutoHashableValueRooter with Rooted<HashableValue>; r=jonco (99e23cf3a4)
- Bug 1190628 - Replace AutoScriptVector with normal Rooted usage; r=jimb (926d499edd)
- Bug 1188445 - Allow PersistentRooted to store StaticTraceable; r=sfink (eac1824298)
- Bug 1195957 - Implement a Swap for Heap<T> that does not put temporaries on the stack, r=terrence (26935b5efd)
- Bug 1203695 - GenerateLcovInfo: Check if JSScripts have associated bytecode before spewing their content. r=bhackett (71854b606a)
- Bug 1204554 part 2 - Split LCov functions to make the aggregation of results incremental. r=terrence,bhackett (3a6bd8719e)
- Bug 1204554 part 3.0 - Collect lcov output on the JSCompartment, and on the JSRuntime. r=terrence,bhackett (35544460b1)
- Bug 1204554 part 3.1 - Prevent lazy parsing if we have to spew lcov result. r=bhackett (b36775e171)
- Bug 1204554 part 3.2 - Collect the source files before any script, as they are swept first. r=terrence (4d841a641e)
- Bug 1204554 part 3.3 - Only collect inner JSScript if they have the same source. r=terrence (b38f45764e)
- Bug 1204554 part 3.4 - Ensure that scriptCountsMaps data are still alive until the destruction of compartments. r=terrence,bhackett (50d4217d20)
- Bug 1200642 - Fix OOM handling issues r=terrence (d8ba8b1714)
- Bug 1206247 - Do not collect coverage information if the source has n filename. r=bhackett (0602188d8e)
- pointer style (09cd14aec3)
- Bug 1207232: Let lockOwner be Atomic; r=terrence (e4aa1e635d)
- Bug 1192304 - Common up the checks when entering GC API; r=jonco (935c618bc4)
- Bug 1192306 - Use a function to check if IsShutdownGC rather than open-coding it; r=jonco (c14a96ba2d)
- Bug 1202865 - Split out Zone selection from stats collection and malloc bytes reset; r=jonco (89d5e9def1)
- Bug 1193428 - Rename the incremental parameter to be more meaningful; r=jonco (5d5f38b39d)
- Bug 1209704 - Part 1: Rename BarrieredBase to WriteBarrieredBase; r=jonco (4559ae15aa)
- Bug 1209704 - Part 2: Share storage and mixins between Read and Write barriers; r=jonco (eddc9ff80c)
- Bug 1177515 - Skip tracing object elements if type information shows they don't contain any GC pointers r=terrence (94e2c1ee71)
- Bug 1209704 - Part 3: Share tracing accessors between all barrier classes; r=jonco (1e30d0f16d)
- Bug 1209704 - Part 4: Add a post-barrier to ReadBarriered; r=jonco (22bb316d70)
- Bug 1211022 - Add a type specialization for weak C++ GC thing references; r=sfink (d3d5e88ba8)
- Bug 1204169 - Push SPS pseudo frame entries when GCing; r=terrence (61501f7597)
- Bug 1211031 - Use WeakRef to manage the LazyScript to JSScript back-reference; r=jandem (7c48b04d35)
- Bug 1204167 - Create the js::AutoSPSEntry RAII class for defining psuedo frame entries; r=djvj (519342668f)
- Bug 1204584 - Ensure that entries created by AutoSPSEntry propogate their category information; r=djvj (bd5d96e8b6)
- Bug 1011786 - Diagnostic patch to detect cyclic ropes, r=terrence (c2ece1d49d)
- fix comment (619a5bff31)
2022-08-15 10:15:32 +08:00

833 lines
24 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#include <ostream>
#include "platform.h"
#include "mozilla/HashFunctions.h"
#ifndef SPS_STANDALONE
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"
// JS
#include "jsapi.h"
#include "jsfriendapi.h"
#include "js/TrackedOptimizationInfo.h"
#endif
// Self
#include "ProfileEntry.h"
#if defined(_MSC_VER) && _MSC_VER < 1900
#define snprintf _snprintf
#endif
using mozilla::MakeUnique;
using mozilla::UniquePtr;
using mozilla::Maybe;
using mozilla::Some;
using mozilla::Nothing;
using mozilla::JSONWriter;
////////////////////////////////////////////////////////////////////////
// BEGIN ProfileEntry
ProfileEntry::ProfileEntry()
: mTagData(nullptr)
, mTagName(0)
{ }
// aTagData must not need release (i.e. be a string from the text segment)
ProfileEntry::ProfileEntry(char aTagName, const char *aTagData)
: mTagData(aTagData)
, mTagName(aTagName)
{ }
ProfileEntry::ProfileEntry(char aTagName, ProfilerMarker *aTagMarker)
: mTagMarker(aTagMarker)
, mTagName(aTagName)
{ }
ProfileEntry::ProfileEntry(char aTagName, void *aTagPtr)
: mTagPtr(aTagPtr)
, mTagName(aTagName)
{ }
ProfileEntry::ProfileEntry(char aTagName, double aTagDouble)
: mTagDouble(aTagDouble)
, mTagName(aTagName)
{ }
ProfileEntry::ProfileEntry(char aTagName, uintptr_t aTagOffset)
: mTagOffset(aTagOffset)
, mTagName(aTagName)
{ }
ProfileEntry::ProfileEntry(char aTagName, Address aTagAddress)
: mTagAddress(aTagAddress)
, mTagName(aTagName)
{ }
ProfileEntry::ProfileEntry(char aTagName, int aTagInt)
: mTagInt(aTagInt)
, mTagName(aTagName)
{ }
ProfileEntry::ProfileEntry(char aTagName, char aTagChar)
: mTagChar(aTagChar)
, mTagName(aTagName)
{ }
bool ProfileEntry::is_ent_hint(char hintChar) {
return mTagName == 'h' && mTagChar == hintChar;
}
bool ProfileEntry::is_ent_hint() {
return mTagName == 'h';
}
bool ProfileEntry::is_ent(char tagChar) {
return mTagName == tagChar;
}
void* ProfileEntry::get_tagPtr() {
// No consistency checking. Oh well.
return mTagPtr;
}
// END ProfileEntry
////////////////////////////////////////////////////////////////////////
class JSONSchemaWriter
{
JSONWriter& mWriter;
uint32_t mIndex;
public:
explicit JSONSchemaWriter(JSONWriter& aWriter)
: mWriter(aWriter)
, mIndex(0)
{
aWriter.StartObjectProperty("schema");
}
void WriteField(const char* aName) {
mWriter.IntProperty(aName, mIndex++);
}
~JSONSchemaWriter() {
mWriter.EndObject();
}
};
#ifndef SPS_STANDALONE
class StreamOptimizationTypeInfoOp : public JS::ForEachTrackedOptimizationTypeInfoOp
{
JSONWriter& mWriter;
UniqueJSONStrings& mUniqueStrings;
bool mStartedTypeList;
public:
StreamOptimizationTypeInfoOp(JSONWriter& aWriter, UniqueJSONStrings& aUniqueStrings)
: mWriter(aWriter)
, mUniqueStrings(aUniqueStrings)
, mStartedTypeList(false)
{ }
void readType(const char* keyedBy, const char* name,
const char* location, Maybe<unsigned> lineno) override {
if (!mStartedTypeList) {
mStartedTypeList = true;
mWriter.StartObjectElement();
mWriter.StartArrayProperty("typeset");
}
mWriter.StartObjectElement();
{
mUniqueStrings.WriteProperty(mWriter, "keyedBy", keyedBy);
if (name) {
mUniqueStrings.WriteProperty(mWriter, "name", name);
}
if (location) {
mUniqueStrings.WriteProperty(mWriter, "location", location);
}
if (lineno.isSome()) {
mWriter.IntProperty("line", *lineno);
}
}
mWriter.EndObject();
}
void operator()(JS::TrackedTypeSite site, const char* mirType) override {
if (mStartedTypeList) {
mWriter.EndArray();
mStartedTypeList = false;
} else {
mWriter.StartObjectElement();
}
{
mUniqueStrings.WriteProperty(mWriter, "site", JS::TrackedTypeSiteString(site));
mUniqueStrings.WriteProperty(mWriter, "mirType", mirType);
}
mWriter.EndObject();
}
};
class StreamOptimizationAttemptsOp : public JS::ForEachTrackedOptimizationAttemptOp
{
JSONWriter& mWriter;
UniqueJSONStrings& mUniqueStrings;
public:
StreamOptimizationAttemptsOp(JSONWriter& aWriter, UniqueJSONStrings& aUniqueStrings)
: mWriter(aWriter),
mUniqueStrings(aUniqueStrings)
{ }
void operator()(JS::TrackedStrategy strategy, JS::TrackedOutcome outcome) override {
// Schema:
// [strategy, outcome]
mWriter.StartArrayElement();
{
mUniqueStrings.WriteElement(mWriter, JS::TrackedStrategyString(strategy));
mUniqueStrings.WriteElement(mWriter, JS::TrackedOutcomeString(outcome));
}
mWriter.EndArray();
}
};
class StreamJSFramesOp : public JS::ForEachProfiledFrameOp
{
void* mReturnAddress;
UniqueStacks::Stack& mStack;
unsigned mDepth;
public:
StreamJSFramesOp(void* aReturnAddr, UniqueStacks::Stack& aStack)
: mReturnAddress(aReturnAddr)
, mStack(aStack)
, mDepth(0)
{ }
unsigned depth() const {
MOZ_ASSERT(mDepth > 0);
return mDepth;
}
void operator()(const JS::ForEachProfiledFrameOp::FrameHandle& aFrameHandle) override {
UniqueStacks::OnStackFrameKey frameKey(mReturnAddress, mDepth, aFrameHandle);
mStack.AppendFrame(frameKey);
mDepth++;
}
};
#endif
uint32_t UniqueJSONStrings::GetOrAddIndex(const char* aStr)
{
uint32_t index;
StringKey key(aStr);
auto it = mStringToIndexMap.find(key);
if (it != mStringToIndexMap.end()) {
return it->second;
}
index = mStringToIndexMap.size();
mStringToIndexMap[key] = index;
mStringTableWriter.StringElement(aStr);
return index;
}
bool UniqueStacks::FrameKey::operator==(const FrameKey& aOther) const
{
return mLocation == aOther.mLocation &&
mLine == aOther.mLine &&
mCategory == aOther.mCategory &&
mJITAddress == aOther.mJITAddress &&
mJITDepth == aOther.mJITDepth;
}
bool UniqueStacks::StackKey::operator==(const StackKey& aOther) const
{
MOZ_ASSERT_IF(mPrefix == aOther.mPrefix, mPrefixHash == aOther.mPrefixHash);
return mPrefix == aOther.mPrefix && mFrame == aOther.mFrame;
}
UniqueStacks::Stack::Stack(UniqueStacks& aUniqueStacks, const OnStackFrameKey& aRoot)
: mUniqueStacks(aUniqueStacks)
, mStack(aUniqueStacks.GetOrAddFrameIndex(aRoot))
{
}
void UniqueStacks::Stack::AppendFrame(const OnStackFrameKey& aFrame)
{
// Compute the prefix hash and index before mutating mStack.
uint32_t prefixHash = mStack.Hash();
uint32_t prefix = mUniqueStacks.GetOrAddStackIndex(mStack);
mStack.UpdateHash(prefixHash, prefix, mUniqueStacks.GetOrAddFrameIndex(aFrame));
}
uint32_t UniqueStacks::Stack::GetOrAddIndex() const
{
return mUniqueStacks.GetOrAddStackIndex(mStack);
}
uint32_t UniqueStacks::FrameKey::Hash() const
{
uint32_t hash = 0;
if (!mLocation.IsEmpty()) {
#ifdef SPS_STANDALONE
hash = mozilla::HashString(mLocation.c_str());
#else
hash = mozilla::HashString(mLocation.get());
#endif
}
if (mLine.isSome()) {
hash = mozilla::AddToHash(hash, *mLine);
}
if (mCategory.isSome()) {
hash = mozilla::AddToHash(hash, *mCategory);
}
if (mJITAddress.isSome()) {
hash = mozilla::AddToHash(hash, *mJITAddress);
if (mJITDepth.isSome()) {
hash = mozilla::AddToHash(hash, *mJITDepth);
}
}
return hash;
}
uint32_t UniqueStacks::StackKey::Hash() const
{
if (mPrefix.isNothing()) {
return mozilla::HashGeneric(mFrame);
}
return mozilla::AddToHash(*mPrefixHash, mFrame);
}
UniqueStacks::Stack UniqueStacks::BeginStack(const OnStackFrameKey& aRoot)
{
return Stack(*this, aRoot);
}
UniqueStacks::UniqueStacks(JSRuntime* aRuntime)
: mRuntime(aRuntime)
, mFrameCount(0)
{
mFrameTableWriter.StartBareList();
mStackTableWriter.StartBareList();
}
#ifdef SPS_STANDALONE
uint32_t UniqueStacks::GetOrAddStackIndex(const StackKey& aStack)
{
uint32_t index;
auto it = mStackToIndexMap.find(aStack);
if (it != mStackToIndexMap.end()) {
return it->second;
}
index = mStackToIndexMap.size();
mStackToIndexMap[aStack] = index;
StreamStack(aStack);
return index;
}
#else
uint32_t UniqueStacks::GetOrAddStackIndex(const StackKey& aStack)
{
uint32_t index;
if (mStackToIndexMap.Get(aStack, &index)) {
MOZ_ASSERT(index < mStackToIndexMap.Count());
return index;
}
index = mStackToIndexMap.Count();
mStackToIndexMap.Put(aStack, index);
StreamStack(aStack);
return index;
}
#endif
#ifdef SPS_STANDALONE
uint32_t UniqueStacks::GetOrAddFrameIndex(const OnStackFrameKey& aFrame)
{
uint32_t index;
auto it = mFrameToIndexMap.find(aFrame);
if (it != mFrameToIndexMap.end()) {
MOZ_ASSERT(it->second < mFrameCount);
return it->second;
}
// A manual count is used instead of mFrameToIndexMap.Count() due to
// forwarding of canonical JIT frames above.
index = mFrameCount++;
mFrameToIndexMap[aFrame] = index;
StreamFrame(aFrame);
return index;
}
#else
uint32_t UniqueStacks::GetOrAddFrameIndex(const OnStackFrameKey& aFrame)
{
uint32_t index;
if (mFrameToIndexMap.Get(aFrame, &index)) {
MOZ_ASSERT(index < mFrameCount);
return index;
}
// If aFrame isn't canonical, forward it to the canonical frame's index.
if (aFrame.mJITFrameHandle) {
void* canonicalAddr = aFrame.mJITFrameHandle->canonicalAddress();
if (canonicalAddr != *aFrame.mJITAddress) {
OnStackFrameKey canonicalKey(canonicalAddr, *aFrame.mJITDepth, *aFrame.mJITFrameHandle);
uint32_t canonicalIndex = GetOrAddFrameIndex(canonicalKey);
mFrameToIndexMap.Put(aFrame, canonicalIndex);
return canonicalIndex;
}
}
// A manual count is used instead of mFrameToIndexMap.Count() due to
// forwarding of canonical JIT frames above.
index = mFrameCount++;
mFrameToIndexMap.Put(aFrame, index);
StreamFrame(aFrame);
return index;
}
#endif
uint32_t UniqueStacks::LookupJITFrameDepth(void* aAddr)
{
uint32_t depth;
auto it = mJITFrameDepthMap.find(aAddr);
if (it != mJITFrameDepthMap.end()) {
depth = it->second;
MOZ_ASSERT(depth > 0);
return depth;
}
return 0;
}
void UniqueStacks::AddJITFrameDepth(void* aAddr, unsigned depth)
{
mJITFrameDepthMap[aAddr] = depth;
}
void UniqueStacks::SpliceFrameTableElements(SpliceableJSONWriter& aWriter)
{
mFrameTableWriter.EndBareList();
aWriter.TakeAndSplice(mFrameTableWriter.WriteFunc());
}
void UniqueStacks::SpliceStackTableElements(SpliceableJSONWriter& aWriter)
{
mStackTableWriter.EndBareList();
aWriter.TakeAndSplice(mStackTableWriter.WriteFunc());
}
void UniqueStacks::StreamStack(const StackKey& aStack)
{
// Schema:
// [prefix, frame]
mStackTableWriter.StartArrayElement();
{
if (aStack.mPrefix.isSome()) {
mStackTableWriter.IntElement(*aStack.mPrefix);
} else {
mStackTableWriter.NullElement();
}
mStackTableWriter.IntElement(aStack.mFrame);
}
mStackTableWriter.EndArray();
}
void UniqueStacks::StreamFrame(const OnStackFrameKey& aFrame)
{
// Schema:
// [location, implementation, optimizations, line, category]
mFrameTableWriter.StartArrayElement();
#ifndef SPS_STANDALONE
if (!aFrame.mJITFrameHandle) {
#else
{
#endif
#ifdef SPS_STANDALONE
mUniqueStrings.WriteElement(mFrameTableWriter, aFrame.mLocation.c_str());
#else
mUniqueStrings.WriteElement(mFrameTableWriter, aFrame.mLocation.get());
#endif
if (aFrame.mLine.isSome()) {
mFrameTableWriter.NullElement(); // implementation
mFrameTableWriter.NullElement(); // optimizations
mFrameTableWriter.IntElement(*aFrame.mLine);
}
if (aFrame.mCategory.isSome()) {
if (aFrame.mLine.isNothing()) {
mFrameTableWriter.NullElement(); // line
}
mFrameTableWriter.IntElement(*aFrame.mCategory);
}
}
#ifndef SPS_STANDALONE
else {
const JS::ForEachProfiledFrameOp::FrameHandle& jitFrame = *aFrame.mJITFrameHandle;
mUniqueStrings.WriteElement(mFrameTableWriter, jitFrame.label());
JS::ProfilingFrameIterator::FrameKind frameKind = jitFrame.frameKind();
MOZ_ASSERT(frameKind == JS::ProfilingFrameIterator::Frame_Ion ||
frameKind == JS::ProfilingFrameIterator::Frame_Baseline);
mUniqueStrings.WriteElement(mFrameTableWriter,
frameKind == JS::ProfilingFrameIterator::Frame_Ion
? "ion"
: "baseline");
if (jitFrame.hasTrackedOptimizations()) {
mFrameTableWriter.StartObjectElement();
{
mFrameTableWriter.StartArrayProperty("types");
{
StreamOptimizationTypeInfoOp typeInfoOp(mFrameTableWriter, mUniqueStrings);
jitFrame.forEachOptimizationTypeInfo(typeInfoOp);
}
mFrameTableWriter.EndArray();
JS::Rooted<JSScript*> script(mRuntime);
jsbytecode* pc;
mFrameTableWriter.StartObjectProperty("attempts");
{
{
JSONSchemaWriter schema(mFrameTableWriter);
schema.WriteField("strategy");
schema.WriteField("outcome");
}
mFrameTableWriter.StartArrayProperty("data");
{
StreamOptimizationAttemptsOp attemptOp(mFrameTableWriter, mUniqueStrings);
jitFrame.forEachOptimizationAttempt(attemptOp, script.address(), &pc);
}
mFrameTableWriter.EndArray();
}
mFrameTableWriter.EndObject();
if (JSAtom* name = js::GetPropertyNameFromPC(script, pc)) {
char buf[512];
JS_PutEscapedFlatString(buf, mozilla::ArrayLength(buf), js::AtomToFlatString(name), 0);
mUniqueStrings.WriteProperty(mFrameTableWriter, "propertyName", buf);
}
unsigned line, column;
line = JS_PCToLineNumber(script, pc, &column);
mFrameTableWriter.IntProperty("line", line);
mFrameTableWriter.IntProperty("column", column);
}
mFrameTableWriter.EndObject();
}
}
#endif
mFrameTableWriter.EndArray();
}
struct ProfileSample
{
uint32_t mStack;
Maybe<double> mTime;
Maybe<double> mResponsiveness;
Maybe<double> mRSS;
Maybe<double> mUSS;
Maybe<int> mFrameNumber;
Maybe<double> mPower;
};
static void WriteSample(SpliceableJSONWriter& aWriter, ProfileSample& aSample)
{
// Schema:
// [stack, time, responsiveness, rss, uss, frameNumber, power]
aWriter.StartArrayElement();
{
// The last non-null index is tracked to save space in the JSON by avoid
// emitting 'null's at the end of the array, as they're only needed if
// followed by non-null elements.
uint32_t index = 0;
uint32_t lastNonNullIndex = 0;
aWriter.IntElement(aSample.mStack);
index++;
if (aSample.mTime.isSome()) {
lastNonNullIndex = index;
aWriter.DoubleElement(*aSample.mTime);
}
index++;
if (aSample.mResponsiveness.isSome()) {
aWriter.NullElements(index - lastNonNullIndex - 1);
lastNonNullIndex = index;
aWriter.DoubleElement(*aSample.mResponsiveness);
}
index++;
if (aSample.mRSS.isSome()) {
aWriter.NullElements(index - lastNonNullIndex - 1);
lastNonNullIndex = index;
aWriter.DoubleElement(*aSample.mRSS);
}
index++;
if (aSample.mUSS.isSome()) {
aWriter.NullElements(index - lastNonNullIndex - 1);
lastNonNullIndex = index;
aWriter.DoubleElement(*aSample.mUSS);
}
index++;
if (aSample.mFrameNumber.isSome()) {
aWriter.NullElements(index - lastNonNullIndex - 1);
lastNonNullIndex = index;
aWriter.IntElement(*aSample.mFrameNumber);
}
index++;
if (aSample.mPower.isSome()) {
aWriter.NullElements(index - lastNonNullIndex - 1);
lastNonNullIndex = index;
aWriter.DoubleElement(*aSample.mPower);
}
index++;
}
aWriter.EndArray();
}
void ProfileBuffer::StreamSamplesToJSON(SpliceableJSONWriter& aWriter, int aThreadId,
double aSinceTime, JSRuntime* aRuntime,
UniqueStacks& aUniqueStacks)
{
Maybe<ProfileSample> sample;
int readPos = mReadPos;
int currentThreadID = -1;
Maybe<double> currentTime;
UniquePtr<char[]> tagBuff = MakeUnique<char[]>(DYNAMIC_MAX_STRING);
while (readPos != mWritePos) {
ProfileEntry entry = mEntries[readPos];
if (entry.mTagName == 'T') {
currentThreadID = entry.mTagInt;
currentTime.reset();
int readAheadPos = (readPos + 1) % mEntrySize;
if (readAheadPos != mWritePos) {
ProfileEntry readAheadEntry = mEntries[readAheadPos];
if (readAheadEntry.mTagName == 't') {
currentTime = Some(readAheadEntry.mTagDouble);
}
}
}
if (currentThreadID == aThreadId && (currentTime.isNothing() || *currentTime >= aSinceTime)) {
switch (entry.mTagName) {
case 'r':
if (sample.isSome()) {
sample->mResponsiveness = Some(entry.mTagDouble);
}
break;
case 'p':
if (sample.isSome()) {
sample->mPower = Some(entry.mTagDouble);
}
break;
case 'R':
if (sample.isSome()) {
sample->mRSS = Some(entry.mTagDouble);
}
break;
case 'U':
if (sample.isSome()) {
sample->mUSS = Some(entry.mTagDouble);
}
break;
case 'f':
if (sample.isSome()) {
sample->mFrameNumber = Some(entry.mTagInt);
}
break;
case 's':
{
// end the previous sample if there was one
if (sample.isSome()) {
WriteSample(aWriter, *sample);
sample.reset();
}
// begin the next sample
sample.emplace();
sample->mTime = currentTime;
// Seek forward through the entire sample, looking for frames
// this is an easier approach to reason about than adding more
// control variables and cases to the loop that goes through the buffer once
UniqueStacks::Stack stack =
aUniqueStacks.BeginStack(UniqueStacks::OnStackFrameKey("(root)"));
int framePos = (readPos + 1) % mEntrySize;
ProfileEntry frame = mEntries[framePos];
while (framePos != mWritePos && frame.mTagName != 's' && frame.mTagName != 'T') {
int incBy = 1;
frame = mEntries[framePos];
// Read ahead to the next tag, if it's a 'd' tag process it now
const char* tagStringData = frame.mTagData;
int readAheadPos = (framePos + 1) % mEntrySize;
// Make sure the string is always null terminated if it fills up
// DYNAMIC_MAX_STRING-2
tagBuff[DYNAMIC_MAX_STRING-1] = '\0';
if (readAheadPos != mWritePos && mEntries[readAheadPos].mTagName == 'd') {
tagStringData = processDynamicTag(framePos, &incBy, tagBuff.get());
}
// Write one frame. It can have either
// 1. only location - 'l' containing a memory address
// 2. location and line number - 'c' followed by 'd's,
// an optional 'n' and an optional 'y'
// 3. a JIT return address - 'j' containing native code address
if (frame.mTagName == 'l') {
// Bug 753041
// We need a double cast here to tell GCC that we don't want to sign
// extend 32-bit addresses starting with 0xFXXXXXX.
unsigned long long pc = (unsigned long long)(uintptr_t)frame.mTagPtr;
snprintf(tagBuff.get(), DYNAMIC_MAX_STRING, "%#llx", pc);
stack.AppendFrame(UniqueStacks::OnStackFrameKey(tagBuff.get()));
} else if (frame.mTagName == 'c') {
UniqueStacks::OnStackFrameKey frameKey(tagStringData);
readAheadPos = (framePos + incBy) % mEntrySize;
if (readAheadPos != mWritePos &&
mEntries[readAheadPos].mTagName == 'n') {
frameKey.mLine = Some((unsigned) mEntries[readAheadPos].mTagInt);
incBy++;
}
readAheadPos = (framePos + incBy) % mEntrySize;
if (readAheadPos != mWritePos &&
mEntries[readAheadPos].mTagName == 'y') {
frameKey.mCategory = Some((unsigned) mEntries[readAheadPos].mTagInt);
incBy++;
}
stack.AppendFrame(frameKey);
#ifndef SPS_STANDALONE
} else if (frame.mTagName == 'J') {
// A JIT frame may expand to multiple frames due to inlining.
void* pc = frame.mTagPtr;
unsigned depth = aUniqueStacks.LookupJITFrameDepth(pc);
if (depth == 0) {
StreamJSFramesOp framesOp(pc, stack);
JS::ForEachProfiledFrame(aRuntime, pc, framesOp);
aUniqueStacks.AddJITFrameDepth(pc, framesOp.depth());
} else {
for (unsigned i = 0; i < depth; i++) {
UniqueStacks::OnStackFrameKey inlineFrameKey(pc, i);
stack.AppendFrame(inlineFrameKey);
}
}
#endif
}
framePos = (framePos + incBy) % mEntrySize;
}
sample->mStack = stack.GetOrAddIndex();
break;
}
}
}
readPos = (readPos + 1) % mEntrySize;
}
if (sample.isSome()) {
WriteSample(aWriter, *sample);
}
}
void ProfileBuffer::StreamMarkersToJSON(SpliceableJSONWriter& aWriter, int aThreadId,
double aSinceTime, UniqueStacks& aUniqueStacks)
{
int readPos = mReadPos;
int currentThreadID = -1;
while (readPos != mWritePos) {
ProfileEntry entry = mEntries[readPos];
if (entry.mTagName == 'T') {
currentThreadID = entry.mTagInt;
} else if (currentThreadID == aThreadId && entry.mTagName == 'm') {
const ProfilerMarker* marker = entry.getMarker();
if (marker->GetTime() >= aSinceTime) {
entry.getMarker()->StreamJSON(aWriter, aUniqueStacks);
}
}
readPos = (readPos + 1) % mEntrySize;
}
}
int ProfileBuffer::FindLastSampleOfThread(int aThreadId)
{
// We search backwards from mWritePos-1 to mReadPos.
// Adding mEntrySize makes the result of the modulus positive.
for (int readPos = (mWritePos + mEntrySize - 1) % mEntrySize;
readPos != (mReadPos + mEntrySize - 1) % mEntrySize;
readPos = (readPos + mEntrySize - 1) % mEntrySize) {
ProfileEntry entry = mEntries[readPos];
if (entry.mTagName == 'T' && entry.mTagInt == aThreadId) {
return readPos;
}
}
return -1;
}
void ProfileBuffer::DuplicateLastSample(int aThreadId)
{
int lastSampleStartPos = FindLastSampleOfThread(aThreadId);
if (lastSampleStartPos == -1) {
return;
}
MOZ_ASSERT(mEntries[lastSampleStartPos].mTagName == 'T');
addTag(mEntries[lastSampleStartPos]);
// Go through the whole entry and duplicate it, until we find the next one.
for (int readPos = (lastSampleStartPos + 1) % mEntrySize;
readPos != mWritePos;
readPos = (readPos + 1) % mEntrySize) {
switch (mEntries[readPos].mTagName) {
case 'T':
// We're done.
return;
case 't':
// Copy with new time
addTag(ProfileEntry('t', (mozilla::TimeStamp::Now() - sStartTime).ToMilliseconds()));
break;
case 'm':
// Don't copy markers
break;
// Copy anything else we don't know about
// L, B, S, c, s, d, l, f, h, r, t, p
default:
addTag(mEntries[readPos]);
break;
}
}
}
// END ProfileBuffer
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
// BEGIN ThreadProfile
// END ThreadProfile
////////////////////////////////////////////////////////////////////////