// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_TRACE_EVENT_MEMORY_DUMP_PROVIDER_H_
#define BASE_TRACE_EVENT_MEMORY_DUMP_PROVIDER_H_

#include <compare>
#include <ostream>
#include <string>
#include <string_view>

#include "base/base_export.h"
#include "base/notreached.h"
#include "base/trace_event/memory_dump_provider_name_variants.h"
#include "base/trace_event/memory_dump_request_args.h"

namespace base {
namespace trace_event {

class ProcessMemoryDump;

// The contract interface that memory dump providers must implement.
class BASE_EXPORT MemoryDumpProvider {
 public:
  // Optional arguments for MemoryDumpManager::RegisterDumpProvider().
  struct Options {
    Options() : dumps_on_single_thread_task_runner(false) {}

    // |dumps_on_single_thread_task_runner| is true if the dump provider runs on
    // a SingleThreadTaskRunner, which is usually the case. It is faster to run
    // all providers that run on the same thread together without thread hops.
    bool dumps_on_single_thread_task_runner;
  };

  // A wrapper class to
  //
  // 1. Convert (often implicitly) a static const char* string into a name param
  // for MemoryDumpManager::RegisterDumpProvider().
  //
  // 2. Check that the name is in the MemoryDumpProviderName histogram variant
  // list, at compile time.
  //
  // The histogram variant name is formed by replacing ":" characters in the
  // static name with "_" (eg. "gpu::TextureManager" converts to
  // "gpu__TextureManager") because ":" has a special meaning in the histograms
  // dashboard.
  //
  // There is nothing special to do to use this class. For example, the
  // following works out of the box:
  //
  // memory_dump_manager->RegisterDumpProvider(provider, "Name", nullptr);
  //
  // However if the name is being passed through an intermediate function that
  // takes generic params, such as `make_unique`, the constructor must be
  // explicitly invoked. For example:
  //
  // auto proxy = std::make_unique<MemoryDumpProviderProxy>(
  //   MemoryDumpProvider::Name("Name"),
  //   ...);
  class Name {
   public:
    // Purposely not explicit to avoid requiring callers to wrap their string.
    // Since this is consteval the value of `name` is validated at compile time.
    consteval Name(const char* name) : static_name_(name) {
      // This will never actually invoke what's under NOTREACHED(), but
      // NOTREACHED() is invalid in a consteval context so compilation will fail
      // iff the string is invalid.
      if (!trace_event_metrics::IsValidMemoryDumpProviderName(
              histogram_name())) {
        NOTREACHED()
            << "Invalid provider name. Did you add it to the "
               "MemoryDumpProviderName variant in memory/histograms.xml?";
      }
      if (histogram_name() == "AllProviders") {
        NOTREACHED() << "Invalid provider name. 'AllProviders' is reserved.";
      }
    }

    // Return the name passed to the constructor.
    constexpr std::string_view static_name() const { return static_name_; }

    // Return a variant of the name to use in histograms.
    constexpr std::string histogram_name() const {
      std::string name(static_name_);
      size_t pos = 0;
      while ((pos = name.find(':', pos)) != std::string::npos) {
        name[pos] = '_';
      }
      return name;
    }

    // Comparators.
    constexpr friend bool operator==(const Name& a, const Name& b) = default;
    constexpr friend auto operator<=>(const Name& a, const Name& b) = default;

   private:
    std::string_view static_name_;
  };

  MemoryDumpProvider(const MemoryDumpProvider&) = delete;
  MemoryDumpProvider& operator=(const MemoryDumpProvider&) = delete;
  virtual ~MemoryDumpProvider() = default;

  // Called by the MemoryDumpManager when generating memory dumps.
  // The |args| specify if the embedder should generate light/heavy dumps on
  // dump requests. The embedder should return true if the |pmd| was
  // successfully populated, false if something went wrong and the dump should
  // be considered invalid.
  // (Note, the MemoryDumpManager has a fail-safe logic which will disable the
  // MemoryDumpProvider for the entire trace session if it fails consistently).
  virtual bool OnMemoryDump(const MemoryDumpArgs& args,
                            ProcessMemoryDump* pmd) = 0;

 protected:
  MemoryDumpProvider() = default;
};

}  // namespace trace_event
}  // namespace base

#endif  // BASE_TRACE_EVENT_MEMORY_DUMP_PROVIDER_H_
