Imported Upstream version 1.0.0
[platform/upstream/js.git] / js / src / jsgcstats.cpp
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=4 sw=4 et tw=99 ft=cpp:
3  *
4  * ***** BEGIN LICENSE BLOCK *****
5  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6  *
7  * The contents of this file are subject to the Mozilla Public License Version
8  * 1.1 (the "License"); you may not use this file except in compliance with
9  * the License. You may obtain a copy of the License at
10  * http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14  * for the specific language governing rights and limitations under the
15  * License.
16  *
17  * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
18  * June 30, 2010
19  *
20  * The Initial Developer of the Original Code is
21  *   the Mozilla Corporation.
22  *
23  * Contributor(s):
24  *
25  * Alternatively, the contents of this file may be used under the terms of
26  * either of the GNU General Public License Version 2 or later (the "GPL"),
27  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28  * in which case the provisions of the GPL or the LGPL are applicable instead
29  * of those above. If you wish to allow use of your version of this file only
30  * under the terms of either the GPL or the LGPL, and not to allow others to
31  * use your version of this file under the terms of the MPL, indicate your
32  * decision by deleting the provisions above and replace them with the notice
33  * and other provisions required by the GPL or the LGPL. If you do not delete
34  * the provisions above, a recipient may use your version of this file under
35  * the terms of any one of the MPL, the GPL or the LGPL.
36  *
37  * ***** END LICENSE BLOCK ***** */
38
39 #include "jstypes.h"
40 #include "jscntxt.h"
41 #include "jsgcstats.h"
42 #include "jsgc.h"
43 #include "jsxml.h"
44 #include "jsbuiltins.h"
45 #include "jscompartment.h"
46
47 using namespace js;
48 using namespace js::gc;
49
50 #define UL(x)       ((unsigned long)(x))
51 #define PERCENT(x,y)  (100.0 * (double) (x) / (double) (y))
52
53 namespace js {
54 namespace gc {
55
56 #if defined(JS_DUMP_CONSERVATIVE_GC_ROOTS) || defined(JS_GCMETER)
57
58 void
59 ConservativeGCStats::dump(FILE *fp)
60 {
61     size_t words = 0;
62     for (size_t i = 0; i != JS_ARRAY_LENGTH(counter); ++i)
63         words += counter[i];
64    
65 #define ULSTAT(x)       ((unsigned long)(x))
66     fprintf(fp, "CONSERVATIVE STACK SCANNING:\n");
67     fprintf(fp, "      number of stack words: %lu\n", ULSTAT(words));
68     fprintf(fp, "      excluded, low bit set: %lu\n", ULSTAT(counter[CGCT_LOWBITSET]));
69     fprintf(fp, "        not withing a chunk: %lu\n", ULSTAT(counter[CGCT_NOTCHUNK]));
70     fprintf(fp, "     not within arena range: %lu\n", ULSTAT(counter[CGCT_NOTARENA]));
71     fprintf(fp, "       points to free arena: %lu\n", ULSTAT(counter[CGCT_FREEARENA]));
72     fprintf(fp, "        excluded, wrong tag: %lu\n", ULSTAT(counter[CGCT_WRONGTAG]));
73     fprintf(fp, "         excluded, not live: %lu\n", ULSTAT(counter[CGCT_NOTLIVE]));
74     fprintf(fp, "            valid GC things: %lu\n", ULSTAT(counter[CGCT_VALID]));
75     fprintf(fp, "      valid but not aligned: %lu\n", ULSTAT(counter[CGCT_VALIDWITHOFFSET]));
76 #undef ULSTAT
77 }
78 #endif
79
80 #ifdef JS_GCMETER
81 void
82 UpdateCompartmentStats(JSCompartment *comp, unsigned thingKind, uint32 nlivearenas,
83                        uint32 nkilledArenas, uint32 nthings)
84 {
85     size_t narenas = 0;
86     JSGCArenaStats *compSt = &comp->compartmentStats[thingKind];
87     JSGCArenaStats *globSt = &comp->rt->globalArenaStats[thingKind];
88     narenas = nlivearenas + nkilledArenas;
89     JS_ASSERT(narenas >= compSt->livearenas);
90
91     compSt->newarenas     = narenas - compSt->livearenas;
92     compSt->narenas       = narenas;
93     compSt->livearenas    = nlivearenas;
94     if (compSt->maxarenas < narenas)
95         compSt->maxarenas = narenas;
96     compSt->totalarenas  += narenas;
97
98     compSt->nthings       = nthings;
99     if (compSt->maxthings < nthings)
100         compSt->maxthings = nthings;
101     compSt->totalthings  += nthings;
102     globSt->newarenas    += compSt->newarenas;
103     globSt->narenas      += narenas;
104     globSt->livearenas   += compSt->livearenas;
105     globSt->totalarenas  += compSt->totalarenas;
106     globSt->nthings      += compSt->nthings;
107     globSt->totalthings  += compSt->totalthings;
108     if (globSt->maxarenas < compSt->maxarenas)
109         globSt->maxarenas = compSt->maxarenas;
110     if (globSt->maxthings < compSt->maxthings)
111         globSt->maxthings = compSt->maxthings;
112 }
113
114 static const char *const GC_ARENA_NAMES[] = {
115     "object_0",
116     "object_2",
117     "object_4",
118     "object_8",
119     "object_12",
120     "object_16",
121     "function",
122 #if JS_HAS_XML_SUPPORT
123     "xml",
124 #endif
125     "short string",
126     "string",
127     "external_string",
128 };
129 JS_STATIC_ASSERT(JS_ARRAY_LENGTH(GC_ARENA_NAMES) == FINALIZE_LIMIT);
130
131 template <typename T>
132 static inline void
133 GetSizeAndThings(size_t &thingSize, size_t &thingsPerArena)
134 {
135     thingSize = sizeof(T);
136     thingsPerArena = Arena<T>::ThingsPerArena;
137 }
138
139 #if defined JS_DUMP_CONSERVATIVE_GC_ROOTS
140 void *
141 GetAlignedThing(void *thing, int thingKind)
142 {
143     Cell *cell = (Cell *)thing;
144     switch (thingKind) {
145         case FINALIZE_OBJECT0:
146             return (void *)GetArena<JSObject>(cell)->getAlignedThing(thing);
147         case FINALIZE_OBJECT2:
148             return (void *)GetArena<JSObject_Slots2>(cell)->getAlignedThing(thing);
149         case FINALIZE_OBJECT4:
150             return (void *)GetArena<JSObject_Slots4>(cell)->getAlignedThing(thing);
151         case FINALIZE_OBJECT8:
152             return (void *)GetArena<JSObject_Slots8>(cell)->getAlignedThing(thing);
153         case FINALIZE_OBJECT12:
154             return (void *)GetArena<JSObject_Slots12>(cell)->getAlignedThing(thing);
155         case FINALIZE_OBJECT16:
156             return (void *)GetArena<JSObject_Slots16>(cell)->getAlignedThing(thing);
157         case FINALIZE_STRING:
158             return (void *)GetArena<JSString>(cell)->getAlignedThing(thing);
159         case FINALIZE_EXTERNAL_STRING:
160             return (void *)GetArena<JSExternalString>(cell)->getAlignedThing(thing);
161         case FINALIZE_SHORT_STRING:
162             return (void *)GetArena<JSShortString>(cell)->getAlignedThing(thing);
163         case FINALIZE_FUNCTION:
164             return (void *)GetArena<JSFunction>(cell)->getAlignedThing(thing);
165 #if JS_HAS_XML_SUPPORT
166         case FINALIZE_XML:
167             return (void *)GetArena<JSXML>(cell)->getAlignedThing(thing);
168 #endif
169         default:
170             JS_ASSERT(false);
171             return NULL;
172     }
173 }
174 #endif
175
176 void GetSizeAndThingsPerArena(int thingKind, size_t &thingSize, size_t &thingsPerArena)
177 {
178     switch (thingKind) {
179         case FINALIZE_OBJECT0:
180             GetSizeAndThings<JSObject>(thingSize, thingsPerArena);
181             break;
182         case FINALIZE_OBJECT2:
183             GetSizeAndThings<JSObject_Slots2>(thingSize, thingsPerArena);
184             break;
185         case FINALIZE_OBJECT4:
186             GetSizeAndThings<JSObject_Slots4>(thingSize, thingsPerArena);
187             break;
188         case FINALIZE_OBJECT8:
189             GetSizeAndThings<JSObject_Slots8>(thingSize, thingsPerArena);
190             break;
191         case FINALIZE_OBJECT12:
192             GetSizeAndThings<JSObject_Slots12>(thingSize, thingsPerArena);
193             break;
194         case FINALIZE_OBJECT16:
195             GetSizeAndThings<JSObject_Slots16>(thingSize, thingsPerArena);
196             break;
197         case FINALIZE_EXTERNAL_STRING:
198         case FINALIZE_STRING:
199             GetSizeAndThings<JSString>(thingSize, thingsPerArena);
200             break;
201         case FINALIZE_SHORT_STRING:
202             GetSizeAndThings<JSShortString>(thingSize, thingsPerArena);
203             break;
204         case FINALIZE_FUNCTION:
205             GetSizeAndThings<JSFunction>(thingSize, thingsPerArena);
206             break;
207 #if JS_HAS_XML_SUPPORT
208         case FINALIZE_XML:
209             GetSizeAndThings<JSXML>(thingSize, thingsPerArena);
210             break;
211 #endif
212         default:
213             JS_ASSERT(false);
214     }
215 }
216
217 void
218 DumpArenaStats(JSGCArenaStats *stp, FILE *fp)
219 {
220     size_t sumArenas = 0, sumTotalArenas = 0, sumThings =0,  sumMaxThings = 0;
221     size_t sumThingSize = 0, sumTotalThingSize = 0, sumArenaCapacity = 0;
222     size_t sumTotalArenaCapacity = 0, sumAlloc = 0, sumLocalAlloc = 0;
223
224     for (int i = 0; i < (int) FINALIZE_LIMIT; i++) {
225         JSGCArenaStats *st = &stp[i];
226         if (st->maxarenas == 0)
227             continue;
228         size_t thingSize = 0, thingsPerArena = 0;
229         GetSizeAndThingsPerArena(i, thingSize, thingsPerArena);
230
231         fprintf(fp, "%s arenas (thing size %lu, %lu things per arena):\n",
232                 GC_ARENA_NAMES[i], UL(thingSize), UL(thingsPerArena));
233         fprintf(fp, "           arenas before GC: %lu\n", UL(st->narenas));
234         fprintf(fp, "            arenas after GC: %lu (%.1f%%)\n",
235                 UL(st->livearenas), PERCENT(st->livearenas, st->narenas));
236         fprintf(fp, "                 max arenas: %lu\n", UL(st->maxarenas));
237         fprintf(fp, "                     things: %lu\n", UL(st->nthings));
238         fprintf(fp, "        GC cell utilization: %.1f%%\n",
239                 PERCENT(st->nthings, thingsPerArena * st->narenas));
240         fprintf(fp, "   average cell utilization: %.1f%%\n",
241                 PERCENT(st->totalthings, thingsPerArena * st->totalarenas));
242         fprintf(fp, "                 max things: %lu\n", UL(st->maxthings));
243         fprintf(fp, "             alloc attempts: %lu\n", UL(st->alloc));
244         fprintf(fp, "        alloc without locks: %lu  (%.1f%%)\n",
245                 UL(st->localalloc), PERCENT(st->localalloc, st->alloc));
246         sumArenas += st->narenas;
247         sumTotalArenas += st->totalarenas;
248         sumThings += st->nthings;
249         sumMaxThings += st->maxthings;
250         sumThingSize += thingSize * st->nthings;
251         sumTotalThingSize += size_t(thingSize * st->totalthings);
252         sumArenaCapacity += thingSize * thingsPerArena * st->narenas;
253         sumTotalArenaCapacity += thingSize * thingsPerArena * st->totalarenas;
254         sumAlloc += st->alloc;
255         sumLocalAlloc += st->localalloc;
256         putc('\n', fp);
257     }
258
259     fputs("Never used arenas:\n", fp);
260     for (int i = 0; i < (int) FINALIZE_LIMIT; i++) {
261         JSGCArenaStats *st = &stp[i];
262         if (st->maxarenas != 0)
263             continue;
264         fprintf(fp, "%s\n", GC_ARENA_NAMES[i]);
265     }
266     fprintf(fp, "\nTOTAL STATS:\n");
267     fprintf(fp, "            total GC arenas: %lu\n", UL(sumArenas));
268     fprintf(fp, "            total GC things: %lu\n", UL(sumThings));
269     fprintf(fp, "        max total GC things: %lu\n", UL(sumMaxThings));
270     fprintf(fp, "        GC cell utilization: %.1f%%\n",
271             PERCENT(sumThingSize, sumArenaCapacity));
272     fprintf(fp, "   average cell utilization: %.1f%%\n",
273             PERCENT(sumTotalThingSize, sumTotalArenaCapacity));
274     fprintf(fp, "             alloc attempts: %lu\n", UL(sumAlloc));
275     fprintf(fp, "        alloc without locks: %lu  (%.1f%%)\n",
276             UL(sumLocalAlloc), PERCENT(sumLocalAlloc, sumAlloc));
277 }
278
279 void
280 DumpCompartmentStats(JSCompartment *comp, FILE *fp)
281 {
282     if (comp->rt->atomsCompartment == comp)
283         fprintf(fp, "\n**** AtomsCompartment Allocation Statistics: %p ****\n\n", (void *) comp);
284     else
285         fprintf(fp, "\n**** Compartment Allocation Statistics: %p ****\n\n", (void *) comp);
286
287     DumpArenaStats(&comp->compartmentStats[0], fp);
288 }
289
290 #endif
291
292 } //gc
293 } //js
294
295 #ifdef JS_GCMETER
296
297 JS_FRIEND_API(void)
298 js_DumpGCStats(JSRuntime *rt, FILE *fp)
299 {
300 #define ULSTAT(x)   UL(rt->gcStats.x)
301     if (JS_WANT_GC_METER_PRINT) {
302         fprintf(fp, "\n**** Global Arena Allocation Statistics: ****\n");
303         DumpArenaStats(&rt->globalArenaStats[0], fp);
304         fprintf(fp, "            bytes allocated: %lu\n", UL(rt->gcBytes));
305         fprintf(fp, "        allocation failures: %lu\n", ULSTAT(fail));
306         fprintf(fp, "         last ditch GC runs: %lu\n", ULSTAT(lastditch));
307         fprintf(fp, "           valid lock calls: %lu\n", ULSTAT(lock));
308         fprintf(fp, "         valid unlock calls: %lu\n", ULSTAT(unlock));
309         fprintf(fp, "      delayed tracing calls: %lu\n", ULSTAT(unmarked));
310 #ifdef DEBUG
311         fprintf(fp, "      max trace later count: %lu\n", ULSTAT(maxunmarked));
312 #endif
313         fprintf(fp, "potentially useful GC calls: %lu\n", ULSTAT(poke));
314         fprintf(fp, "  thing arenas freed so far: %lu\n\n", ULSTAT(afree));
315     }
316
317     if (JS_WANT_GC_PER_COMPARTMENT_PRINT)
318         for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
319             DumpCompartmentStats(*c, fp);
320     PodZero(&rt->globalArenaStats);
321     if (JS_WANT_CONSERVATIVE_GC_PRINT)
322         rt->gcStats.conservative.dump(fp);
323 #undef ULSTAT
324 }
325 #endif
326
327 namespace js {
328
329 #ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS
330 void
331 GCMarker::dumpConservativeRoots()
332 {
333     if (!conservativeDumpFileName)
334         return;
335
336     FILE *fp;
337     if (!strcmp(conservativeDumpFileName, "stdout")) {
338         fp = stdout;
339     } else if (!strcmp(conservativeDumpFileName, "stderr")) {
340         fp = stderr;
341     } else if (!(fp = fopen(conservativeDumpFileName, "aw"))) {
342         fprintf(stderr,
343                 "Warning: cannot open %s to dump the conservative roots\n",
344                 conservativeDumpFileName);
345         return;
346     }
347
348     conservativeStats.dump(fp);
349
350     for (ConservativeRoot *i = conservativeRoots.begin();
351          i != conservativeRoots.end();
352          ++i) {
353         fprintf(fp, "  %p: ", i->thing);
354         switch (GetFinalizableTraceKind(i->thingKind)) {
355           default:
356             JS_NOT_REACHED("Unknown trace kind");
357
358           case JSTRACE_OBJECT: {
359             JSObject *obj = (JSObject *) i->thing;
360             fprintf(fp, "object %s", obj->getClass()->name);
361             break;
362           }
363           case JSTRACE_STRING: {
364             JSString *str = (JSString *) i->thing;
365             if (str->isLinear()) {
366                 char buf[50];
367                 PutEscapedString(buf, sizeof buf, str->assertIsLinear(), '"');
368                 fprintf(fp, "string %s", buf);
369             } else {
370                 fprintf(fp, "rope: length %d", (int)str->length());
371             }
372             break;
373           }
374 # if JS_HAS_XML_SUPPORT
375           case JSTRACE_XML: {
376             JSXML *xml = (JSXML *) i->thing;
377             fprintf(fp, "xml %u", (unsigned)xml->xml_class);
378             break;
379           }
380 # endif
381         }
382         fputc('\n', fp);
383     }
384     fputc('\n', fp);
385
386     if (fp != stdout && fp != stderr)
387         fclose(fp);
388 }
389 #endif /* JS_DUMP_CONSERVATIVE_GC_ROOTS */
390
391 #ifdef MOZ_GCTIMER
392
393 jsrefcount newChunkCount = 0;
394 jsrefcount destroyChunkCount = 0;
395
396 GCTimer::GCTimer() {
397     getFirstEnter();
398     memset(this, 0, sizeof(GCTimer));
399     enter = rdtsc();
400 }
401
402 uint64
403 GCTimer::getFirstEnter() {
404     static uint64 firstEnter = rdtsc();
405     return firstEnter;
406 }
407
408 void
409 GCTimer::finish(bool lastGC) {
410     end = rdtsc();
411
412     if (startMark > 0) {
413         if (JS_WANT_GC_SUITE_PRINT) {
414             fprintf(stderr, "%f %f %f\n",
415                     (double)(end - enter) / 1e6,
416                     (double)(startSweep - startMark) / 1e6,
417                     (double)(sweepDestroyEnd - startSweep) / 1e6);
418         } else {
419             static FILE *gcFile;
420
421             if (!gcFile) {
422                 gcFile = fopen("gcTimer.dat", "a");
423
424                 fprintf(gcFile, "     AppTime,  Total,   Mark,  Sweep, FinObj,");
425                 fprintf(gcFile, " FinStr, SwShapes, Destroy, +Chunks, -Chunks\n");
426             }
427             JS_ASSERT(gcFile);
428             fprintf(gcFile, "%12.1f, %6.1f, %6.1f, %6.1f, %6.1f, %6.1f, %8.1f,  %6.1f, ",
429                     (double)(enter - getFirstEnter()) / 1e6,
430                     (double)(end - enter) / 1e6,
431                     (double)(startSweep - startMark) / 1e6,
432                     (double)(sweepDestroyEnd - startSweep) / 1e6,
433                     (double)(sweepObjectEnd - startSweep) / 1e6,
434                     (double)(sweepStringEnd - sweepObjectEnd) / 1e6,
435                     (double)(sweepShapeEnd - sweepStringEnd) / 1e6,
436                     (double)(sweepDestroyEnd - sweepShapeEnd) / 1e6);
437             fprintf(gcFile, "%7d, %7d \n", newChunkCount,
438                     destroyChunkCount);
439             fflush(gcFile);
440
441             if (lastGC) {
442                 fclose(gcFile);
443                 gcFile = NULL;
444             }
445         }
446     }
447     newChunkCount = 0;
448     destroyChunkCount = 0;
449 }
450
451 #endif
452
453 } //js
454
455 #undef UL
456 #undef PERCENT