// 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_ARM_CFI_TABLE_H_
#define BASE_PROFILER_ARM_CFI_TABLE_H_

#include <memory>

#include "base/containers/buffer_iterator.h"
#include "base/containers/span.h"
#include "base/macros.h"
#include "third_party/abseil-cpp/absl/types/optional.h"

namespace base {

// This class implements methods to read and parse the arm Call Frame
// Information (CFI) for Chrome, which contains tables for unwinding Chrome
// functions. For detailed description of the format, see
// extract_unwind_tables.py.
class BASE_EXPORT ArmCFITable {
 public:
  // The CFI information that correspond to an instruction. {0, 0} is a valid
  // entry and should be interpreted as the default rule:
  // .cfa: sp; .cfa = lr (link register).
  struct FrameEntry {
    // The offset of the call frame address (CFA) of previous function, relative
    // to the current stack pointer. Rule for unwinding CFA:
    // .cfa: sp + cfa_offset.
    uint16_t cfa_offset = 0;
    // The offset of location of return address (RA), relative to the previous
    // call frame address. Rule for unwinding RA:
    // .ra = *(cfa - ra_offset).
    uint16_t ra_offset = 0;
  };

  // Parses |cfi_data| and creates a ArmCFITable that reads from it.
  // |cfi_data| is required to remain valid for the lifetime of the object.
  static std::unique_ptr<ArmCFITable> Parse(span<const uint8_t> cfi_data);

  ArmCFITable(span<const uint32_t> function_addresses,
              span<const uint16_t> entry_data_indices,
              span<const uint8_t> entry_data);

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

  ~ArmCFITable();

  // Finds the CFI row for the given |address| in terms of offset from the
  // start of the current binary. Concurrent calls are thread safe.
  absl::optional<FrameEntry> FindEntryForAddress(uintptr_t address) const;

  size_t GetTableSizeForTesting() const { return function_addresses_.size(); }

 private:
  // The UNW_INDEX table allows readers to map functions start addresses to
  // that function's respective entry in the UNW_DATA table.
  //   - A function's start address is at 0x123, and
  //   - function_addresses_[2] == 0x123, and
  //   - entry_data_indices_[2] = 42, then
  //   - entry_data_[42] is the corresponding entry in the UNW_DATA table for
  //     the function with the start address of 0x123
  //
  // Note that function_addresses is sorted to facilitate easy lookup.
  const span<const uint32_t> function_addresses_;
  const span<const uint16_t> entry_data_indices_;

  // A reference to the UNW_DATA table. Each entry in the UNW_DATA table
  // corresponds to a function, which in turn corresponds to an array of
  // CFIDataRows. (see arm_cfi_reader.cc).
  const span<const uint8_t> entry_data_;
};

}  // namespace base

#endif  // BASE_PROFILER_ARM_CFI_TABLE_H_
