//===-- llvm/Debuginfod/Debuginfod.h - Debuginfod client --------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file contains several declarations for the debuginfod client and
/// server. The client functions are getDefaultDebuginfodUrls,
/// getCachedOrDownloadArtifact, and several convenience functions for specific
/// artifact types: getCachedOrDownloadSource, getCachedOrDownloadExecutable,
/// and getCachedOrDownloadDebuginfo. For the server, this file declares the
/// DebuginfodLogEntry and DebuginfodServer structs, as well as the
/// DebuginfodLog, DebuginfodCollection classes.
///
//===----------------------------------------------------------------------===//

#ifndef LLVM_DEBUGINFOD_DEBUGINFOD_H
#define LLVM_DEBUGINFOD_DEBUGINFOD_H

#include "HTTPServer.h"

#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Object/BuildID.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Mutex.h"
#include "llvm/Support/RWMutex.h"
#include "llvm/Support/Timer.h"

#include <chrono>
#include <condition_variable>
#include <optional>
#include <queue>

namespace llvm {

/// Returns false if a debuginfod lookup can be determined to have no chance of
/// succeeding.
bool canUseDebuginfod();

/// Finds default array of Debuginfod server URLs by checking DEBUGINFOD_URLS
/// environment variable.
SmallVector<StringRef> getDefaultDebuginfodUrls();

/// Finds a default local file caching directory for the debuginfod client,
/// first checking DEBUGINFOD_CACHE_PATH.
Expected<std::string> getDefaultDebuginfodCacheDirectory();

/// Finds a default timeout for debuginfod HTTP requests. Checks
/// DEBUGINFOD_TIMEOUT environment variable, default is 90 seconds (90000 ms).
std::chrono::milliseconds getDefaultDebuginfodTimeout();

/// Fetches a specified source file by searching the default local cache
/// directory and server URLs.
Expected<std::string> getCachedOrDownloadSource(object::BuildIDRef ID,
                                                StringRef SourceFilePath);

/// Fetches an executable by searching the default local cache directory and
/// server URLs.
Expected<std::string> getCachedOrDownloadExecutable(object::BuildIDRef ID);

/// Fetches a debug binary by searching the default local cache directory and
/// server URLs.
Expected<std::string> getCachedOrDownloadDebuginfo(object::BuildIDRef ID);

/// Fetches any debuginfod artifact using the default local cache directory and
/// server URLs.
Expected<std::string> getCachedOrDownloadArtifact(StringRef UniqueKey,
                                                  StringRef UrlPath);

/// Fetches any debuginfod artifact using the specified local cache directory,
/// server URLs, and request timeout (in milliseconds). If the artifact is
/// found, uses the UniqueKey for the local cache file.
Expected<std::string> getCachedOrDownloadArtifact(
    StringRef UniqueKey, StringRef UrlPath, StringRef CacheDirectoryPath,
    ArrayRef<StringRef> DebuginfodUrls, std::chrono::milliseconds Timeout);

class ThreadPool;

struct DebuginfodLogEntry {
  std::string Message;
  DebuginfodLogEntry() = default;
  DebuginfodLogEntry(const Twine &Message);
};

class DebuginfodLog {
  std::mutex QueueMutex;
  std::condition_variable QueueCondition;
  std::queue<DebuginfodLogEntry> LogEntryQueue;

public:
  // Adds a log entry to end of the queue.
  void push(DebuginfodLogEntry Entry);
  // Adds a log entry to end of the queue.
  void push(const Twine &Message);
  // Blocks until there are log entries in the queue, then pops and returns the
  // first one.
  DebuginfodLogEntry pop();
};

/// Tracks a collection of debuginfod artifacts on the local filesystem.
class DebuginfodCollection {
  SmallVector<std::string, 1> Paths;
  sys::RWMutex BinariesMutex;
  StringMap<std::string> Binaries;
  sys::RWMutex DebugBinariesMutex;
  StringMap<std::string> DebugBinaries;
  Error findBinaries(StringRef Path);
  Expected<std::optional<std::string>> getDebugBinaryPath(object::BuildIDRef);
  Expected<std::optional<std::string>> getBinaryPath(object::BuildIDRef);
  // If the collection has not been updated since MinInterval, call update() and
  // return true. Otherwise return false. If update returns an error, return the
  // error.
  Expected<bool> updateIfStale();
  DebuginfodLog &Log;
  ThreadPool &Pool;
  Timer UpdateTimer;
  sys::Mutex UpdateMutex;

  // Minimum update interval, in seconds, for on-demand updates triggered when a
  // build-id is not found.
  double MinInterval;

public:
  DebuginfodCollection(ArrayRef<StringRef> Paths, DebuginfodLog &Log,
                       ThreadPool &Pool, double MinInterval);
  Error update();
  Error updateForever(std::chrono::milliseconds Interval);
  Expected<std::string> findDebugBinaryPath(object::BuildIDRef);
  Expected<std::string> findBinaryPath(object::BuildIDRef);
};

struct DebuginfodServer {
  HTTPServer Server;
  DebuginfodLog &Log;
  DebuginfodCollection &Collection;
  DebuginfodServer(DebuginfodLog &Log, DebuginfodCollection &Collection);
};

} // end namespace llvm

#endif
