/* 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/. */

// This header contains all definitions related to Base Profiler labels (outside
// of XUL).

#ifndef BaseProfilerLabels_h
#define BaseProfilerLabels_h

#include "BaseProfilingStack.h"

#include "mozilla/Attributes.h"
#include "mozilla/Maybe.h"
#include "mozilla/BaseProfilerRAIIMacro.h"
#include "mozilla/BaseProfilerState.h"
#include "mozilla/ThreadLocal.h"

#include <stdint.h>
#include <string>

namespace mozilla::baseprofiler {

// Insert an RAII object in this scope to enter a label stack frame. Any
// samples collected in this scope will contain this label in their stack.
// The label argument must be a static C string. It is usually of the
// form "ClassName::FunctionName". (Ideally we'd use the compiler to provide
// that for us, but __func__ gives us the function name without the class
// name.) If the label applies to only part of a function, you can qualify it
// like this: "ClassName::FunctionName:PartName".
//
// Use AUTO_BASE_PROFILER_LABEL_DYNAMIC_* if you want to add additional /
// dynamic information to the label stack frame.
#define AUTO_BASE_PROFILER_LABEL(label, categoryPair)       \
  ::mozilla::baseprofiler::AutoProfilerLabel PROFILER_RAII( \
      label, nullptr,                                       \
      ::mozilla::baseprofiler::ProfilingCategoryPair::categoryPair)

// Similar to AUTO_BASE_PROFILER_LABEL, but with only one argument: the category
// pair. The label string is taken from the category pair. This is convenient
// for labels like
// AUTO_BASE_PROFILER_LABEL_CATEGORY_PAIR(GRAPHICS_LayerBuilding) which would
// otherwise just repeat the string.
#define AUTO_BASE_PROFILER_LABEL_CATEGORY_PAIR(categoryPair)         \
  ::mozilla::baseprofiler::AutoProfilerLabel PROFILER_RAII(          \
      "", nullptr,                                                   \
      ::mozilla::baseprofiler::ProfilingCategoryPair::categoryPair,  \
      uint32_t(::mozilla::baseprofiler::ProfilingStackFrame::Flags:: \
                   LABEL_DETERMINED_BY_CATEGORY_PAIR))

// Similar to AUTO_BASE_PROFILER_LABEL, but with an additional string. The
// inserted RAII object stores the cStr pointer in a field; it does not copy the
// string.
//
// WARNING: This means that the string you pass to this macro needs to live at
// least until the end of the current scope. Be careful using this macro with
// ns[C]String; the other AUTO_BASE_PROFILER_LABEL_DYNAMIC_* macros below are
// preferred because they avoid this problem.
//
// If the profiler samples the current thread and walks the label stack while
// this RAII object is on the stack, it will copy the supplied string into the
// profile buffer. So there's one string copy operation, and it happens at
// sample time.
//
// Compare this to the plain AUTO_BASE_PROFILER_LABEL macro, which only accepts
// literal strings: When the label stack frames generated by
// AUTO_BASE_PROFILER_LABEL are sampled, no string copy needs to be made because
// the profile buffer can just store the raw pointers to the literal strings.
// Consequently, AUTO_BASE_PROFILER_LABEL frames take up considerably less space
// in the profile buffer than AUTO_BASE_PROFILER_LABEL_DYNAMIC_* frames.
#define AUTO_BASE_PROFILER_LABEL_DYNAMIC_CSTR(label, categoryPair, cStr) \
  ::mozilla::baseprofiler::AutoProfilerLabel PROFILER_RAII(              \
      label, cStr,                                                       \
      ::mozilla::baseprofiler::ProfilingCategoryPair::categoryPair)

// Similar to AUTO_BASE_PROFILER_LABEL_DYNAMIC_CSTR, but takes an std::string.
//
// Note: The use of the Maybe<>s ensures the scopes for the dynamic string and
// the AutoProfilerLabel are appropriate, while also not incurring the runtime
// cost of the string assignment unless the profiler is active. Therefore,
// unlike AUTO_BASE_PROFILER_LABEL and AUTO_BASE_PROFILER_LABEL_DYNAMIC_CSTR,
// this macro doesn't push/pop a label when the profiler is inactive.
#define AUTO_BASE_PROFILER_LABEL_DYNAMIC_STRING(label, categoryPair, str) \
  Maybe<std::string> autoStr;                                             \
  Maybe<::mozilla::baseprofiler::AutoProfilerLabel> raiiObjectString;     \
  if (::mozilla::baseprofiler::profiler_is_active()) {                    \
    autoStr.emplace(str);                                                 \
    raiiObjectString.emplace(                                             \
        label, autoStr->c_str(),                                          \
        ::mozilla::baseprofiler::ProfilingCategoryPair::categoryPair);    \
  }

// Similar to AUTO_BASE_PROFILER_LABEL, but accepting a JSContext* parameter,
// and a no-op if the profiler is disabled. Used to annotate functions for which
// overhead in the range of nanoseconds is noticeable. It avoids overhead from
// the TLS lookup because it can get the ProfilingStack from the JS context, and
// avoids almost all overhead in the case where the profiler is disabled.
#define AUTO_BASE_PROFILER_LABEL_FAST(label, categoryPair, ctx) \
  ::mozilla::baseprofiler::AutoProfilerLabel PROFILER_RAII(     \
      ctx, label, nullptr,                                      \
      ::mozilla::baseprofiler::ProfilingCategoryPair::categoryPair)

// Similar to AUTO_BASE_PROFILER_LABEL_FAST, but also takes an extra string and
// an additional set of flags. The flags parameter should carry values from the
// ProfilingStackFrame::Flags enum.
#define AUTO_BASE_PROFILER_LABEL_DYNAMIC_FAST(label, dynamicString,     \
                                              categoryPair, ctx, flags) \
  ::mozilla::baseprofiler::AutoProfilerLabel PROFILER_RAII(             \
      ctx, label, dynamicString,                                        \
      ::mozilla::baseprofiler::ProfilingCategoryPair::categoryPair, flags)

// This class creates a non-owning ProfilingStack reference. Objects of this
// class are stack-allocated, and so exist within a thread, and are thus bounded
// by the lifetime of the thread, which ensures that the references held can't
// be used after the ProfilingStack is destroyed.
class MOZ_RAII AutoProfilerLabel {
 public:
  // This is the AUTO_BASE_PROFILER_LABEL and AUTO_BASE_PROFILER_LABEL_DYNAMIC
  // variant.
  AutoProfilerLabel(const char* aLabel, const char* aDynamicString,
                    ProfilingCategoryPair aCategoryPair, uint32_t aFlags = 0) {
    // Get the ProfilingStack from TLS.
    Push(GetProfilingStack(), aLabel, aDynamicString, aCategoryPair, aFlags);
  }

  void Push(ProfilingStack* aProfilingStack, const char* aLabel,
            const char* aDynamicString, ProfilingCategoryPair aCategoryPair,
            uint32_t aFlags = 0) {
    // This function runs both on and off the main thread.

    mProfilingStack = aProfilingStack;
    if (mProfilingStack) {
      mProfilingStack->pushLabelFrame(aLabel, aDynamicString, this,
                                      aCategoryPair, aFlags);
    }
  }

  ~AutoProfilerLabel() {
    // This function runs both on and off the main thread.

    if (mProfilingStack) {
      mProfilingStack->pop();
    }
  }

  MFBT_API static ProfilingStack* GetProfilingStack();

 private:
  // We save a ProfilingStack pointer in the ctor so we don't have to redo the
  // TLS lookup in the dtor.
  ProfilingStack* mProfilingStack;

 public:
  // See the comment on the definition in platform.cpp for details about this.
  static MOZ_THREAD_LOCAL(ProfilingStack*) sProfilingStack;
};

}  // namespace mozilla::baseprofiler

#endif  // BaseProfilerLabels_h
