1//===-- RestartRequestHandler.cpp -----------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "DAP.h"
10#include "EventHelper.h"
11#include "JSONUtils.h"
12#include "LLDBUtils.h"
13#include "Protocol/ProtocolRequests.h"
14#include "RequestHandler.h"
15#include "llvm/Support/JSON.h"
16#include "llvm/Support/raw_ostream.h"
17
18namespace lldb_dap {
19
20// "RestartRequest": {
21// "allOf": [ { "$ref": "#/definitions/Request" }, {
22// "type": "object",
23// "description": "Restarts a debug session. Clients should only call this
24// request if the corresponding capability `supportsRestartRequest` is
25// true.\nIf the capability is missing or has the value false, a typical
26// client emulates `restart` by terminating the debug adapter first and then
27// launching it anew.",
28// "properties": {
29// "command": {
30// "type": "string",
31// "enum": [ "restart" ]
32// },
33// "arguments": {
34// "$ref": "#/definitions/RestartArguments"
35// }
36// },
37// "required": [ "command" ]
38// }]
39// },
40// "RestartArguments": {
41// "type": "object",
42// "description": "Arguments for `restart` request.",
43// "properties": {
44// "arguments": {
45// "oneOf": [
46// { "$ref": "#/definitions/LaunchRequestArguments" },
47// { "$ref": "#/definitions/AttachRequestArguments" }
48// ],
49// "description": "The latest version of the `launch` or `attach`
50// configuration."
51// }
52// }
53// },
54// "RestartResponse": {
55// "allOf": [ { "$ref": "#/definitions/Response" }, {
56// "type": "object",
57// "description": "Response to `restart` request. This is just an
58// acknowledgement, so no body field is required."
59// }]
60// },
61void RestartRequestHandler::operator()(
62 const llvm::json::Object &request) const {
63 llvm::json::Object response;
64 FillResponse(request, response);
65 if (!dap.target.GetProcess().IsValid()) {
66 response["success"] = llvm::json::Value(false);
67 EmplaceSafeString(obj&: response, key: "message",
68 str: "Restart request received but no process was launched.");
69 dap.SendJSON(json: llvm::json::Value(std::move(response)));
70 return;
71 }
72
73 const llvm::json::Object *arguments = request.getObject(K: "arguments");
74 if (arguments) {
75 // The optional `arguments` field in RestartRequest can contain an updated
76 // version of the launch arguments. If there's one, use it.
77 if (const llvm::json::Value *restart_arguments =
78 arguments->get(K: "arguments")) {
79 protocol::LaunchRequestArguments updated_arguments;
80 llvm::json::Path::Root root;
81 if (!fromJSON(*restart_arguments, updated_arguments, root)) {
82 response["success"] = llvm::json::Value(false);
83 EmplaceSafeString(
84 obj&: response, key: "message",
85 str: llvm::formatv(Fmt: "Failed to parse updated launch arguments: {0}",
86 Vals: llvm::toString(E: root.getError()))
87 .str());
88 dap.SendJSON(json: llvm::json::Value(std::move(response)));
89 return;
90 }
91 dap.last_launch_request = updated_arguments;
92 // Update DAP configuration based on the latest copy of the launch
93 // arguments.
94 dap.SetConfiguration(confing: updated_arguments.configuration, is_attach: false);
95 dap.ConfigureSourceMaps();
96 }
97 }
98
99 // Keep track of the old PID so when we get a "process exited" event from the
100 // killed process we can detect it and not shut down the whole session.
101 lldb::SBProcess process = dap.target.GetProcess();
102 dap.restarting_process_id = process.GetProcessID();
103
104 // Stop the current process if necessary. The logic here is similar to
105 // CommandObjectProcessLaunchOrAttach::StopProcessIfNecessary, except that
106 // we don't ask the user for confirmation.
107 if (process.IsValid()) {
108 ScopeSyncMode scope_sync_mode(dap.debugger);
109 lldb::StateType state = process.GetState();
110 if (state != lldb::eStateConnected) {
111 process.Kill();
112 }
113 // Clear the list of thread ids to avoid sending "thread exited" events
114 // for threads of the process we are terminating.
115 dap.thread_ids.clear();
116 }
117
118 // FIXME: Should we run 'preRunCommands'?
119 // FIXME: Should we add a 'preRestartCommands'?
120 if (llvm::Error err = LaunchProcess(request: *dap.last_launch_request)) {
121 response["success"] = llvm::json::Value(false);
122 EmplaceSafeString(obj&: response, key: "message", str: llvm::toString(E: std::move(err)));
123 dap.SendJSON(json: llvm::json::Value(std::move(response)));
124 return;
125 }
126
127 // This is normally done after receiving a "configuration done" request.
128 // Because we're restarting, configuration has already happened so we can
129 // continue the process right away.
130 if (dap.stop_at_entry) {
131 if (llvm::Error err = SendThreadStoppedEvent(dap, /*on_entry=*/true)) {
132 EmplaceSafeString(obj&: response, key: "message", str: llvm::toString(E: std::move(err)));
133 dap.SendJSON(json: llvm::json::Value(std::move(response)));
134 return;
135 }
136 } else {
137 dap.target.GetProcess().Continue();
138 }
139
140 dap.SendJSON(json: llvm::json::Value(std::move(response)));
141}
142
143} // namespace lldb_dap
144

source code of lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp