DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Implementation

Mercurial (b6d82b1a6b02)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef vm_CodeCoverage_h
#define vm_CodeCoverage_h

#include "mozilla/Vector.h"

#include "ds/LifoAlloc.h"

#include "js/HashTable.h"
#include "js/TypeDecls.h"
#include "js/Utility.h"

#include "vm/Printer.h"

namespace js {

class ScriptSourceObject;

namespace coverage {

class LCovSource {
 public:
  LCovSource(LifoAlloc* alloc, JS::UniqueChars name);
  LCovSource(LCovSource&& src);
  ~LCovSource() = default;

  // Whether the given script name matches this LCovSource.
  bool match(const char* name) const { return strcmp(name_.get(), name) == 0; }

  // Whether the current source is complete and if it can be flushed.
  bool isComplete() const { return hasTopLevelScript_; }

  // Iterate over the bytecode and collect the lcov output based on the
  // ScriptCounts counters.
  bool writeScript(JSScript* script);

  // Write the Lcov output in a buffer, such as the one associated with
  // the runtime code coverage trace file.
  void exportInto(GenericPrinter& out);

 private:
  // Write the script name in out.
  bool writeScriptName(LSprinter& out, JSScript* script);

 private:
  // Name of the source file.
  JS::UniqueChars name_;

  // LifoAlloc strings which hold the filename of each function as
  // well as the number of hits for each function.
  LSprinter outFN_;
  LSprinter outFNDA_;
  size_t numFunctionsFound_;
  size_t numFunctionsHit_;

  // LifoAlloc string which hold branches statistics.
  LSprinter outBRDA_;
  size_t numBranchesFound_;
  size_t numBranchesHit_;

  // Holds lines statistics. When processing a line hit count, the hit count
  // is added to any hit count already in the hash map so that we handle
  // lines that belong to more than one JSScript or function in the same
  // source file.
  HashMap<size_t, uint64_t, DefaultHasher<size_t>, SystemAllocPolicy> linesHit_;
  size_t numLinesInstrumented_;
  size_t numLinesHit_;
  size_t maxLineHit_;

  // Status flags.
  bool hasTopLevelScript_ : 1;
};

class LCovRealm {
 public:
  LCovRealm();
  ~LCovRealm();

  // Collect code coverage information for the given source.
  void collectCodeCoverageInfo(JS::Realm* realm, JSScript* topLevel,
                               const char* name);

  // Write the Lcov output in a buffer, such as the one associated with
  // the runtime code coverage trace file.
  void exportInto(GenericPrinter& out, bool* isEmpty) const;

 private:
  // Write the script name in out.
  bool writeRealmName(JS::Realm* realm);

  // Return the LCovSource entry which matches the given ScriptSourceObject.
  LCovSource* lookupOrAdd(JS::Realm* realm, const char* name);

 private:
  typedef mozilla::Vector<LCovSource, 16, LifoAllocPolicy<Fallible>>
      LCovSourceVector;

  // LifoAlloc backend for all temporary allocations needed to stash the
  // strings to be written in the file.
  LifoAlloc alloc_;

  // LifoAlloc string which hold the name of the realm.
  LSprinter outTN_;

  // Vector of all sources which are used in this realm.
  LCovSourceVector* sources_;
};

class LCovRuntime {
 public:
  LCovRuntime();
  ~LCovRuntime();

  // If the environment variable JS_CODE_COVERAGE_OUTPUT_DIR is set to a
  // directory, create a file inside this directory which uses the process
  // ID, the thread ID and a timestamp to ensure the uniqueness of the
  // file.
  //
  // At the end of the execution, this file should contains the LCOV output of
  // all the scripts executed in the current JSRuntime.
  void init();

  // Write the aggregated result of the code coverage of a realm
  // into a file.
  void writeLCovResult(LCovRealm& realm);

 private:
  // When a process forks, the file will remain open, but 2 processes will
  // have the same file. To avoid conflicting writes, we open a new file for
  // the child process.
  void maybeReopenAfterFork();

  // Fill an array with the name of the file. Return false if we are unable to
  // serialize the filename in this array.
  bool fillWithFilename(char* name, size_t length);

  // Finish the current opened file, and remove if it does not have any
  // content.
  void finishFile();

 private:
  // Output file which is created if code coverage is enabled.
  Fprinter out_;

  // The process' PID is used to watch for fork. When the process fork,
  // we want to close the current file and open a new one.
  uint32_t pid_;

  // Flag used to report if the generated file is empty or not. If it is empty
  // when the runtime is destroyed, then the file would be removed as an empty
  // file is not a valid LCov file.
  bool isEmpty_;
};

extern void InitLCov();

extern void EnableLCov();

inline bool IsLCovEnabled() {
  extern bool gLCovIsEnabled;
  return gLCovIsEnabled;
}

}  // namespace coverage
}  // namespace js

#endif  // vm_Printer_h