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

#include "builtin/String.h"

#include "js/AllocPolicy.h"

// Tree items, meaning they have a start and stop and form a nested tree.
#define TRACELOGGER_TREE_ITEMS(_)                     \
    _(AnnotateScripts)                                \
    _(Baseline)                                       \
    _(BaselineCompilation)                            \
    _(Engine)                                         \
    _(GC)                                             \
    _(GCAllocation)                                   \
    _(GCSweeping)                                     \
    _(Interpreter)                                    \
    _(InlinedScripts)                                 \
    _(IonAnalysis)                                    \
    _(IonCompilation)                                 \
    _(IonLinking)                                     \
    _(IonMonkey)                                      \
    _(IrregexpCompile)                                \
    _(IrregexpExecute)                                \
    _(MinorGC)                                        \
    _(Frontend)                                       \
    _(ParsingFull)                                    \
    _(ParsingSyntax)                                  \
    _(BytecodeEmission)                               \
    _(BytecodeFoldConstants)                          \
    _(BytecodeNameFunctions)                          \
    _(DecodeScript)                                   \
    _(DecodeFunction)                                 \
    _(EncodeScript)                                   \
    _(EncodeFunction)                                 \
    _(Scripts)                                        \
    _(VM)                                             \
    _(CompressSource)                                 \
    _(WasmCompilation)                                \
    _(Call)                                           \
                                                      \
    /* Specific passes during ion compilation */      \
    _(PruneUnusedBranches)                            \
    _(FoldTests)                                      \
    _(FoldEmptyBlocks)                                \
    _(SplitCriticalEdges)                             \
    _(RenumberBlocks)                                 \
    _(ScalarReplacement)                              \
    _(DominatorTree)                                  \
    _(PhiAnalysis)                                    \
    _(MakeLoopsContiguous)                            \
    _(ApplyTypes)                                     \
    _(EagerSimdUnbox)                                 \
    _(AliasAnalysis)                                  \
    _(GVN)                                            \
    _(LICM)                                           \
    _(Sincos)                                         \
    _(RangeAnalysis)                                  \
    _(LoopUnrolling)                                  \
    _(Sink)                                           \
    _(RemoveUnnecessaryBitops)                        \
    _(FoldLinearArithConstants)                       \
    _(EffectiveAddressAnalysis)                       \
    _(AlignmentMaskAnalysis)                          \
    _(EliminateDeadCode)                              \
    _(ReorderInstructions)                            \
    _(EdgeCaseAnalysis)                               \
    _(EliminateRedundantChecks)                       \
    _(AddKeepAliveInstructions)                       \
    _(GenerateLIR)                                    \
    _(RegisterAllocation)                             \
    _(GenerateCode)                                   \
    _(IonBuilderRestartLoop)                          \
    _(VMSpecific)

// Log items, with timestamp only.
#define TRACELOGGER_LOG_ITEMS(_)                      \
    _(Bailout)                                        \
    _(Invalidation)                                   \
    _(Disable)                                        \
    _(Enable)                                         \
    _(Stop)

// Predefined IDs for common operations. These IDs can be used
// without using TraceLogCreateTextId, because there are already created.
enum TraceLoggerTextId {
    TraceLogger_Error = 0,
    TraceLogger_Internal,
#define DEFINE_TEXT_ID(textId) TraceLogger_ ## textId,
    TRACELOGGER_TREE_ITEMS(DEFINE_TEXT_ID)
    TraceLogger_TreeItemEnd,
    TRACELOGGER_LOG_ITEMS(DEFINE_TEXT_ID)
#undef DEFINE_TEXT_ID
    TraceLogger_Last
};

inline const char*
TLTextIdString(TraceLoggerTextId id)
{
    switch (id) {
      case TraceLogger_Error:
        return "TraceLogger failed to process text";
      case TraceLogger_Internal:
        return "TraceLogger overhead";
#define NAME(textId) case TraceLogger_ ## textId: return #textId;
        TRACELOGGER_TREE_ITEMS(NAME)
        TRACELOGGER_LOG_ITEMS(NAME)
#undef NAME
      default:
        MOZ_CRASH();
    }
}

uint32_t
TLStringToTextId(JSLinearString* str);

// Return whether a given item id can be enabled/disabled.
inline bool
TLTextIdIsTogglable(uint32_t id)
{
    if (id == TraceLogger_Error) {
        return false;
    }
    if (id == TraceLogger_Internal) {
        return false;
    }
    if (id == TraceLogger_Stop) {
        return false;
    }
    // Actually never used. But added here so it doesn't show as toggle
    if (id == TraceLogger_TreeItemEnd) {
        return false;
    }
    if (id == TraceLogger_Last) {
        return false;
    }
    // Cannot toggle the logging of one engine on/off, because at the stop
    // event it is sometimes unknown which engine was running.
    if (id == TraceLogger_IonMonkey || id == TraceLogger_Baseline || id == TraceLogger_Interpreter) {
        return false;
    }
    return true;
}

inline bool
TLTextIdIsTreeEvent(uint32_t id)
{
    // Everything between TraceLogger_Error and TraceLogger_TreeItemEnd are tree events and
    // atm also every custom event.
    return (id > TraceLogger_Error && id < TraceLogger_TreeItemEnd) ||
           id >= TraceLogger_Last;
}

inline bool
TLTextIdIsLogEvent(uint32_t id)
{
    // These id's do not have start & stop events.
    return (id > TraceLogger_TreeItemEnd && id < TraceLogger_Last);
}

inline bool
TLTextIdIsInternalEvent(uint32_t id)
{
    // Id's used for bookkeeping.  Does not correspond to real events.
    return (id == TraceLogger_Error       ||
            id == TraceLogger_Last        ||
            id == TraceLogger_TreeItemEnd ||
            id == TraceLogger_Internal    ||
            id == TraceLogger_Stop);
}

template <class T>
class ContinuousSpace {
    T* data_;
    uint32_t size_;
    uint32_t capacity_;

    // The maximum number of bytes of RAM a continuous space structure can take.
    static const uint32_t LIMIT = 200 * 1024 * 1024;

  public:
    ContinuousSpace ()
     : data_(nullptr),
       size_(0),
       capacity_(0)
    { }

    bool init() {
        capacity_ = 64;
        size_ = 0;
        data_ = js_pod_malloc<T>(capacity_);
        if (!data_) {
            return false;
        }

        return true;
    }

    ~ContinuousSpace()
    {
        js_free(data_);
        data_ = nullptr;
    }

    static uint32_t maxSize() {
        return LIMIT / sizeof(T);
    }

    T* data() {
        return data_;
    }

    uint32_t capacity() const {
        return capacity_;
    }

    uint32_t size() const {
        return size_;
    }

    bool empty() const {
        return size_ == 0;
    }

    uint32_t lastEntryId() const {
        MOZ_ASSERT(!empty());
        return size_ - 1;
    }

    T& lastEntry() {
        return data()[lastEntryId()];
    }

    bool hasSpaceForAdd(uint32_t count = 1) {
        if (size_ + count <= capacity_) {
            return true;
        }
        return false;
    }

    bool ensureSpaceBeforeAdd(uint32_t count = 1) {
        MOZ_ASSERT(data_);
        if (hasSpaceForAdd(count)) {
            return true;
        }

        // Limit the size of a continuous buffer.
        if (size_ + count > maxSize()) {
            return false;
        }

        uint32_t nCapacity = capacity_ * 2;
        nCapacity = (nCapacity < maxSize()) ? nCapacity : maxSize();

        T* entries = js_pod_realloc<T>(data_, capacity_, nCapacity);
        if (!entries) {
            return false;
        }

        data_ = entries;
        capacity_ = nCapacity;

        return true;
    }

    T& operator[](size_t i) {
        MOZ_ASSERT(i < size_);
        return data()[i];
    }

    void push(T& data) {
        MOZ_ASSERT(size_ < capacity_);
        data()[size_++] = data;
    }

    T& pushUninitialized() {
        MOZ_ASSERT(size_ < capacity_);
        return data()[size_++];
    }

    void pop() {
        MOZ_ASSERT(!empty());
        size_--;
    }

    void clear() {
        size_ = 0;
    }

    bool reset() {
        size_t oldCapacity = data_ ? capacity_ : 0;
        capacity_ = 64;
        size_ = 0;
        data_ = js_pod_realloc<T>(data_, oldCapacity, capacity_);

        if (!data_) {
            return false;
        }

        return true;
    }

    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
        return mallocSizeOf(data_);
    }
};

// The layout of the event log in memory and in the log file.
// Readable by JS using TypedArrays.
struct EventEntry {
    uint64_t time;
    uint32_t textId;
    EventEntry(uint64_t time, uint32_t textId)
      : time(time), textId(textId)
    { }
};

#endif /* TraceLoggingTypes_h */
