Skip to content

[lldb-dap] Add an option to provide a format for threads #72196

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lldb/include/lldb/API/SBFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class LLDB_API SBFormat {

protected:
friend class SBFrame;
friend class SBThread;

/// \return
/// The underlying shared pointer storage for this object.
Expand Down
15 changes: 15 additions & 0 deletions lldb/include/lldb/API/SBThread.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,21 @@ class LLDB_API SBThread {

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

/// Similar to \a GetDescription() but the format of the description can be
/// configured via the \p format parameter. See
/// https://lldb.llvm.org/use/formatting.html for more information on format
/// strings.
///
/// \param[in] format
/// The format to use for generating the description.
///
/// \param[out] output
/// The stream where the description will be written to.
///
/// \return
/// An error object with an error message in case of failures.
SBError GetDescriptionWithFormat(const SBFormat &format, SBStream &output);

bool GetStatus(lldb::SBStream &status) const;

SBThread GetExtendedBacktraceThread(const char *type);
Expand Down
17 changes: 17 additions & 0 deletions lldb/include/lldb/Target/Thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,23 @@ class Thread : public std::enable_shared_from_this<Thread>,
void DumpTraceInstructions(Stream &s, size_t count,
size_t start_position = 0) const;

/// Print a description of this thread using the provided thread format.
///
/// \param[out] strm
/// The Stream to print the description to.
///
/// \param[in] frame_idx
/// If not \b LLDB_INVALID_FRAME_ID, then use this frame index as context to
/// generate the description.
///
/// \param[in] format
/// The input format.
///
/// \return
/// \b true if and only if dumping with the given \p format worked.
bool DumpUsingFormat(Stream &strm, uint32_t frame_idx,
const FormatEntity::Entry *format);

// If stop_format is true, this will be the form used when we print stop
// info. If false, it will be the form we use for thread list and co.
void DumpUsingSettingsFormat(Stream &strm, uint32_t frame_idx,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,7 @@ def request_launch(
enableSyntheticChildDebugging=False,
commandEscapePrefix="`",
customFrameFormat=None,
customThreadFormat=None,
):
args_dict = {"program": program}
if args:
Expand Down Expand Up @@ -776,6 +777,8 @@ def request_launch(
args_dict["postRunCommands"] = postRunCommands
if customFrameFormat:
args_dict["customFrameFormat"] = customFrameFormat
if customThreadFormat:
args_dict["customThreadFormat"] = customThreadFormat

args_dict["enableAutoVariableSummaries"] = enableAutoVariableSummaries
args_dict["enableSyntheticChildDebugging"] = enableSyntheticChildDebugging
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,7 @@ def launch(
enableSyntheticChildDebugging=False,
commandEscapePrefix="`",
customFrameFormat=None,
customThreadFormat=None,
):
"""Sending launch request to dap"""

Expand Down Expand Up @@ -393,6 +394,7 @@ def cleanup():
enableSyntheticChildDebugging=enableSyntheticChildDebugging,
commandEscapePrefix=commandEscapePrefix,
customFrameFormat=customFrameFormat,
customThreadFormat=customThreadFormat,
)

if expectFailure:
Expand Down Expand Up @@ -431,6 +433,7 @@ def build_and_launch(
enableSyntheticChildDebugging=False,
commandEscapePrefix="`",
customFrameFormat=None,
customThreadFormat=None,
):
"""Build the default Makefile target, create the DAP debug adaptor,
and launch the process.
Expand Down Expand Up @@ -463,4 +466,5 @@ def build_and_launch(
enableSyntheticChildDebugging=enableSyntheticChildDebugging,
commandEscapePrefix=commandEscapePrefix,
customFrameFormat=customFrameFormat,
customThreadFormat=customThreadFormat,
)
35 changes: 30 additions & 5 deletions lldb/source/API/SBThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "lldb/API/SBDebugger.h"
#include "lldb/API/SBEvent.h"
#include "lldb/API/SBFileSpec.h"
#include "lldb/API/SBFormat.h"
#include "lldb/API/SBFrame.h"
#include "lldb/API/SBProcess.h"
#include "lldb/API/SBStream.h"
Expand Down Expand Up @@ -1223,17 +1224,41 @@ bool SBThread::GetDescription(SBStream &description, bool stop_format) const {
ExecutionContext exe_ctx(m_opaque_sp.get(), lock);

if (exe_ctx.HasThreadScope()) {
exe_ctx.GetThreadPtr()->DumpUsingSettingsFormat(strm,
LLDB_INVALID_THREAD_ID,
stop_format);
// strm.Printf("SBThread: tid = 0x%4.4" PRIx64,
// exe_ctx.GetThreadPtr()->GetID());
exe_ctx.GetThreadPtr()->DumpUsingSettingsFormat(
strm, LLDB_INVALID_THREAD_ID, stop_format);
} else
strm.PutCString("No value");

return true;
}

SBError SBThread::GetDescriptionWithFormat(const SBFormat &format,
SBStream &output) {
Stream &strm = output.ref();

SBError error;
if (!format) {
error.SetErrorString("The provided SBFormat object is invalid");
return error;
}

std::unique_lock<std::recursive_mutex> lock;
ExecutionContext exe_ctx(m_opaque_sp.get(), lock);

if (exe_ctx.HasThreadScope()) {
if (exe_ctx.GetThreadPtr()->DumpUsingFormat(
strm, LLDB_INVALID_THREAD_ID, format.GetFormatEntrySP().get())) {
return error;
}
}

error.SetErrorStringWithFormat(
"It was not possible to generate a thread description with the given "
"format string '%s'",
format.GetFormatEntrySP()->string.c_str());
return error;
}

SBThread SBThread::GetExtendedBacktraceThread(const char *type) {
LLDB_INSTRUMENT_VA(this, type);

Expand Down
19 changes: 13 additions & 6 deletions lldb/source/Target/Thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1589,12 +1589,12 @@ Status Thread::JumpToLine(const FileSpec &file, uint32_t line,
return Status();
}

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

StackFrameSP frame_sp;
SymbolContext frame_sc;
Expand All @@ -1606,6 +1606,14 @@ void Thread::DumpUsingSettingsFormat(Stream &strm, uint32_t frame_idx,
}
}

return FormatEntity::Format(*format, strm, frame_sp ? &frame_sc : nullptr,
&exe_ctx, nullptr, nullptr, false, false);
}

void Thread::DumpUsingSettingsFormat(Stream &strm, uint32_t frame_idx,
bool stop_format) {
ExecutionContext exe_ctx(shared_from_this());

const FormatEntity::Entry *thread_format;
if (stop_format)
thread_format = exe_ctx.GetTargetRef().GetDebugger().GetThreadStopFormat();
Expand All @@ -1614,8 +1622,7 @@ void Thread::DumpUsingSettingsFormat(Stream &strm, uint32_t frame_idx,

assert(thread_format);

FormatEntity::Format(*thread_format, strm, frame_sp ? &frame_sc : nullptr,
&exe_ctx, nullptr, nullptr, false, false);
DumpUsingFormat(strm, frame_idx, thread_format);
}

void Thread::SettingsInitialize() {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@
Test lldb-dap setBreakpoints request
"""

import dap_server
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
import lldbdap_testcase


class TestDAP_correct_thread(lldbdap_testcase.DAPTestCaseBase):
class TestDAP_threads(lldbdap_testcase.DAPTestCaseBase):
@skipIfWindows
@skipIfRemote
def test_correct_thread(self):
Expand Down Expand Up @@ -44,3 +43,27 @@ def test_correct_thread(self):
)
self.assertFalse(stopped_event[0]["body"]["preserveFocusHint"])
self.assertTrue(stopped_event[0]["body"]["threadCausedFocus"])

@skipIfWindows
@skipIfRemote
def test_thread_format(self):
"""
Tests the support for custom thread formats.
"""
program = self.getBuildArtifact("a.out")
self.build_and_launch(
program, customThreadFormat="This is thread index #${thread.index}"
)
source = "main.c"
breakpoint_line = line_number(source, "// break here")
lines = [breakpoint_line]
# Set breakpoint in the thread function
breakpoint_ids = self.set_source_breakpoints(source, lines)
self.assertEqual(
len(breakpoint_ids), len(lines), "expect correct number of breakpoints"
)
self.continue_to_breakpoints(breakpoint_ids)
# We are stopped at the second thread
threads = self.dap_server.get_threads()
self.assertEquals(threads[0]["name"], "This is thread index #1")
self.assertEquals(threads[1]["name"], "This is thread index #2")
17 changes: 16 additions & 1 deletion lldb/tools/lldb-dap/DAP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -828,7 +828,7 @@ void DAP::SetFrameFormat(llvm::StringRef format) {
if (format.empty())
return;
lldb::SBError error;
g_dap.frame_format = lldb::SBFormat(format.data(), error);
g_dap.frame_format = lldb::SBFormat(format.str().c_str(), error);
if (error.Fail()) {
g_dap.SendOutput(
OutputType::Console,
Expand All @@ -839,4 +839,19 @@ void DAP::SetFrameFormat(llvm::StringRef format) {
}
}

void DAP::SetThreadFormat(llvm::StringRef format) {
if (format.empty())
return;
lldb::SBError error;
g_dap.thread_format = lldb::SBFormat(format.str().c_str(), error);
if (error.Fail()) {
g_dap.SendOutput(
OutputType::Console,
llvm::formatv(
"The provided thread format '{0}' couldn't be parsed: {1}\n",
format, error.GetCString())
.str());
}
}

} // namespace lldb_dap
3 changes: 3 additions & 0 deletions lldb/tools/lldb-dap/DAP.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ struct DAP {
bool auto_repl_mode_collision_warning;
std::string command_escape_prefix = "`";
lldb::SBFormat frame_format;
lldb::SBFormat thread_format;

DAP();
~DAP();
Expand Down Expand Up @@ -309,6 +310,8 @@ struct DAP {

void SetFrameFormat(llvm::StringRef format);

void SetThreadFormat(llvm::StringRef format);

private:
// Send the JSON in "json_str" to the "out" stream. Correctly send the
// "Content-Length:" field followed by the length, followed by the raw
Expand Down
43 changes: 25 additions & 18 deletions lldb/tools/lldb-dap/JSONUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -852,26 +852,33 @@ llvm::json::Value CreateStackFrame(lldb::SBFrame &frame) {
llvm::json::Value CreateThread(lldb::SBThread &thread) {
llvm::json::Object object;
object.try_emplace("id", (int64_t)thread.GetThreadID());
const char *thread_name = thread.GetName();
const char *queue_name = thread.GetQueueName();

std::string thread_str;
if (thread_name) {
thread_str = std::string(thread_name);
} else if (queue_name) {
auto kind = thread.GetQueue().GetKind();
std::string queue_kind_label = "";
if (kind == lldb::eQueueKindSerial) {
queue_kind_label = " (serial)";
} else if (kind == lldb::eQueueKindConcurrent) {
queue_kind_label = " (concurrent)";
}

thread_str = llvm::formatv("Thread {0} Queue: {1}{2}", thread.GetIndexID(),
queue_name, queue_kind_label)
.str();
lldb::SBStream stream;
if (g_dap.thread_format &&
thread.GetDescriptionWithFormat(g_dap.thread_format, stream).Success()) {
thread_str = stream.GetData();
} else {
thread_str = llvm::formatv("Thread {0}", thread.GetIndexID()).str();
const char *thread_name = thread.GetName();
const char *queue_name = thread.GetQueueName();

if (thread_name) {
thread_str = std::string(thread_name);
} else if (queue_name) {
auto kind = thread.GetQueue().GetKind();
std::string queue_kind_label = "";
if (kind == lldb::eQueueKindSerial) {
queue_kind_label = " (serial)";
} else if (kind == lldb::eQueueKindConcurrent) {
queue_kind_label = " (concurrent)";
}

thread_str =
llvm::formatv("Thread {0} Queue: {1}{2}", thread.GetIndexID(),
queue_name, queue_kind_label)
.str();
} else {
thread_str = llvm::formatv("Thread {0}", thread.GetIndexID()).str();
}
}

EmplaceSafeString(object, "name", thread_str);
Expand Down
2 changes: 2 additions & 0 deletions lldb/tools/lldb-dap/lldb-dap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,7 @@ void request_attach(const llvm::json::Object &request) {
g_dap.command_escape_prefix =
GetString(arguments, "commandEscapePrefix", "`");
g_dap.SetFrameFormat(GetString(arguments, "customFrameFormat"));
g_dap.SetThreadFormat(GetString(arguments, "customThreadFormat"));

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

// This is a hack for loading DWARF in .o files on Mac where the .o files
// in the debug map of the main executable have relative paths which require
Expand Down
10 changes: 10 additions & 0 deletions lldb/tools/lldb-dap/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,11 @@
"type": "string",
"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.",
"default": ""
},
"customThreadFormat": {
"type": "string",
"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.",
"default": ""
}
}
},
Expand Down Expand Up @@ -359,6 +364,11 @@
"type": "string",
"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.",
"default": ""
},
"customThreadFormat": {
"type": "string",
"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.",
"default": ""
}
}
}
Expand Down