LCOV - code coverage report
Current view: top level - js/src/vm - CodeCoverage.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 15 312 4.8 %
Date: 2017-07-14 16:53:18 Functions: 4 17 23.5 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : #include "vm/CodeCoverage.h"
       8             : 
       9             : #include "mozilla/Atomics.h"
      10             : #include "mozilla/IntegerPrintfMacros.h"
      11             : 
      12             : #include <stdio.h>
      13             : #ifdef XP_WIN
      14             : #include <process.h>
      15             : #define getpid _getpid
      16             : #else
      17             : #include <unistd.h>
      18             : #endif
      19             : 
      20             : #include "jscompartment.h"
      21             : #include "jsopcode.h"
      22             : #include "jsprf.h"
      23             : #include "jsscript.h"
      24             : 
      25             : #include "vm/Runtime.h"
      26             : #include "vm/Time.h"
      27             : 
      28             : // This file contains a few functions which are used to produce files understood
      29             : // by lcov tools. A detailed description of the format is available in the man
      30             : // page for "geninfo" [1].  To make it short, the following paraphrases what is
      31             : // commented in the man page by using curly braces prefixed by for-each to
      32             : // express repeated patterns.
      33             : //
      34             : //   TN:<compartment name>
      35             : //   for-each <source file> {
      36             : //     SN:<filename>
      37             : //     for-each <script> {
      38             : //       FN:<line>,<name>
      39             : //     }
      40             : //     for-each <script> {
      41             : //       FNDA:<hits>,<name>
      42             : //     }
      43             : //     FNF:<number of scripts>
      44             : //     FNH:<sum of scripts hits>
      45             : //     for-each <script> {
      46             : //       for-each <branch> {
      47             : //         BRDA:<line>,<block id>,<target id>,<taken>
      48             : //       }
      49             : //     }
      50             : //     BRF:<number of branches>
      51             : //     BRH:<sum of branches hits>
      52             : //     for-each <script> {
      53             : //       for-each <line> {
      54             : //         DA:<line>,<hits>
      55             : //       }
      56             : //     }
      57             : //     LF:<number of lines>
      58             : //     LH:<sum of lines hits>
      59             : //   }
      60             : //
      61             : // [1] http://ltp.sourceforge.net/coverage/lcov/geninfo.1.php
      62             : //
      63             : namespace js {
      64             : namespace coverage {
      65             : 
      66           0 : LCovSource::LCovSource(LifoAlloc* alloc, const char* name)
      67             :   : name_(name),
      68             :     outFN_(alloc),
      69             :     outFNDA_(alloc),
      70             :     numFunctionsFound_(0),
      71             :     numFunctionsHit_(0),
      72             :     outBRDA_(alloc),
      73             :     numBranchesFound_(0),
      74             :     numBranchesHit_(0),
      75             :     outDA_(alloc),
      76             :     numLinesInstrumented_(0),
      77             :     numLinesHit_(0),
      78           0 :     hasTopLevelScript_(false)
      79             : {
      80           0 : }
      81             : 
      82           0 : LCovSource::LCovSource(LCovSource&& src)
      83           0 :   : name_(src.name_),
      84             :     outFN_(src.outFN_),
      85             :     outFNDA_(src.outFNDA_),
      86           0 :     numFunctionsFound_(src.numFunctionsFound_),
      87           0 :     numFunctionsHit_(src.numFunctionsHit_),
      88             :     outBRDA_(src.outBRDA_),
      89           0 :     numBranchesFound_(src.numBranchesFound_),
      90           0 :     numBranchesHit_(src.numBranchesHit_),
      91             :     outDA_(src.outDA_),
      92           0 :     numLinesInstrumented_(src.numLinesInstrumented_),
      93           0 :     numLinesHit_(src.numLinesHit_),
      94           0 :     hasTopLevelScript_(src.hasTopLevelScript_)
      95             : {
      96           0 :     src.name_ = nullptr;
      97           0 : }
      98             : 
      99           0 : LCovSource::~LCovSource()
     100             : {
     101           0 :     js_delete(name_);
     102           0 : }
     103             : 
     104             : void
     105           0 : LCovSource::exportInto(GenericPrinter& out) const
     106             : {
     107             :     // Only write if everything got recorded.
     108           0 :     if (!hasTopLevelScript_)
     109           0 :         return;
     110             : 
     111           0 :     out.printf("SF:%s\n", name_);
     112             : 
     113           0 :     outFN_.exportInto(out);
     114           0 :     outFNDA_.exportInto(out);
     115           0 :     out.printf("FNF:%" PRIuSIZE "\n", numFunctionsFound_);
     116           0 :     out.printf("FNH:%" PRIuSIZE "\n", numFunctionsHit_);
     117             : 
     118           0 :     outBRDA_.exportInto(out);
     119           0 :     out.printf("BRF:%" PRIuSIZE "\n", numBranchesFound_);
     120           0 :     out.printf("BRH:%" PRIuSIZE "\n", numBranchesHit_);
     121             : 
     122           0 :     outDA_.exportInto(out);
     123           0 :     out.printf("LF:%" PRIuSIZE "\n", numLinesInstrumented_);
     124           0 :     out.printf("LH:%" PRIuSIZE "\n", numLinesHit_);
     125             : 
     126           0 :     out.put("end_of_record\n");
     127             : }
     128             : 
     129             : bool
     130           0 : LCovSource::writeScriptName(LSprinter& out, JSScript* script)
     131             : {
     132           0 :     JSFunction* fun = script->functionNonDelazifying();
     133           0 :     if (fun && fun->displayAtom())
     134           0 :         return EscapedStringPrinter(out, fun->displayAtom(), 0);
     135           0 :     out.printf("top-level");
     136           0 :     return true;
     137             : }
     138             : 
     139             : bool
     140           0 : LCovSource::writeScript(JSScript* script)
     141             : {
     142           0 :     numFunctionsFound_++;
     143           0 :     outFN_.printf("FN:%" PRIuSIZE ",", script->lineno());
     144           0 :     if (!writeScriptName(outFN_, script))
     145           0 :         return false;
     146           0 :     outFN_.put("\n", 1);
     147             : 
     148           0 :     uint64_t hits = 0;
     149           0 :     ScriptCounts* sc = nullptr;
     150           0 :     if (script->hasScriptCounts()) {
     151           0 :         sc = &script->getScriptCounts();
     152           0 :         numFunctionsHit_++;
     153           0 :         const PCCounts* counts = sc->maybeGetPCCounts(script->pcToOffset(script->main()));
     154           0 :         outFNDA_.printf("FNDA:%" PRIu64 ",", counts->numExec());
     155           0 :         if (!writeScriptName(outFNDA_, script))
     156           0 :             return false;
     157           0 :         outFNDA_.put("\n", 1);
     158             : 
     159             :         // Set the hit count of the pre-main code to 1, if the function ever got
     160             :         // visited.
     161           0 :         hits = 1;
     162             :     }
     163             : 
     164           0 :     jsbytecode* snpc = script->code();
     165           0 :     jssrcnote* sn = script->notes();
     166           0 :     if (!SN_IS_TERMINATOR(sn))
     167           0 :         snpc += SN_DELTA(sn);
     168             : 
     169           0 :     size_t lineno = script->lineno();
     170           0 :     jsbytecode* end = script->codeEnd();
     171           0 :     size_t branchId = 0;
     172           0 :     size_t tableswitchExitOffset = 0;
     173           0 :     for (jsbytecode* pc = script->code(); pc != end; pc = GetNextPc(pc)) {
     174           0 :         JSOp op = JSOp(*pc);
     175           0 :         bool jump = IsJumpOpcode(op) || op == JSOP_TABLESWITCH;
     176           0 :         bool fallsthrough = BytecodeFallsThrough(op) && op != JSOP_GOSUB;
     177             : 
     178             :         // If the current script & pc has a hit-count report, then update the
     179             :         // current number of hits.
     180           0 :         if (sc) {
     181           0 :             const PCCounts* counts = sc->maybeGetPCCounts(script->pcToOffset(pc));
     182           0 :             if (counts)
     183           0 :                 hits = counts->numExec();
     184             :         }
     185             : 
     186             :         // If we have additional source notes, walk all the source notes of the
     187             :         // current pc.
     188           0 :         if (snpc <= pc) {
     189           0 :             size_t oldLine = lineno;
     190           0 :             while (!SN_IS_TERMINATOR(sn) && snpc <= pc) {
     191           0 :                 SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
     192           0 :                 if (type == SRC_SETLINE)
     193           0 :                     lineno = size_t(GetSrcNoteOffset(sn, 0));
     194           0 :                 else if (type == SRC_NEWLINE)
     195           0 :                     lineno++;
     196           0 :                 else if (type == SRC_TABLESWITCH)
     197           0 :                     tableswitchExitOffset = GetSrcNoteOffset(sn, 0);
     198             : 
     199           0 :                 sn = SN_NEXT(sn);
     200           0 :                 snpc += SN_DELTA(sn);
     201             :             }
     202             : 
     203           0 :             if (oldLine != lineno && fallsthrough) {
     204           0 :                 outDA_.printf("DA:%" PRIuSIZE ",%" PRIu64 "\n", lineno, hits);
     205             : 
     206             :                 // Count the number of lines instrumented & hit.
     207           0 :                 numLinesInstrumented_++;
     208           0 :                 if (hits)
     209           0 :                     numLinesHit_++;
     210             :             }
     211             :         }
     212             : 
     213             :         // If the current instruction has thrown, then decrement the hit counts
     214             :         // with the number of throws.
     215           0 :         if (sc) {
     216           0 :             const PCCounts* counts = sc->maybeGetThrowCounts(script->pcToOffset(pc));
     217           0 :             if (counts)
     218           0 :                 hits -= counts->numExec();
     219             :         }
     220             : 
     221             :         // If the current pc corresponds to a conditional jump instruction, then reports
     222             :         // branch hits.
     223           0 :         if (jump && fallsthrough) {
     224           0 :             jsbytecode* fallthroughTarget = GetNextPc(pc);
     225           0 :             uint64_t fallthroughHits = 0;
     226           0 :             if (sc) {
     227           0 :                 const PCCounts* counts = sc->maybeGetPCCounts(script->pcToOffset(fallthroughTarget));
     228           0 :                 if (counts)
     229           0 :                     fallthroughHits = counts->numExec();
     230             :             }
     231             : 
     232           0 :             uint64_t taken = hits - fallthroughHits;
     233           0 :             outBRDA_.printf("BRDA:%" PRIuSIZE ",%" PRIuSIZE ",0,", lineno, branchId);
     234           0 :             if (taken)
     235           0 :                 outBRDA_.printf("%" PRIu64 "\n", taken);
     236             :             else
     237           0 :                 outBRDA_.put("-\n", 2);
     238             : 
     239           0 :             outBRDA_.printf("BRDA:%" PRIuSIZE ",%" PRIuSIZE ",1,", lineno, branchId);
     240           0 :             if (fallthroughHits)
     241           0 :                 outBRDA_.printf("%" PRIu64 "\n", fallthroughHits);
     242             :             else
     243           0 :                 outBRDA_.put("-\n", 2);
     244             : 
     245             :             // Count the number of branches, and the number of branches hit.
     246           0 :             numBranchesFound_ += 2;
     247           0 :             if (hits)
     248           0 :                 numBranchesHit_ += !!taken + !!fallthroughHits;
     249           0 :             branchId++;
     250             :         }
     251             : 
     252             :         // If the current pc corresponds to a pre-computed switch case, then
     253             :         // reports branch hits for each case statement.
     254           0 :         if (jump && op == JSOP_TABLESWITCH) {
     255           0 :             MOZ_ASSERT(tableswitchExitOffset != 0);
     256             : 
     257             :             // Get the default and exit pc
     258           0 :             jsbytecode* exitpc = pc + tableswitchExitOffset;
     259           0 :             jsbytecode* defaultpc = pc + GET_JUMP_OFFSET(pc);
     260           0 :             MOZ_ASSERT(defaultpc > pc && defaultpc <= exitpc);
     261             : 
     262             :             // Get the low and high from the tableswitch
     263           0 :             int32_t low = GET_JUMP_OFFSET(pc + JUMP_OFFSET_LEN * 1);
     264           0 :             int32_t high = GET_JUMP_OFFSET(pc + JUMP_OFFSET_LEN * 2);
     265           0 :             MOZ_ASSERT(high - low + 1 >= 0);
     266           0 :             size_t numCases = high - low + 1;
     267           0 :             jsbytecode* jumpTable = pc + JUMP_OFFSET_LEN * 3;
     268             : 
     269           0 :             jsbytecode* firstcasepc = exitpc;
     270           0 :             for (size_t j = 0; j < numCases; j++) {
     271           0 :                 jsbytecode* testpc = pc + GET_JUMP_OFFSET(jumpTable + JUMP_OFFSET_LEN * j);
     272           0 :                 if (testpc < firstcasepc)
     273           0 :                     firstcasepc = testpc;
     274             :             }
     275             : 
     276             :             // Count the number of hits of the default branch, by subtracting
     277             :             // the number of hits of each cases.
     278           0 :             uint64_t defaultHits = hits;
     279             : 
     280             :             // Count the number of hits of the previous case entry.
     281           0 :             uint64_t fallsThroughHits = 0;
     282             : 
     283             :             // Record branches for each cases.
     284           0 :             size_t caseId = 0;
     285           0 :             for (size_t i = 0; i < numCases; i++) {
     286           0 :                 jsbytecode* casepc = pc + GET_JUMP_OFFSET(jumpTable + JUMP_OFFSET_LEN * i);
     287             :                 // The case is not present, and jumps to the default pc if used.
     288           0 :                 if (casepc == pc)
     289           0 :                     continue;
     290             : 
     291             :                 // PCs might not be in increasing order of case indexes.
     292           0 :                 jsbytecode* lastcasepc = firstcasepc - 1;
     293           0 :                 for (size_t j = 0; j < numCases; j++) {
     294           0 :                     jsbytecode* testpc = pc + GET_JUMP_OFFSET(jumpTable + JUMP_OFFSET_LEN * j);
     295           0 :                     if (lastcasepc < testpc && (testpc < casepc || (j < i && testpc == casepc)))
     296           0 :                         lastcasepc = testpc;
     297             :                 }
     298             : 
     299           0 :                 if (casepc != lastcasepc) {
     300             :                     // Case (i + low)
     301           0 :                     uint64_t caseHits = 0;
     302           0 :                     if (sc) {
     303           0 :                         const PCCounts* counts = sc->maybeGetPCCounts(script->pcToOffset(casepc));
     304           0 :                         if (counts)
     305           0 :                             caseHits = counts->numExec();
     306             : 
     307             :                         // Remove fallthrough.
     308           0 :                         fallsThroughHits = 0;
     309           0 :                         if (casepc != firstcasepc) {
     310           0 :                             jsbytecode* endpc = lastcasepc;
     311           0 :                             while (GetNextPc(endpc) < casepc)
     312           0 :                                 endpc = GetNextPc(endpc);
     313             : 
     314           0 :                             if (BytecodeFallsThrough(JSOp(*endpc)))
     315           0 :                                 fallsThroughHits = script->getHitCount(endpc);
     316             :                         }
     317             : 
     318           0 :                         caseHits -= fallsThroughHits;
     319             :                     }
     320             : 
     321           0 :                     outBRDA_.printf("BRDA:%" PRIuSIZE ",%" PRIuSIZE ",%" PRIuSIZE ",",
     322           0 :                                     lineno, branchId, caseId);
     323           0 :                     if (caseHits)
     324           0 :                         outBRDA_.printf("%" PRIu64 "\n", caseHits);
     325             :                     else
     326           0 :                         outBRDA_.put("-\n", 2);
     327             : 
     328           0 :                     numBranchesFound_++;
     329           0 :                     numBranchesHit_ += !!caseHits;
     330           0 :                     defaultHits -= caseHits;
     331           0 :                     caseId++;
     332             :                 }
     333             :             }
     334             : 
     335             :             // Compute the number of hits of the default branch, if it has its
     336             :             // own case clause.
     337           0 :             bool defaultHasOwnClause = true;
     338           0 :             if (defaultpc != exitpc) {
     339           0 :                 defaultHits = 0;
     340             : 
     341             :                 // Look for the last case entry before the default pc.
     342           0 :                 jsbytecode* lastcasepc = firstcasepc - 1;
     343           0 :                 for (size_t j = 0; j < numCases; j++) {
     344           0 :                     jsbytecode* testpc = pc + GET_JUMP_OFFSET(jumpTable + JUMP_OFFSET_LEN * j);
     345           0 :                     if (lastcasepc < testpc && testpc <= defaultpc)
     346           0 :                         lastcasepc = testpc;
     347             :                 }
     348             : 
     349           0 :                 if (lastcasepc == defaultpc)
     350           0 :                     defaultHasOwnClause = false;
     351             : 
     352             :                 // Look if the last case entry fallthrough to the default case,
     353             :                 // in which case we have to remove the number of fallthrough
     354             :                 // hits out of the default case hits.
     355           0 :                 if (sc && lastcasepc != pc) {
     356           0 :                     jsbytecode* endpc = lastcasepc;
     357           0 :                     while (GetNextPc(endpc) < defaultpc)
     358           0 :                         endpc = GetNextPc(endpc);
     359             : 
     360           0 :                     if (BytecodeFallsThrough(JSOp(*endpc)))
     361           0 :                         fallsThroughHits = script->getHitCount(endpc);
     362             :                 }
     363             : 
     364           0 :                 if (sc) {
     365           0 :                     const PCCounts* counts = sc->maybeGetPCCounts(script->pcToOffset(defaultpc));
     366           0 :                     if (counts)
     367           0 :                         defaultHits = counts->numExec();
     368             :                 }
     369           0 :                 defaultHits -= fallsThroughHits;
     370             :             }
     371             : 
     372           0 :             if (defaultHasOwnClause) {
     373           0 :                 outBRDA_.printf("BRDA:%" PRIuSIZE ",%" PRIuSIZE ",%" PRIuSIZE ",",
     374           0 :                                 lineno, branchId, caseId);
     375           0 :                 if (defaultHits)
     376           0 :                     outBRDA_.printf("%" PRIu64 "\n", defaultHits);
     377             :                 else
     378           0 :                     outBRDA_.put("-\n", 2);
     379           0 :                 numBranchesFound_++;
     380           0 :                 numBranchesHit_ += !!defaultHits;
     381             :             }
     382             : 
     383             :             // Increment the branch identifier, and go to the next instruction.
     384           0 :             branchId++;
     385           0 :             tableswitchExitOffset = 0;
     386             :         }
     387             :     }
     388             : 
     389             :     // Report any new OOM.
     390           0 :     if (outFN_.hadOutOfMemory() ||
     391           0 :         outFNDA_.hadOutOfMemory() ||
     392           0 :         outBRDA_.hadOutOfMemory() ||
     393           0 :         outDA_.hadOutOfMemory())
     394             :     {
     395           0 :         return false;
     396             :     }
     397             : 
     398             :     // If this script is the top-level script, then record it such that we can
     399             :     // assume that the code coverage report is complete, as this script has
     400             :     // references on all inner scripts.
     401           0 :     if (script->isTopLevel())
     402           0 :         hasTopLevelScript_ = true;
     403             : 
     404           0 :     return true;
     405             : }
     406             : 
     407         315 : LCovCompartment::LCovCompartment()
     408             :   : alloc_(4096),
     409             :     outTN_(&alloc_),
     410         315 :     sources_(nullptr)
     411             : {
     412         315 :     MOZ_ASSERT(alloc_.isEmpty());
     413         315 : }
     414             : 
     415             : void
     416           0 : LCovCompartment::collectCodeCoverageInfo(JSCompartment* comp, JSScript* script, const char* name)
     417             : {
     418             :     // Skip any operation if we already some out-of memory issues.
     419           0 :     if (outTN_.hadOutOfMemory())
     420           0 :         return;
     421             : 
     422           0 :     if (!script->code())
     423           0 :         return;
     424             : 
     425             :     // Get the existing source LCov summary, or create a new one.
     426           0 :     LCovSource* source = lookupOrAdd(comp, name);
     427           0 :     if (!source)
     428           0 :         return;
     429             : 
     430             :     // Write code coverage data into the LCovSource.
     431           0 :     if (!source->writeScript(script)) {
     432           0 :         outTN_.reportOutOfMemory();
     433           0 :         return;
     434             :     }
     435             : }
     436             : 
     437             : LCovSource*
     438           0 : LCovCompartment::lookupOrAdd(JSCompartment* comp, const char* name)
     439             : {
     440             :     // On the first call, write the compartment name, and allocate a LCovSource
     441             :     // vector in the LifoAlloc.
     442           0 :     if (!sources_) {
     443           0 :         if (!writeCompartmentName(comp))
     444           0 :             return nullptr;
     445             : 
     446           0 :         LCovSourceVector* raw = alloc_.pod_malloc<LCovSourceVector>();
     447           0 :         if (!raw) {
     448           0 :             outTN_.reportOutOfMemory();
     449           0 :             return nullptr;
     450             :         }
     451             : 
     452           0 :         sources_ = new(raw) LCovSourceVector(alloc_);
     453             :     } else {
     454             :         // Find the first matching source.
     455           0 :         for (LCovSource& source : *sources_) {
     456           0 :             if (source.match(name))
     457           0 :                 return &source;
     458             :         }
     459             :     }
     460             : 
     461           0 :     char* source_name = js_strdup(name);
     462           0 :     if (!source_name) {
     463           0 :         outTN_.reportOutOfMemory();
     464           0 :         return nullptr;
     465             :     }
     466             : 
     467             :     // Allocate a new LCovSource for the current top-level.
     468           0 :     if (!sources_->append(Move(LCovSource(&alloc_, source_name)))) {
     469           0 :         outTN_.reportOutOfMemory();
     470           0 :         return nullptr;
     471             :     }
     472             : 
     473           0 :     return &sources_->back();
     474             : }
     475             : 
     476             : void
     477           0 : LCovCompartment::exportInto(GenericPrinter& out, bool* isEmpty) const
     478             : {
     479           0 :     if (!sources_ || outTN_.hadOutOfMemory())
     480           0 :         return;
     481             : 
     482             :     // If we only have cloned function, then do not serialize anything.
     483           0 :     bool someComplete = false;
     484           0 :     for (const LCovSource& sc : *sources_) {
     485           0 :         if (sc.isComplete()) {
     486           0 :             someComplete = true;
     487           0 :             break;
     488             :         };
     489             :     }
     490             : 
     491           0 :     if (!someComplete)
     492           0 :         return;
     493             : 
     494           0 :     *isEmpty = false;
     495           0 :     outTN_.exportInto(out);
     496           0 :     for (const LCovSource& sc : *sources_) {
     497           0 :         if (sc.isComplete())
     498           0 :             sc.exportInto(out);
     499             :     }
     500             : }
     501             : 
     502             : bool
     503           0 : LCovCompartment::writeCompartmentName(JSCompartment* comp)
     504             : {
     505           0 :     JSContext* cx = TlsContext.get();
     506             : 
     507             :     // lcov trace files are starting with an optional test case name, that we
     508             :     // recycle to be a compartment name.
     509             :     //
     510             :     // Note: The test case name has some constraint in terms of valid character,
     511             :     // thus we escape invalid chracters with a "_" symbol in front of its
     512             :     // hexadecimal code.
     513           0 :     outTN_.put("TN:");
     514           0 :     if (cx->runtime()->compartmentNameCallback) {
     515             :         char name[1024];
     516             :         {
     517             :             // Hazard analysis cannot tell that the callback does not GC.
     518           0 :             JS::AutoSuppressGCAnalysis nogc;
     519           0 :             (*cx->runtime()->compartmentNameCallback)(cx, comp, name, sizeof(name));
     520             :         }
     521           0 :         for (char *s = name; s < name + sizeof(name) && *s; s++) {
     522           0 :             if (('a' <= *s && *s <= 'z') ||
     523           0 :                 ('A' <= *s && *s <= 'Z') ||
     524           0 :                 ('0' <= *s && *s <= '9'))
     525             :             {
     526           0 :                 outTN_.put(s, 1);
     527           0 :                 continue;
     528             :             }
     529           0 :             outTN_.printf("_%p", (void*) size_t(*s));
     530             :         }
     531           0 :         outTN_.put("\n", 1);
     532             :     } else {
     533           0 :         outTN_.printf("Compartment_%p%p\n", (void*) size_t('_'), comp);
     534             :     }
     535             : 
     536           0 :     return !outTN_.hadOutOfMemory();
     537             : }
     538             : 
     539           4 : LCovRuntime::LCovRuntime()
     540             :   : out_(),
     541           4 :     pid_(getpid()),
     542           8 :     isEmpty_(false)
     543             : {
     544           4 : }
     545             : 
     546           0 : LCovRuntime::~LCovRuntime()
     547             : {
     548           0 :     if (out_.isInitialized())
     549           0 :         finishFile();
     550           0 : }
     551             : 
     552             : bool
     553           4 : LCovRuntime::fillWithFilename(char *name, size_t length)
     554             : {
     555           4 :     const char* outDir = getenv("JS_CODE_COVERAGE_OUTPUT_DIR");
     556           4 :     if (!outDir || *outDir == 0)
     557           4 :         return false;
     558             : 
     559           0 :     int64_t timestamp = static_cast<double>(PRMJ_Now()) / PRMJ_USEC_PER_SEC;
     560             :     static mozilla::Atomic<size_t> globalRuntimeId(0);
     561           0 :     size_t rid = globalRuntimeId++;
     562             : 
     563           0 :     int len = snprintf(name, length, "%s/%" PRId64 "-%" PRIu32 "-%" PRIuSIZE ".info",
     564           0 :                        outDir, timestamp, pid_, rid);
     565           0 :     if (len < 0 || size_t(len) >= length) {
     566           0 :         fprintf(stderr, "Warning: LCovRuntime::init: Cannot serialize file name.");
     567           0 :         return false;
     568             :     }
     569             : 
     570           0 :     return true;
     571             : }
     572             : 
     573             : void
     574           4 : LCovRuntime::init()
     575             : {
     576             :     char name[1024];
     577           4 :     if (!fillWithFilename(name, sizeof(name)))
     578           4 :         return;
     579             : 
     580             :     // If we cannot open the file, report a warning.
     581           0 :     if (!out_.init(name))
     582           0 :         fprintf(stderr, "Warning: LCovRuntime::init: Cannot open file named '%s'.", name);
     583           0 :     isEmpty_ = true;
     584             : }
     585             : 
     586             : void
     587           0 : LCovRuntime::finishFile()
     588             : {
     589           0 :     MOZ_ASSERT(out_.isInitialized());
     590           0 :     out_.finish();
     591             : 
     592           0 :     if (isEmpty_) {
     593             :         char name[1024];
     594           0 :         if (!fillWithFilename(name, sizeof(name)))
     595           0 :             return;
     596           0 :         remove(name);
     597             :     }
     598             : }
     599             : 
     600             : void
     601           0 : LCovRuntime::writeLCovResult(LCovCompartment& comp)
     602             : {
     603           0 :     if (!out_.isInitialized())
     604           0 :         return;
     605             : 
     606           0 :     uint32_t p = getpid();
     607           0 :     if (pid_ != p) {
     608           0 :         pid_ = p;
     609           0 :         finishFile();
     610           0 :         init();
     611           0 :         if (!out_.isInitialized())
     612           0 :             return;
     613             :     }
     614             : 
     615           0 :     comp.exportInto(out_, &isEmpty_);
     616           0 :     out_.flush();
     617             : }
     618             : 
     619             : } // namespace coverage
     620             : } // namespace js

Generated by: LCOV version 1.13