LCOV - code coverage report
Current view: directory - js/src/gc - Statistics.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 191 143 74.9 %
Date: 2012-06-02 Functions: 17 16 94.1 %

       1                 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2                 :  * vim: set ts=8 sw=4 et tw=78:
       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 SpiderMonkey JavaScript engine.
      18                 :  *
      19                 :  * The Initial Developer of the Original Code is
      20                 :  * the Mozilla Foundation.
      21                 :  * Portions created by the Initial Developer are Copyright (C) 2011
      22                 :  * the Initial Developer. All Rights Reserved.
      23                 :  *
      24                 :  * Contributor(s):
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      28                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : #include <stdio.h>
      41                 : #include <stdarg.h>
      42                 : 
      43                 : #include "jscntxt.h"
      44                 : #include "jscompartment.h"
      45                 : #include "jscrashformat.h"
      46                 : #include "jscrashreport.h"
      47                 : #include "jsprf.h"
      48                 : #include "jsprobes.h"
      49                 : #include "jsutil.h"
      50                 : #include "prmjtime.h"
      51                 : 
      52                 : #include "gc/Statistics.h"
      53                 : 
      54                 : namespace js {
      55                 : namespace gcstats {
      56                 : 
      57                 : static const char *
      58            4446 : ExplainReason(gcreason::Reason reason)
      59                 : {
      60            4446 :     switch (reason) {
      61                 : #define SWITCH_REASON(name)                     \
      62                 :         case gcreason::name:                    \
      63                 :           return #name;
      64            2164 :         GCREASONS(SWITCH_REASON)
      65                 : 
      66                 :         default:
      67               0 :           JS_NOT_REACHED("bad GC reason");
      68                 :           return "?";
      69                 : #undef SWITCH_REASON
      70                 :     }
      71                 : }
      72                 : 
      73                 : void
      74           98694 : Statistics::fmt(const char *f, ...)
      75                 : {
      76                 :     va_list va;
      77           98694 :     size_t off = strlen(buffer);
      78                 : 
      79           98694 :     va_start(va, f);
      80           98694 :     JS_vsnprintf(buffer + off, BUFFER_SIZE - off, f, va);
      81           98694 :     va_end(va);
      82           98694 : }
      83                 : 
      84                 : void
      85           57798 : Statistics::fmtIfNonzero(const char *name, double t)
      86                 : {
      87           57798 :     if (t) {
      88           38232 :         if (needComma)
      89           33786 :             fmt(", ");
      90           38232 :         fmt("%s: %.1f", name, t);
      91           38232 :         needComma = true;
      92                 :     }
      93           57798 : }
      94                 : 
      95                 : void
      96            4446 : Statistics::formatPhases(int64_t *times)
      97                 : {
      98            4446 :     needComma = false;
      99            4446 :     fmtIfNonzero("mark", t(times[PHASE_MARK]));
     100            4446 :     fmtIfNonzero("mark-roots", t(times[PHASE_MARK_ROOTS]));
     101            4446 :     fmtIfNonzero("mark-delayed", t(times[PHASE_MARK_DELAYED]));
     102            4446 :     fmtIfNonzero("mark-other", t(times[PHASE_MARK_OTHER]));
     103            4446 :     fmtIfNonzero("sweep", t(times[PHASE_SWEEP]));
     104            4446 :     fmtIfNonzero("sweep-obj", t(times[PHASE_SWEEP_OBJECT]));
     105            4446 :     fmtIfNonzero("sweep-string", t(times[PHASE_SWEEP_STRING]));
     106            4446 :     fmtIfNonzero("sweep-script", t(times[PHASE_SWEEP_SCRIPT]));
     107            4446 :     fmtIfNonzero("sweep-shape", t(times[PHASE_SWEEP_SHAPE]));
     108            4446 :     fmtIfNonzero("discard-code", t(times[PHASE_DISCARD_CODE]));
     109            4446 :     fmtIfNonzero("discard-analysis", t(times[PHASE_DISCARD_ANALYSIS]));
     110            4446 :     fmtIfNonzero("xpconnect", t(times[PHASE_XPCONNECT]));
     111            4446 :     fmtIfNonzero("deallocate", t(times[PHASE_DESTROY]));
     112            4446 : }
     113                 : 
     114                 : /* Except for the first and last, slices of less than 12ms are not reported. */
     115                 : static const int64_t SLICE_MIN_REPORT_TIME = 12 * PRMJ_USEC_PER_MSEC;
     116                 : 
     117                 : const char *
     118            4446 : Statistics::formatData()
     119                 : {
     120            4446 :     buffer[0] = 0x00;
     121                 : 
     122            4446 :     int64_t total = 0, longest = 0;
     123                 : 
     124            8892 :     for (SliceData *slice = slices.begin(); slice != slices.end(); slice++) {
     125            4446 :         total += slice->duration();
     126            4446 :         if (slice->duration() > longest)
     127            4446 :             longest = slice->duration();
     128                 :     }
     129                 : 
     130            4446 :     double mmu20 = computeMMU(20 * PRMJ_USEC_PER_MSEC);
     131            4446 :     double mmu50 = computeMMU(50 * PRMJ_USEC_PER_MSEC);
     132                 : 
     133            4446 :     fmt("TotalTime: %.1fms, Type: %s", t(total), compartment ? "compartment" : "global");
     134            4446 :     fmt(", MMU(20ms): %d%%, MMU(50ms): %d%%", int(mmu20 * 100), int(mmu50 * 100));
     135                 : 
     136            4446 :     if (slices.length() > 1)
     137               0 :         fmt(", MaxPause: %.1f", t(longest));
     138                 :     else
     139            4446 :         fmt(", Reason: %s", ExplainReason(slices[0].reason));
     140                 : 
     141            4446 :     if (nonincrementalReason)
     142            4446 :         fmt(", NonIncrementalReason: %s", nonincrementalReason);
     143                 : 
     144            4446 :     fmt(", +chunks: %d, -chunks: %d\n", counts[STAT_NEW_CHUNK], counts[STAT_DESTROY_CHUNK]);
     145                 : 
     146            4446 :     if (slices.length() > 1) {
     147               0 :         for (size_t i = 0; i < slices.length(); i++) {
     148               0 :             int64_t width = slices[i].duration();
     149               0 :             if (i != 0 && i != slices.length() - 1 && width < SLICE_MIN_REPORT_TIME &&
     150               0 :                 !slices[i].resetReason)
     151                 :             {
     152               0 :                 continue;
     153                 :             }
     154                 : 
     155                 :             fmt("    Slice %d @ %.1fms (Pause: %.1f, Reason: %s",
     156                 :                 i,
     157               0 :                 t(slices[i].end - slices[0].start),
     158                 :                 t(width),
     159               0 :                 ExplainReason(slices[i].reason));
     160               0 :             if (slices[i].resetReason)
     161               0 :                 fmt(", Reset: %s", slices[i].resetReason);
     162               0 :             fmt("): ");
     163               0 :             formatPhases(slices[i].phaseTimes);
     164               0 :             fmt("\n");
     165                 :         }
     166                 : 
     167               0 :         fmt("    Totals: ");
     168                 :     }
     169                 : 
     170            4446 :     formatPhases(phaseTimes);
     171            4446 :     fmt("\n");
     172                 : 
     173            4446 :     return buffer;
     174                 : }
     175                 : 
     176           19910 : Statistics::Statistics(JSRuntime *rt)
     177                 :   : runtime(rt),
     178           19910 :     startupTime(PRMJ_Now()),
     179                 :     fp(NULL),
     180                 :     fullFormat(false),
     181                 :     compartment(NULL),
     182                 :     nonincrementalReason(NULL),
     183           39820 :     needComma(false)
     184                 : {
     185           19910 :     PodArrayZero(phaseTotals);
     186           19910 :     PodArrayZero(counts);
     187                 : 
     188           19910 :     char *env = getenv("MOZ_GCTIMER");
     189           19910 :     if (!env || strcmp(env, "none") == 0) {
     190           19910 :         fp = NULL;
     191           19910 :         return;
     192                 :     }
     193                 : 
     194               0 :     if (strcmp(env, "stdout") == 0) {
     195               0 :         fullFormat = false;
     196               0 :         fp = stdout;
     197               0 :     } else if (strcmp(env, "stderr") == 0) {
     198               0 :         fullFormat = false;
     199               0 :         fp = stderr;
     200                 :     } else {
     201               0 :         fullFormat = true;
     202                 : 
     203               0 :         fp = fopen(env, "a");
     204               0 :         JS_ASSERT(fp);
     205                 :     }
     206                 : }
     207                 : 
     208           39816 : Statistics::~Statistics()
     209                 : {
     210           19908 :     if (fp) {
     211               0 :         if (fullFormat) {
     212               0 :             buffer[0] = 0x00;
     213               0 :             formatPhases(phaseTotals);
     214               0 :             fprintf(fp, "TOTALS\n%s\n\n-------\n", buffer);
     215                 :         }
     216                 : 
     217               0 :         if (fp != stdout && fp != stderr)
     218               0 :             fclose(fp);
     219                 :     }
     220           19908 : }
     221                 : 
     222                 : double
     223          121156 : Statistics::t(int64_t t)
     224                 : {
     225          121156 :     return double(t) / PRMJ_USEC_PER_MSEC;
     226                 : }
     227                 : 
     228                 : int64_t
     229           14728 : Statistics::gcDuration()
     230                 : {
     231           14728 :     return slices.back().end - slices[0].start;
     232                 : }
     233                 : 
     234                 : void
     235               0 : Statistics::printStats()
     236                 : {
     237               0 :     if (fullFormat) {
     238                 :         fprintf(fp, "GC(T+%.3fs) %s\n",
     239               0 :                 t(slices[0].start - startupTime) / 1000.0,
     240               0 :                 formatData());
     241                 :     } else {
     242                 :         fprintf(fp, "%f %f %f\n",
     243                 :                 t(gcDuration()),
     244                 :                 t(phaseTimes[PHASE_MARK]),
     245               0 :                 t(phaseTimes[PHASE_SWEEP]));
     246                 :     }
     247               0 :     fflush(fp);
     248               0 : }
     249                 : 
     250                 : void
     251           51092 : Statistics::beginGC()
     252                 : {
     253           51092 :     PodArrayZero(phaseStarts);
     254           51092 :     PodArrayZero(phaseTimes);
     255                 : 
     256           51092 :     slices.clearAndFree();
     257           51092 :     nonincrementalReason = NULL;
     258                 : 
     259           51092 :     Probes::GCStart();
     260           51092 : }
     261                 : 
     262                 : void
     263           51092 : Statistics::endGC()
     264                 : {
     265           51092 :     Probes::GCEnd();
     266           51092 :     crash::SnapshotGCStack();
     267                 : 
     268          715288 :     for (int i = 0; i < PHASE_LIMIT; i++)
     269          664196 :         phaseTotals[i] += phaseTimes[i];
     270                 : 
     271           51092 :     if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback) {
     272           14728 :         (*cb)(JS_TELEMETRY_GC_IS_COMPARTMENTAL, compartment ? 1 : 0);
     273           14728 :         (*cb)(JS_TELEMETRY_GC_MS, t(gcDuration()));
     274           14728 :         (*cb)(JS_TELEMETRY_GC_MARK_MS, t(phaseTimes[PHASE_MARK]));
     275           14728 :         (*cb)(JS_TELEMETRY_GC_SWEEP_MS, t(phaseTimes[PHASE_SWEEP]));
     276           14728 :         (*cb)(JS_TELEMETRY_GC_NON_INCREMENTAL, !!nonincrementalReason);
     277           14728 :         (*cb)(JS_TELEMETRY_GC_INCREMENTAL_DISABLED, !runtime->gcIncrementalEnabled);
     278                 : 
     279           14728 :         double mmu50 = computeMMU(50 * PRMJ_USEC_PER_MSEC);
     280           14728 :         (*cb)(JS_TELEMETRY_GC_MMU_50, mmu50 * 100);
     281                 :     }
     282                 : 
     283           51092 :     if (fp)
     284               0 :         printStats();
     285                 : 
     286           51092 :     PodArrayZero(counts);
     287           51092 : }
     288                 : 
     289                 : void
     290           51092 : Statistics::beginSlice(JSCompartment *comp, gcreason::Reason reason)
     291                 : {
     292           51092 :     compartment = comp;
     293                 : 
     294           51092 :     bool first = runtime->gcIncrementalState == gc::NO_INCREMENTAL;
     295           51092 :     if (first)
     296           51092 :         beginGC();
     297                 : 
     298           51092 :     SliceData data(reason, PRMJ_Now());
     299           51092 :     (void) slices.append(data); /* Ignore any OOMs here. */
     300                 : 
     301           51092 :     if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback)
     302           14728 :         (*cb)(JS_TELEMETRY_GC_REASON, reason);
     303                 : 
     304           51092 :     if (GCSliceCallback cb = runtime->gcSliceCallback) {
     305            4446 :         GCDescription desc(NULL, !!compartment);
     306            4446 :         (*cb)(runtime, first ? GC_CYCLE_BEGIN : GC_SLICE_BEGIN, desc);
     307                 :     }
     308           51092 : }
     309                 : 
     310                 : void
     311           51092 : Statistics::endSlice()
     312                 : {
     313           51092 :     slices.back().end = PRMJ_Now();
     314                 : 
     315           51092 :     if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback) {
     316           14728 :         (*cb)(JS_TELEMETRY_GC_SLICE_MS, t(slices.back().end - slices.back().start));
     317           14728 :         (*cb)(JS_TELEMETRY_GC_RESET, !!slices.back().resetReason);
     318                 :     }
     319                 : 
     320           51092 :     bool last = runtime->gcIncrementalState == gc::NO_INCREMENTAL;
     321           51092 :     if (last)
     322           51092 :         endGC();
     323                 : 
     324           51092 :     if (GCSliceCallback cb = runtime->gcSliceCallback) {
     325            4446 :         if (last)
     326            4446 :             (*cb)(runtime, GC_CYCLE_END, GCDescription(formatData(), !!compartment));
     327                 :         else
     328               0 :             (*cb)(runtime, GC_SLICE_END, GCDescription(NULL, !!compartment));
     329                 :     }
     330           51092 : }
     331                 : 
     332                 : void
     333          858024 : Statistics::beginPhase(Phase phase)
     334                 : {
     335          858024 :     phaseStarts[phase] = PRMJ_Now();
     336                 : 
     337          858024 :     if (phase == gcstats::PHASE_MARK)
     338          153276 :         Probes::GCStartMarkPhase();
     339          704748 :     else if (phase == gcstats::PHASE_SWEEP)
     340           51092 :         Probes::GCStartSweepPhase();
     341          858024 : }
     342                 : 
     343                 : void
     344          858024 : Statistics::endPhase(Phase phase)
     345                 : {
     346          858024 :     int64_t now = PRMJ_Now();
     347          858024 :     int64_t t = now - phaseStarts[phase];
     348          858024 :     slices.back().phaseTimes[phase] += t;
     349          858024 :     phaseTimes[phase] += t;
     350                 : 
     351          858024 :     if (phase == gcstats::PHASE_MARK)
     352          153276 :         Probes::GCEndMarkPhase();
     353          704748 :     else if (phase == gcstats::PHASE_SWEEP)
     354           51092 :         Probes::GCEndSweepPhase();
     355          858024 : }
     356                 : 
     357                 : /*
     358                 :  * MMU (minimum mutator utilization) is a measure of how much garbage collection
     359                 :  * is affecting the responsiveness of the system. MMU measurements are given
     360                 :  * with respect to a certain window size. If we report MMU(50ms) = 80%, then
     361                 :  * that means that, for any 50ms window of time, at least 80% of the window is
     362                 :  * devoted to the mutator. In other words, the GC is running for at most 20% of
     363                 :  * the window, or 10ms. The GC can run multiple slices during the 50ms window
     364                 :  * as long as the total time it spends is at most 10ms.
     365                 :  */
     366                 : double
     367           23620 : Statistics::computeMMU(int64_t window)
     368                 : {
     369           23620 :     JS_ASSERT(!slices.empty());
     370                 : 
     371           23620 :     int64_t gc = slices[0].end - slices[0].start;
     372           23620 :     int64_t gcMax = gc;
     373                 : 
     374           23620 :     if (gc >= window)
     375            3021 :         return 0.0;
     376                 : 
     377           20599 :     int startIndex = 0;
     378           20599 :     for (size_t endIndex = 1; endIndex < slices.length(); endIndex++) {
     379               0 :         gc += slices[endIndex].end - slices[endIndex].start;
     380                 : 
     381               0 :         while (slices[endIndex].end - slices[startIndex].end >= window) {
     382               0 :             gc -= slices[startIndex].end - slices[startIndex].start;
     383               0 :             startIndex++;
     384                 :         }
     385                 : 
     386               0 :         int64_t cur = gc;
     387               0 :         if (slices[endIndex].end - slices[startIndex].start > window)
     388               0 :             cur -= (slices[endIndex].end - slices[startIndex].start - window);
     389               0 :         if (cur > gcMax)
     390               0 :             gcMax = cur;
     391                 :     }
     392                 : 
     393           20599 :     return double(window - gcMax) / window;
     394                 : }
     395                 : 
     396                 : } /* namespace gcstats */
     397                 : } /* namespace js */

Generated by: LCOV version 1.7