| // 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_CLIENT_STEP_THREAD_CONTROLLER_H_ | |
| #define SRC_DEVELOPER_DEBUG_ZXDB_CLIENT_STEP_THREAD_CONTROLLER_H_ | |
| #include <optional> | |
| #include "src/developer/debug/zxdb/client/frame_fingerprint.h" | |
| #include "src/developer/debug/zxdb/client/step_mode.h" | |
| #include "src/developer/debug/zxdb/client/thread_controller.h" | |
| #include "src/developer/debug/zxdb/common/address_ranges.h" | |
| #include "src/developer/debug/zxdb/symbols/file_line.h" | |
| namespace zxdb { | |
| class FinishThreadController; | |
| // Implements a low-level "step into" command. It knows how to step by source lines, over a range of | |
| // addresses, or by single instruction. | |
| // | |
| // This is the main low-level thread controller used by other ones. Generally programmatic uses | |
| // (e.g. from with "step over") will use this class. It will not generally be used directly, a | |
| // user-level "step into" should use the StepIntoThreadController which provides some additional | |
| // functionality. | |
| // | |
| // When stepping by file/line, this class will generate synthetic exceptions and adjust the stack to | |
| // simulate stepping into inline function calls (even though there is no actual call instruction). | |
| class StepThreadController : public ThreadController { | |
| public: | |
| // Constructor for kSourceLine and kInstruction modes. It will initialize itself to the thread's | |
| // current position when the thread is attached. | |
| explicit StepThreadController(StepMode mode); | |
| // Steps given the source file/line. | |
| explicit StepThreadController(const FileLine& line); | |
| // Constructor for a kAddressRange mode (the mode is implicit). Continues execution as long as the | |
| // IP is in range. | |
| explicit StepThreadController(AddressRanges ranges); | |
| ~StepThreadController() override; | |
| // Controls whether the thread will stop when it encounters code with no symbols. When false, if a | |
| // function is called with no symbols, it will automatically step out or through it. | |
| // | |
| // This only affects "step by line" mode which is symbol-aware. | |
| bool stop_on_no_symbols() const { return stop_on_no_symbols_; } | |
| void set_stop_on_no_symbols(bool stop) { stop_on_no_symbols_ = stop; } | |
| // ThreadController implementation. | |
| void InitWithThread(Thread* thread, fit::callback<void(const Err&)> cb) override; | |
| ContinueOp GetContinueOp() override; | |
| StopOp OnThreadStop(debug_ipc::ExceptionType stop_type, | |
| const std::vector<fxl::WeakPtr<Breakpoint>>& hit_breakpoints) override; | |
| const char* GetName() const override { return "Step"; } | |
| private: | |
| enum class StepIntoInline { | |
| // Actually performs the inline step, modifying the hidden ambiguous Stack items as necessary. | |
| kCommit, | |
| // Does the operations to compute whether an inline step can be completed and returns the | |
| // corresponding result, but does not actually change any state. | |
| kQuery | |
| }; | |
| // Attempts to step into an inline function that starts and the current stack addresss. This will | |
| // make it look like the user stepped into the inline function even though no code was executed. | |
| // | |
| // If there is an inline to step into, this will fix up the current stack to appear as if the | |
| // inline function is stepped into and return true. False means there was not an inline function | |
| // starting at the current address. | |
| bool TrySteppingIntoInline(StepIntoInline command); | |
| // Version of OnThreadStop that handles the case where the current code has no line information. | |
| StopOp OnThreadStopOnUnsymbolizedCode(); | |
| StepMode step_mode_; | |
| // When step_mode_ == kSourceLine, this represents the line information and the stack fingerprint | |
| // of where stepping started. The file/line may be given in the constructor or we may need to | |
| // compute it upon init from the current location (whether it needs setting is encoded by the | |
| // optional). | |
| std::optional<FileLine> file_line_; | |
| FrameFingerprint original_frame_fingerprint_; | |
| // Range of addresses we're currently stepping in. This may change when we're stepping over source | |
| // lines and wind up in a region with no line numbers. It will be empty when stepping by | |
| // instruction. | |
| AddressRanges current_ranges_; | |
| bool stop_on_no_symbols_ = false; | |
| // Used to step out of unsymbolized functions. When non-null, the user wants to skip unsymbolized | |
| // code and has stepped into an unsymbolized function. | |
| std::unique_ptr<FinishThreadController> finish_unsymolized_function_; | |
| }; | |
| } // namespace zxdb | |
| #endif // SRC_DEVELOPER_DEBUG_ZXDB_CLIENT_STEP_THREAD_CONTROLLER_H_ |