|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
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 |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #ifndef gc_Statistics_h |
|
8 #define gc_Statistics_h |
|
9 |
|
10 #include "mozilla/DebugOnly.h" |
|
11 #include "mozilla/PodOperations.h" |
|
12 |
|
13 #include "jsalloc.h" |
|
14 #include "jspubtd.h" |
|
15 |
|
16 #include "js/GCAPI.h" |
|
17 #include "js/Vector.h" |
|
18 |
|
19 struct JSCompartment; |
|
20 |
|
21 namespace js { |
|
22 namespace gcstats { |
|
23 |
|
24 enum Phase { |
|
25 PHASE_GC_BEGIN, |
|
26 PHASE_WAIT_BACKGROUND_THREAD, |
|
27 PHASE_MARK_DISCARD_CODE, |
|
28 PHASE_PURGE, |
|
29 PHASE_MARK, |
|
30 PHASE_MARK_ROOTS, |
|
31 PHASE_MARK_DELAYED, |
|
32 PHASE_SWEEP, |
|
33 PHASE_SWEEP_MARK, |
|
34 PHASE_SWEEP_MARK_TYPES, |
|
35 PHASE_SWEEP_MARK_INCOMING_BLACK, |
|
36 PHASE_SWEEP_MARK_WEAK, |
|
37 PHASE_SWEEP_MARK_INCOMING_GRAY, |
|
38 PHASE_SWEEP_MARK_GRAY, |
|
39 PHASE_SWEEP_MARK_GRAY_WEAK, |
|
40 PHASE_FINALIZE_START, |
|
41 PHASE_SWEEP_ATOMS, |
|
42 PHASE_SWEEP_COMPARTMENTS, |
|
43 PHASE_SWEEP_DISCARD_CODE, |
|
44 PHASE_SWEEP_TABLES, |
|
45 PHASE_SWEEP_TABLES_WRAPPER, |
|
46 PHASE_SWEEP_TABLES_BASE_SHAPE, |
|
47 PHASE_SWEEP_TABLES_INITIAL_SHAPE, |
|
48 PHASE_SWEEP_TABLES_TYPE_OBJECT, |
|
49 PHASE_SWEEP_TABLES_BREAKPOINT, |
|
50 PHASE_SWEEP_TABLES_REGEXP, |
|
51 PHASE_DISCARD_ANALYSIS, |
|
52 PHASE_DISCARD_TI, |
|
53 PHASE_FREE_TI_ARENA, |
|
54 PHASE_SWEEP_TYPES, |
|
55 PHASE_SWEEP_OBJECT, |
|
56 PHASE_SWEEP_STRING, |
|
57 PHASE_SWEEP_SCRIPT, |
|
58 PHASE_SWEEP_SHAPE, |
|
59 PHASE_SWEEP_JITCODE, |
|
60 PHASE_FINALIZE_END, |
|
61 PHASE_DESTROY, |
|
62 PHASE_GC_END, |
|
63 |
|
64 PHASE_LIMIT |
|
65 }; |
|
66 |
|
67 enum Stat { |
|
68 STAT_NEW_CHUNK, |
|
69 STAT_DESTROY_CHUNK, |
|
70 STAT_MINOR_GC, |
|
71 |
|
72 STAT_LIMIT |
|
73 }; |
|
74 |
|
75 class StatisticsSerializer; |
|
76 |
|
77 struct Statistics { |
|
78 Statistics(JSRuntime *rt); |
|
79 ~Statistics(); |
|
80 |
|
81 void beginPhase(Phase phase); |
|
82 void endPhase(Phase phase); |
|
83 |
|
84 void beginSlice(int collectedCount, int zoneCount, int compartmentCount, JS::gcreason::Reason reason); |
|
85 void endSlice(); |
|
86 |
|
87 void reset(const char *reason) { slices.back().resetReason = reason; } |
|
88 void nonincremental(const char *reason) { nonincrementalReason = reason; } |
|
89 |
|
90 void count(Stat s) { |
|
91 JS_ASSERT(s < STAT_LIMIT); |
|
92 counts[s]++; |
|
93 } |
|
94 |
|
95 int64_t beginSCC(); |
|
96 void endSCC(unsigned scc, int64_t start); |
|
97 |
|
98 jschar *formatMessage(); |
|
99 jschar *formatJSON(uint64_t timestamp); |
|
100 |
|
101 private: |
|
102 JSRuntime *runtime; |
|
103 |
|
104 int64_t startupTime; |
|
105 |
|
106 FILE *fp; |
|
107 bool fullFormat; |
|
108 |
|
109 /* |
|
110 * GCs can't really nest, but a second GC can be triggered from within the |
|
111 * JSGC_END callback. |
|
112 */ |
|
113 int gcDepth; |
|
114 |
|
115 int collectedCount; |
|
116 int zoneCount; |
|
117 int compartmentCount; |
|
118 const char *nonincrementalReason; |
|
119 |
|
120 struct SliceData { |
|
121 SliceData(JS::gcreason::Reason reason, int64_t start, size_t startFaults) |
|
122 : reason(reason), resetReason(nullptr), start(start), startFaults(startFaults) |
|
123 { |
|
124 mozilla::PodArrayZero(phaseTimes); |
|
125 } |
|
126 |
|
127 JS::gcreason::Reason reason; |
|
128 const char *resetReason; |
|
129 int64_t start, end; |
|
130 size_t startFaults, endFaults; |
|
131 int64_t phaseTimes[PHASE_LIMIT]; |
|
132 |
|
133 int64_t duration() const { return end - start; } |
|
134 }; |
|
135 |
|
136 Vector<SliceData, 8, SystemAllocPolicy> slices; |
|
137 |
|
138 /* Most recent time when the given phase started. */ |
|
139 int64_t phaseStartTimes[PHASE_LIMIT]; |
|
140 |
|
141 /* Total time in a given phase for this GC. */ |
|
142 int64_t phaseTimes[PHASE_LIMIT]; |
|
143 |
|
144 /* Total time in a given phase over all GCs. */ |
|
145 int64_t phaseTotals[PHASE_LIMIT]; |
|
146 |
|
147 /* Number of events of this type for this GC. */ |
|
148 unsigned int counts[STAT_LIMIT]; |
|
149 |
|
150 /* Allocated space before the GC started. */ |
|
151 size_t preBytes; |
|
152 |
|
153 #ifdef DEBUG |
|
154 /* Phases that are currently on stack. */ |
|
155 static const size_t MAX_NESTING = 8; |
|
156 Phase phaseNesting[MAX_NESTING]; |
|
157 #endif |
|
158 mozilla::DebugOnly<size_t> phaseNestingDepth; |
|
159 |
|
160 /* Sweep times for SCCs of compartments. */ |
|
161 Vector<int64_t, 0, SystemAllocPolicy> sccTimes; |
|
162 |
|
163 void beginGC(); |
|
164 void endGC(); |
|
165 |
|
166 void gcDuration(int64_t *total, int64_t *maxPause); |
|
167 void sccDurations(int64_t *total, int64_t *maxPause); |
|
168 void printStats(); |
|
169 bool formatData(StatisticsSerializer &ss, uint64_t timestamp); |
|
170 |
|
171 double computeMMU(int64_t resolution); |
|
172 }; |
|
173 |
|
174 struct AutoGCSlice |
|
175 { |
|
176 AutoGCSlice(Statistics &stats, int collectedCount, int zoneCount, int compartmentCount, |
|
177 JS::gcreason::Reason reason |
|
178 MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
|
179 : stats(stats) |
|
180 { |
|
181 MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
|
182 stats.beginSlice(collectedCount, zoneCount, compartmentCount, reason); |
|
183 } |
|
184 ~AutoGCSlice() { stats.endSlice(); } |
|
185 |
|
186 Statistics &stats; |
|
187 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
|
188 }; |
|
189 |
|
190 struct AutoPhase |
|
191 { |
|
192 AutoPhase(Statistics &stats, Phase phase |
|
193 MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
|
194 : stats(stats), phase(phase) |
|
195 { |
|
196 MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
|
197 stats.beginPhase(phase); |
|
198 } |
|
199 ~AutoPhase() { |
|
200 stats.endPhase(phase); |
|
201 } |
|
202 |
|
203 Statistics &stats; |
|
204 Phase phase; |
|
205 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
|
206 }; |
|
207 |
|
208 struct MaybeAutoPhase |
|
209 { |
|
210 MaybeAutoPhase(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) |
|
211 : stats(nullptr) |
|
212 { |
|
213 MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
|
214 } |
|
215 void construct(Statistics &statsArg, Phase phaseArg) |
|
216 { |
|
217 JS_ASSERT(!stats); |
|
218 stats = &statsArg; |
|
219 phase = phaseArg; |
|
220 stats->beginPhase(phase); |
|
221 } |
|
222 ~MaybeAutoPhase() { |
|
223 if (stats) |
|
224 stats->endPhase(phase); |
|
225 } |
|
226 |
|
227 Statistics *stats; |
|
228 Phase phase; |
|
229 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
|
230 }; |
|
231 |
|
232 struct AutoSCC |
|
233 { |
|
234 AutoSCC(Statistics &stats, unsigned scc |
|
235 MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
|
236 : stats(stats), scc(scc) |
|
237 { |
|
238 MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
|
239 start = stats.beginSCC(); |
|
240 } |
|
241 ~AutoSCC() { |
|
242 stats.endSCC(scc, start); |
|
243 } |
|
244 |
|
245 Statistics &stats; |
|
246 unsigned scc; |
|
247 int64_t start; |
|
248 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
|
249 }; |
|
250 |
|
251 const char *ExplainReason(JS::gcreason::Reason reason); |
|
252 |
|
253 } /* namespace gcstats */ |
|
254 } /* namespace js */ |
|
255 |
|
256 #endif /* gc_Statistics_h */ |