1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115

base / profiler / unwinder.h [blame]

// Copyright 2019 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_PROFILER_UNWINDER_H_
#define BASE_PROFILER_UNWINDER_H_

#include <vector>

#include "base/base_export.h"
#include "base/memory/raw_ptr.h"
#include "base/profiler/frame.h"
#include "base/profiler/module_cache.h"
#include "base/profiler/register_context.h"

namespace base {

// The result of attempting to unwind stack frames.
enum class UnwindResult {
  // The end of the stack was reached successfully.
  kCompleted,

  // The walk reached a frame that it doesn't know how to unwind, but might be
  // unwindable by the other native/aux unwinder.
  kUnrecognizedFrame,

  // The walk was aborted and is not resumable.
  kAborted,
};

// State information from stack capture. This may capture information from
// the `Unwinder` that is outside of the scope of the capture stack. This
// is allocated (and destroyed) per each stack capture and is provided
// to the Unwinder on each of its virtuals.
class BASE_EXPORT UnwinderStateCapture {
 public:
  virtual ~UnwinderStateCapture();
};

// Unwinder provides an interface for stack frame unwinder implementations for
// use with the StackSamplingProfiler. Initialize() must be invoked prior to the
// invocation of any other function on the interface. The profiler is expected
// to call CanUnwind() to determine if the Unwinder thinks it can unwind from
// the frame represented by the context values, then TryUnwind() to attempt the
// unwind.
class BASE_EXPORT Unwinder {
 public:
  virtual ~Unwinder() = default;

  // Initializes this unwinder to use |module_cache| in subsequent methods
  // UpdateModules() and TryUnwinder(). This unwinder may add any modules it
  // recognizes or register a module factory to the ModuleCache. |module_cache|
  // must outlive this Unwinder.
  void Initialize(ModuleCache* module_cache);

  // Invoked before the stack is captured. This can allocate any memory needed
  // to capture state in `OnStateCapture`. This is invoked on the stack sampling
  // thread.
  virtual std::unique_ptr<UnwinderStateCapture> CreateUnwinderStateCapture();

  // Invoked at the time the stack is captured. IMPORTANT NOTE: this function is
  // invoked while the target thread is suspended. To avoid deadlock it 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 OnStackCapture(UnwinderStateCapture* capture_state) {}

  // Allows the unwinder to update ModuleCache with any modules it's responsible
  // for. Invoked for each sample between OnStackCapture() and the initial
  // invocations of CanUnwindFrom()/TryUnwind().
  virtual void UpdateModules(UnwinderStateCapture* capture_state) {}

  // Returns true if the unwinder recognizes the code referenced by
  // |current_frame| as code from which it should be able to unwind. When
  // multiple unwinders are in use, each should return true for a disjoint set
  // of frames. Note that if the unwinder returns true it may still legitmately
  // fail to unwind; e.g. in the case of a native unwind for a function that
  // doesn't have unwind information.
  virtual bool CanUnwindFrom(const Frame& current_frame) const = 0;

  // Attempts to unwind the frame represented by the context values.
  // Walks the native frames on the stack pointed to by the stack pointer in
  // `thread_context`, appending the frames to `stack`. When invoked
  // stack->back() contains the frame corresponding to the state in
  // `thread_context`.
  // `capture_state` was allocated in the call to `CreateUnwinderStateCapture`
  // Precondition: RegisterContextStackPointer(thread_context) is less than
  // `stack_top`.
  // Postcondition: If the implementation returns UNRECOGNIZED_FRAME, indicating
  // that it successfully unwound, RegisterContextStackPointer(thread_context)
  // is greater than the previous value and less than `stack_top`.
  virtual UnwindResult TryUnwind(UnwinderStateCapture* capture_state,
                                 RegisterContext* thread_context,
                                 uintptr_t stack_top,
                                 std::vector<Frame>* stack) = 0;

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

 protected:
  Unwinder() = default;

  // Invoked to allow the unwinder to add any modules it recognizes or register
  // a module factory to the ModuleCache.
  virtual void InitializeModules() {}

  ModuleCache* module_cache() const { return module_cache_; }

 private:
  raw_ptr<ModuleCache> module_cache_ = nullptr;
};

}  // namespace base

#endif  // BASE_PROFILER_UNWINDER_H_