// Copyright 2018 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 COMPONENTS_TRACING_COMMON_STACK_UNWINDER_ANDROID_H_
#define COMPONENTS_TRACING_COMMON_STACK_UNWINDER_ANDROID_H_

#include <ucontext.h>

#include <map>
#include <vector>

#include "base/debug/proc_maps_linux.h"
#include "base/profiler/native_stack_sampler.h"
#include "base/threading/platform_thread.h"
#include "components/tracing/tracing_export.h"

namespace jni_generator {
struct JniJavaCallContextUnchecked;
}
struct unw_context_t;

namespace tracing {

// Utility to unwind stacks for current thread on ARM devices. Contains ability
// to unwind stacks based on EHABI section in Android libraries and using the
// custom stack unwind information in Chrome. This works on top of
// base::trace_event::CFIBacktraceAndroid, which unwinds Chrome only stacks.
// This class does not provide any thread safety guarantees. It is also unsafe
// to use multiple instances of this class at the same time due to signal
// handling. So, the client must ensure synchronization between multiple
// instances of this class.
class TRACING_EXPORT StackUnwinderAndroid {
 public:
  using JniMarker = jni_generator::JniJavaCallContextUnchecked;

  // Whether to use libunwind for android framework frames.
  static const bool kUseLibunwind;

  StackUnwinderAndroid();
  ~StackUnwinderAndroid();

  // Initializes the unwinder for current process. It finds all loaded libraries
  // in current process and also initializes CFIBacktraceAndroid, with file IO.
  // Can be called multiple times, to update the loaded modules.
  void Initialize();

  // Unwinds stack frames for current thread and stores the program counters in
  // |out_trace|, and returns the number of frames stored.
  size_t TraceStack(const void** out_trace, size_t max_depth) const;

  // Same as above function, but pauses the thread with the given |tid| and then
  // unwinds. |tid| should not be current thread's.
  size_t TraceStack(base::PlatformThreadId tid,
                    base::NativeStackSampler::StackBuffer* stack_buffer,
                    const void** out_trace,
                    size_t max_depth) const;

  // Returns the end address of the memory map with given |addr|.
  uintptr_t GetEndAddressOfRegion(uintptr_t addr) const;

  // Returns true if the given |pc| was part of any mapped segments in the
  // process.
  bool IsAddressMapped(uintptr_t pc) const;

  bool is_initialized() const { return is_initialized_; }

 private:
  // Sends a SIGURG signal to the thread with id |tid| and copies the stack
  // segment of the thread, along with register context. Returns true on
  // success.
  bool SuspendThreadAndRecordStack(
      base::PlatformThreadId tid,
      base::NativeStackSampler::StackBuffer* stack_buffer,
      uintptr_t* sp,
      size_t* stack_size,
      unw_context_t* context,
      ucontext_t* signal_context) const;

  // Replaces any pointers to the old stack to point to the new stack segment.
  // Returns the jni markers found on stack while scanning stack for pointers.
  std::vector<const JniMarker*> RewritePointersAndGetMarkers(
      base::NativeStackSampler::StackBuffer* stack_buffer,
      uintptr_t sp,
      size_t stack_size) const;

  bool is_initialized_ = false;

  // Stores all the memory mapped regions in the current process, including all
  // the files mapped and anonymous regions. This data could be stale, but the
  // error caused by changes in library loads would be missing stackframes and
  // is acceptable.
  std::vector<base::debug::MappedMemoryRegion> regions_;
};

}  // namespace tracing

#endif  // COMPONENTS_TRACING_COMMON_STACK_UNWINDER_ANDROID_H_
