How to Write an LLDB Plugin with Python: New Commands

Adding a new LLDB command using a python script is pretty straightforward. We are going to write a command that does the following:

  1. Print each frame in the stack trace
  2. For each frame print all arguments and local variables

The Plugin Code

Place this code in a new directory, say lldb_plugin, in a file named custom_command.py:


import lldb
import commands
import optparse
import shlex

def my_cmd(debugger, command, result, internal_dict):
    target = debugger.GetSelectedTarget()
    process = target.GetProcess()
    thread = process.GetSelectedThread()

    for frame in thread:

        print str(frame)

        function = frame.GetFunction()
        print 'FUNCTION = ', function

        if frame.IsInlined():
            print 'INLINED'
        else:
            args = frame.get_arguments()

            print '# of arguments = ', len(args)

            for arg in args:
                print arg

            vars = frame.get_all_variables()

            print '# of vars =', len(args)

            for var in vars:
                print var

def __lldb_init_module(debugger, internal_dict):
    debugger.HandleCommand('command script add -f custom_command.my_cmd my_cmd')
    print 'The "my_cmd" python command has been installed and is ready for use.'

This code contains two functions. __lldb_init_module is boilerplate that is used to load the command during an lldb session.

The meat of the command is the my_cmd function. This function takes four arguments, but the main one we will use is debugger. debugger represents the actual debug session, we call its GetSelectedTarget method to get the target, or running program, that we are debugging. From the target we get the running process, and from the process we get the running thread.

Things get interesting inside the thread. We can check if the frame is inlined, and if not we can list all arguments, list all variables and print out information about the function itself.

Using the Plugin

To use this plugin we first need to create an executable to use as the target. In this example we will use a simple C++ program. First enter this code into a file named contrived.cpp:


#include "contrived.h"

int contrived_1(const int l, const int v, const int q, my_value my_v) {
int r1 = l - v;
int r2 = q*l;

if (my_v.useless) {
return r1 - r2;
} else {
return my_v.x + my_v.y + my_v.z - r1*r2;
}
}

Next create the corresponding contrived.h file:

struct my_value {
int x;
int y;
int z;

bool useless;
};

int contrived_1(const int l, const int v, const int q, my_value my_v);

Finally create a main.cpp file:


#include <iostream>

#include "contrived.h"

using namespace std;

int main() {
int x = 2;
int y = 4;
int z = 9;

bool useless = true;

my_value v{x, y, z, useless};
int k = contrived_1(3, 4, -12, v);

cout << k << endl;
}

With these files created compile them with:


clang++ -g -O0 -std=c++11 contrived.cpp main.cpp

This will create an executable, a.out. With a.out created we can debug it and use our plugin.

In the directory lldb_plugin run the commands:


lldb ./a.out
breakpoint set --n contrived_1
r

command script import custom_command.py

After the import you should see:

The "my_cmd" python command has been installed and is ready for use.

Now run the command:


my_cmd

and you should see something like:


frame #0: 0x0000000100000d15 a.out`contrived_1(l=3, v=4, q=-12, my_v=(x = 2, y = 4, z = 9, useless = true)) + 21 at contrived.cpp:4
FUNCTION =  SBFunction: id = 0x0000008d, name = contrived_1(int, int, int, my_value), type = contrived_1
# of arguments =  4
(const int) l = 3
(const int) v = 4
(const int) q = -12
(my_value) my_v = (x = 2, y = 4, z = 9, useless = true)
# of vars = 4
(const int) l = 3
(const int) v = 4
(const int) q = -12
(my_value) my_v = (x = 2, y = 4, z = 9, useless = true)
(int) r1 = 1606579087
(int) r2 = 1
frame #1: 0x0000000100000dc8 a.out`main + 104 at main.cpp:15
FUNCTION =  SBFunction: id = 0x00008d35, name = main, type = main
# of arguments =  0
# of vars = 0
(int) x = 2
(int) y = 4
(int) z = 9
(bool) useless = true
(my_value) v = (x = 2, y = 4, z = 9, useless = true)
(int) k = 0
frame #2: 0x00007fff9dca45ad libdyld.dylib`start + 1
FUNCTION =  No value
# of arguments =  0
# of vars = 0

More LLDB Info

Full documentation for the LLDB python API can be found here

Leave a Reply

Your email address will not be published. Required fields are marked *