| // Copyright 2018 The Fuchsia 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 SRC_DEVELOPER_DEBUG_ZXDB_SYMBOLS_SYMBOL_DATA_PROVIDER_H_ |
| #define SRC_DEVELOPER_DEBUG_ZXDB_SYMBOLS_SYMBOL_DATA_PROVIDER_H_ |
| |
| #include <stdint.h> |
| |
| #include <map> |
| #include <optional> |
| #include <vector> |
| |
| #include "lib/fit/function.h" |
| #include "src/developer/debug/shared/arch.h" |
| #include "src/developer/debug/shared/register_id.h" |
| #include "src/developer/debug/zxdb/common/err_or.h" |
| #include "src/developer/debug/zxdb/common/int128_t.h" |
| #include "src/developer/debug/zxdb/symbols/symbol.h" |
| #include "src/lib/containers/cpp/array_view.h" |
| #include "src/lib/fxl/memory/ref_counted.h" |
| |
| namespace zxdb { |
| |
| class Err; |
| class SymbolContext; |
| |
| // This interface is how the debugger backend provides memory and register data to the symbol system |
| // to evaluate expressions. |
| // |
| // By default, this class returns no information. In this form it can be used to evaluate |
| // expressions in contexts without a running process. To access data, most callers will want to use |
| // the implementation associated with a frame or a process. |
| // |
| // Registers are the most commonly accessed data type and they are often available synchronously. So |
| // the interface provides a synchronous main register getter function and a fallback asynchronous |
| // one. They are separated to avoid overhead of closure creation in the synchronous case, and to |
| // avoid having a callback that's never issued. |
| // |
| // This object is reference counted since evaluating a DWARF expression is asynchronous. |
| class SymbolDataProvider : public fxl::RefCountedThreadSafe<SymbolDataProvider> { |
| public: |
| using GetMemoryCallback = fit::callback<void(const Err&, std::vector<uint8_t>)>; |
| |
| using GetTLSSegmentCallback = fit::callback<void(ErrOr<uint64_t>)>; |
| |
| // The Err indicates whether the operation was successful. Common failure cases are the thread is |
| // running or this register wasn't saved on the stack frame. |
| using GetRegisterCallback = fit::callback<void(const Err&, std::vector<uint8_t>)>; |
| |
| // CAllback for multiple register values. The map will contain the requested register values when |
| // the error is not set. |
| using GetRegistersCallback = |
| fit::callback<void(const Err&, std::map<debug::RegisterID, std::vector<uint8_t>>)>; |
| |
| using GetFrameBaseCallback = fit::callback<void(const Err&, uint64_t value)>; |
| |
| using WriteCallback = fit::callback<void(const Err&)>; |
| |
| virtual debug::Arch GetArch(); |
| |
| // Returns a SymbolDataProvider that will retrieve register values from the entrypoint of the |
| // current function. |
| // |
| // Returns null if there is no entrypoint (like maybe there's no current function) or can't have |
| // registers retrieved from it. This data provider is used to evaluate DW_OP_entry_value |
| // expressions. |
| virtual fxl::RefPtr<SymbolDataProvider> GetEntryDataProvider() const; |
| |
| // Request for synchronous register data if possible. |
| // |
| // If the value is not synchronously known, the return value will be std::nullopt. In this case, |
| // GetRegisterAsync() should be called to retrieve the value. |
| // |
| // The return value can be an empty view if the implementation knows synchronously that we don't |
| // know the value. An example is an unsaved register in a non-topmost stack frame. |
| // |
| // On successful data return, the data is owned by the implementor and should not be saved. |
| virtual std::optional<containers::array_view<uint8_t>> GetRegister(debug::RegisterID id); |
| |
| // Request for register data with an asynchronous callback. The callback will be issued when the |
| // register data is available. |
| virtual void GetRegisterAsync(debug::RegisterID id, GetRegisterCallback callback); |
| |
| // A wrapper around GetRegister and GetRegisterAsync that collects all the requested register |
| // values. The callback will be issued with all collected values. If all values are known |
| // synchronously, the callback will be called reentrantly. |
| void GetRegisters(const std::vector<debug::RegisterID>& regs, GetRegistersCallback cb); |
| |
| // Writes the given canonical register ID. |
| // |
| // This must be a canonical register as identified by debug::RegisterInfo::canonical_id, which |
| // means that it's a whole hardware register and needs no shifting nor masking. |
| virtual void WriteRegister(debug::RegisterID id, std::vector<uint8_t> data, WriteCallback cb); |
| |
| // Synchronously returns the frame base pointer if possible. As with GetRegister, if this is not |
| // available the implementation should call GetFrameBaseAsync(). |
| // |
| // The frame base is the DW_AT_frame_base for the current function. Often this will be the "base |
| // pointer" register in the CPU, but could be other registers, especially if compiled without full |
| // stack frames. Getting this value may involve evaluating another DWARF expression which may or |
| // may not be asynchronous. |
| virtual std::optional<uint64_t> GetFrameBase(); |
| |
| // Asynchronous version of GetFrameBase. |
| virtual void GetFrameBaseAsync(GetFrameBaseCallback callback); |
| |
| // Returns the canonical frame address of the current frame. Returns 0 if it is not known. See |
| // Frame::GetCanonicalFrameAddress(). |
| virtual uint64_t GetCanonicalFrameAddress() const; |
| |
| // Synchronously returns the debug address for a symbol context if available. |
| virtual std::optional<uint64_t> GetDebugAddressForContext(const SymbolContext& context) const; |
| |
| // Get the address of the TLS segment for the given context. The TLS segment is where thread-local |
| // variables live. |
| virtual void GetTLSSegment(const SymbolContext& symbol_context, GetTLSSegmentCallback cb); |
| |
| // Request to retrieve a memory block from the debugged process. On success, the implementation |
| // will call the callback with the retrieved data pointer. |
| // |
| // It will read valid memory up to the maximum. It will do short reads if it encounters invalid |
| // memory, so the result may be shorter than requested or empty (if the first byte is invalid). |
| virtual void GetMemoryAsync(uint64_t address, uint32_t size, GetMemoryCallback callback); |
| |
| // Asynchronously writes to the given memory. The callback will be issued when the write is |
| // complete. |
| virtual void WriteMemory(uint64_t address, std::vector<uint8_t> data, WriteCallback cb); |
| |
| protected: |
| FRIEND_MAKE_REF_COUNTED(SymbolDataProvider); |
| FRIEND_REF_COUNTED_THREAD_SAFE(SymbolDataProvider); |
| |
| SymbolDataProvider() = default; |
| virtual ~SymbolDataProvider() = default; |
| }; |
| |
| } // namespace zxdb |
| |
| #endif // SRC_DEVELOPER_DEBUG_ZXDB_SYMBOLS_SYMBOL_DATA_PROVIDER_H_ |