Breakpoints carry two orthogonal sets of information: one specifies where to set the breakpoint, and the other how to react when the breakpoint is hit. The latter set of information (e.g. commands, conditions, hit-count, auto-continue...) we call breakpoint options.
It is fairly common to want to apply one set of options to a number of breakpoints. For instance, you might want to check that self == nil and if it is, print a backtrace and continue, on a number of methods. One convenient way to do that would be to make all the breakpoints, then configure the options with:
That's not too bad, but you have to repeat this for every new breakpoint you make, and if you wanted to change the options, you have to remember all the ones you are using this way.
Breakpoint names provide a convenient solution to this problem. The simple solution would be to use the name to gather the breakpoints you want to affect this way into a group. So when you make the breakpoint you would do:
::
(lldb) breakpoint set -N SelfNil
Then when you've made all your breakpoints, you can set up or modify the options using the name to collect all the relevant breakpoints.
To launch a program in lldb we use the "process launch" command or one of its built in aliases:
::
(lldb) process launch
(lldb) run
(lldb) r
You can also attach to a process by process ID or process name. When attaching
to a process by name, lldb also supports the "--waitfor" option which waits for
the next process that has that name to show up, and attaches to it
::
(lldb) process attach --pid 123
(lldb) process attach --name Sketch
(lldb) process attach --name Sketch --waitfor
After you launch or attach to a process, your process might stop somewhere:
::
(lldb) process attach -p 12345
Process 46915 Attaching
Process 46915 Stopped
1 of 3 threads stopped with reasons:
* thread #1: tid = 0x2c03, 0x00007fff85cac76a, where = libSystem.B.dylib`__getdirentries64 + 10, stop reason = signal = SIGSTOP, queue = com.apple.main-thread
Note the line that says "1 of 3 threads stopped with reasons:" and the lines
that follow it. In a multi-threaded environment it is very common for more than
one thread to hit your breakpoint(s) before the kernel actually returns control
to the debugger. In that case, you will see all the threads that stopped for
some interesting reason listed in the stop message.
Controlling Your Program
------------------------
After launching, we can continue until we hit our breakpoint. The primitive commands for process control all exist under the "thread" command:
::
(lldb) thread continue
Resuming thread 0x2c03 in process 46915
Resuming process 46915
(lldb)
At present you can only operate on one thread at a time, but the design will ultimately support saying "step over the function in Thread 1, and step into the function in Thread 2, and continue Thread 3" etc. When we eventually support keeping some threads running while others are stopped this will be particularly important. For convenience, however, all the stepping commands have easy aliases. So "thread continue" is just "c", etc.
The other program stepping commands are pretty much the same as in gdb. You've got:
::
(lldb) thread step-in // The same as gdb's "step" or "s"
(lldb) thread step-over // The same as gdb's "next" or "n"
(lldb) thread step-out // The same as gdb's "finish" or "f"
By default, lldb does defined aliases to all common gdb process control commands ("s", "step", "n", "next", "finish"). If we have missed any, please add them to your ~/.lldbinit file using the "command alias" command.
lldb also supported the step by instruction versions:
::
(lldb) thread step-inst // The same as gdb's "stepi" / "si"
(lldb) thread step-over-inst // The same as gdb's "nexti" / "ni"
Finally, lldb has a run until line or frame exit stepping mode:
::
(lldb) thread until 100
This command will run the thread in the current frame till it reaches line 100
in this frame or stops if it leaves the current frame. This is a pretty close
equivalent to gdb's "until" command.
A process, by default, will share the lldb terminal with the inferior process.
When in this mode, much like when debugging with gdb, when the process is
running anything you type will go to the STDIN of the inferior process. To
interrupt your inferior program, type CTRL+C.
If you attach to a process, or launch a process with the "--no-stdin" option,
the command interpreter is always available to enter commands. This might be a
little disconcerting to gdb users when always have an (lldb) prompt. This
allows you to set a breakpoint, etc without having to explicitly interrupt the
program you are debugging:
::
(lldb) process continue
(lldb) breakpoint set --name stop_here
There are many commands that won't work while running, and the command
interpreter should do a good job of letting you know when this is the case. If
you find any instances where the command interpreter isn't doing its job,
please file a bug. This way of operation will set us up for a future debugging
mode called thread centric debugging. This mode will allow us to run all
threads and only stop the threads that are at breakpoints or have exceptions or
signals.
The commands that currently work while running include interrupting the process
to halt execution ("process interrupt"), getting the process status ("process
status"), breakpoint setting and clearing (" breakpoint
[set|clear|enable|disable|list] ..."), and memory reading and writing (" memory
[read|write] ...").
The question of disabling stdio when running brings up a good opportunity to
show how to set debugger properties in general. If you always want to run in
the --no-stdin mode, you can set this as a generic process property using the
lldb "settings" command, which is equivalent to gdb's "set" command. For
instance, in this case you would say:
::
(lldb) settings set target.process.disable-stdio true
Over time, gdb's "set command became a wilderness of disordered options, so
that there were useful options that even experienced gdb users didn't know
about because they were too hard to find. We tried to organize the settings
hierarchically using the structure of the basic entities in the debugger. For
the most part anywhere you can specify a setting on a generic entity (threads,
for example) you can also apply the option to a particular instance, which can
also be convenient at times. You can view the available settings with "settings
list" and there is help on the settings command explaining how it works more
generally.
Examining Thread State
----------------------
Once you've stopped, lldb will choose a current thread, usually the one that
stopped "for a reason", and a current frame in that thread (on stop this is
always the bottom-most frame). Many the commands for inspecting state work on
this current thread/frame.
To inspect the current state of your process, you can start with the threads:
::
(lldb) thread list
Process 46915 state is Stopped
* thread #1: tid = 0x2c03, 0x00007fff85cac76a, where = libSystem.B.dylib`__getdirentries64 + 10, stop reason = signal = SIGSTOP, queue = com.apple.main-thread
thread #2: tid = 0x2e03, 0x00007fff85cbb08a, where = libSystem.B.dylib`kevent + 10, queue = com.apple.libdispatch-manager
thread #3: tid = 0x2f03, 0x00007fff85cbbeaa, where = libSystem.B.dylib`__workq_kernreturn + 10
The ``*`` indicates that Thread 1 is the current thread. To get a backtrace for
You can select another frame to view with the "frame select" command
(lldb) frame select 9
frame #9: 0x0000000100015ae3, where = Sketch`function1 + 33 at /Projects/Sketch/SKTFunctions.m:11
You can also move up and down the stack by passing the "--relative" ("-r") option. And we have built-in aliases "u" and "d" which behave like their gdb equivalents.