/* -*- 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 #include #include "GeckoProfiler.h" #include "platform.h" #include "JSStreamWriter.h" #include "ProfilerBacktrace.h" #include "nsRefPtr.h" #include "mozilla/Maybe.h" #include "mozilla/Mutex.h" #include "mozilla/Vector.h" #include "gtest/MozGtestFriend.h" #include "mozilla/UniquePtr.h" class ThreadProfile; #pragma pack(push, 1) 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, float aTagFloat); 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; float mTagFloat; Address mTagAddress; uintptr_t mTagOffset; int mTagInt; char mTagChar; }; char mTagName; }; #pragma pack(pop) typedef void (*IterateTagsCallback)(const ProfileEntry& entry, const char* tagStringData); class UniqueJITOptimizations { public: bool empty() const { return mOpts.empty(); } mozilla::Maybe getIndex(void* addr, JSRuntime* rt); void stream(JSStreamWriter& b, JSRuntime* rt); private: struct OptimizationKey { void* mEntryAddr; uint8_t mIndex; bool operator<(const OptimizationKey& other) const; }; mozilla::Vector mOpts; std::map mOptToIndexMap; }; class ProfileBuffer { public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ProfileBuffer) explicit ProfileBuffer(int aEntrySize, uint32_t aGeneration = 0); void addTag(const ProfileEntry& aTag); void IterateTagsForThread(IterateTagsCallback aCallback, int aThreadId); void StreamSamplesToJSObject(JSStreamWriter& b, int aThreadId, JSRuntime* rt, UniqueJITOptimizations& aUniqueOpts); void StreamMarkersToJSObject(JSStreamWriter& b, int aThreadId); void DuplicateLastSample(int aThreadId); void addStoredMarker(ProfilerMarker* aStoredMarker); void deleteExpiredStoredMarkers(); protected: char* processDynamicTag(int readPos, int* tagsConsumed, char* tagBuff); int FindLastSampleOfThread(int aThreadId); ~ProfileBuffer(); public: // Circular buffer 'Keep One Slot Open' implementation for simplicity mozilla::UniquePtr mEntries; // Points to the next entry we will write to, which is also the one at which // we need to stop reading. int mWritePos; // Points to the entry at which we can start reading. int mReadPos; // The number of entries in our buffer. int mEntrySize; // How many times mWritePos has wrapped around. uint32_t mGeneration; // Markers that marker entries in the buffer might refer to. ProfilerMarkerLinkedList mStoredMarkers; }; class ThreadProfile { public: ThreadProfile(ThreadInfo* aThreadInfo, ProfileBuffer* aBuffer); virtual ~ThreadProfile(); void addTag(const ProfileEntry& aTag); /** * Track a marker which has been inserted into the ThreadProfile. * This marker can safely be deleted once the generation has * expired. */ void addStoredMarker(ProfilerMarker *aStoredMarker); void IterateTags(IterateTagsCallback aCallback); void ToStreamAsJSON(std::ostream& stream); JSObject *ToJSObject(JSContext *aCx); PseudoStack* GetPseudoStack(); mozilla::Mutex* GetMutex(); void StreamJSObject(JSStreamWriter& b); void BeginUnwind(); virtual void EndUnwind(); virtual SyncProfile* AsSyncProfile() { return nullptr; } bool IsMainThread() const { return mIsMainThread; } const char* Name() const { return mThreadInfo->Name(); } int ThreadId() const { return mThreadId; } PlatformData* GetPlatformData() const { return mPlatformData; } void* GetStackTop() const { return mStackTop; } void DuplicateLastSample(); ThreadInfo* GetThreadInfo() const { return mThreadInfo; } ThreadResponsiveness* GetThreadResponsiveness() { return &mRespInfo; } void SetPendingDelete() { mPseudoStack = nullptr; mPlatformData = nullptr; } uint32_t bufferGeneration() const { return mBuffer->mGeneration; } private: FRIEND_TEST(ThreadProfile, InsertOneTag); FRIEND_TEST(ThreadProfile, InsertOneTagWithTinyBuffer); FRIEND_TEST(ThreadProfile, InsertTagsNoWrap); FRIEND_TEST(ThreadProfile, InsertTagsWrap); FRIEND_TEST(ThreadProfile, MemoryMeasure); ThreadInfo* mThreadInfo; const nsRefPtr mBuffer; PseudoStack* mPseudoStack; mozilla::Mutex mMutex; int mThreadId; bool mIsMainThread; PlatformData* mPlatformData; // Platform specific data. void* const mStackTop; ThreadResponsiveness mRespInfo; // Only Linux is using a signal sender, instead of stopping the thread, so we // need some space to store the data which cannot be collected in the signal // handler code. #ifdef XP_LINUX public: int64_t mRssMemory; int64_t mUssMemory; #endif }; #endif /* ndef MOZ_PROFILE_ENTRY_H */