Rust-lldb: conditional breakpoint on string value?

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