| // 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_CONSOLE_ANALYZE_MEMORY_H_ |
| #define SRC_DEVELOPER_DEBUG_ZXDB_CONSOLE_ANALYZE_MEMORY_H_ |
| |
| #include <map> |
| #include <string> |
| #include <vector> |
| |
| #include "lib/fit/function.h" |
| #include "src/developer/debug/ipc/records.h" |
| #include "src/developer/debug/shared/register_id.h" |
| #include "src/developer/debug/shared/register_info.h" |
| #include "src/developer/debug/shared/register_value.h" |
| #include "src/developer/debug/zxdb/client/memory_dump.h" |
| #include "src/lib/fxl/memory/ref_counted.h" |
| #include "src/lib/fxl/memory/weak_ptr.h" |
| |
| namespace zxdb { |
| |
| class Err; |
| class Frame; |
| class OutputBuffer; |
| class Process; |
| class Stack; |
| class Thread; |
| |
| struct AnalyzeMemoryOptions { |
| // Required. |
| Process* process = nullptr; |
| |
| // Optional. If provided, the current thread registers and stack frames will be queried and the |
| // dump will be annotated with matches if they're available. |
| Thread* thread = nullptr; |
| |
| // The address to begin dumping. |
| uint64_t begin_address = 0; |
| |
| // Number of bytes following begin_address to analyze. |
| uint32_t bytes_to_read = 0; |
| }; |
| |
| // Runs a stack analysis on the given thread. When the analysis is complete, the callback will be |
| // issued with the output and the address immediately following the last one analyzed (this is so |
| // the caller knows the aligned address to continue at if desired). |
| // |
| // On error, the Err will be set, the output buffer will be empty, and next_addr will be 0. |
| void AnalyzeMemory( |
| const AnalyzeMemoryOptions& opts, |
| fit::callback<void(const Err& err, OutputBuffer analysis, uint64_t next_addr)> cb); |
| |
| namespace internal { |
| |
| // Implementation of the memory analysis. Consumers should use AnalyzeMemory above, this is in the |
| // header so it can be unit tested more easily. |
| // |
| // This class is refcounted and manages its own lifetime across various asynchronous callbacks to |
| // issue the final complete callback. |
| class MemoryAnalysis : public fxl::RefCountedThreadSafe<MemoryAnalysis> { |
| public: |
| using Callback = fit::callback<void(const Err& err, OutputBuffer analysis, uint64_t next_addr)>; |
| |
| // Opts is passed again so we don't have to save it in the constructor, which is unsafe (the |
| // process and thread pointers aren't weak and may disappear). |
| void Schedule(const AnalyzeMemoryOptions& opts); |
| |
| // Tests can call these functions to manually provide the data that would normally be provided via |
| // IPC call. To use, call before "Schedule". Note: Frame 0's registers should be set first. |
| void SetAspace(std::vector<debug_ipc::AddressRegion> aspace); |
| void SetStack(const Stack& stack); |
| void SetMemory(MemoryDump dump); |
| |
| private: |
| FRIEND_REF_COUNTED_THREAD_SAFE(MemoryAnalysis); |
| FRIEND_MAKE_REF_COUNTED(MemoryAnalysis); |
| |
| MemoryAnalysis(const AnalyzeMemoryOptions& opts, Callback cb); |
| ~MemoryAnalysis() = default; |
| |
| void DoAnalysis(); |
| |
| // Request callbacks. |
| void OnAspace(const Err& err, std::vector<debug_ipc::AddressRegion> aspace); |
| void OnMemory(const Err& err, MemoryDump dump); |
| void OnFrames(fxl::WeakPtr<Thread> thread); |
| |
| // Returns true when all asynchronous things are available. |
| bool HasEverything() const; |
| |
| // Call when something goes wrong to issue the callback with the given error printed to it. |
| void IssueError(const Err& err); |
| |
| // Saves the registers for the given frame index. |
| void AddRegisters(int frame_no, const std::vector<debug::RegisterValue>& regs); |
| |
| // Adds to the annotations map the given description for the given address. If there is already an |
| // annotation at that address, adds to the end. |
| void AddAnnotation(uint64_t address, const std::string& str); |
| |
| // Retrieves the data value at the given address. Returns true if there was data, or false if the |
| // memory is invalid. |
| bool GetData(uint64_t address, uint64_t* out_value) const; |
| |
| // Returns a formatted string representing all annotations in the range (end non-inclusive). |
| OutputBuffer GetAnnotationsBetween(uint64_t address_begin, uint64_t address_end) const; |
| |
| // Returns a formatted string representing with the given data value points to (if possible). |
| // Returns an empty string otherwise. |
| OutputBuffer GetPointedToAnnotation(uint64_t data) const; |
| |
| // May become invalid across the async callbacks, check before using. |
| fxl::WeakPtr<Process> process_; |
| |
| // This map collects the address of everything we want to annotate in the stack. This will include |
| // registers and frame pointers. |
| std::map<uint64_t, std::string> annotations_; |
| |
| uint64_t begin_address_; |
| uint32_t bytes_to_read_; |
| |
| Callback callback_; |
| |
| MemoryDump memory_; |
| |
| std::vector<debug_ipc::AddressRegion> aspace_; |
| |
| // Set when an asynchronous operation has failed. The callback will already have been issued, so |
| // everything should immediately exit when this flag is set. |
| bool aborted_ = false; |
| |
| // The things that need to be queried asynchronously before dumping. |
| bool have_memory_ = false; |
| bool have_frames_ = false; |
| bool have_aspace_ = false; |
| |
| // The register values from frame 0. See AddRegisters(). |
| std::map<debug::RegisterID, uint64_t> frame_0_regs_; |
| }; |
| |
| } // namespace internal |
| } // namespace zxdb |
| |
| #endif // SRC_DEVELOPER_DEBUG_ZXDB_CONSOLE_ANALYZE_MEMORY_H_ |