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

#ifndef BASE_PROFILER_WIN32_STACK_FRAME_UNWINDER_H_
#define BASE_PROFILER_WIN32_STACK_FRAME_UNWINDER_H_

#include <windows.h>

#include <memory>

#include "base/base_export.h"
#include "base/macros.h"
#include "base/profiler/module_cache.h"
#include "build/build_config.h"

namespace base {

#if !defined(ARCH_CPU_64_BITS)
// Allows code to compile for x86. Actual support for x86 will require either
// refactoring these interfaces or separate architecture-specific interfaces.
struct RUNTIME_FUNCTION {
  DWORD BeginAddress;
  DWORD EndAddress;
};
using PRUNTIME_FUNCTION = RUNTIME_FUNCTION*;
#endif  // !defined(ARCH_CPU_64_BITS)

inline ULONG64 ContextPC(CONTEXT* context) {
#if defined(ARCH_CPU_X86_64)
  return context->Rip;
#elif defined(ARCH_CPU_X86)
  return context->Eip;
#elif defined(ARCH_CPU_ARM64)
  return context->Pc;
#else
#error Unsupported Windows Arch
#endif
}

// This class is not used while the target thread is suspended, so may allocate
// from the default heap.
class BASE_EXPORT Win32StackFrameUnwinder {
 public:
  // Interface for Win32 unwind-related functionality this class depends
  // on. Provides a seam for testing.
  class BASE_EXPORT UnwindFunctions {
   public:
    UnwindFunctions(const UnwindFunctions&) = delete;
    UnwindFunctions& operator=(const UnwindFunctions&) = delete;

    virtual ~UnwindFunctions();

    virtual PRUNTIME_FUNCTION LookupFunctionEntry(DWORD64 program_counter,
                                                  PDWORD64 image_base) = 0;
    virtual void VirtualUnwind(DWORD64 image_base,
                               DWORD64 program_counter,
                               PRUNTIME_FUNCTION runtime_function,
                               CONTEXT* context) = 0;

   protected:
    UnwindFunctions();
  };

  explicit Win32StackFrameUnwinder();

  Win32StackFrameUnwinder(const Win32StackFrameUnwinder&) = delete;
  Win32StackFrameUnwinder& operator=(const Win32StackFrameUnwinder&) = delete;

  ~Win32StackFrameUnwinder();

  // Attempts to unwind the frame represented by |context|, where the
  // instruction pointer is known to be in |module|. Updates |context| if
  // successful.
  bool TryUnwind(bool at_top_frame,
                 CONTEXT* context,
                 const ModuleCache::Module* module);

 private:
  // This function is for internal and test purposes only.
  Win32StackFrameUnwinder(std::unique_ptr<UnwindFunctions> unwind_functions);
  friend class Win32StackFrameUnwinderTest;

  std::unique_ptr<UnwindFunctions> unwind_functions_;
};

}  // namespace base

#endif  // BASE_PROFILER_WIN32_STACK_FRAME_UNWINDER_H_
