1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=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 file,
5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef tools_profiler_MemoryProfiler_h
8 #define tools_profiler_MemoryProfiler_h
9 
10 #include "nsIMemoryProfiler.h"
11 
12 #include "mozilla/StaticPtr.h"
13 #include "mozilla/TimeStamp.h"
14 
15 #include "CompactTraceTable.h"
16 #include "nsTArray.h"
17 #include "prlock.h"
18 
19 #define MEMORY_PROFILER_CID                                     \
20   { 0xf976eaa2, 0xcc1f, 0x47ee,                                 \
21     { 0x81, 0x29, 0xb8, 0x26, 0x2a, 0x3d, 0xb6, 0xb2 } }
22 
23 #define MEMORY_PROFILER_CONTRACT_ID "@mozilla.org/tools/memory-profiler;1"
24 
25 struct PRLock;
26 
27 namespace mozilla {
28 
29 class NativeProfilerImpl;
30 class GCHeapProfilerImpl;
31 
32 struct ProfilerForJSContext
33 {
ProfilerForJSContextProfilerForJSContext34   ProfilerForJSContext()
35     : mProfiler(nullptr)
36     , mEnabled(false)
37   {}
38   GCHeapProfilerImpl* mProfiler;
39   bool mEnabled;
40 };
41 using JSContextProfilerMap =
42   nsDataHashtable<nsClearingPtrHashKey<JSContext>, ProfilerForJSContext>;
43 
44 class MemoryProfiler final : public nsIMemoryProfiler
45 {
46 public:
47   NS_DECL_ISUPPORTS
48   NS_DECL_NSIMEMORYPROFILER
49 
50 private:
51   static void InitOnce();
~MemoryProfiler()52   ~MemoryProfiler() {}
53 
54   // The accesses to other static member are guarded by sLock and
55   // sProfileContextCount.
56   static PRLock* sLock;
57   static uint32_t sProfileContextCount;
58 
59   static StaticAutoPtr<NativeProfilerImpl> sNativeProfiler;
60   static StaticAutoPtr<JSContextProfilerMap> sJSContextProfilerMap;
61   static TimeStamp sStartTime;
62 };
63 
64 // Allocation events to be reported.
65 struct AllocEvent {
66   TimeStamp mTimestamp;
67   // index to a stacktrace singleton.
68   uint32_t mTraceIdx;
69   // Allocation size
70   int32_t mSize;
71 
AllocEventAllocEvent72   AllocEvent(uint32_t aTraceIdx, int32_t aSize, TimeStamp aTimestamp)
73     : mTimestamp(aTimestamp)
74     , mTraceIdx(aTraceIdx)
75     , mSize(aSize)
76   {}
77 };
78 
79 // Index to allocation events but also a mark bit to be GC-able.
80 struct AllocEntry {
81   uint32_t mEventIdx : 31;
82   bool mMarked : 1;
83 
84   // Default constructor for uninitialized stack value required by
85   // getter methods.
AllocEntryAllocEntry86   AllocEntry()
87     : mEventIdx(0)
88     , mMarked(false)
89   {}
AllocEntryAllocEntry90   explicit AllocEntry(int aEventIdx)
91     : mEventIdx(aEventIdx)
92     , mMarked(false)
93   {}
94 };
95 
96 using AllocMap = nsDataHashtable<nsClearingVoidPtrHashKey, AllocEntry>;
97 
98 class ProfilerImpl
99 {
100 public:
101   static nsTArray<nsCString> GetStacktrace();
102   static double DRandom();
103 
104   ProfilerImpl();
105   virtual nsTArray<nsCString> GetNames() const = 0;
106   virtual nsTArray<TrieNode> GetTraces() const = 0;
107   virtual const nsTArray<AllocEvent>& GetEvents() const = 0;
108 
109 protected:
110   /**
111    * The sampler generates a random variable which conforms to a geometric
112    * distribution of probability p = 1 / mSampleSize to calculate the
113    * next-to-be-sampled byte directly; It avoids rolling a dice on each byte.
114    *
115    * Let Bn denote a Bernoulli process with first success on n-th trial, the
116    * cumulative distribution function of Bn is Cn = 1 - (1 - p) ^ n.
117    * Let U denote a uniformly distributed random variable in [0, 1).
118    * A geometric random variable can be generated by Cn's reverse function:
119    * G = floor(log(1 - U) / log(1 - p)).
120    *
121    * @param aSize the number of bytes seen
122    * @return the number of events sampled
123    */
124   size_t AddBytesSampled(uint32_t aBytes);
125 
126   uint32_t mSampleSize;
127 
128 private:
129   uint32_t mRemainingBytes;
130   double mLog1minusP;
131 };
132 
133 /*
134  * This class is used to make sure the profile data is only accessed
135  * on one thread at a time. Don't use mozilla::Mutex because we don't
136  * want to allocate memory.
137  */
138 class AutoMPLock
139 {
140 public:
AutoMPLock(PRLock * aLock)141   explicit AutoMPLock(PRLock* aLock)
142   {
143     MOZ_ASSERT(aLock);
144     mLock = aLock;
145     PR_Lock(mLock);
146   }
147 
~AutoMPLock()148   ~AutoMPLock()
149   {
150     PR_Unlock(mLock);
151   }
152 
153 private:
154   PRLock* mLock;
155 };
156 
157 } // namespace mozilla
158 
159 #endif
160