| # Copyright 2022-2025 Free Software Foundation, Inc. |
| |
| # This program is free software; you can redistribute it and/or modify |
| # it under the terms of the GNU General Public License as published by |
| # the Free Software Foundation; either version 3 of the License, or |
| # (at your option) any later version. |
| # |
| # This program is distributed in the hope that it will be useful, |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| # GNU General Public License for more details. |
| # |
| # You should have received a copy of the GNU General Public License |
| # along with this program. If not, see <http://www.gnu.org/licenses/>. |
| |
| import gdb |
| |
| from .events import exec_and_expect_stop |
| from .server import capability, request |
| from .startup import DAPException, exec_and_log, in_gdb_thread |
| from .state import set_thread |
| |
| |
| # Helper function to set the current thread and the scheduler-locking |
| # mode. Returns True if scheduler-locking was successfully set to |
| # 'on', False in all other cases, including error. When SELECT is |
| # True, also select that thread's newest frame. |
| @in_gdb_thread |
| def _handle_thread_step(thread_id, single_thread, select=False): |
| # Ensure we're going to step the correct thread. |
| set_thread(thread_id) |
| if single_thread: |
| result = True |
| arg = "on" |
| else: |
| result = False |
| arg = "off" |
| try: |
| # This can fail, depending on the target, so catch any error. |
| exec_and_log("set scheduler-locking " + arg) |
| except DAPException: |
| result = False |
| # Other DAP code may select a frame, and the "finish" command uses |
| # the selected frame. |
| if select: |
| gdb.newest_frame().select() |
| return result |
| |
| |
| @request("next", response=False) |
| def next( |
| *, threadId: int, singleThread: bool = False, granularity: str = "statement", **args |
| ): |
| _handle_thread_step(threadId, singleThread) |
| cmd = "next" |
| if granularity == "instruction": |
| cmd += "i" |
| exec_and_expect_stop(cmd) |
| |
| |
| @capability("supportsSteppingGranularity") |
| @capability("supportsSingleThreadExecutionRequests") |
| @request("stepIn", response=False) |
| def step_in( |
| *, threadId: int, singleThread: bool = False, granularity: str = "statement", **args |
| ): |
| _handle_thread_step(threadId, singleThread) |
| cmd = "step" |
| if granularity == "instruction": |
| cmd += "i" |
| exec_and_expect_stop(cmd) |
| |
| |
| @request("stepOut") |
| def step_out(*, threadId: int, singleThread: bool = False, **args): |
| _handle_thread_step(threadId, singleThread, True) |
| exec_and_expect_stop("finish &") |
| |
| |
| @request("continue") |
| def continue_request(*, threadId: int, singleThread: bool = False, **args): |
| locked = _handle_thread_step(threadId, singleThread) |
| exec_and_expect_stop("continue &") |
| return {"allThreadsContinued": not locked} |