Skip to content

Commit b6205d1

Browse files
author
walter erquinigo
committed
thread format
1 parent e9453f3 commit b6205d1

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
@@ -200,6 +200,21 @@ class LLDB_API SBThread {
200200

201201
bool GetDescription(lldb::SBStream &description, bool stop_format) const;
202202

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

205220
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
@@ -733,6 +733,7 @@ def request_launch(
733733
enableSyntheticChildDebugging=False,
734734
commandEscapePrefix="`",
735735
customFrameFormat=None,
736+
customThreadFormat=None,
736737
):
737738
args_dict = {"program": program}
738739
if args:
@@ -776,6 +777,8 @@ def request_launch(
776777
args_dict["postRunCommands"] = postRunCommands
777778
if customFrameFormat:
778779
args_dict["customFrameFormat"] = customFrameFormat
780+
if customThreadFormat:
781+
args_dict["customThreadFormat"] = customThreadFormat
779782

780783
args_dict["enableAutoVariableSummaries"] = enableAutoVariableSummaries
781784
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"
@@ -1223,17 +1224,41 @@ bool SBThread::GetDescription(SBStream &description, bool stop_format) const {
12231224
ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
12241225

12251226
if (exe_ctx.HasThreadScope()) {
1226-
exe_ctx.GetThreadPtr()->DumpUsingSettingsFormat(strm,
1227-
LLDB_INVALID_THREAD_ID,
1228-
stop_format);
1229-
// strm.Printf("SBThread: tid = 0x%4.4" PRIx64,
1230-
// exe_ctx.GetThreadPtr()->GetID());
1227+
exe_ctx.GetThreadPtr()->DumpUsingSettingsFormat(
1228+
strm, LLDB_INVALID_THREAD_ID, stop_format);
12311229
} else
12321230
strm.PutCString("No value");
12331231

12341232
return true;
12351233
}
12361234

1235+
SBError SBThread::GetDescriptionWithFormat(const SBFormat &format,
1236+
SBStream &output) {
1237+
Stream &strm = output.ref();
1238+
1239+
SBError error;
1240+
if (!format) {
1241+
error.SetErrorString("The provided SBFormat object is invalid");
1242+
return error;
1243+
}
1244+
1245+
std::unique_lock<std::recursive_mutex> lock;
1246+
ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
1247+
1248+
if (exe_ctx.HasThreadScope()) {
1249+
if (exe_ctx.GetThreadPtr()->DumpUsingFormat(
1250+
strm, LLDB_INVALID_THREAD_ID, format.GetFormatEntrySP().get())) {
1251+
return error;
1252+
}
1253+
}
1254+
1255+
error.SetErrorStringWithFormat(
1256+
"It was not possible to generate a thread description with the given "
1257+
"format string '%s'",
1258+
format.GetFormatEntrySP()->string.c_str());
1259+
return error;
1260+
}
1261+
12371262
SBThread SBThread::GetExtendedBacktraceThread(const char *type) {
12381263
LLDB_INSTRUMENT_VA(this, type);
12391264

lldb/source/Target/Thread.cpp

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1589,12 +1589,12 @@ Status Thread::JumpToLine(const FileSpec &file, uint32_t line,
15891589
return Status();
15901590
}
15911591

1592-
void Thread::DumpUsingSettingsFormat(Stream &strm, uint32_t frame_idx,
1593-
bool stop_format) {
1592+
bool Thread::DumpUsingFormat(Stream &strm, uint32_t frame_idx,
1593+
const FormatEntity::Entry *format) {
15941594
ExecutionContext exe_ctx(shared_from_this());
15951595
Process *process = exe_ctx.GetProcessPtr();
1596-
if (process == nullptr)
1597-
return;
1596+
if (!process || !format)
1597+
return false;
15981598

15991599
StackFrameSP frame_sp;
16001600
SymbolContext frame_sc;
@@ -1606,6 +1606,14 @@ void Thread::DumpUsingSettingsFormat(Stream &strm, uint32_t frame_idx,
16061606
}
16071607
}
16081608

1609+
return FormatEntity::Format(*format, strm, frame_sp ? &frame_sc : nullptr,
1610+
&exe_ctx, nullptr, nullptr, false, false);
1611+
}
1612+
1613+
void Thread::DumpUsingSettingsFormat(Stream &strm, uint32_t frame_idx,
1614+
bool stop_format) {
1615+
ExecutionContext exe_ctx(shared_from_this());
1616+
16091617
const FormatEntity::Entry *thread_format;
16101618
if (stop_format)
16111619
thread_format = exe_ctx.GetTargetRef().GetDebugger().GetThreadStopFormat();
@@ -1614,8 +1622,7 @@ void Thread::DumpUsingSettingsFormat(Stream &strm, uint32_t frame_idx,
16141622

16151623
assert(thread_format);
16161624

1617-
FormatEntity::Format(*thread_format, strm, frame_sp ? &frame_sc : nullptr,
1618-
&exe_ctx, nullptr, nullptr, false, false);
1625+
DumpUsingFormat(strm, frame_idx, thread_format);
16191626
}
16201627

16211628
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
@@ -828,7 +828,7 @@ void DAP::SetFrameFormat(llvm::StringRef format) {
828828
if (format.empty())
829829
return;
830830
lldb::SBError error;
831-
g_dap.frame_format = lldb::SBFormat(format.data(), error);
831+
g_dap.frame_format = lldb::SBFormat(format.str().c_str(), error);
832832
if (error.Fail()) {
833833
g_dap.SendOutput(
834834
OutputType::Console,
@@ -839,4 +839,19 @@ void DAP::SetFrameFormat(llvm::StringRef format) {
839839
}
840840
}
841841

842+
void DAP::SetThreadFormat(llvm::StringRef format) {
843+
if (format.empty())
844+
return;
845+
lldb::SBError error;
846+
g_dap.thread_format = lldb::SBFormat(format.str().c_str(), error);
847+
if (error.Fail()) {
848+
g_dap.SendOutput(
849+
OutputType::Console,
850+
llvm::formatv(
851+
"The provided thread format '{0}' couldn't be parsed: {1}\n",
852+
format, error.GetCString())
853+
.str());
854+
}
855+
}
856+
842857
} // 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
@@ -852,26 +852,33 @@ llvm::json::Value CreateStackFrame(lldb::SBFrame &frame) {
852852
llvm::json::Value CreateThread(lldb::SBThread &thread) {
853853
llvm::json::Object object;
854854
object.try_emplace("id", (int64_t)thread.GetThreadID());
855-
const char *thread_name = thread.GetName();
856-
const char *queue_name = thread.GetQueueName();
857-
858855
std::string thread_str;
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 = llvm::formatv("Thread {0} Queue: {1}{2}", thread.GetIndexID(),
871-
queue_name, queue_kind_label)
872-
.str();
856+
lldb::SBStream stream;
857+
if (g_dap.thread_format &&
858+
thread.GetDescriptionWithFormat(g_dap.thread_format, stream).Success()) {
859+
thread_str = stream.GetData();
873860
} else {
874-
thread_str = llvm::formatv("Thread {0}", thread.GetIndexID()).str();
861+
const char *thread_name = thread.GetName();
862+
const char *queue_name = thread.GetQueueName();
863+
864+
if (thread_name) {
865+
thread_str = std::string(thread_name);
866+
} else if (queue_name) {
867+
auto kind = thread.GetQueue().GetKind();
868+
std::string queue_kind_label = "";
869+
if (kind == lldb::eQueueKindSerial) {
870+
queue_kind_label = " (serial)";
871+
} else if (kind == lldb::eQueueKindConcurrent) {
872+
queue_kind_label = " (concurrent)";
873+
}
874+
875+
thread_str =
876+
llvm::formatv("Thread {0} Queue: {1}{2}", thread.GetIndexID(),
877+
queue_name, queue_kind_label)
878+
.str();
879+
} else {
880+
thread_str = llvm::formatv("Thread {0}", thread.GetIndexID()).str();
881+
}
875882
}
876883

877884
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
@@ -654,6 +654,7 @@ void request_attach(const llvm::json::Object &request) {
654654
g_dap.command_escape_prefix =
655655
GetString(arguments, "commandEscapePrefix", "`");
656656
g_dap.SetFrameFormat(GetString(arguments, "customFrameFormat"));
657+
g_dap.SetThreadFormat(GetString(arguments, "customThreadFormat"));
657658

658659
// This is a hack for loading DWARF in .o files on Mac where the .o files
659660
// in the debug map of the main executable have relative paths which require
@@ -1807,6 +1808,7 @@ void request_launch(const llvm::json::Object &request) {
18071808
g_dap.command_escape_prefix =
18081809
GetString(arguments, "commandEscapePrefix", "`");
18091810
g_dap.SetFrameFormat(GetString(arguments, "customFrameFormat"));
1811+
g_dap.SetThreadFormat(GetString(arguments, "customThreadFormat"));
18101812

18111813
// This is a hack for loading DWARF in .o files on Mac where the .o files
18121814
// 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
@@ -260,6 +260,11 @@
260260
"type": "string",
261261
"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.",
262262
"default": ""
263+
},
264+
"customThreadFormat": {
265+
"type": "string",
266+
"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.",
267+
"default": ""
263268
}
264269
}
265270
},
@@ -359,6 +364,11 @@
359364
"type": "string",
360365
"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.",
361366
"default": ""
367+
},
368+
"customThreadFormat": {
369+
"type": "string",
370+
"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.",
371+
"default": ""
362372
}
363373
}
364374
}

0 commit comments

Comments
 (0)