Coverage Report

Created: 2018-09-25 14:53

/work/obj-fuzz/dist/include/js/ProfilingFrameIterator.h
Line
Count
Source (jump to first uncovered line)
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
#ifndef js_ProfilingFrameIterator_h
8
#define js_ProfilingFrameIterator_h
9
10
#include "mozilla/Attributes.h"
11
#include "mozilla/Maybe.h"
12
13
#include "js/GCAPI.h"
14
#include "js/TypeDecls.h"
15
#include "js/Utility.h"
16
17
namespace js {
18
    class Activation;
19
    namespace jit {
20
        class JitActivation;
21
        class JSJitProfilingFrameIterator;
22
        class JitcodeGlobalEntry;
23
    } // namespace jit
24
    namespace wasm {
25
        class ProfilingFrameIterator;
26
    } // namespace wasm
27
} // namespace js
28
29
namespace JS {
30
31
struct ForEachTrackedOptimizationAttemptOp;
32
struct ForEachTrackedOptimizationTypeInfoOp;
33
34
// This iterator can be used to walk the stack of a thread suspended at an
35
// arbitrary pc. To provide accurate results, profiling must have been enabled
36
// (via EnableRuntimeProfilingStack) before executing the callstack being
37
// unwound.
38
//
39
// Note that the caller must not do anything that could cause GC to happen while
40
// the iterator is alive, since this could invalidate Ion code and cause its
41
// contents to become out of date.
42
class MOZ_NON_PARAM JS_PUBLIC_API(ProfilingFrameIterator)
43
{
44
  public:
45
    enum class Kind : bool {
46
        JSJit,
47
        Wasm
48
    };
49
50
  private:
51
    JSContext* cx_;
52
    mozilla::Maybe<uint64_t> samplePositionInProfilerBuffer_;
53
    js::Activation* activation_;
54
    Kind kind_;
55
56
    static const unsigned StorageSpace = 8 * sizeof(void*);
57
    alignas(void*) unsigned char storage_[StorageSpace];
58
59
0
    void* storage() { return storage_; }
60
0
    const void* storage() const { return storage_; }
61
62
0
    js::wasm::ProfilingFrameIterator& wasmIter() {
63
0
        MOZ_ASSERT(!done());
64
0
        MOZ_ASSERT(isWasm());
65
0
        return *static_cast<js::wasm::ProfilingFrameIterator*>(storage());
66
0
    }
67
0
    const js::wasm::ProfilingFrameIterator& wasmIter() const {
68
0
        MOZ_ASSERT(!done());
69
0
        MOZ_ASSERT(isWasm());
70
0
        return *static_cast<const js::wasm::ProfilingFrameIterator*>(storage());
71
0
    }
72
73
0
    js::jit::JSJitProfilingFrameIterator& jsJitIter() {
74
0
        MOZ_ASSERT(!done());
75
0
        MOZ_ASSERT(isJSJit());
76
0
        return *static_cast<js::jit::JSJitProfilingFrameIterator*>(storage());
77
0
    }
78
79
0
    const js::jit::JSJitProfilingFrameIterator& jsJitIter() const {
80
0
        MOZ_ASSERT(!done());
81
0
        MOZ_ASSERT(isJSJit());
82
0
        return *static_cast<const js::jit::JSJitProfilingFrameIterator*>(storage());
83
0
    }
84
85
    void settleFrames();
86
    void settle();
87
88
  public:
89
    struct RegisterState
90
    {
91
0
        RegisterState() : pc(nullptr), sp(nullptr), fp(nullptr), lr(nullptr) {}
92
        void* pc;
93
        void* sp;
94
        void* fp;
95
        void* lr;
96
    };
97
98
    ProfilingFrameIterator(JSContext* cx, const RegisterState& state,
99
                           const mozilla::Maybe<uint64_t>& samplePositionInProfilerBuffer = mozilla::Nothing());
100
    ~ProfilingFrameIterator();
101
    void operator++();
102
0
    bool done() const { return !activation_; }
103
104
    // Assuming the stack grows down (we do), the return value:
105
    //  - always points into the stack
106
    //  - is weakly monotonically increasing (may be equal for successive frames)
107
    //  - will compare greater than newer native and psuedo-stack frame addresses
108
    //    and less than older native and psuedo-stack frame addresses
109
    void* stackAddress() const;
110
111
    enum FrameKind
112
    {
113
      Frame_Baseline,
114
      Frame_Ion,
115
      Frame_Wasm
116
    };
117
118
    struct Frame
119
    {
120
        FrameKind kind;
121
        void* stackAddress;
122
        void* returnAddress;
123
        void* activation;
124
        void* endStackAddress;
125
        const char* label;
126
    } JS_HAZ_GC_INVALIDATED;
127
128
    bool isWasm() const;
129
    bool isJSJit() const;
130
131
    uint32_t extractStack(Frame* frames, uint32_t offset, uint32_t end) const;
132
133
    mozilla::Maybe<Frame> getPhysicalFrameWithoutLabel() const;
134
135
  private:
136
    mozilla::Maybe<Frame> getPhysicalFrameAndEntry(js::jit::JitcodeGlobalEntry* entry) const;
137
138
    void iteratorConstruct(const RegisterState& state);
139
    void iteratorConstruct();
140
    void iteratorDestroy();
141
    bool iteratorDone();
142
} JS_HAZ_GC_INVALIDATED;
143
144
JS_FRIEND_API(bool)
145
IsProfilingEnabledForContext(JSContext* cx);
146
147
/**
148
 * After each sample run, this method should be called with the current buffer
149
 * position at which the buffer contents start. This will update the
150
 * corresponding field on the JSRuntime.
151
 *
152
 * See the field |profilerSampleBufferRangeStart| on JSRuntime for documentation
153
 * about what this value is used for.
154
 */
155
JS_FRIEND_API(void)
156
SetJSContextProfilerSampleBufferRangeStart(JSContext* cx, uint64_t rangeStart);
157
158
class ProfiledFrameRange;
159
160
// A handle to the underlying JitcodeGlobalEntry, so as to avoid repeated
161
// lookups on JitcodeGlobalTable.
162
class MOZ_STACK_CLASS ProfiledFrameHandle
163
{
164
    friend class ProfiledFrameRange;
165
166
    JSRuntime* rt_;
167
    js::jit::JitcodeGlobalEntry& entry_;
168
    void* addr_;
169
    void* canonicalAddr_;
170
    const char* label_;
171
    uint32_t depth_;
172
    mozilla::Maybe<uint8_t> optsIndex_;
173
174
    ProfiledFrameHandle(JSRuntime* rt, js::jit::JitcodeGlobalEntry& entry,
175
                        void* addr, const char* label, uint32_t depth);
176
177
    void updateHasTrackedOptimizations();
178
179
public:
180
0
    const char* label() const { return label_; }
181
0
    uint32_t depth() const { return depth_; }
182
0
    bool hasTrackedOptimizations() const { return optsIndex_.isSome(); }
183
0
    void* canonicalAddress() const { return canonicalAddr_; }
184
185
    JS_PUBLIC_API(ProfilingFrameIterator::FrameKind) frameKind() const;
186
    JS_PUBLIC_API(void) forEachOptimizationAttempt(ForEachTrackedOptimizationAttemptOp& op,
187
                                                    JSScript** scriptOut,
188
                                                    jsbytecode** pcOut) const;
189
190
    JS_PUBLIC_API(void)
191
    forEachOptimizationTypeInfo(ForEachTrackedOptimizationTypeInfoOp& op) const;
192
};
193
194
class ProfiledFrameRange
195
{
196
public:
197
    class Iter final
198
    {
199
    public:
200
        Iter(const ProfiledFrameRange& range, uint32_t index)
201
          : range_(range)
202
          , index_(index)
203
0
        {}
204
205
        JS_PUBLIC_API(ProfiledFrameHandle) operator*() const;
206
207
        // Provide the bare minimum of iterator methods that are needed for
208
        // C++ ranged for loops.
209
0
        Iter& operator++() { ++index_; return *this; }
210
0
        bool operator==(const Iter& rhs) { return index_ == rhs.index_; }
211
0
        bool operator!=(const Iter& rhs) { return !(*this == rhs); }
212
213
    private:
214
        const ProfiledFrameRange& range_;
215
        uint32_t index_;
216
    };
217
218
0
    Iter begin() const { return Iter(*this, 0); }
219
0
    Iter end() const { return Iter(*this, depth_); }
220
221
private:
222
    friend JS_PUBLIC_API(ProfiledFrameRange) GetProfiledFrames(JSContext* cx,
223
                                                               void* addr);
224
225
    ProfiledFrameRange(JSRuntime* rt, void* addr, js::jit::JitcodeGlobalEntry* entry)
226
      : rt_(rt)
227
      , addr_(addr)
228
      , entry_(entry)
229
      , depth_(0)
230
0
    {}
231
232
    JSRuntime* rt_;
233
    void* addr_;
234
    js::jit::JitcodeGlobalEntry* entry_;
235
    // Assume maximum inlining depth is <64
236
    const char* labels_[64];
237
    uint32_t depth_;
238
};
239
240
// Returns a range that can be iterated over using C++ ranged for loops.
241
JS_PUBLIC_API(ProfiledFrameRange)
242
GetProfiledFrames(JSContext* cx, void* addr);
243
244
} // namespace JS
245
246
#endif  /* js_ProfilingFrameIterator_h */