I was hoping to set a breakpoint in rust-lldb based on the value of a string. However, I'm not having any luck. I'm trying to debug using expr to see what certain conditionals would return:
(lldb) expr self.description == "Interest Paid"
error: warning: <user expression 89>:1:18: result of comparison against a string literal is unspecified (use an explicit string comparison function instead)
1 | self.description == "Interest Paid"
| ^ ~~~~~~~~~~~~~~~
error: <user expression 89>:1:18: invalid operands to binary expression ('alloc::string::String' and 'const char[14]')
1 | self.description == "Interest Paid"
| ~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~
(lldb) expr (int) strcmp(self.description, "Interest Paid")
(int) $15 = -60
(lldb) p self.description
(alloc::string::String) "Interest Paid" {
[0] = 'I'
[1] = 'n'
[2] = 't'
[3] = 'e'
[4] = 'r'
[5] = 'e'
[6] = 's'
[7] = 't'
[8] = ' '
[9] = 'P'
[10] = 'a'
[11] = 'i'
[12] = 'd'
}
Does anyone know how (or if) this should work?
String::is_empty() doesn't seem to work in lldb, but I figured out that I can use self.description.vec.len == 0
And I can compare the first letter:
(lldb) expr *self.description.vec.buf.inner.ptr.pointer.pointer == 'I'
(bool) $14 = true
I came across the same issue, but with GDB. It compelled me to create a convenience function using Python API. I think you have to go the same way with LLDB. I can't find information about creating a convenience function in LLDB. However, I can try to create a custom command. The command accepts two strings to compare. You can refer to Rust LLDB scripts to see how to evaluate a Rust sting in a Python script. I hope it gives some idea
1 Like
Awesome, thanks for sharing!
Great idea. My first (and only!) contribution to rust was related to lldb and Path representations, using the Python api, so I am at least somewhat familiar with the territory and imagine this would be possible.
Struggling a bit trying to use the LLVM tooling the "right way," but as long as one has write access to the code in question, it's really easy to just add a couple lines to the source and use it for the "conditional" breakpoint:
if haystack.contains("NEEDLE") {
dbg!(&haystack); // <- breakpoint here
}
I was able to figure out a way to accomplish this with the LLDB tooling. It is not the only way, and it may not be the best way, but it worked for my situation.
I wrote a small python module with a few helper functions:
"""strcompare.py
Adds Rust string comparison breakpoint to lldb
Usage:
```
$ rust-lldb target/debug/my_binary
(lldb) command script import /path/to/strcompare.py
(lldb) break set -f project_name/src/main.rs -l 42
(lldb) break command add -F strcompare.break_if_contains -k haystack -v self.description -k needle -v hello 1
(lldb) run
```
Alternatively, using the `run_break` helper:
```
$ rust-lldb target/debug/my_binary
(lldb) command script import /path/to/strcompare.py
(lldb) run_break -f project_name/src/main.rs -l 42 -n hello -h self.description
```
Further reading:
<https://n8henrie.com/2025/12/how-to-set-a-conditional-breakpoint-when-debugging-rust-with-lldb/>
<https://lldb.llvm.org/use/tutorials/writing-custom-commands.html>
<https://lldb.llvm.org/use/tutorials/breakpoint-triggered-scripts.html>
"""
def break_if_contains(frame, bp_loc, extra_args, internal_dict):
"""Break if a Rust string contains a substring
Usage:
(lldb) break command add -F filename.break_if_contains \
-k haystack -v UNQUOTED_VAR_NAME \
-k needle -v UNQUOTED_STRING \
BREAKPOINT_NUMBER
Example:
(lldb) break command add -F strcompare.break_if_contains \
-k haystack -v self.description \
-k needle -v Hello \
1
"""
needle = str(extra_args.GetValueForKey("needle"))
haystack = str(extra_args.GetValueForKey("haystack"))
parts = haystack.split(".")
current = frame.FindVariable(parts[0])
if not current.IsValid():
return False
for part in parts[1:]:
current = current.GetChildMemberWithName(part)
if not current.IsValid():
return False
summary = current.summary.strip('"')
return needle in summary
def run_break(debugger, command, exe_ctx, result, internal_dict):
"""Helper to run `break_on_contains`
All arguments are required:
`-f`: file for breakpoint
`-l`: line for breakpoint
`-n`: needle
`-h`: haystack
example:
(lldb) run_break -f foo/src/main.rs -l 17 -n hello -h parent.child.data
"""
args = command.split()
args.reverse()
while True:
try:
arg = args.pop()
except IndexError:
break
match arg:
case "-f":
file = args.pop()
case "-l":
line = args.pop()
case "-n":
needle = args.pop()
case "-h":
haystack = args.pop()
debugger.HandleCommand(f"break set -f {file} -l {line}")
debugger.HandleCommand(
" ".join(
[
"breakpoint command add --python-function strcompare.break_if_contains",
f"-k haystack -v {haystack}",
f"-k needle -v {needle}",
]
)
)
debugger.HandleCommand("run")
def __lldb_init_module(debugger, internal_dict):
debugger.HandleCommand(
"command script add -f strcompare.run_break run_break"
)
I think the docstrings are likely sufficient to get most users up and running, but I added a modest amount of additional context and detail in a writeup at https://n8henrie.com/2025/12/how-to-set-a-conditional-breakpoint-when-debugging-rust-with-lldb/.
For anyone looking for a quick-and-dirty approach, the following cast (of a variable named var) also seems to work:
(lldb) b -f foo/src/main.rs -l 3 -c '(char*)strstr((char*)var.vec.buf.inner.ptr.pointer.pointer, "asdf")'
Hope others find this useful!
1 Like