-
Notifications
You must be signed in to change notification settings - Fork 11.8k
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
[lldb-dap] Add support for data breakpoint. #81541
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -561,6 +561,46 @@ void EventThreadFunction() { | |
} | ||
} | ||
|
||
lldb::SBValue FindVariable(uint64_t variablesReference, llvm::StringRef name) { | ||
lldb::SBValue variable; | ||
if (lldb::SBValueList *top_scope = GetTopLevelScope(variablesReference)) { | ||
bool is_duplicated_variable_name = name.contains(" @"); | ||
// variablesReference is one of our scopes, not an actual variable it is | ||
// asking for a variable in locals or globals or registers | ||
int64_t end_idx = top_scope->GetSize(); | ||
// Searching backward so that we choose the variable in closest scope | ||
// among variables of the same name. | ||
for (int64_t i = end_idx - 1; i >= 0; --i) { | ||
lldb::SBValue curr_variable = top_scope->GetValueAtIndex(i); | ||
std::string variable_name = CreateUniqueVariableNameForDisplay( | ||
curr_variable, is_duplicated_variable_name); | ||
if (variable_name == name) { | ||
variable = curr_variable; | ||
break; | ||
} | ||
} | ||
} else { | ||
// This is not under the globals or locals scope, so there are no duplicated | ||
// names. | ||
|
||
// We have a named item within an actual variable so we need to find it | ||
// withing the container variable by name. | ||
lldb::SBValue container = g_dap.variables.GetVariable(variablesReference); | ||
variable = container.GetChildMemberWithName(name.data()); | ||
if (!variable.IsValid()) { | ||
if (name.starts_with("[")) { | ||
llvm::StringRef index_str(name.drop_front(1)); | ||
uint64_t index = 0; | ||
if (!index_str.consumeInteger(0, index)) { | ||
if (index_str == "]") | ||
variable = container.GetChildAtIndex(index); | ||
} | ||
} | ||
} | ||
} | ||
return variable; | ||
} | ||
|
||
// Both attach and launch take a either a sourcePath or sourceMap | ||
// argument (or neither), from which we need to set the target.source-map. | ||
void SetSourceMapFromArguments(const llvm::json::Object &arguments) { | ||
|
@@ -2697,58 +2737,41 @@ void request_dataBreakpointInfo(const llvm::json::Object &request) { | |
GetUnsigned(arguments, "variablesReference", 0); | ||
llvm::StringRef name = GetString(arguments, "name"); | ||
lldb::SBFrame frame = g_dap.GetLLDBFrame(*arguments); | ||
bool is_duplicated_variable_name = name.contains(" @"); | ||
lldb::SBValue variable = FindVariable(variablesReference, name); | ||
std::string addr, size; | ||
|
||
lldb::SBValue variable; | ||
if (lldb::SBValueList *top_scope = GetTopLevelScope(variablesReference)) { | ||
// variablesReference is one of our scopes, not an actual variable it is | ||
// asking for a variable in locals or globals or registers | ||
int64_t end_idx = top_scope->GetSize(); | ||
// Searching backward so that we choose the variable in closest scope | ||
// among variables of the same name. | ||
for (int64_t i = end_idx - 1; i >= 0; --i) { | ||
lldb::SBValue curr_variable = top_scope->GetValueAtIndex(i); | ||
std::string variable_name = CreateUniqueVariableNameForDisplay( | ||
curr_variable, is_duplicated_variable_name); | ||
if (variable_name == name) { | ||
variable = curr_variable; | ||
break; | ||
} | ||
} | ||
if (variable.IsValid()) { | ||
addr = llvm::utohexstr(variable.GetLoadAddress()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need some error checking here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sent a fix at #81680 |
||
size = llvm::utostr(variable.GetByteSize()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need to check if the byte size is not zero and return an error if it is. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sent a fix at #81680 |
||
} else if (variablesReference == 0 && frame.IsValid()) { | ||
// Name might be an expression. In this case we assume that name is composed | ||
// of the number of bytes to watch and expression, separated by '@': | ||
// "${size}@${expression}" | ||
Comment on lines
+2747
to
+2749
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this format VS Code supported, or just something we are making up? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm making this up, since the spec doesn't say how to specify the number of bytes to watch. Do you have better idea? |
||
llvm::StringRef expr; | ||
std::tie(size, expr) = name.split('@'); | ||
lldb::SBValue value = frame.EvaluateExpression(expr.data()); | ||
if (value.GetError().Fail()) { | ||
lldb::SBError error = value.GetError(); | ||
const char *error_cstr = error.GetCString(); | ||
body.try_emplace("dataId", nullptr); | ||
body.try_emplace("description", error_cstr && error_cstr[0] | ||
? std::string(error_cstr) | ||
: "evaluation failed"); | ||
} else | ||
addr = llvm::utohexstr(value.GetValueAsUnsigned()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We probably want to check this address by making sure it is valid. What if this return zero? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sent a fix at #81680 |
||
} else { | ||
// We are expanding a variable that has children, so we will return its | ||
// children. | ||
lldb::SBValue container = g_dap.variables.GetVariable(variablesReference); | ||
variable = container.GetChildMemberWithName(name.data()); | ||
if (!variable.IsValid()) { | ||
if (name.starts_with("[")) { | ||
llvm::StringRef index_str(name.drop_front(1)); | ||
uint64_t index = 0; | ||
if (!index_str.consumeInteger(0, index)) { | ||
if (index_str == "]") | ||
variable = container.GetChildAtIndex(index); | ||
} | ||
} | ||
} | ||
auto state = g_dap.target.GetProcess().GetState(); | ||
body.try_emplace("dataId", nullptr); | ||
body.try_emplace("description", | ||
"variable not found: " + llvm::utostr(state)); | ||
} | ||
|
||
if (variable.IsValid()) { | ||
std::string addr = llvm::utohexstr(variable.GetLoadAddress()); | ||
std::string size = llvm::utostr(variable.GetByteSize()); | ||
if (!body.getObject("dataId")) { | ||
body.try_emplace("dataId", addr + "/" + size); | ||
body.try_emplace("accessTypes", std::move(accessTypes)); | ||
body.try_emplace("description", | ||
size + " bytes at " + addr + " " + name.str()); | ||
} else { | ||
// TODO: name might be an expression if variablesReference == 0. In that | ||
// case, we need to evaluate expression in the scope of given frame (or | ||
// global scope if not given), but SBTarget::EvaluateExpression can only | ||
// evalute expression on selected thread and frame. Also, it doesn't specify | ||
// the number of bytes to watch. | ||
body.try_emplace("dataId", nullptr); | ||
body.try_emplace("description", "variable not found."); | ||
} | ||
|
||
response.try_emplace("body", std::move(body)); | ||
g_dap.SendJSON(llvm::json::Value(std::move(response))); | ||
} | ||
|
@@ -3319,7 +3342,6 @@ void request_setVariable(const llvm::json::Object &request) { | |
const auto variablesReference = | ||
GetUnsigned(arguments, "variablesReference", 0); | ||
llvm::StringRef name = GetString(arguments, "name"); | ||
bool is_duplicated_variable_name = name.contains(" @"); | ||
|
||
const auto value = GetString(arguments, "value"); | ||
// Set success to false just in case we don't find the variable by name | ||
|
@@ -3340,40 +3362,8 @@ void request_setVariable(const llvm::json::Object &request) { | |
const auto id_value = GetUnsigned(arguments, "id", UINT64_MAX); | ||
if (id_value != UINT64_MAX) { | ||
variable = g_dap.variables.GetVariable(id_value); | ||
} else if (lldb::SBValueList *top_scope = | ||
GetTopLevelScope(variablesReference)) { | ||
// variablesReference is one of our scopes, not an actual variable it is | ||
// asking for a variable in locals or globals or registers | ||
int64_t end_idx = top_scope->GetSize(); | ||
// Searching backward so that we choose the variable in closest scope | ||
// among variables of the same name. | ||
for (int64_t i = end_idx - 1; i >= 0; --i) { | ||
lldb::SBValue curr_variable = top_scope->GetValueAtIndex(i); | ||
std::string variable_name = CreateUniqueVariableNameForDisplay( | ||
curr_variable, is_duplicated_variable_name); | ||
if (variable_name == name) { | ||
variable = curr_variable; | ||
break; | ||
} | ||
} | ||
} else { | ||
// This is not under the globals or locals scope, so there are no duplicated | ||
// names. | ||
|
||
// We have a named item within an actual variable so we need to find it | ||
// withing the container variable by name. | ||
lldb::SBValue container = g_dap.variables.GetVariable(variablesReference); | ||
variable = container.GetChildMemberWithName(name.data()); | ||
if (!variable.IsValid()) { | ||
if (name.starts_with("[")) { | ||
llvm::StringRef index_str(name.drop_front(1)); | ||
uint64_t index = 0; | ||
if (!index_str.consumeInteger(0, index)) { | ||
if (index_str == "]") | ||
variable = container.GetChildAtIndex(index); | ||
} | ||
} | ||
} | ||
variable = FindVariable(variablesReference, name); | ||
} | ||
|
||
if (variable.IsValid()) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to check the name that might include the "foo @ main.cpp:12" or just look for "foo"? Also, some members of a struct be anonymous unions, we probably want to make sure we can do the right thing with those
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This functions is shared between
request_setVariable
andrequest_dataBreakpointInfo
, so it checks name that containsfoo @ main.cpp:12
. I tested this manually.For anonymous unions inside struct, I also tested manually. If we watch either
c
ord
, changes on either value stops the program as expected.