/* -*- 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 js_ProfilingStack_h
#define js_ProfilingStack_h

#include "mozilla/NullPtr.h"
 
#include "jsbytecode.h"
#include "jstypes.h"

#include "js/Utility.h"

struct JSRuntime;

namespace js {

// A call stack can be specified to the JS engine such that all JS entry/exits
// to functions push/pop an entry to/from the specified stack.
//
// For more detailed information, see vm/SPSProfiler.h.
//
class ProfileEntry
{
    // All fields are marked volatile to prevent the compiler from re-ordering
    // instructions. Namely this sequence:
    //
    //    entry[size] = ...;
    //    size++;
    //
    // If the size modification were somehow reordered before the stores, then
    // if a sample were taken it would be examining bogus information.
    //
    // A ProfileEntry represents both a C++ profile entry and a JS one. Both use
    // the string as a description, but JS uses the sp as nullptr to indicate
    // that it is a JS entry. The script_ is then only ever examined for a JS
    // entry, and the idx is used by both, but with different meanings.
    //
    const char * volatile string; // Descriptive string of this entry
    void * volatile sp;           // Relevant stack pointer for the entry
    JSScript * volatile script_;  // if js(), non-null script which is running
    int32_t volatile idx;         // if js(), idx of pc, otherwise line number

  public:
    // All of these methods are marked with the 'volatile' keyword because SPS's
    // representation of the stack is stored such that all ProfileEntry
    // instances are volatile. These methods would not be available unless they
    // were marked as volatile as well.

    bool js() const volatile {
        MOZ_ASSERT_IF(sp == nullptr, script_ != nullptr);
        return sp == nullptr;
    }

    uint32_t line() const volatile { MOZ_ASSERT(!js()); return idx; }
    JSScript *script() const volatile { MOZ_ASSERT(js()); return script_; }
    void *stackAddress() const volatile { return sp; }
    const char *label() const volatile { return string; }

    void setLine(uint32_t aLine) volatile { MOZ_ASSERT(!js()); idx = aLine; }
    void setLabel(const char *aString) volatile { string = aString; }
    void setStackAddress(void *aSp) volatile { sp = aSp; }
    void setScript(JSScript *aScript) volatile { script_ = aScript; }

    // We can't know the layout of JSScript, so look in vm/SPSProfiler.cpp.
    JS_FRIEND_API(jsbytecode *) pc() const volatile;
    JS_FRIEND_API(void) setPC(jsbytecode *pc) volatile;

    static size_t offsetOfString() { return offsetof(ProfileEntry, string); }
    static size_t offsetOfStackAddress() { return offsetof(ProfileEntry, sp); }
    static size_t offsetOfPCIdx() { return offsetof(ProfileEntry, idx); }
    static size_t offsetOfScript() { return offsetof(ProfileEntry, script_); }

    // The index used in the entry can either be a line number or the offset of
    // a pc into a script's code. To signify a nullptr pc, use a -1 index. This
    // is checked against in pc() and setPC() to set/get the right pc.
    static const int32_t NullPCIndex = -1;

    // This bit is added to the stack address to indicate that copying the
    // frame label is not necessary when taking a sample of the pseudostack.
    static const uintptr_t NoCopyBit = 1;
};

JS_FRIEND_API(void)
SetRuntimeProfilingStack(JSRuntime *rt, ProfileEntry *stack, uint32_t *size,
                         uint32_t max);

JS_FRIEND_API(void)
EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled);

JS_FRIEND_API(void)
RegisterRuntimeProfilingEventMarker(JSRuntime *rt, void (*fn)(const char *));

JS_FRIEND_API(jsbytecode*)
ProfilingGetPC(JSRuntime *rt, JSScript *script, void *ip);

} // namespace js

#endif  /* js_ProfilingStack_h */
