js/src/vm/SPSProfiler.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/src/vm/SPSProfiler.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,518 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99:
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#ifndef vm_SPSProfiler_h
    1.11 +#define vm_SPSProfiler_h
    1.12 +
    1.13 +#include "mozilla/DebugOnly.h"
    1.14 +#include "mozilla/GuardObjects.h"
    1.15 +
    1.16 +#include <stddef.h>
    1.17 +
    1.18 +#include "jslock.h"
    1.19 +#include "jsscript.h"
    1.20 +
    1.21 +#include "js/ProfilingStack.h"
    1.22 +
    1.23 +/*
    1.24 + * SPS Profiler integration with the JS Engine
    1.25 + * https://developer.mozilla.org/en/Performance/Profiling_with_the_Built-in_Profiler
    1.26 + *
    1.27 + * The SPS profiler (found in tools/profiler) is an implementation of a profiler
    1.28 + * which has the ability to walk the C++ stack as well as use instrumentation to
    1.29 + * gather information. When dealing with JS, however, SPS needs integration
    1.30 + * with the engine because otherwise it is very difficult to figure out what
    1.31 + * javascript is executing.
    1.32 + *
    1.33 + * The current method of integration with SPS is a form of instrumentation:
    1.34 + * every time a JS function is entered, a bit of information is pushed onto a
    1.35 + * stack that SPS owns and maintains. This information is then popped at the end
    1.36 + * of the JS function. SPS informs the JS engine of this stack at runtime, and
    1.37 + * it can by turned on/off dynamically.
    1.38 + *
    1.39 + * The SPS stack has three parameters: a base pointer, a size, and a maximum
    1.40 + * size. The stack is the ProfileEntry stack which will have information written
    1.41 + * to it. The size location is a pointer to an integer which represents the
    1.42 + * current size of the stack (number of valid frames). This size will be
    1.43 + * modified when JS functions are called. The maximum specified is the maximum
    1.44 + * capacity of the ProfileEntry stack.
    1.45 + *
    1.46 + * Throughout execution, the size of the stack recorded in memory may exceed the
    1.47 + * maximum. The JS engine will not write any information past the maximum limit,
    1.48 + * but it will still maintain the size of the stack. SPS code is aware of this
    1.49 + * and iterates the stack accordingly.
    1.50 + *
    1.51 + * There is some information pushed on the SPS stack for every JS function that
    1.52 + * is entered. First is a char* pointer of a description of what function was
    1.53 + * entered. Currently this string is of the form "function (file:line)" if
    1.54 + * there's a function name, or just "file:line" if there's no function name
    1.55 + * available. The other bit of information is the relevant C++ (native) stack
    1.56 + * pointer. This stack pointer is what enables the interleaving of the C++ and
    1.57 + * the JS stack. Finally, throughout execution of the function, some extra
    1.58 + * information may be updated on the ProfileEntry structure.
    1.59 + *
    1.60 + * = Profile Strings
    1.61 + *
    1.62 + * The profile strings' allocations and deallocation must be carefully
    1.63 + * maintained, and ideally at a very low overhead cost. For this reason, the JS
    1.64 + * engine maintains a mapping of all known profile strings. These strings are
    1.65 + * keyed in lookup by a JSScript*, but are serialized with a JSFunction*,
    1.66 + * JSScript* pair. A JSScript will destroy its corresponding profile string when
    1.67 + * the script is finalized.
    1.68 + *
    1.69 + * For this reason, a char* pointer pushed on the SPS stack is valid only while
    1.70 + * it is on the SPS stack. SPS uses sampling to read off information from this
    1.71 + * instrumented stack, and it therefore copies the string byte for byte when a
    1.72 + * JS function is encountered during sampling.
    1.73 + *
    1.74 + * = Native Stack Pointer
    1.75 + *
    1.76 + * The actual value pushed as the native pointer is nullptr for most JS
    1.77 + * functions. The reason for this is that there's actually very little
    1.78 + * correlation between the JS stack and the C++ stack because many JS functions
    1.79 + * all run in the same C++ frame, or can even go backwards in C++ when going
    1.80 + * from the JIT back to the interpreter.
    1.81 + *
    1.82 + * To alleviate this problem, all JS functions push nullptr as their "native
    1.83 + * stack pointer" to indicate that it's a JS function call. The function
    1.84 + * RunScript(), however, pushes an actual C++ stack pointer onto the SPS stack.
    1.85 + * This way when interleaving C++ and JS, if SPS sees a nullptr native stack
    1.86 + * pointer on the SPS stack, it looks backwards for the first non-nullptr
    1.87 + * pointer and uses that for all subsequent nullptr native stack pointers.
    1.88 + *
    1.89 + * = Line Numbers
    1.90 + *
    1.91 + * One goal of sampling is to get both a backtrace of the JS stack, but also
    1.92 + * know where within each function on the stack execution currently is. For
    1.93 + * this, each ProfileEntry has a 'pc' field to tell where its execution
    1.94 + * currently is. This field is updated whenever a call is made to another JS
    1.95 + * function, and for the JIT it is also updated whenever the JIT is left.
    1.96 + *
    1.97 + * This field is in a union with a uint32_t 'line' so that C++ can make use of
    1.98 + * the field as well. It was observed that tracking 'line' via PCToLineNumber in
    1.99 + * JS was far too expensive, so that is why the pc instead of the translated
   1.100 + * line number is stored.
   1.101 + *
   1.102 + * As an invariant, if the pc is nullptr, then the JIT is currently executing
   1.103 + * generated code. Otherwise execution is in another JS function or in C++. With
   1.104 + * this in place, only the top entry of the stack can ever have nullptr as its
   1.105 + * pc. Additionally with this invariant, it is possible to maintain mappings of
   1.106 + * JIT code to pc which can be accessed safely because they will only be
   1.107 + * accessed from a signal handler when the JIT code is executing.
   1.108 + */
   1.109 +
   1.110 +namespace js {
   1.111 +
   1.112 +class ProfileEntry;
   1.113 +
   1.114 +typedef HashMap<JSScript*, const char*, DefaultHasher<JSScript*>, SystemAllocPolicy>
   1.115 +        ProfileStringMap;
   1.116 +
   1.117 +class SPSEntryMarker;
   1.118 +
   1.119 +class SPSProfiler
   1.120 +{
   1.121 +    friend class SPSEntryMarker;
   1.122 +
   1.123 +    JSRuntime            *rt;
   1.124 +    ProfileStringMap     strings;
   1.125 +    ProfileEntry         *stack_;
   1.126 +    uint32_t             *size_;
   1.127 +    uint32_t             max_;
   1.128 +    bool                 slowAssertions;
   1.129 +    uint32_t             enabled_;
   1.130 +    PRLock               *lock_;
   1.131 +    void                (*eventMarker_)(const char *);
   1.132 +
   1.133 +    const char *allocProfileString(JSScript *script, JSFunction *function);
   1.134 +    void push(const char *string, void *sp, JSScript *script, jsbytecode *pc);
   1.135 +    void pushNoCopy(const char *string, void *sp,
   1.136 +                    JSScript *script, jsbytecode *pc) {
   1.137 +        push(string, reinterpret_cast<void*>(
   1.138 +            reinterpret_cast<uintptr_t>(sp) | ProfileEntry::NoCopyBit),
   1.139 +            script, pc);
   1.140 +    }
   1.141 +    void pop();
   1.142 +
   1.143 +  public:
   1.144 +    SPSProfiler(JSRuntime *rt);
   1.145 +    ~SPSProfiler();
   1.146 +
   1.147 +    bool init();
   1.148 +
   1.149 +    uint32_t **addressOfSizePointer() {
   1.150 +        return &size_;
   1.151 +    }
   1.152 +
   1.153 +    uint32_t *addressOfMaxSize() {
   1.154 +        return &max_;
   1.155 +    }
   1.156 +
   1.157 +    ProfileEntry **addressOfStack() {
   1.158 +        return &stack_;
   1.159 +    }
   1.160 +
   1.161 +    uint32_t *sizePointer() { return size_; }
   1.162 +    uint32_t maxSize() { return max_; }
   1.163 +    ProfileEntry *stack() { return stack_; }
   1.164 +
   1.165 +    /* management of whether instrumentation is on or off */
   1.166 +    bool enabled() { JS_ASSERT_IF(enabled_, installed()); return enabled_; }
   1.167 +    bool installed() { return stack_ != nullptr && size_ != nullptr; }
   1.168 +    void enable(bool enabled);
   1.169 +    void enableSlowAssertions(bool enabled) { slowAssertions = enabled; }
   1.170 +    bool slowAssertionsEnabled() { return slowAssertions; }
   1.171 +
   1.172 +    /*
   1.173 +     * Functions which are the actual instrumentation to track run information
   1.174 +     *
   1.175 +     *   - enter: a function has started to execute
   1.176 +     *   - updatePC: updates the pc information about where a function
   1.177 +     *               is currently executing
   1.178 +     *   - exit: this function has ceased execution, and no further
   1.179 +     *           entries/exits will be made
   1.180 +     */
   1.181 +    bool enter(JSScript *script, JSFunction *maybeFun);
   1.182 +    void exit(JSScript *script, JSFunction *maybeFun);
   1.183 +    void updatePC(JSScript *script, jsbytecode *pc) {
   1.184 +        if (enabled() && *size_ - 1 < max_) {
   1.185 +            JS_ASSERT(*size_ > 0);
   1.186 +            JS_ASSERT(stack_[*size_ - 1].script() == script);
   1.187 +            stack_[*size_ - 1].setPC(pc);
   1.188 +        }
   1.189 +    }
   1.190 +
   1.191 +    /* Enter a C++ function. */
   1.192 +    void enterNative(const char *string, void *sp);
   1.193 +    void exitNative() { pop(); }
   1.194 +
   1.195 +    jsbytecode *ipToPC(JSScript *script, size_t ip) { return nullptr; }
   1.196 +
   1.197 +    void setProfilingStack(ProfileEntry *stack, uint32_t *size, uint32_t max);
   1.198 +    void setEventMarker(void (*fn)(const char *));
   1.199 +    const char *profileString(JSScript *script, JSFunction *maybeFun);
   1.200 +    void onScriptFinalized(JSScript *script);
   1.201 +
   1.202 +    void markEvent(const char *event);
   1.203 +
   1.204 +    /* meant to be used for testing, not recommended to call in normal code */
   1.205 +    size_t stringsCount();
   1.206 +    void stringsReset();
   1.207 +
   1.208 +    uint32_t *addressOfEnabled() {
   1.209 +        return &enabled_;
   1.210 +    }
   1.211 +};
   1.212 +
   1.213 +/*
   1.214 + * This class is used to make sure the strings table
   1.215 + * is only accessed on one thread at a time.
   1.216 + */
   1.217 +class AutoSPSLock
   1.218 +{
   1.219 +  public:
   1.220 +#ifdef JS_THREADSAFE
   1.221 +    AutoSPSLock(PRLock *lock)
   1.222 +    {
   1.223 +        MOZ_ASSERT(lock, "Parameter should not be null!");
   1.224 +        lock_ = lock;
   1.225 +        PR_Lock(lock);
   1.226 +    }
   1.227 +    ~AutoSPSLock() { PR_Unlock(lock_); }
   1.228 +#else
   1.229 +    AutoSPSLock(PRLock *) {}
   1.230 +#endif
   1.231 +
   1.232 +  private:
   1.233 +    PRLock *lock_;
   1.234 +};
   1.235 +
   1.236 +inline size_t
   1.237 +SPSProfiler::stringsCount()
   1.238 +{
   1.239 +    AutoSPSLock lock(lock_);
   1.240 +    return strings.count();
   1.241 +}
   1.242 +
   1.243 +inline void
   1.244 +SPSProfiler::stringsReset()
   1.245 +{
   1.246 +    AutoSPSLock lock(lock_);
   1.247 +    strings.clear();
   1.248 +}
   1.249 +
   1.250 +/*
   1.251 + * This class is used in RunScript() to push the marker onto the sampling stack
   1.252 + * that we're about to enter JS function calls. This is the only time in which a
   1.253 + * valid stack pointer is pushed to the sampling stack.
   1.254 + */
   1.255 +class SPSEntryMarker
   1.256 +{
   1.257 +  public:
   1.258 +    SPSEntryMarker(JSRuntime *rt
   1.259 +                   MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
   1.260 +    ~SPSEntryMarker();
   1.261 +
   1.262 +  private:
   1.263 +    SPSProfiler *profiler;
   1.264 +    mozilla::DebugOnly<uint32_t> size_before;
   1.265 +    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
   1.266 +};
   1.267 +
   1.268 +/*
   1.269 + * SPS is the profiling backend used by the JS engine to enable time profiling.
   1.270 + * More information can be found in vm/SPSProfiler.{h,cpp}. This class manages
   1.271 + * the instrumentation portion of the profiling for JIT code.
   1.272 + *
   1.273 + * The instrumentation tracks entry into functions, leaving those functions via
   1.274 + * a function call, reentering the functions from a function call, and exiting
   1.275 + * the functions from returning. This class also handles inline frames and
   1.276 + * manages the instrumentation which needs to be attached to them as well.
   1.277 + *
   1.278 + * The basic methods which emit instrumentation are at the end of this class,
   1.279 + * and the management functions are all described in the middle.
   1.280 + */
   1.281 +template<class Assembler, class Register>
   1.282 +class SPSInstrumentation
   1.283 +{
   1.284 +    /* Because of inline frames, this is a nested structure in a vector */
   1.285 +    struct FrameState {
   1.286 +        JSScript *script; // script for this frame, nullptr if not pushed yet
   1.287 +        jsbytecode *pc;   // pc at which this frame was left for entry into a callee
   1.288 +        bool skipNext;    // should the next call to reenter be skipped?
   1.289 +        int  left;        // number of leave() calls made without a matching reenter()
   1.290 +    };
   1.291 +
   1.292 +    SPSProfiler *profiler_; // Instrumentation location management
   1.293 +
   1.294 +    Vector<FrameState, 1, SystemAllocPolicy> frames;
   1.295 +    FrameState *frame;
   1.296 +
   1.297 +    static void clearFrame(FrameState *frame) {
   1.298 +        frame->script = nullptr;
   1.299 +        frame->pc = nullptr;
   1.300 +        frame->skipNext = false;
   1.301 +        frame->left = 0;
   1.302 +    }
   1.303 +
   1.304 +  public:
   1.305 +    /*
   1.306 +     * Creates instrumentation which writes information out the the specified
   1.307 +     * profiler's stack and constituent fields.
   1.308 +     */
   1.309 +    SPSInstrumentation(SPSProfiler *profiler)
   1.310 +      : profiler_(profiler), frame(nullptr)
   1.311 +    {
   1.312 +        enterInlineFrame(nullptr);
   1.313 +    }
   1.314 +
   1.315 +    /* Small proxies around SPSProfiler */
   1.316 +    bool enabled() { return profiler_ && profiler_->enabled(); }
   1.317 +    SPSProfiler *profiler() { JS_ASSERT(enabled()); return profiler_; }
   1.318 +    void disable() { profiler_ = nullptr; }
   1.319 +
   1.320 +    /* Signals an inline function returned, reverting to the previous state */
   1.321 +    void leaveInlineFrame() {
   1.322 +        if (!enabled())
   1.323 +            return;
   1.324 +        JS_ASSERT(frame->left == 0);
   1.325 +        JS_ASSERT(frame->script != nullptr);
   1.326 +        frames.shrinkBy(1);
   1.327 +        JS_ASSERT(frames.length() > 0);
   1.328 +        frame = &frames[frames.length() - 1];
   1.329 +    }
   1.330 +
   1.331 +    /* Saves the current state and assumes a fresh one for the inline function */
   1.332 +    bool enterInlineFrame(jsbytecode *callerPC) {
   1.333 +        if (!enabled())
   1.334 +            return true;
   1.335 +        JS_ASSERT_IF(frames.empty(), callerPC == nullptr);
   1.336 +
   1.337 +        JS_ASSERT_IF(frame != nullptr, frame->script != nullptr);
   1.338 +        JS_ASSERT_IF(frame != nullptr, frame->left == 1);
   1.339 +        if (!frames.empty()) {
   1.340 +            JS_ASSERT(frame == &frames[frames.length() - 1]);
   1.341 +            frame->pc = callerPC;
   1.342 +        }
   1.343 +        if (!frames.growBy(1))
   1.344 +            return false;
   1.345 +        frame = &frames[frames.length() - 1];
   1.346 +        clearFrame(frame);
   1.347 +        return true;
   1.348 +    }
   1.349 +
   1.350 +    /* Prepares the instrumenter state for generating OOL code, by
   1.351 +     * setting up the frame state to seem as if there are exactly
   1.352 +     * two pushed frames: a frame for the top-level script, and
   1.353 +     * a frame for the OOL code being generated.  Any
   1.354 +     * vm-calls from the OOL code will "leave" the OOL frame and
   1.355 +     * return back to it.
   1.356 +     */
   1.357 +    bool prepareForOOL() {
   1.358 +        if (!enabled())
   1.359 +            return true;
   1.360 +        JS_ASSERT(!frames.empty());
   1.361 +        if (frames.length() >= 2) {
   1.362 +            frames.shrinkBy(frames.length() - 2);
   1.363 +
   1.364 +        } else { // frames.length() == 1
   1.365 +            if (!frames.growBy(1))
   1.366 +                return false;
   1.367 +        }
   1.368 +        frames[0].pc = frames[0].script->code();
   1.369 +        frame = &frames[1];
   1.370 +        clearFrame(frame);
   1.371 +        return true;
   1.372 +    }
   1.373 +    void finishOOL() {
   1.374 +        if (!enabled())
   1.375 +            return;
   1.376 +        JS_ASSERT(!frames.empty());
   1.377 +        frames.shrinkBy(frames.length() - 1);
   1.378 +    }
   1.379 +
   1.380 +    /* Number of inline frames currently active (doesn't include original one) */
   1.381 +    unsigned inliningDepth() {
   1.382 +        return frames.length() - 1;
   1.383 +    }
   1.384 +
   1.385 +    /*
   1.386 +     * When debugging or with slow assertions, sometimes a C++ method will be
   1.387 +     * invoked to perform the pop operation from the SPS stack. When we leave
   1.388 +     * JIT code, we need to record the current PC, but upon reentering JIT
   1.389 +     * code, no update back to nullptr should happen. This method exists to
   1.390 +     * flag this behavior. The next leave() will emit instrumentation, but the
   1.391 +     * following reenter() will be a no-op.
   1.392 +     */
   1.393 +    void skipNextReenter() {
   1.394 +        /* If we've left the frame, the reenter will be skipped anyway */
   1.395 +        if (!enabled() || frame->left != 0)
   1.396 +            return;
   1.397 +        JS_ASSERT(frame->script);
   1.398 +        JS_ASSERT(!frame->skipNext);
   1.399 +        frame->skipNext = true;
   1.400 +    }
   1.401 +
   1.402 +    /*
   1.403 +     * In some cases, a frame needs to be flagged as having been pushed, but no
   1.404 +     * instrumentation should be emitted. This updates internal state to flag
   1.405 +     * that further instrumentation should actually be emitted.
   1.406 +     */
   1.407 +    void setPushed(JSScript *script) {
   1.408 +        if (!enabled())
   1.409 +            return;
   1.410 +        JS_ASSERT(frame->left == 0);
   1.411 +        frame->script = script;
   1.412 +    }
   1.413 +
   1.414 +    /*
   1.415 +     * Flags entry into a JS function for the first time. Before this is called,
   1.416 +     * no instrumentation is emitted, but after this instrumentation is emitted.
   1.417 +     */
   1.418 +    bool push(JSScript *script, Assembler &masm, Register scratch, bool inlinedFunction = false) {
   1.419 +        if (!enabled())
   1.420 +            return true;
   1.421 +#ifdef JS_ION
   1.422 +        if (!inlinedFunction || jit::js_JitOptions.profileInlineFrames) {
   1.423 +#endif
   1.424 +            const char *string = profiler_->profileString(script, script->functionNonDelazifying());
   1.425 +            if (string == nullptr)
   1.426 +                return false;
   1.427 +            masm.spsPushFrame(profiler_, string, script, scratch);
   1.428 +#ifdef JS_ION
   1.429 +        }
   1.430 +#endif
   1.431 +        setPushed(script);
   1.432 +        return true;
   1.433 +    }
   1.434 +
   1.435 +    /*
   1.436 +     * Signifies that C++ performed the push() for this function. C++ always
   1.437 +     * sets the current PC to something non-null, however, so as soon as JIT
   1.438 +     * code is reentered this updates the current pc to nullptr.
   1.439 +     */
   1.440 +    void pushManual(JSScript *script, Assembler &masm, Register scratch,
   1.441 +                    bool inlinedFunction = false)
   1.442 +    {
   1.443 +        if (!enabled())
   1.444 +            return;
   1.445 +
   1.446 +#ifdef JS_ION
   1.447 +        if (!inlinedFunction || jit::js_JitOptions.profileInlineFrames)
   1.448 +#endif
   1.449 +            masm.spsUpdatePCIdx(profiler_, ProfileEntry::NullPCIndex, scratch);
   1.450 +
   1.451 +        setPushed(script);
   1.452 +    }
   1.453 +
   1.454 +    /*
   1.455 +     * Signals that the current function is leaving for a function call. This
   1.456 +     * can happen both on JS function calls and also calls to C++. This
   1.457 +     * internally manages how many leave() calls have been seen, and only the
   1.458 +     * first leave() emits instrumentation. Similarly, only the last
   1.459 +     * corresponding reenter() actually emits instrumentation.
   1.460 +     */
   1.461 +    void leave(jsbytecode *pc, Assembler &masm, Register scratch, bool inlinedFunction = false) {
   1.462 +        if (enabled() && frame->script && frame->left++ == 0) {
   1.463 +            jsbytecode *updatePC = pc;
   1.464 +            JSScript *script = frame->script;
   1.465 +#ifdef JS_ION
   1.466 +            if (!inlinedFunction) {
   1.467 +                // We may be leaving an inlined frame for entry into a C++
   1.468 +                // frame.  If profileInlineFrames is turned off, use the top
   1.469 +                // script's pc offset instead of the innermost script's.
   1.470 +                if (!jit::js_JitOptions.profileInlineFrames && inliningDepth() > 0) {
   1.471 +                    JS_ASSERT(frames[0].pc);
   1.472 +                    updatePC = frames[0].pc;
   1.473 +                    script = frames[0].script;
   1.474 +                }
   1.475 +            }
   1.476 +#endif
   1.477 +
   1.478 +#ifdef JS_ION
   1.479 +            if (!inlinedFunction || jit::js_JitOptions.profileInlineFrames)
   1.480 +#endif
   1.481 +                masm.spsUpdatePCIdx(profiler_, script->pcToOffset(updatePC), scratch);
   1.482 +        }
   1.483 +    }
   1.484 +
   1.485 +    /*
   1.486 +     * Flags that the leaving of the current function has returned. This tracks
   1.487 +     * state with leave() to only emit instrumentation at proper times.
   1.488 +     */
   1.489 +    void reenter(Assembler &masm, Register scratch, bool inlinedFunction = false) {
   1.490 +        if (!enabled() || !frame->script || frame->left-- != 1)
   1.491 +            return;
   1.492 +        if (frame->skipNext) {
   1.493 +            frame->skipNext = false;
   1.494 +        } else {
   1.495 +#ifdef JS_ION
   1.496 +             if (!inlinedFunction || jit::js_JitOptions.profileInlineFrames)
   1.497 +#endif
   1.498 +                 masm.spsUpdatePCIdx(profiler_, ProfileEntry::NullPCIndex, scratch);
   1.499 +        }
   1.500 +    }
   1.501 +
   1.502 +    /*
   1.503 +     * Signifies exiting a JS frame, popping the SPS entry. Because there can be
   1.504 +     * multiple return sites of a function, this does not cease instrumentation
   1.505 +     * emission.
   1.506 +     */
   1.507 +    void pop(Assembler &masm, Register scratch, bool inlinedFunction = false) {
   1.508 +        if (enabled()) {
   1.509 +            JS_ASSERT(frame->left == 0);
   1.510 +            JS_ASSERT(frame->script);
   1.511 +#ifdef JS_ION
   1.512 +            if (!inlinedFunction || jit::js_JitOptions.profileInlineFrames)
   1.513 +#endif
   1.514 +                masm.spsPopFrame(profiler_, scratch);
   1.515 +        }
   1.516 +    }
   1.517 +};
   1.518 +
   1.519 +} /* namespace js */
   1.520 +
   1.521 +#endif /* vm_SPSProfiler_h */

mercurial