// Copyright 2019 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_STACK_COPIER_H_
#define BASE_PROFILER_STACK_COPIER_H_

#include <stdint.h>

#include "base/base_export.h"
#include "base/profiler/register_context.h"
#include "base/time/time.h"

namespace base {

class StackBuffer;

// StackCopier causes a thread to be suspended, copies its stack, and resumes
// the thread's execution. It's intended to provide an abstraction over stack
// copying techniques where the thread suspension is performed directly by the
// profiler thread (Windows and Mac platforms) vs. where the thread suspension
// is performed by the OS through signals (Android).
class BASE_EXPORT StackCopier {
 public:
  // Interface that may be implemented by the caller of CopyStack() to receive a
  // callback when the stack is copied, while the target thread is suspended.
  class BASE_EXPORT Delegate {
   public:
    virtual ~Delegate() {}

    // Invoked at the time the stack is copied.
    // IMPORTANT NOTE: to avoid deadlock implementations of this interface must
    // not invoke any non-reentrant code that is also invoked by the target
    // thread. In particular, it may not perform any heap allocation or
    // deallocation, including indirectly via use of DCHECK/CHECK or other
    // logging statements.
    virtual void OnStackCopy() = 0;

    // Invoked after the stack has been copied and the target thread resumed.
    virtual void OnThreadResume() = 0;
  };

  virtual ~StackCopier();

  // Copies the thread's register context into |thread_context|, the stack into
  // |stack_buffer|, and the top of stack address into |stack_top|. Records
  // |timestamp| at the time the stack was copied. delegate->OnStackCopy() will
  // be invoked while the thread is suspended. Returns true if successful.
  virtual bool CopyStack(StackBuffer* stack_buffer,
                         uintptr_t* stack_top,
                         TimeTicks* timestamp,
                         RegisterContext* thread_context,
                         Delegate* delegate) = 0;

 protected:
  // If the value at |pointer| points to the original stack, rewrite it to point
  // to the corresponding location in the copied stack.
  //
  // NO HEAP ALLOCATIONS.
  static uintptr_t RewritePointerIfInOriginalStack(
      const uint8_t* original_stack_bottom,
      const uintptr_t* original_stack_top,
      const uint8_t* stack_copy_bottom,
      uintptr_t pointer);

  // Copies the stack to a buffer while rewriting possible pointers to locations
  // within the stack to point to the corresponding locations in the copy. This
  // is necessary to handle stack frames with dynamic stack allocation, where a
  // pointer to the beginning of the dynamic allocation area is stored on the
  // stack and/or in a non-volatile register.
  //
  // Eager rewriting of anything that looks like a pointer to the stack, as done
  // in this function, does not adversely affect the stack unwinding. The only
  // other values on the stack the unwinding depends on are return addresses,
  // which should not point within the stack memory. The rewriting is guaranteed
  // to catch all pointers because the stacks are guaranteed by the ABI to be
  // sizeof(uintptr_t*) aligned.
  //
  // |original_stack_bottom| and |original_stack_top| are different pointer
  // types due on their differing guaranteed alignments -- the bottom may only
  // be 1-byte aligned while the top is aligned to double the pointer width.
  //
  // Returns a pointer to the bottom address in the copied stack. This value
  // matches the alignment of |original_stack_bottom| to ensure that the stack
  // contents have the same alignment as in the original stack. As a result the
  // value will be different than |stack_buffer_bottom| if
  // |original_stack_bottom| is not aligned to double the pointer width.
  //
  // NO HEAP ALLOCATIONS.
  static const uint8_t* CopyStackContentsAndRewritePointers(
      const uint8_t* original_stack_bottom,
      const uintptr_t* original_stack_top,
      int platform_stack_alignment,
      uintptr_t* stack_buffer_bottom);
};

}  // namespace base

#endif  // BASE_PROFILER_STACK_COPIER_H_
