xref: /llvm-project/lldb/tools/lldb-dap/LLDBUtils.cpp (revision 22561cfb443267905d4190f0e2a738e6b412457f)
1 //===-- LLDBUtils.cpp -------------------------------------------*- C++ -*-===//
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 "LLDBUtils.h"
10 #include "DAP.h"
11 #include "JSONUtils.h"
12 #include "lldb/API/SBStringList.h"
13 
14 #include <mutex>
15 
16 namespace lldb_dap {
17 
18 bool RunLLDBCommands(lldb::SBDebugger &debugger, llvm::StringRef prefix,
19                      const llvm::ArrayRef<std::string> &commands,
20                      llvm::raw_ostream &strm, bool parse_command_directives) {
21   if (commands.empty())
22     return true;
23 
24   bool did_print_prefix = false;
25 
26   lldb::SBCommandInterpreter interp = debugger.GetCommandInterpreter();
27   for (llvm::StringRef command : commands) {
28     lldb::SBCommandReturnObject result;
29     bool quiet_on_success = false;
30     bool check_error = false;
31 
32     while (parse_command_directives) {
33       if (command.starts_with("?")) {
34         command = command.drop_front();
35         quiet_on_success = true;
36       } else if (command.starts_with("!")) {
37         command = command.drop_front();
38         check_error = true;
39       } else {
40         break;
41       }
42     }
43 
44     {
45       // Prevent simultaneous calls to HandleCommand, e.g. EventThreadFunction
46       // may asynchronously call RunExitCommands when we are already calling
47       // RunTerminateCommands.
48       static std::mutex handle_command_mutex;
49       std::lock_guard<std::mutex> locker(handle_command_mutex);
50       interp.HandleCommand(command.str().c_str(), result,
51                            /*add_to_history=*/true);
52     }
53 
54     const bool got_error = !result.Succeeded();
55     // The if statement below is assuming we always print out `!` prefixed
56     // lines. The only time we don't print is when we have `quiet_on_success ==
57     // true` and we don't have an error.
58     if (quiet_on_success ? got_error : true) {
59       if (!did_print_prefix && !prefix.empty()) {
60         strm << prefix << "\n";
61         did_print_prefix = true;
62       }
63       strm << "(lldb) " << command << "\n";
64       auto output_len = result.GetOutputSize();
65       if (output_len) {
66         const char *output = result.GetOutput();
67         strm << output;
68       }
69       auto error_len = result.GetErrorSize();
70       if (error_len) {
71         const char *error = result.GetError();
72         strm << error;
73       }
74     }
75     if (check_error && got_error)
76       return false; // Stop running commands.
77   }
78   return true;
79 }
80 
81 std::string RunLLDBCommands(lldb::SBDebugger &debugger, llvm::StringRef prefix,
82                             const llvm::ArrayRef<std::string> &commands,
83                             bool &required_command_failed,
84                             bool parse_command_directives) {
85   required_command_failed = false;
86   std::string s;
87   llvm::raw_string_ostream strm(s);
88   required_command_failed = !RunLLDBCommands(debugger, prefix, commands, strm,
89                                              parse_command_directives);
90   return s;
91 }
92 
93 std::string
94 RunLLDBCommandsVerbatim(lldb::SBDebugger &debugger, llvm::StringRef prefix,
95                         const llvm::ArrayRef<std::string> &commands) {
96   bool required_command_failed = false;
97   return RunLLDBCommands(debugger, prefix, commands, required_command_failed,
98                          /*parse_command_directives=*/false);
99 }
100 
101 bool ThreadHasStopReason(lldb::SBThread &thread) {
102   switch (thread.GetStopReason()) {
103   case lldb::eStopReasonTrace:
104   case lldb::eStopReasonPlanComplete:
105   case lldb::eStopReasonBreakpoint:
106   case lldb::eStopReasonWatchpoint:
107   case lldb::eStopReasonInstrumentation:
108   case lldb::eStopReasonSignal:
109   case lldb::eStopReasonException:
110   case lldb::eStopReasonExec:
111   case lldb::eStopReasonProcessorTrace:
112   case lldb::eStopReasonFork:
113   case lldb::eStopReasonVFork:
114   case lldb::eStopReasonVForkDone:
115   case lldb::eStopReasonInterrupt:
116     return true;
117   case lldb::eStopReasonThreadExiting:
118   case lldb::eStopReasonInvalid:
119   case lldb::eStopReasonNone:
120     break;
121   }
122   return false;
123 }
124 
125 static uint32_t constexpr THREAD_INDEX_SHIFT = 19;
126 
127 uint32_t GetLLDBThreadIndexID(uint64_t dap_frame_id) {
128   return dap_frame_id >> THREAD_INDEX_SHIFT;
129 }
130 
131 uint32_t GetLLDBFrameID(uint64_t dap_frame_id) {
132   return dap_frame_id & ((1u << THREAD_INDEX_SHIFT) - 1);
133 }
134 
135 int64_t MakeDAPFrameID(lldb::SBFrame &frame) {
136   return ((int64_t)frame.GetThread().GetIndexID() << THREAD_INDEX_SHIFT) |
137          frame.GetFrameID();
138 }
139 
140 lldb::SBEnvironment
141 GetEnvironmentFromArguments(const llvm::json::Object &arguments) {
142   lldb::SBEnvironment envs{};
143   constexpr llvm::StringRef env_key = "env";
144   const llvm::json::Value *raw_json_env = arguments.get(env_key);
145 
146   if (!raw_json_env)
147     return envs;
148 
149   if (raw_json_env->kind() == llvm::json::Value::Object) {
150     auto env_map = GetStringMap(arguments, env_key);
151     for (const auto &[key, value] : env_map)
152       envs.Set(key.c_str(), value.c_str(), true);
153 
154   } else if (raw_json_env->kind() == llvm::json::Value::Array) {
155     const auto envs_strings = GetStrings(&arguments, env_key);
156     lldb::SBStringList entries{};
157     for (const auto &env : envs_strings)
158       entries.AppendString(env.c_str());
159 
160     envs.SetEntries(entries, true);
161   }
162   return envs;
163 }
164 
165 } // namespace lldb_dap
166