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 | |
18 | namespace 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 | // }, |
61 | void 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 | |