1//===-- ProtocolUtils.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 "ProtocolUtils.h"
10#include "LLDBUtils.h"
11
12#include "lldb/API/SBDebugger.h"
13#include "lldb/API/SBFormat.h"
14#include "lldb/API/SBMutex.h"
15#include "lldb/API/SBStream.h"
16#include "lldb/API/SBTarget.h"
17#include "lldb/API/SBThread.h"
18#include "lldb/Host/PosixApi.h" // Adds PATH_MAX for windows
19#include <optional>
20
21using namespace lldb_dap::protocol;
22namespace lldb_dap {
23
24static bool ShouldDisplayAssemblySource(
25 lldb::SBAddress address,
26 lldb::StopDisassemblyType stop_disassembly_display) {
27 if (stop_disassembly_display == lldb::eStopDisassemblyTypeNever)
28 return false;
29
30 if (stop_disassembly_display == lldb::eStopDisassemblyTypeAlways)
31 return true;
32
33 // A line entry of 0 indicates the line is compiler generated i.e. no source
34 // file is associated with the frame.
35 auto line_entry = address.GetLineEntry();
36 auto file_spec = line_entry.GetFileSpec();
37 if (!file_spec.IsValid() || line_entry.GetLine() == 0 ||
38 line_entry.GetLine() == LLDB_INVALID_LINE_NUMBER)
39 return true;
40
41 if (stop_disassembly_display == lldb::eStopDisassemblyTypeNoSource &&
42 !file_spec.Exists()) {
43 return true;
44 }
45
46 return false;
47}
48
49static protocol::Source CreateAssemblySource(const lldb::SBTarget &target,
50 lldb::SBAddress address) {
51 protocol::Source source;
52
53 auto symbol = address.GetSymbol();
54 std::string name;
55 if (symbol.IsValid()) {
56 source.sourceReference = symbol.GetStartAddress().GetLoadAddress(target);
57 name = symbol.GetName();
58 } else {
59 const auto load_addr = address.GetLoadAddress(target);
60 source.sourceReference = load_addr;
61 name = GetLoadAddressString(addr: load_addr);
62 }
63
64 lldb::SBModule module = address.GetModule();
65 if (module.IsValid()) {
66 lldb::SBFileSpec file_spec = module.GetFileSpec();
67 if (file_spec.IsValid()) {
68 std::string path = GetSBFileSpecPath(file_spec);
69 if (!path.empty())
70 source.path = path + '`' + name;
71 }
72 }
73
74 source.name = std::move(name);
75
76 // Mark the source as deemphasized since users will only be able to view
77 // assembly for these frames.
78 source.presentationHint =
79 protocol::Source::PresentationHint::eSourcePresentationHintDeemphasize;
80
81 return source;
82}
83
84protocol::Source CreateSource(const lldb::SBFileSpec &file) {
85 protocol::Source source;
86 if (file.IsValid()) {
87 if (const char *name = file.GetFilename())
88 source.name = name;
89 char path[PATH_MAX] = "";
90 if (file.GetPath(dst_path: path, dst_len: sizeof(path)) &&
91 lldb::SBFileSpec::ResolvePath(src_path: path, dst_path: path, PATH_MAX))
92 source.path = path;
93 }
94 return source;
95}
96
97protocol::Source CreateSource(lldb::SBAddress address, lldb::SBTarget &target) {
98 lldb::SBDebugger debugger = target.GetDebugger();
99 lldb::StopDisassemblyType stop_disassembly_display =
100 GetStopDisassemblyDisplay(debugger);
101 if (ShouldDisplayAssemblySource(address, stop_disassembly_display))
102 return CreateAssemblySource(target, address);
103
104 lldb::SBLineEntry line_entry = GetLineEntryForAddress(target, address);
105 return CreateSource(file: line_entry.GetFileSpec());
106}
107
108bool IsAssemblySource(const protocol::Source &source) {
109 // According to the specification, a source must have either `path` or
110 // `sourceReference` specified. We use `path` for sources with known source
111 // code, and `sourceReferences` when falling back to assembly.
112 return source.sourceReference.value_or(u: 0) != 0;
113}
114
115std::string GetLoadAddressString(const lldb::addr_t addr) {
116 return "0x" + llvm::utohexstr(X: addr, LowerCase: false, Width: 16);
117}
118
119protocol::Thread CreateThread(lldb::SBThread &thread, lldb::SBFormat &format) {
120 std::string name;
121 lldb::SBStream stream;
122 if (format && thread.GetDescriptionWithFormat(format, output&: stream).Success()) {
123 name = stream.GetData();
124 } else {
125 llvm::StringRef thread_name(thread.GetName());
126 llvm::StringRef queue_name(thread.GetQueueName());
127
128 if (!thread_name.empty()) {
129 name = thread_name.str();
130 } else if (!queue_name.empty()) {
131 auto kind = thread.GetQueue().GetKind();
132 std::string queue_kind_label = "";
133 if (kind == lldb::eQueueKindSerial)
134 queue_kind_label = " (serial)";
135 else if (kind == lldb::eQueueKindConcurrent)
136 queue_kind_label = " (concurrent)";
137
138 name = llvm::formatv(Fmt: "Thread {0} Queue: {1}{2}", Vals: thread.GetIndexID(),
139 Vals&: queue_name, Vals&: queue_kind_label)
140 .str();
141 } else {
142 name = llvm::formatv(Fmt: "Thread {0}", Vals: thread.GetIndexID()).str();
143 }
144 }
145 return protocol::Thread{.id: thread.GetThreadID(), .name: name};
146}
147
148std::vector<protocol::Thread> GetThreads(lldb::SBProcess process,
149 lldb::SBFormat &format) {
150 lldb::SBMutex lock = process.GetTarget().GetAPIMutex();
151 std::lock_guard<lldb::SBMutex> guard(lock);
152
153 std::vector<protocol::Thread> threads;
154
155 const uint32_t num_threads = process.GetNumThreads();
156 threads.reserve(n: num_threads);
157 for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
158 lldb::SBThread thread = process.GetThreadAtIndex(index: thread_idx);
159 threads.emplace_back(args: CreateThread(thread, format));
160 }
161 return threads;
162}
163
164} // namespace lldb_dap
165

source code of lldb/tools/lldb-dap/ProtocolUtils.cpp