mozjs_sys 0.67.1

System crate for the Mozilla SpiderMonkey JavaScript engine.
/* -*- 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();

  // Check if we should collect code coverage information.
  bool isEnabled() const {
    static bool isEnabled_ = ([]() {
      const char* outDir = getenv("JS_CODE_COVERAGE_OUTPUT_DIR");
      return outDir && *outDir != 0;
    })();
    return isEnabled_;
  }

  // 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_;
};

}  // namespace coverage
}  // namespace js

#endif  // vm_Printer_h