diff options
Diffstat (limited to 'tools/profiler/core/ProfileEntry.h')
-rw-r--r-- | tools/profiler/core/ProfileEntry.h | 407 |
1 files changed, 407 insertions, 0 deletions
diff --git a/tools/profiler/core/ProfileEntry.h b/tools/profiler/core/ProfileEntry.h new file mode 100644 index 000000000..b82a2f271 --- /dev/null +++ b/tools/profiler/core/ProfileEntry.h @@ -0,0 +1,407 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef MOZ_PROFILE_ENTRY_H +#define MOZ_PROFILE_ENTRY_H + +#include <ostream> +#include "GeckoProfiler.h" +#include "platform.h" +#include "ProfileJSONWriter.h" +#include "ProfilerBacktrace.h" +#include "mozilla/RefPtr.h" +#include <string> +#include <map> +#ifndef SPS_STANDALONE +#include "js/ProfilingFrameIterator.h" +#include "js/TrackedOptimizationInfo.h" +#include "nsHashKeys.h" +#include "nsDataHashtable.h" +#endif +#include "mozilla/Maybe.h" +#include "mozilla/Vector.h" +#ifndef SPS_STANDALONE +#include "gtest/MozGtestFriend.h" +#else +#define FRIEND_TEST(a, b) // TODO Support standalone gtest +#endif +#include "mozilla/HashFunctions.h" +#include "mozilla/UniquePtr.h" + +class ThreadProfile; + +// NB: Packing this structure has been shown to cause SIGBUS issues on ARM. +#ifndef __arm__ +#pragma pack(push, 1) +#endif + +class ProfileEntry +{ +public: + ProfileEntry(); + + // aTagData must not need release (i.e. be a string from the text segment) + ProfileEntry(char aTagName, const char *aTagData); + ProfileEntry(char aTagName, void *aTagPtr); + ProfileEntry(char aTagName, ProfilerMarker *aTagMarker); + ProfileEntry(char aTagName, double aTagDouble); + ProfileEntry(char aTagName, uintptr_t aTagOffset); + ProfileEntry(char aTagName, Address aTagAddress); + ProfileEntry(char aTagName, int aTagLine); + ProfileEntry(char aTagName, char aTagChar); + bool is_ent_hint(char hintChar); + bool is_ent_hint(); + bool is_ent(char tagName); + void* get_tagPtr(); + const ProfilerMarker* getMarker() { + MOZ_ASSERT(mTagName == 'm'); + return mTagMarker; + } + + char getTagName() const { return mTagName; } + +private: + FRIEND_TEST(ThreadProfile, InsertOneTag); + FRIEND_TEST(ThreadProfile, InsertOneTagWithTinyBuffer); + FRIEND_TEST(ThreadProfile, InsertTagsNoWrap); + FRIEND_TEST(ThreadProfile, InsertTagsWrap); + FRIEND_TEST(ThreadProfile, MemoryMeasure); + friend class ProfileBuffer; + union { + const char* mTagData; + char mTagChars[sizeof(void*)]; + void* mTagPtr; + ProfilerMarker* mTagMarker; + double mTagDouble; + Address mTagAddress; + uintptr_t mTagOffset; + int mTagInt; + char mTagChar; + }; + char mTagName; +}; + +#ifndef __arm__ +#pragma pack(pop) +#endif + +class UniqueJSONStrings +{ +public: + UniqueJSONStrings() { + mStringTableWriter.StartBareList(); + } + + void SpliceStringTableElements(SpliceableJSONWriter& aWriter) { + aWriter.TakeAndSplice(mStringTableWriter.WriteFunc()); + } + + void WriteProperty(mozilla::JSONWriter& aWriter, const char* aName, const char* aStr) { + aWriter.IntProperty(aName, GetOrAddIndex(aStr)); + } + + void WriteElement(mozilla::JSONWriter& aWriter, const char* aStr) { + aWriter.IntElement(GetOrAddIndex(aStr)); + } + + uint32_t GetOrAddIndex(const char* aStr); + + struct StringKey { + + explicit StringKey(const char* aStr) + : mStr(strdup(aStr)) + { + mHash = mozilla::HashString(mStr); + } + + StringKey(const StringKey& aOther) + : mStr(strdup(aOther.mStr)) + { + mHash = aOther.mHash; + } + + ~StringKey() { + free(mStr); + } + + uint32_t Hash() const; + bool operator==(const StringKey& aOther) const { + return strcmp(mStr, aOther.mStr) == 0; + } + bool operator<(const StringKey& aOther) const { + return mHash < aOther.mHash; + } + + private: + uint32_t mHash; + char* mStr; + }; +private: + SpliceableChunkedJSONWriter mStringTableWriter; + std::map<StringKey, uint32_t> mStringToIndexMap; +}; + +class UniqueStacks +{ +public: + struct FrameKey { +#ifdef SPS_STANDALONE + std::string mLocation; +#else + // This cannot be a std::string, as it is not memmove compatible, which + // is used by nsHashTable + nsCString mLocation; +#endif + mozilla::Maybe<unsigned> mLine; + mozilla::Maybe<unsigned> mCategory; + mozilla::Maybe<void*> mJITAddress; + mozilla::Maybe<uint32_t> mJITDepth; + + explicit FrameKey(const char* aLocation) + : mLocation(aLocation) + { + mHash = Hash(); + } + + FrameKey(const FrameKey& aToCopy) + : mLocation(aToCopy.mLocation) + , mLine(aToCopy.mLine) + , mCategory(aToCopy.mCategory) + , mJITAddress(aToCopy.mJITAddress) + , mJITDepth(aToCopy.mJITDepth) + { + mHash = Hash(); + } + + FrameKey(void* aJITAddress, uint32_t aJITDepth) + : mJITAddress(mozilla::Some(aJITAddress)) + , mJITDepth(mozilla::Some(aJITDepth)) + { + mHash = Hash(); + } + + uint32_t Hash() const; + bool operator==(const FrameKey& aOther) const; + bool operator<(const FrameKey& aOther) const { + return mHash < aOther.mHash; + } + + private: + uint32_t mHash; + }; + + // A FrameKey that holds a scoped reference to a JIT FrameHandle. + struct MOZ_STACK_CLASS OnStackFrameKey : public FrameKey { + explicit OnStackFrameKey(const char* aLocation) + : FrameKey(aLocation) +#ifndef SPS_STANDALONE + , mJITFrameHandle(nullptr) +#endif + { } + + OnStackFrameKey(const OnStackFrameKey& aToCopy) + : FrameKey(aToCopy) +#ifndef SPS_STANDALONE + , mJITFrameHandle(aToCopy.mJITFrameHandle) +#endif + { } + +#ifndef SPS_STANDALONE + const JS::ForEachProfiledFrameOp::FrameHandle* mJITFrameHandle; + + OnStackFrameKey(void* aJITAddress, unsigned aJITDepth) + : FrameKey(aJITAddress, aJITDepth) + , mJITFrameHandle(nullptr) + { } + + OnStackFrameKey(void* aJITAddress, unsigned aJITDepth, + const JS::ForEachProfiledFrameOp::FrameHandle& aJITFrameHandle) + : FrameKey(aJITAddress, aJITDepth) + , mJITFrameHandle(&aJITFrameHandle) + { } +#endif + }; + + struct StackKey { + mozilla::Maybe<uint32_t> mPrefixHash; + mozilla::Maybe<uint32_t> mPrefix; + uint32_t mFrame; + + explicit StackKey(uint32_t aFrame) + : mFrame(aFrame) + { + mHash = Hash(); + } + + uint32_t Hash() const; + bool operator==(const StackKey& aOther) const; + bool operator<(const StackKey& aOther) const { + return mHash < aOther.mHash; + } + + void UpdateHash(uint32_t aPrefixHash, uint32_t aPrefix, uint32_t aFrame) { + mPrefixHash = mozilla::Some(aPrefixHash); + mPrefix = mozilla::Some(aPrefix); + mFrame = aFrame; + mHash = Hash(); + } + + private: + uint32_t mHash; + }; + + class Stack { + public: + Stack(UniqueStacks& aUniqueStacks, const OnStackFrameKey& aRoot); + + void AppendFrame(const OnStackFrameKey& aFrame); + uint32_t GetOrAddIndex() const; + + private: + UniqueStacks& mUniqueStacks; + StackKey mStack; + }; + + explicit UniqueStacks(JSContext* aContext); + + Stack BeginStack(const OnStackFrameKey& aRoot); + uint32_t LookupJITFrameDepth(void* aAddr); + void AddJITFrameDepth(void* aAddr, unsigned depth); + void SpliceFrameTableElements(SpliceableJSONWriter& aWriter); + void SpliceStackTableElements(SpliceableJSONWriter& aWriter); + +private: + uint32_t GetOrAddFrameIndex(const OnStackFrameKey& aFrame); + uint32_t GetOrAddStackIndex(const StackKey& aStack); + void StreamFrame(const OnStackFrameKey& aFrame); + void StreamStack(const StackKey& aStack); + +public: + UniqueJSONStrings mUniqueStrings; + +private: + JSContext* mContext; + + // To avoid incurring JitcodeGlobalTable lookup costs for every JIT frame, + // we cache the depth of frames keyed by JIT code address. If an address a + // maps to a depth d, then frames keyed by a for depths 0 to d are + // guaranteed to be in mFrameToIndexMap. + std::map<void*, uint32_t> mJITFrameDepthMap; + + uint32_t mFrameCount; + SpliceableChunkedJSONWriter mFrameTableWriter; +#ifdef SPS_STANDALNOE + std::map<FrameKey, uint32_t> mFrameToIndexMap; +#else + nsDataHashtable<nsGenericHashKey<FrameKey>, uint32_t> mFrameToIndexMap; +#endif + + SpliceableChunkedJSONWriter mStackTableWriter; + + // This sucks but this is really performance critical, nsDataHashtable is way faster + // than map/unordered_map but nsDataHashtable is tied to xpcom so we ifdef + // until we can find a better solution. +#ifdef SPS_STANDALONE + std::map<StackKey, uint32_t> mStackToIndexMap; +#else + nsDataHashtable<nsGenericHashKey<StackKey>, uint32_t> mStackToIndexMap; +#endif +}; + +// +// ThreadProfile JSON Format +// ------------------------- +// +// The profile contains much duplicate information. The output JSON of the +// profile attempts to deduplicate strings, frames, and stack prefixes, to cut +// down on size and to increase JSON streaming speed. Deduplicated values are +// streamed as indices into their respective tables. +// +// Further, arrays of objects with the same set of properties (e.g., samples, +// frames) are output as arrays according to a schema instead of an object +// with property names. A property that is not present is represented in the +// array as null or undefined. +// +// The format of the thread profile JSON is shown by the following example +// with 1 sample and 1 marker: +// +// { +// "name": "Foo", +// "tid": 42, +// "samples": +// { +// "schema": +// { +// "stack": 0, /* index into stackTable */ +// "time": 1, /* number */ +// "responsiveness": 2, /* number */ +// "rss": 3, /* number */ +// "uss": 4, /* number */ +// "frameNumber": 5, /* number */ +// "power": 6 /* number */ +// }, +// "data": +// [ +// [ 1, 0.0, 0.0 ] /* { stack: 1, time: 0.0, responsiveness: 0.0 } */ +// ] +// }, +// +// "markers": +// { +// "schema": +// { +// "name": 0, /* index into stringTable */ +// "time": 1, /* number */ +// "data": 2 /* arbitrary JSON */ +// }, +// "data": +// [ +// [ 3, 0.1 ] /* { name: 'example marker', time: 0.1 } */ +// ] +// }, +// +// "stackTable": +// { +// "schema": +// { +// "prefix": 0, /* index into stackTable */ +// "frame": 1 /* index into frameTable */ +// }, +// "data": +// [ +// [ null, 0 ], /* (root) */ +// [ 0, 1 ] /* (root) > foo.js */ +// ] +// }, +// +// "frameTable": +// { +// "schema": +// { +// "location": 0, /* index into stringTable */ +// "implementation": 1, /* index into stringTable */ +// "optimizations": 2, /* arbitrary JSON */ +// "line": 3, /* number */ +// "category": 4 /* number */ +// }, +// "data": +// [ +// [ 0 ], /* { location: '(root)' } */ +// [ 1, 2 ] /* { location: 'foo.js', implementation: 'baseline' } */ +// ] +// }, +// +// "stringTable": +// [ +// "(root)", +// "foo.js", +// "baseline", +// "example marker" +// ] +// } +// + +#endif /* ndef MOZ_PROFILE_ENTRY_H */ |