summaryrefslogtreecommitdiff
path: root/js/src/vm/CodeCoverage.h
blob: 8ab6272d13db04b8b3c4cae39ffcb0cbbffc507a (plain)
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
173
174
175
176
177
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=8 sts=4 et sw=4 tw=99:
 * 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 "vm/Printer.h"

struct JSCompartment;
class JSScript;
class JSObject;

namespace js {

class ScriptSourceObject;

namespace coverage {

class LCovCompartment;

class LCovSource
{
  public:
    explicit LCovSource(LifoAlloc* alloc, JSObject* sso);

    // Whether the given script source object matches this LCovSource.
    bool match(JSObject* sso) const {
        return sso == source_;
    }

    // Whether the current source is complete and if it can be flushed.
    bool isComplete() const {
        return hasFilename_ && 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) const;

    // Write the script name in out.
    bool writeSourceFilename(ScriptSourceObject* sso);

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

  private:
    // Weak pointer of the Script Source Object used by the current source.
    JSObject *source_;

    // LifoAlloc string which hold the filename of the source.
    LSprinter outSF_;

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

    // LifoAlloc string which hold lines statistics.
    LSprinter outDA_;
    size_t numLinesInstrumented_;
    size_t numLinesHit_;

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

class LCovCompartment
{
  public:
    LCovCompartment();

    // Collect code coverage information for the given source.
    void collectCodeCoverageInfo(JSCompartment* comp, JSObject* sso, JSScript* topLevel);

    // Create an ebtry for the current ScriptSourceObject.
    void collectSourceFile(JSCompartment* comp, ScriptSourceObject* sso);

    // 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 writeCompartmentName(JSCompartment* comp);

    // Return the LCovSource entry which matches the given ScriptSourceObject.
    LCovSource* lookupOrAdd(JSCompartment* comp, JSObject* sso);

  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 compartment.
    LSprinter outTN_;

    // Vector of all sources which are used in this compartment.
    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 { return out_.isInitialized(); }

    // Write the aggregated result of the code coverage of a compartment
    // into a file.
    void writeLCovResult(LCovCompartment& comp);

  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.
    size_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