Debugger: Add Value Formatting support for StackTrace request

Add support for the "format" property of the Debug Adapter Protocol
StackTrace request to fulfill the host's request to format the resulting
StackFrame name differently.
This commit is contained in:
Jonathan Phippen
2024-10-23 21:19:41 -07:00
parent 4b08b10900
commit 41621c3afb
9 changed files with 104 additions and 13 deletions

View File

@@ -148,6 +148,7 @@ cmDebuggerAdapter::cmDebuggerAdapter(
SupportsVariableType = req.supportsVariableType.value(false);
dap::CMakeInitializeResponse response;
response.supportsConfigurationDoneRequest = true;
response.supportsValueFormattingOptions = true;
response.cmakeVersion.major = CMake_VERSION_MAJOR;
response.cmakeVersion.minor = CMake_VERSION_MINOR;
response.cmakeVersion.patch = CMake_VERSION_PATCH;
@@ -186,7 +187,7 @@ cmDebuggerAdapter::cmDebuggerAdapter(
std::unique_lock<std::mutex> lock(Mutex);
cm::optional<dap::StackTraceResponse> response =
ThreadManager->GetThreadStackTraceResponse(request.threadId);
ThreadManager->GetThreadStackTraceResponse(request);
if (response.has_value()) {
return response.value();
}

View File

@@ -25,4 +25,10 @@ int64_t cmDebuggerStackFrame::GetLine() const noexcept
return this->Function.Line();
}
std::vector<cmListFileArgument> const& cmDebuggerStackFrame::GetArguments()
const noexcept
{
return this->Function.Arguments();
}
} // namespace cmDebugger

View File

@@ -7,8 +7,10 @@
#include <atomic>
#include <cstdint>
#include <string>
#include <vector>
class cmListFileFunction;
struct cmListFileArgument;
class cmMakefile;
namespace cmDebugger {
@@ -32,6 +34,7 @@ public:
{
return this->Function;
}
std::vector<cmListFileArgument> const& GetArguments() const noexcept;
};
} // namespace cmDebugger

View File

@@ -14,6 +14,7 @@
#include "cmDebuggerVariablesHelper.h"
#include "cmDebuggerVariablesManager.h"
#include "cmListFileCache.h"
#include "cmStringAlgorithms.h"
namespace cmDebugger {
@@ -117,8 +118,27 @@ dap::VariablesResponse cmDebuggerThread::GetVariablesResponse(
}
dap::StackTraceResponse GetStackTraceResponse(
std::shared_ptr<cmDebuggerThread> const& thread)
std::shared_ptr<cmDebuggerThread> const& thread,
dap::optional<dap::StackFrameFormat> format)
{
dap::boolean showParameters = false;
dap::boolean showParameterValues = false;
dap::boolean showLine = false;
if (format.has_value()) {
auto formatValue = format.value();
if (formatValue.parameters.has_value()) {
showParameters = formatValue.parameters.value();
}
if (formatValue.parameterValues.has_value()) {
showParameterValues = formatValue.parameterValues.value();
}
if (formatValue.line.has_value()) {
showLine = formatValue.line.value();
}
}
dap::StackTraceResponse response;
std::unique_lock<std::mutex> lock(thread->Mutex);
for (int i = static_cast<int>(thread->Frames.size()) - 1; i >= 0; --i) {
@@ -136,10 +156,29 @@ dap::StackTraceResponse GetStackTraceResponse(
#endif
stackFrame.line = thread->Frames[i]->GetLine();
stackFrame.column = 1;
stackFrame.name = thread->Frames[i]->GetFunction().OriginalName();
stackFrame.id = thread->Frames[i]->GetId();
stackFrame.source = source;
auto stackName = thread->Frames[i]->GetFunction().OriginalName();
if (showParameters) {
stackName.push_back('(');
if (showParameterValues && !thread->Frames[i]->GetArguments().empty()) {
for (auto const& arg : thread->Frames[i]->GetArguments()) {
stackName = cmStrCat(stackName, arg.Value, ", ");
}
stackName.erase(stackName.end() - 2, stackName.end());
}
stackName.push_back(')');
}
if (showLine) {
stackName =
cmStrCat(stackName, " Line: ", static_cast<int64_t>(stackFrame.line));
}
stackFrame.name = stackName;
response.stackFrames.push_back(stackFrame);
}

View File

@@ -23,6 +23,11 @@ class cmDebuggerVariables;
class cmDebuggerVariablesManager;
}
namespace dap {
template <typename T>
class optional;
}
namespace cmDebugger {
class cmDebuggerThread
@@ -53,7 +58,8 @@ public:
dap::VariablesResponse GetVariablesResponse(
dap::VariablesRequest const& request);
friend dap::StackTraceResponse GetStackTraceResponse(
std::shared_ptr<cmDebuggerThread> const& thread);
std::shared_ptr<cmDebuggerThread> const& thread,
dap::optional<dap::StackFrameFormat> format);
};
} // namespace cmDebugger

View File

@@ -6,6 +6,7 @@
#include <algorithm>
#include <cm3p/cppdap/protocol.h>
#include <cm3p/cppdap/types.h>
#include "cmDebuggerThread.h"
@@ -30,18 +31,19 @@ void cmDebuggerThreadManager::EndThread(
}
cm::optional<dap::StackTraceResponse>
cmDebuggerThreadManager::GetThreadStackTraceResponse(int64_t id)
cmDebuggerThreadManager::GetThreadStackTraceResponse(
const dap::StackTraceRequest& request)
{
auto it = find_if(Threads.begin(), Threads.end(),
[&](const std::shared_ptr<cmDebuggerThread>& t) {
return t->GetId() == id;
return t->GetId() == request.threadId;
});
if (it == Threads.end()) {
return {};
}
return GetStackTraceResponse(*it);
return GetStackTraceResponse(*it, request.format);
}
} // namespace cmDebugger

View File

@@ -17,6 +17,7 @@ class cmDebuggerThread;
}
namespace dap {
struct StackTraceRequest;
struct StackTraceResponse;
}
@@ -32,7 +33,7 @@ public:
std::shared_ptr<cmDebuggerThread> StartThread(std::string const& name);
void EndThread(std::shared_ptr<cmDebuggerThread> const& thread);
cm::optional<dap::StackTraceResponse> GetThreadStackTraceResponse(
std::int64_t id);
const dap::StackTraceRequest& request);
};
} // namespace cmDebugger

View File

@@ -132,6 +132,7 @@ bool runTest(std::function<bool(dap::Session&)> onThreadExitedEvent)
ASSERT_TRUE(initializeResponse.response.supportsExceptionInfoRequest);
ASSERT_TRUE(
initializeResponse.response.exceptionBreakpointFilters.has_value());
ASSERT_TRUE(initializeResponse.response.supportsValueFormattingOptions);
dap::LaunchRequest launchRequest;
auto launchResponse = client->send(launchRequest).get();

View File

@@ -2,6 +2,7 @@
#include <string>
#include <vector>
#include <cm3p/cppdap/optional.h>
#include <cm3p/cppdap/protocol.h>
#include <cm3p/cppdap/types.h>
@@ -10,21 +11,52 @@
#include "testCommon.h"
static bool testStackFrameFunctionName()
static bool testStackFrameFunctionName(
dap::optional<dap::StackFrameFormat> format, const char* expectedName)
{
auto thread = std::make_shared<cmDebugger::cmDebuggerThread>(0, "name");
const auto* functionName = "function_name";
auto arguments = std::vector<cmListFileArgument>{};
auto arguments = std::vector<cmListFileArgument>{ cmListFileArgument(
"arg", cmListFileArgument::Delimiter::Unquoted, 0) };
cmListFileFunction func(functionName, 10, 20, arguments);
thread->PushStackFrame(nullptr, "CMakeLists.txt", func);
auto stackTrace = GetStackTraceResponse(thread);
auto stackTrace = GetStackTraceResponse(thread, format);
ASSERT_TRUE(stackTrace.stackFrames[0].name == functionName);
ASSERT_TRUE(stackTrace.stackFrames[0].name == expectedName);
return true;
}
bool testStackFrameNoFormatting()
{
return testStackFrameFunctionName({}, "function_name");
}
bool testStackFrameFormatParameters()
{
dap::StackFrameFormat format;
format.parameters = true;
return testStackFrameFunctionName(format, "function_name()");
}
bool testStackFrameFormatParameterValues()
{
dap::StackFrameFormat format;
format.parameters = true;
format.parameterValues = true;
return testStackFrameFunctionName(format, "function_name(arg)");
}
bool testStackFrameFormatLine()
{
dap::StackFrameFormat format;
format.line = true;
return testStackFrameFunctionName(format, "function_name Line: 10");
}
int testDebuggerThread(int, char*[])
{
return runTests({ testStackFrameFunctionName });
return runTests({ testStackFrameNoFormatting, testStackFrameFormatParameters,
testStackFrameFormatParameterValues,
testStackFrameFormatLine });
}