Skip to content

Commit e0e14be

Browse files
walter-erquinigoadrian-prantl
authored andcommitted
[lldb-dap] Add an option to provide a format for threads (llvm#72196)
When this option gets enabled, descriptions of threads will be generated using the format provided in the launch configuration instead of generating it manually in the dap code. This allows lldb-dap to show an output similar to the one in the CLI. This is very similar to llvm#71843 (cherry picked from commit 1654d7d)
1 parent 18b502d commit e0e14be

File tree

15 files changed

+164
-32
lines changed

15 files changed

+164
-32
lines changed

lldb/include/lldb/API/SBFormat.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class LLDB_API SBFormat {
5252

5353
protected:
5454
friend class SBFrame;
55+
friend class SBThread;
5556

5657
/// \return
5758
/// The underlying shared pointer storage for this object.

lldb/include/lldb/API/SBThread.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,21 @@ class LLDB_API SBThread {
204204

205205
bool GetDescription(lldb::SBStream &description, bool stop_format) const;
206206

207+
/// Similar to \a GetDescription() but the format of the description can be
208+
/// configured via the \p format parameter. See
209+
/// https://lldb.llvm.org/use/formatting.html for more information on format
210+
/// strings.
211+
///
212+
/// \param[in] format
213+
/// The format to use for generating the description.
214+
///
215+
/// \param[out] output
216+
/// The stream where the description will be written to.
217+
///
218+
/// \return
219+
/// An error object with an error message in case of failures.
220+
SBError GetDescriptionWithFormat(const SBFormat &format, SBStream &output);
221+
207222
bool GetStatus(lldb::SBStream &status) const;
208223

209224
SBThread GetExtendedBacktraceThread(const char *type);

lldb/include/lldb/Target/Thread.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,23 @@ class Thread : public std::enable_shared_from_this<Thread>,
490490
void DumpTraceInstructions(Stream &s, size_t count,
491491
size_t start_position = 0) const;
492492

493+
/// Print a description of this thread using the provided thread format.
494+
///
495+
/// \param[out] strm
496+
/// The Stream to print the description to.
497+
///
498+
/// \param[in] frame_idx
499+
/// If not \b LLDB_INVALID_FRAME_ID, then use this frame index as context to
500+
/// generate the description.
501+
///
502+
/// \param[in] format
503+
/// The input format.
504+
///
505+
/// \return
506+
/// \b true if and only if dumping with the given \p format worked.
507+
bool DumpUsingFormat(Stream &strm, uint32_t frame_idx,
508+
const FormatEntity::Entry *format);
509+
493510
// If stop_format is true, this will be the form used when we print stop
494511
// info. If false, it will be the form we use for thread list and co.
495512
void DumpUsingSettingsFormat(Stream &strm, uint32_t frame_idx,

lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,7 @@ def request_launch(
731731
enableSyntheticChildDebugging=False,
732732
commandEscapePrefix="`",
733733
customFrameFormat=None,
734+
customThreadFormat=None,
734735
):
735736
args_dict = {"program": program}
736737
if args:
@@ -774,6 +775,8 @@ def request_launch(
774775
args_dict["postRunCommands"] = postRunCommands
775776
if customFrameFormat:
776777
args_dict["customFrameFormat"] = customFrameFormat
778+
if customThreadFormat:
779+
args_dict["customThreadFormat"] = customThreadFormat
777780

778781
args_dict["enableAutoVariableSummaries"] = enableAutoVariableSummaries
779782
args_dict["enableSyntheticChildDebugging"] = enableSyntheticChildDebugging

lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@ def launch(
353353
enableSyntheticChildDebugging=False,
354354
commandEscapePrefix="`",
355355
customFrameFormat=None,
356+
customThreadFormat=None,
356357
):
357358
"""Sending launch request to dap"""
358359

@@ -393,6 +394,7 @@ def cleanup():
393394
enableSyntheticChildDebugging=enableSyntheticChildDebugging,
394395
commandEscapePrefix=commandEscapePrefix,
395396
customFrameFormat=customFrameFormat,
397+
customThreadFormat=customThreadFormat,
396398
)
397399

398400
if expectFailure:
@@ -431,6 +433,7 @@ def build_and_launch(
431433
enableSyntheticChildDebugging=False,
432434
commandEscapePrefix="`",
433435
customFrameFormat=None,
436+
customThreadFormat=None,
434437
):
435438
"""Build the default Makefile target, create the DAP debug adaptor,
436439
and launch the process.
@@ -463,4 +466,5 @@ def build_and_launch(
463466
enableSyntheticChildDebugging=enableSyntheticChildDebugging,
464467
commandEscapePrefix=commandEscapePrefix,
465468
customFrameFormat=customFrameFormat,
469+
customThreadFormat=customThreadFormat,
466470
)

lldb/source/API/SBThread.cpp

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "lldb/API/SBDebugger.h"
1313
#include "lldb/API/SBEvent.h"
1414
#include "lldb/API/SBFileSpec.h"
15+
#include "lldb/API/SBFormat.h"
1516
#include "lldb/API/SBFrame.h"
1617
#include "lldb/API/SBProcess.h"
1718
#include "lldb/API/SBStream.h"
@@ -1251,17 +1252,41 @@ bool SBThread::GetDescription(SBStream &description, bool stop_format) const {
12511252
ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
12521253

12531254
if (exe_ctx.HasThreadScope()) {
1254-
exe_ctx.GetThreadPtr()->DumpUsingSettingsFormat(strm,
1255-
LLDB_INVALID_THREAD_ID,
1256-
stop_format);
1257-
// strm.Printf("SBThread: tid = 0x%4.4" PRIx64,
1258-
// exe_ctx.GetThreadPtr()->GetID());
1255+
exe_ctx.GetThreadPtr()->DumpUsingSettingsFormat(
1256+
strm, LLDB_INVALID_THREAD_ID, stop_format);
12591257
} else
12601258
strm.PutCString("No value");
12611259

12621260
return true;
12631261
}
12641262

1263+
SBError SBThread::GetDescriptionWithFormat(const SBFormat &format,
1264+
SBStream &output) {
1265+
Stream &strm = output.ref();
1266+
1267+
SBError error;
1268+
if (!format) {
1269+
error.SetErrorString("The provided SBFormat object is invalid");
1270+
return error;
1271+
}
1272+
1273+
std::unique_lock<std::recursive_mutex> lock;
1274+
ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
1275+
1276+
if (exe_ctx.HasThreadScope()) {
1277+
if (exe_ctx.GetThreadPtr()->DumpUsingFormat(
1278+
strm, LLDB_INVALID_THREAD_ID, format.GetFormatEntrySP().get())) {
1279+
return error;
1280+
}
1281+
}
1282+
1283+
error.SetErrorStringWithFormat(
1284+
"It was not possible to generate a thread description with the given "
1285+
"format string '%s'",
1286+
format.GetFormatEntrySP()->string.c_str());
1287+
return error;
1288+
}
1289+
12651290
SBThread SBThread::GetExtendedBacktraceThread(const char *type) {
12661291
LLDB_INSTRUMENT_VA(this, type);
12671292

lldb/source/Target/Thread.cpp

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1654,12 +1654,12 @@ Status Thread::JumpToLine(const FileSpec &file, uint32_t line,
16541654
return Status();
16551655
}
16561656

1657-
void Thread::DumpUsingSettingsFormat(Stream &strm, uint32_t frame_idx,
1658-
bool stop_format) {
1657+
bool Thread::DumpUsingFormat(Stream &strm, uint32_t frame_idx,
1658+
const FormatEntity::Entry *format) {
16591659
ExecutionContext exe_ctx(shared_from_this());
16601660
Process *process = exe_ctx.GetProcessPtr();
1661-
if (process == nullptr)
1662-
return;
1661+
if (!process || !format)
1662+
return false;
16631663

16641664
StackFrameSP frame_sp;
16651665
SymbolContext frame_sc;
@@ -1671,6 +1671,14 @@ void Thread::DumpUsingSettingsFormat(Stream &strm, uint32_t frame_idx,
16711671
}
16721672
}
16731673

1674+
return FormatEntity::Format(*format, strm, frame_sp ? &frame_sc : nullptr,
1675+
&exe_ctx, nullptr, nullptr, false, false);
1676+
}
1677+
1678+
void Thread::DumpUsingSettingsFormat(Stream &strm, uint32_t frame_idx,
1679+
bool stop_format) {
1680+
ExecutionContext exe_ctx(shared_from_this());
1681+
16741682
const FormatEntity::Entry *thread_format;
16751683
if (stop_format)
16761684
thread_format = exe_ctx.GetTargetRef().GetDebugger().GetThreadStopFormat();
@@ -1679,8 +1687,7 @@ void Thread::DumpUsingSettingsFormat(Stream &strm, uint32_t frame_idx,
16791687

16801688
assert(thread_format);
16811689

1682-
FormatEntity::Format(*thread_format, strm, frame_sp ? &frame_sc : nullptr,
1683-
&exe_ctx, nullptr, nullptr, false, false);
1690+
DumpUsingFormat(strm, frame_idx, thread_format);
16841691
}
16851692

16861693
void Thread::SettingsInitialize() {}

lldb/test/API/tools/lldb-dap/correct-thread/TestDAP_correct_thread.py renamed to lldb/test/API/tools/lldb-dap/threads/TestDAP_threads.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22
Test lldb-dap setBreakpoints request
33
"""
44

5-
import dap_server
65
from lldbsuite.test.decorators import *
76
from lldbsuite.test.lldbtest import *
87
from lldbsuite.test import lldbutil
98
import lldbdap_testcase
109

1110

12-
class TestDAP_correct_thread(lldbdap_testcase.DAPTestCaseBase):
11+
class TestDAP_threads(lldbdap_testcase.DAPTestCaseBase):
1312
@skipIfWindows
1413
@skipIfRemote
1514
def test_correct_thread(self):
@@ -44,3 +43,27 @@ def test_correct_thread(self):
4443
)
4544
self.assertFalse(stopped_event[0]["body"]["preserveFocusHint"])
4645
self.assertTrue(stopped_event[0]["body"]["threadCausedFocus"])
46+
47+
@skipIfWindows
48+
@skipIfRemote
49+
def test_thread_format(self):
50+
"""
51+
Tests the support for custom thread formats.
52+
"""
53+
program = self.getBuildArtifact("a.out")
54+
self.build_and_launch(
55+
program, customThreadFormat="This is thread index #${thread.index}"
56+
)
57+
source = "main.c"
58+
breakpoint_line = line_number(source, "// break here")
59+
lines = [breakpoint_line]
60+
# Set breakpoint in the thread function
61+
breakpoint_ids = self.set_source_breakpoints(source, lines)
62+
self.assertEqual(
63+
len(breakpoint_ids), len(lines), "expect correct number of breakpoints"
64+
)
65+
self.continue_to_breakpoints(breakpoint_ids)
66+
# We are stopped at the second thread
67+
threads = self.dap_server.get_threads()
68+
self.assertEquals(threads[0]["name"], "This is thread index #1")
69+
self.assertEquals(threads[1]["name"], "This is thread index #2")

lldb/tools/lldb-dap/DAP.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -824,7 +824,7 @@ void DAP::SetFrameFormat(llvm::StringRef format) {
824824
if (format.empty())
825825
return;
826826
lldb::SBError error;
827-
g_dap.frame_format = lldb::SBFormat(format.data(), error);
827+
g_dap.frame_format = lldb::SBFormat(format.str().c_str(), error);
828828
if (error.Fail()) {
829829
g_dap.SendOutput(
830830
OutputType::Console,
@@ -835,4 +835,19 @@ void DAP::SetFrameFormat(llvm::StringRef format) {
835835
}
836836
}
837837

838+
void DAP::SetThreadFormat(llvm::StringRef format) {
839+
if (format.empty())
840+
return;
841+
lldb::SBError error;
842+
g_dap.thread_format = lldb::SBFormat(format.str().c_str(), error);
843+
if (error.Fail()) {
844+
g_dap.SendOutput(
845+
OutputType::Console,
846+
llvm::formatv(
847+
"The provided thread format '{0}' couldn't be parsed: {1}\n",
848+
format, error.GetCString())
849+
.str());
850+
}
851+
}
852+
838853
} // namespace lldb_dap

lldb/tools/lldb-dap/DAP.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ struct DAP {
191191
bool auto_repl_mode_collision_warning;
192192
std::string command_escape_prefix = "`";
193193
lldb::SBFormat frame_format;
194+
lldb::SBFormat thread_format;
194195

195196
DAP();
196197
~DAP();
@@ -309,6 +310,8 @@ struct DAP {
309310

310311
void SetFrameFormat(llvm::StringRef format);
311312

313+
void SetThreadFormat(llvm::StringRef format);
314+
312315
private:
313316
// Send the JSON in "json_str" to the "out" stream. Correctly send the
314317
// "Content-Length:" field followed by the length, followed by the raw

lldb/tools/lldb-dap/JSONUtils.cpp

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -847,26 +847,33 @@ llvm::json::Value CreateStackFrame(lldb::SBFrame &frame) {
847847
llvm::json::Value CreateThread(lldb::SBThread &thread) {
848848
llvm::json::Object object;
849849
object.try_emplace("id", (int64_t)thread.GetThreadID());
850-
const char *thread_name = thread.GetName();
851-
const char *queue_name = thread.GetQueueName();
852-
853850
std::string thread_str;
854-
if (thread_name) {
855-
thread_str = std::string(thread_name);
856-
} else if (queue_name) {
857-
auto kind = thread.GetQueue().GetKind();
858-
std::string queue_kind_label = "";
859-
if (kind == lldb::eQueueKindSerial) {
860-
queue_kind_label = " (serial)";
861-
} else if (kind == lldb::eQueueKindConcurrent) {
862-
queue_kind_label = " (concurrent)";
863-
}
864-
865-
thread_str = llvm::formatv("Thread {0} Queue: {1}{2}", thread.GetIndexID(),
866-
queue_name, queue_kind_label)
867-
.str();
851+
lldb::SBStream stream;
852+
if (g_dap.thread_format &&
853+
thread.GetDescriptionWithFormat(g_dap.thread_format, stream).Success()) {
854+
thread_str = stream.GetData();
868855
} else {
869-
thread_str = llvm::formatv("Thread {0}", thread.GetIndexID()).str();
856+
const char *thread_name = thread.GetName();
857+
const char *queue_name = thread.GetQueueName();
858+
859+
if (thread_name) {
860+
thread_str = std::string(thread_name);
861+
} else if (queue_name) {
862+
auto kind = thread.GetQueue().GetKind();
863+
std::string queue_kind_label = "";
864+
if (kind == lldb::eQueueKindSerial) {
865+
queue_kind_label = " (serial)";
866+
} else if (kind == lldb::eQueueKindConcurrent) {
867+
queue_kind_label = " (concurrent)";
868+
}
869+
870+
thread_str =
871+
llvm::formatv("Thread {0} Queue: {1}{2}", thread.GetIndexID(),
872+
queue_name, queue_kind_label)
873+
.str();
874+
} else {
875+
thread_str = llvm::formatv("Thread {0}", thread.GetIndexID()).str();
876+
}
870877
}
871878

872879
EmplaceSafeString(object, "name", thread_str);

lldb/tools/lldb-dap/lldb-dap.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,7 @@ void request_attach(const llvm::json::Object &request) {
652652
g_dap.command_escape_prefix =
653653
GetString(arguments, "commandEscapePrefix", "`");
654654
g_dap.SetFrameFormat(GetString(arguments, "customFrameFormat"));
655+
g_dap.SetThreadFormat(GetString(arguments, "customThreadFormat"));
655656

656657
// This is a hack for loading DWARF in .o files on Mac where the .o files
657658
// in the debug map of the main executable have relative paths which require
@@ -1805,6 +1806,7 @@ void request_launch(const llvm::json::Object &request) {
18051806
g_dap.command_escape_prefix =
18061807
GetString(arguments, "commandEscapePrefix", "`");
18071808
g_dap.SetFrameFormat(GetString(arguments, "customFrameFormat"));
1809+
g_dap.SetThreadFormat(GetString(arguments, "customThreadFormat"));
18081810

18091811
// This is a hack for loading DWARF in .o files on Mac where the .o files
18101812
// in the debug map of the main executable have relative paths which require

lldb/tools/lldb-dap/package.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,11 @@
256256
"type": "string",
257257
"description": "If non-empty, stack frames will have descriptions generated based on the provided format. See https://lldb.llvm.org/use/formatting.html for an explanation on format strings for frames. If the format string contains errors, an error message will be displayed on the Debug Console and the default frame names will be used. This might come with a performance cost because debug information might need to be processed to generate the description.",
258258
"default": ""
259+
},
260+
"customThreadFormat": {
261+
"type": "string",
262+
"description": "If non-empty, threads will have descriptions generated based on the provided format. See https://lldb.llvm.org/use/formatting.html for an explanation on format strings for threads. If the format string contains errors, an error message will be displayed on the Debug Console and the default thread names will be used. This might come with a performance cost because debug information might need to be processed to generate the description.",
263+
"default": ""
259264
}
260265
}
261266
},
@@ -355,6 +360,11 @@
355360
"type": "string",
356361
"description": "If non-empty, stack frames will have descriptions generated based on the provided format. See https://lldb.llvm.org/use/formatting.html for an explanation on format strings for frames. If the format string contains errors, an error message will be displayed on the Debug Console and the default frame names will be used. This might come with a performance cost because debug information might need to be processed to generate the description.",
357362
"default": ""
363+
},
364+
"customThreadFormat": {
365+
"type": "string",
366+
"description": "If non-empty, threads will have descriptions generated based on the provided format. See https://lldb.llvm.org/use/formatting.html for an explanation on format strings for threads. If the format string contains errors, an error message will be displayed on the Debug Console and the default thread names will be used. This might come with a performance cost because debug information might need to be processed to generate the description.",
367+
"default": ""
358368
}
359369
}
360370
}

0 commit comments

Comments
 (0)