1 : /* ***** BEGIN LICENSE BLOCK *****
2 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 : *
4 : * The contents of this file are subject to the Mozilla Public License Version
5 : * 1.1 (the "License"); you may not use this file except in compliance with
6 : * the License. You may obtain a copy of the License at
7 : * http://www.mozilla.org/MPL/
8 : *
9 : * Software distributed under the License is distributed on an "AS IS" basis,
10 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 : * for the specific language governing rights and limitations under the
12 : * License.
13 : *
14 : * The Original Code is about:memory glue.
15 : *
16 : * The Initial Developer of the Original Code is
17 : * Ms2ger <ms2ger@gmail.com>.
18 : * Portions created by the Initial Developer are Copyright (C) 2011
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : *
23 : * Alternatively, the contents of this file may be used under the terms of
24 : * either the GNU General Public License Version 2 or later (the "GPL"), or
25 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 : * in which case the provisions of the GPL or the LGPL are applicable instead
27 : * of those above. If you wish to allow use of your version of this file only
28 : * under the terms of either the GPL or the LGPL, and not to allow others to
29 : * use your version of this file under the terms of the MPL, indicate your
30 : * decision by deleting the provisions above and replace them with the notice
31 : * and other provisions required by the GPL or the LGPL. If you do not delete
32 : * the provisions above, a recipient may use your version of this file under
33 : * the terms of any one of the MPL, the GPL or the LGPL.
34 : *
35 : * ***** END LICENSE BLOCK ***** */
36 :
37 : #include "js/MemoryMetrics.h"
38 :
39 : #include "mozilla/Assertions.h"
40 :
41 : #include "jsapi.h"
42 : #include "jscntxt.h"
43 : #include "jscompartment.h"
44 : #include "jsgc.h"
45 : #include "jsobj.h"
46 : #include "jsscope.h"
47 : #include "jsscript.h"
48 :
49 : #include "jsobjinlines.h"
50 :
51 : #ifdef JS_THREADSAFE
52 :
53 : namespace JS {
54 :
55 : using namespace js;
56 :
57 : static void
58 9 : StatsCompartmentCallback(JSRuntime *rt, void *data, JSCompartment *compartment)
59 : {
60 : // Append a new CompartmentStats to the vector.
61 9 : RuntimeStats *rtStats = static_cast<RuntimeStats *>(data);
62 :
63 : // CollectRuntimeStats reserves enough space.
64 9 : MOZ_ALWAYS_TRUE(rtStats->compartmentStatsVector.growBy(1));
65 9 : CompartmentStats &cStats = rtStats->compartmentStatsVector.back();
66 9 : rtStats->initExtraCompartmentStats(compartment, &cStats);
67 9 : rtStats->currCompartmentStats = &cStats;
68 :
69 : // Get the compartment-level numbers.
70 : #ifdef JS_METHODJIT
71 9 : cStats.mjitCode = compartment->sizeOfMjitCode();
72 : #endif
73 9 : compartment->sizeOfTypeInferenceData(&cStats.typeInferenceSizes, rtStats->mallocSizeOf);
74 9 : cStats.shapesCompartmentTables = compartment->sizeOfShapeTable(rtStats->mallocSizeOf);
75 9 : }
76 :
77 : static void
78 9 : StatsChunkCallback(JSRuntime *rt, void *data, gc::Chunk *chunk)
79 : {
80 : // Nb: This function is only called for dirty chunks, which is why we
81 : // increment gcHeapChunkDirtyDecommitted.
82 9 : RuntimeStats *rtStats = static_cast<RuntimeStats *>(data);
83 2277 : for (size_t i = 0; i < gc::ArenasPerChunk; i++)
84 2268 : if (chunk->decommittedArenas.get(i))
85 0 : rtStats->gcHeapChunkDirtyDecommitted += gc::ArenaSize;
86 9 : }
87 :
88 : static void
89 1089 : StatsArenaCallback(JSRuntime *rt, void *data, gc::Arena *arena,
90 : JSGCTraceKind traceKind, size_t thingSize)
91 : {
92 1089 : RuntimeStats *rtStats = static_cast<RuntimeStats *>(data);
93 :
94 1089 : rtStats->currCompartmentStats->gcHeapArenaHeaders += sizeof(gc::ArenaHeader);
95 1089 : size_t allocationSpace = arena->thingsSpan(thingSize);
96 : rtStats->currCompartmentStats->gcHeapArenaPadding +=
97 1089 : gc::ArenaSize - allocationSpace - sizeof(gc::ArenaHeader);
98 : // We don't call the callback on unused things. So we compute the
99 : // unused space like this: arenaUnused = maxArenaUnused - arenaUsed.
100 : // We do this by setting arenaUnused to maxArenaUnused here, and then
101 : // subtracting thingSize for every used cell, in StatsCellCallback().
102 1089 : rtStats->currCompartmentStats->gcHeapArenaUnused += allocationSpace;
103 1089 : }
104 :
105 : static void
106 142578 : StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKind,
107 : size_t thingSize)
108 : {
109 142578 : RuntimeStats *rtStats = static_cast<RuntimeStats *>(data);
110 142578 : CompartmentStats *cStats = rtStats->currCompartmentStats;
111 142578 : switch (traceKind) {
112 : case JSTRACE_OBJECT:
113 : {
114 25914 : JSObject *obj = static_cast<JSObject *>(thing);
115 25914 : if (obj->isFunction()) {
116 16046 : cStats->gcHeapObjectsFunction += thingSize;
117 : } else {
118 9868 : cStats->gcHeapObjectsNonFunction += thingSize;
119 : }
120 : size_t slotsSize, elementsSize, miscSize;
121 : obj->sizeOfExcludingThis(rtStats->mallocSizeOf, &slotsSize,
122 25914 : &elementsSize, &miscSize);
123 25914 : cStats->objectSlots += slotsSize;
124 25914 : cStats->objectElements += elementsSize;
125 25914 : cStats->objectMisc += miscSize;
126 25914 : break;
127 : }
128 : case JSTRACE_STRING:
129 : {
130 61918 : JSString *str = static_cast<JSString *>(thing);
131 61918 : cStats->gcHeapStrings += thingSize;
132 61918 : cStats->stringChars += str->sizeOfExcludingThis(rtStats->mallocSizeOf);
133 61918 : break;
134 : }
135 : case JSTRACE_SHAPE:
136 : {
137 43133 : Shape *shape = static_cast<Shape*>(thing);
138 : size_t propTableSize, kidsSize;
139 43133 : shape->sizeOfExcludingThis(rtStats->mallocSizeOf, &propTableSize, &kidsSize);
140 43133 : if (shape->inDictionary()) {
141 14112 : cStats->gcHeapShapesDict += thingSize;
142 14112 : cStats->shapesExtraDictTables += propTableSize;
143 14112 : JS_ASSERT(kidsSize == 0);
144 : } else {
145 29021 : cStats->gcHeapShapesTree += thingSize;
146 29021 : cStats->shapesExtraTreeTables += propTableSize;
147 29021 : cStats->shapesExtraTreeShapeKids += kidsSize;
148 : }
149 43133 : break;
150 : }
151 : case JSTRACE_BASE_SHAPE:
152 : {
153 6944 : cStats->gcHeapShapesBase += thingSize;
154 6944 : break;
155 : }
156 : case JSTRACE_SCRIPT:
157 : {
158 3818 : JSScript *script = static_cast<JSScript *>(thing);
159 3818 : cStats->gcHeapScripts += thingSize;
160 3818 : cStats->scriptData += script->sizeOfData(rtStats->mallocSizeOf);
161 : #ifdef JS_METHODJIT
162 3818 : cStats->mjitData += script->sizeOfJitScripts(rtStats->mallocSizeOf);
163 : #endif
164 3818 : break;
165 : }
166 : case JSTRACE_TYPE_OBJECT:
167 : {
168 851 : types::TypeObject *obj = static_cast<types::TypeObject *>(thing);
169 851 : cStats->gcHeapTypeObjects += thingSize;
170 851 : obj->sizeOfExcludingThis(&cStats->typeInferenceSizes, rtStats->mallocSizeOf);
171 851 : break;
172 : }
173 : case JSTRACE_XML:
174 : {
175 0 : cStats->gcHeapXML += thingSize;
176 0 : break;
177 : }
178 : }
179 : // Yes, this is a subtraction: see StatsArenaCallback() for details.
180 142578 : cStats->gcHeapArenaUnused -= thingSize;
181 142578 : }
182 :
183 : JS_PUBLIC_API(bool)
184 3 : CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats)
185 : {
186 3 : if (!rtStats->compartmentStatsVector.reserve(rt->compartments.length()))
187 0 : return false;
188 :
189 : rtStats->gcHeapChunkCleanDecommitted =
190 3 : rt->gcChunkPool.countCleanDecommittedArenas(rt) * gc::ArenaSize;
191 : rtStats->gcHeapChunkCleanUnused =
192 3 : size_t(JS_GetGCParameter(rt, JSGC_UNUSED_CHUNKS)) * gc::ChunkSize -
193 3 : rtStats->gcHeapChunkCleanDecommitted;
194 : rtStats->gcHeapChunkTotal =
195 3 : size_t(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) * gc::ChunkSize;
196 :
197 : IterateCompartmentsArenasCells(rt, rtStats, StatsCompartmentCallback,
198 3 : StatsArenaCallback, StatsCellCallback);
199 3 : IterateChunks(rt, rtStats, StatsChunkCallback);
200 :
201 3 : rtStats->runtimeObject = rtStats->mallocSizeOf(rt);
202 :
203 : rt->sizeOfExcludingThis(rtStats->mallocSizeOf,
204 : &rtStats->runtimeNormal,
205 : &rtStats->runtimeTemporary,
206 : &rtStats->runtimeRegexpCode,
207 : &rtStats->runtimeStackCommitted,
208 3 : &rtStats->runtimeGCMarker);
209 :
210 : rtStats->runtimeAtomsTable =
211 3 : rt->atomState.atoms.sizeOfExcludingThis(rtStats->mallocSizeOf);
212 :
213 12 : for (ContextIter acx(rt); !acx.done(); acx.next())
214 9 : rtStats->runtimeContexts += acx->sizeOfIncludingThis(rtStats->mallocSizeOf);
215 :
216 : // This is initialized to all bytes stored in used chunks, and then we
217 : // subtract used space from it each time around the loop.
218 : rtStats->gcHeapChunkDirtyUnused = rtStats->gcHeapChunkTotal -
219 : rtStats->gcHeapChunkCleanUnused -
220 : rtStats->gcHeapChunkCleanDecommitted -
221 3 : rtStats->gcHeapChunkDirtyDecommitted;
222 :
223 24 : for (size_t index = 0;
224 12 : index < rtStats->compartmentStatsVector.length();
225 : index++) {
226 9 : CompartmentStats &cStats = rtStats->compartmentStatsVector[index];
227 :
228 : size_t used = cStats.gcHeapArenaHeaders +
229 : cStats.gcHeapArenaPadding +
230 : cStats.gcHeapArenaUnused +
231 : cStats.gcHeapObjectsNonFunction +
232 : cStats.gcHeapObjectsFunction +
233 : cStats.gcHeapStrings +
234 : cStats.gcHeapShapesTree +
235 : cStats.gcHeapShapesDict +
236 : cStats.gcHeapShapesBase +
237 : cStats.gcHeapScripts +
238 : cStats.gcHeapTypeObjects +
239 9 : cStats.gcHeapXML;
240 :
241 9 : rtStats->gcHeapChunkDirtyUnused -= used;
242 9 : rtStats->gcHeapArenaUnused += cStats.gcHeapArenaUnused;
243 : rtStats->totalObjects += cStats.gcHeapObjectsNonFunction +
244 : cStats.gcHeapObjectsFunction +
245 : cStats.objectSlots +
246 : cStats.objectElements +
247 9 : cStats.objectMisc;
248 : rtStats->totalShapes += cStats.gcHeapShapesTree +
249 : cStats.gcHeapShapesDict +
250 : cStats.gcHeapShapesBase +
251 : cStats.shapesExtraTreeTables +
252 : cStats.shapesExtraDictTables +
253 9 : cStats.shapesCompartmentTables;
254 : rtStats->totalScripts += cStats.gcHeapScripts +
255 9 : cStats.scriptData;
256 : rtStats->totalStrings += cStats.gcHeapStrings +
257 9 : cStats.stringChars;
258 : #ifdef JS_METHODJIT
259 : rtStats->totalMjit += cStats.mjitCode +
260 9 : cStats.mjitData;
261 : #endif
262 : rtStats->totalTypeInference += cStats.gcHeapTypeObjects +
263 : cStats.typeInferenceSizes.objects +
264 : cStats.typeInferenceSizes.scripts +
265 9 : cStats.typeInferenceSizes.tables;
266 9 : rtStats->totalAnalysisTemp += cStats.typeInferenceSizes.temporary;
267 : }
268 :
269 : size_t numDirtyChunks = (rtStats->gcHeapChunkTotal -
270 : rtStats->gcHeapChunkCleanUnused) /
271 3 : gc::ChunkSize;
272 : size_t perChunkAdmin =
273 3 : sizeof(gc::Chunk) - (sizeof(gc::Arena) * gc::ArenasPerChunk);
274 3 : rtStats->gcHeapChunkAdmin = numDirtyChunks * perChunkAdmin;
275 3 : rtStats->gcHeapChunkDirtyUnused -= rtStats->gcHeapChunkAdmin;
276 :
277 : // Why 10000x? 100x because it's a percentage, and another 100x
278 : // because nsIMemoryReporter requires that for percentage amounts so
279 : // they can be fractional.
280 : rtStats->gcHeapUnusedPercentage = (rtStats->gcHeapChunkCleanUnused +
281 : rtStats->gcHeapChunkDirtyUnused +
282 : rtStats->gcHeapChunkCleanDecommitted +
283 : rtStats->gcHeapChunkDirtyDecommitted +
284 : rtStats->gcHeapArenaUnused) * 10000 /
285 3 : rtStats->gcHeapChunkTotal;
286 :
287 3 : return true;
288 : }
289 :
290 : static void
291 9 : ExplicitNonHeapCompartmentCallback(JSRuntime *rt, void *data, JSCompartment *compartment)
292 : {
293 : #ifdef JS_METHODJIT
294 9 : size_t *n = static_cast<size_t *>(data);
295 9 : *n += compartment->sizeOfMjitCode();
296 : #endif
297 9 : }
298 :
299 : JS_PUBLIC_API(int64_t)
300 3 : GetExplicitNonHeapForRuntime(JSRuntime *rt, JSMallocSizeOfFun mallocSizeOf)
301 : {
302 : // explicit/<compartment>/gc-heap/*
303 3 : size_t n = size_t(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) * gc::ChunkSize;
304 :
305 : // explicit/<compartment>/mjit-code
306 3 : JS_IterateCompartments(rt, &n, ExplicitNonHeapCompartmentCallback);
307 :
308 : // explicit/runtime/regexp-code
309 : // explicit/runtime/stack-committed
310 : size_t regexpCode, stackCommitted;
311 : rt->sizeOfExcludingThis(mallocSizeOf,
312 : NULL,
313 : NULL,
314 : ®expCode,
315 : &stackCommitted,
316 3 : NULL);
317 :
318 3 : n += regexpCode;
319 3 : n += stackCommitted;
320 :
321 3 : return int64_t(n);
322 : }
323 :
324 : JS_PUBLIC_API(size_t)
325 3 : SystemCompartmentCount(const JSRuntime *rt)
326 : {
327 3 : size_t n = 0;
328 12 : for (size_t i = 0; i < rt->compartments.length(); i++) {
329 9 : if (rt->compartments[i]->isSystemCompartment)
330 6 : ++n;
331 : }
332 3 : return n;
333 : }
334 :
335 : JS_PUBLIC_API(size_t)
336 3 : UserCompartmentCount(const JSRuntime *rt)
337 : {
338 3 : size_t n = 0;
339 12 : for (size_t i = 0; i < rt->compartments.length(); i++) {
340 9 : if (!rt->compartments[i]->isSystemCompartment)
341 3 : ++n;
342 : }
343 3 : return n;
344 : }
345 :
346 : } // namespace JS
347 :
348 : #endif // JS_THREADSAFE
|