Line data Source code
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 : #include "GCHeapProfilerImpl.h"
8 :
9 : #include "UncensoredAllocator.h"
10 :
11 : namespace mozilla {
12 :
13 0 : GCHeapProfilerImpl::GCHeapProfilerImpl()
14 : {
15 0 : mLock = PR_NewLock();
16 0 : mMarking = false;
17 0 : }
18 :
19 0 : GCHeapProfilerImpl::~GCHeapProfilerImpl()
20 : {
21 0 : if (mLock) {
22 0 : PR_DestroyLock(mLock);
23 : }
24 0 : }
25 :
26 : nsTArray<nsCString>
27 0 : GCHeapProfilerImpl::GetNames() const
28 : {
29 0 : return mTraceTable.GetNames();
30 : }
31 :
32 : nsTArray<TrieNode>
33 0 : GCHeapProfilerImpl::GetTraces() const
34 : {
35 0 : return mTraceTable.GetTraces();
36 : }
37 :
38 : const nsTArray<AllocEvent>&
39 0 : GCHeapProfilerImpl::GetEvents() const
40 : {
41 0 : return mAllocEvents;
42 : }
43 :
44 : void
45 0 : GCHeapProfilerImpl::reset()
46 : {
47 0 : mTraceTable.Reset();
48 0 : mAllocEvents.Clear();
49 0 : mNurseryEntries.Clear();
50 0 : mTenuredEntriesFG.Clear();
51 0 : mTenuredEntriesBG.Clear();
52 0 : }
53 :
54 : void
55 0 : GCHeapProfilerImpl::sampleTenured(void* addr, uint32_t size)
56 : {
57 0 : SampleInternal(addr, size, mTenuredEntriesFG);
58 0 : }
59 :
60 : void
61 0 : GCHeapProfilerImpl::sampleNursery(void* addr, uint32_t size)
62 : {
63 0 : SampleInternal(addr, size, mNurseryEntries);
64 0 : }
65 :
66 : void
67 0 : GCHeapProfilerImpl::markTenuredStart()
68 : {
69 0 : AutoUseUncensoredAllocator ua;
70 0 : AutoMPLock lock(mLock);
71 0 : if (!mMarking) {
72 0 : mMarking = true;
73 0 : mTenuredEntriesFG.SwapElements(mTenuredEntriesBG);
74 0 : MOZ_ASSERT(mTenuredEntriesFG.Count() == 0);
75 : }
76 0 : }
77 :
78 : void
79 0 : GCHeapProfilerImpl::markTenured(void* addr)
80 : {
81 0 : AutoUseUncensoredAllocator ua;
82 0 : AutoMPLock lock(mLock);
83 0 : if (mMarking) {
84 0 : AllocEntry entry;
85 0 : if (mTenuredEntriesBG.Get(addr, &entry)) {
86 0 : entry.mMarked = true;
87 0 : mTenuredEntriesBG.Put(addr, entry);
88 : }
89 : }
90 0 : }
91 :
92 : void
93 0 : GCHeapProfilerImpl::sweepTenured()
94 : {
95 0 : AutoUseUncensoredAllocator ua;
96 0 : AutoMPLock lock(mLock);
97 0 : if (mMarking) {
98 0 : mMarking = false;
99 0 : for (auto iter = mTenuredEntriesBG.Iter(); !iter.Done(); iter.Next()) {
100 0 : if (iter.Data().mMarked) {
101 0 : iter.Data().mMarked = false;
102 0 : mTenuredEntriesFG.Put(iter.Key(), iter.Data());
103 : } else {
104 0 : AllocEvent& oldEvent = mAllocEvents[iter.Data().mEventIdx];
105 0 : AllocEvent newEvent(oldEvent.mTraceIdx, -oldEvent.mSize, TimeStamp::Now());
106 0 : mAllocEvents.AppendElement(newEvent);
107 : }
108 : }
109 0 : mTenuredEntriesBG.Clear();
110 : }
111 0 : }
112 :
113 : void
114 0 : GCHeapProfilerImpl::sweepNursery()
115 : {
116 0 : AutoUseUncensoredAllocator ua;
117 0 : AutoMPLock lock(mLock);
118 0 : for (auto iter = mNurseryEntries.Iter(); !iter.Done(); iter.Next()) {
119 0 : AllocEvent& oldEvent = mAllocEvents[iter.Data().mEventIdx];
120 0 : AllocEvent newEvent(oldEvent.mTraceIdx, -oldEvent.mSize, TimeStamp::Now());
121 0 : mAllocEvents.AppendElement(newEvent);
122 : }
123 0 : mNurseryEntries.Clear();
124 0 : }
125 :
126 : void
127 0 : GCHeapProfilerImpl::moveNurseryToTenured(void* addrOld, void* addrNew)
128 : {
129 0 : AutoUseUncensoredAllocator ua;
130 0 : AutoMPLock lock(mLock);
131 0 : AllocEntry entryOld;
132 0 : if (!mNurseryEntries.Get(addrOld, &entryOld)) {
133 0 : return;
134 : }
135 :
136 : // Because the tenured heap is sampled, the address might already be there.
137 : // If not, the address is inserted with the old event.
138 0 : AllocEntry tenuredEntryOld;
139 0 : if (!mTenuredEntriesFG.Get(addrNew, &tenuredEntryOld)) {
140 0 : mTenuredEntriesFG.Put(addrNew, AllocEntry(entryOld.mEventIdx));
141 : } else {
142 : // If it is already inserted, the insertion above will fail and the
143 : // iterator of the already-inserted element is returned.
144 : // We choose to ignore the the new event by setting its size zero and point
145 : // the newly allocated address to the old event.
146 : // An event of size zero will be skipped when reporting.
147 0 : mAllocEvents[entryOld.mEventIdx].mSize = 0;
148 0 : tenuredEntryOld.mEventIdx = entryOld.mEventIdx;
149 0 : mTenuredEntriesFG.Put(addrNew, tenuredEntryOld);
150 : }
151 0 : mNurseryEntries.Remove(addrOld);
152 : }
153 :
154 : void
155 0 : GCHeapProfilerImpl::SampleInternal(void* aAddr, uint32_t aSize, AllocMap& aTable)
156 : {
157 0 : AutoUseUncensoredAllocator ua;
158 0 : AutoMPLock lock(mLock);
159 0 : size_t nSamples = AddBytesSampled(aSize);
160 0 : if (nSamples > 0) {
161 0 : nsTArray<nsCString> trace = GetStacktrace();
162 0 : AllocEvent ai(mTraceTable.Insert(trace), nSamples * mSampleSize, TimeStamp::Now());
163 0 : aTable.Put(aAddr, AllocEntry(mAllocEvents.Length()));
164 0 : mAllocEvents.AppendElement(ai);
165 : }
166 0 : }
167 :
168 : } // namespace mozilla
|