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

Generated by: LCOV version 1.13-14-ga5dd952