# Script example You can script LLDB in two ways: - A Unix Python session can initiate and run a debug session non-interactively using LLDB. - Within the LLDB debugger tool, you can use Python scripts to perform many tasks, including inspecting program data, iterating over containers, and determining if a breakpoint should stop execution or continue. The following sections show how to use Python scripting to find a bug in a program that searches for text in a large binary tree. ## The test program and input The test program is a simple C program (dictionary.c) that reads a text file and stores all the words from the file in a binary search tree, sorted alphabetically. It then enters a loop prompting yo for a word, searching for the word in the tree (using a binary search), and reporting whether it found the word in the tree. The input text file used to test the program contains the text for William Shakespeare’s famous tragedy, *Romeo and Juliet*. ## The bug When you try running the program, you will find there is a problem. While it successfully finds some of the words it is expected to find, such as *love* or *sun*, it fails to find the word *Romeo*, which must be in the input text file: % ./dictionary Romeo-and-Juliet.txt Dictionary loaded. Enter search word: love Yes! Enter search word: sun Yes! Enter search word: Romeo No! Enter search word: ^D % Copy to clipboard ## Check for the word in the tree: use DFS The first task is to determine whether the word, *Romeo*, was inserted into the tree. Because *Romeo and Juliet* has thousands of words, trying to examine the binary search tree by hand is completely impractical. Therefore, a Python script is written to search the tree. The script is written as a recursive depth-first search (DFS) function that traverses the entire tree, searching for a word while maintaining information about the path from the root of the tree to the current node. If the function finds the word in the tree, it returns the path from the root to the node containing the word. Following is the DFS function in Python, with line numbers added for easy reference in the subsequent explanations of the code: 1: def DFS (root, word, cur_path): 2: root_word_ptr = root.GetChildMemberWithName ("word") 3: left_child_ptr = root.GetChildMemberWithName ("left") 4: right_child_ptr = root.GetChildMemberWithName ("right") 5: root_word = root_word_ptr.GetSummary() 6: end = len (root_word) - 1 7: if root_word[0] == '"' and root_word[end] == '"': 8: root_word = root_word[1:end] 9: end = len (root_word) - 1 10: if root_word[0] == '\'' and root_word[end] == '\'': 11: root_word = root_word[1:end] 12: if root_word == word: 13: return cur_path 14: elif word < root_word: 15: if left_child_ptr.GetValue() == None: 16: return "" 17: else: 18: cur_path = cur_path + "L" 19: return DFS (left_child_ptr, word, cur_path) 20: else: 21: if right_child_ptr.GetValue() == None: 22: return "" 23: else: 24: cur_path = cur_path + "R" 25: return DFS (right_child_ptr, word, cur_path) Copy to clipboard ## Work with program variables in Python Before calling a Python function on any of the program’s variables, put the variable into a form that Python can access. To do this, examine the parameters for the DFS function: - The first parameter is a node in a binary search tree, put into a Python variable. - The second parameter is the word being searched for (a string). The third parameter is a string representing the path from the root of the tree to the current node. The first parameter is the most interesting: it is the Python variable that must contain a node in the search tree. How can a variable be taken out of your program and stored in a Python variable? What kind of Python variable will it be? The answers are to use the LLDB API functions, which are provided as part of the LLDB Python module. When running Python from inside LLDB, LLDB automatically provides the current frame object as a Python variable, lldb.frame. This variable is of type SBFrame (see the LLDB API for more information on SBFrame objects). A frame object can be asked to find and return its local variable. The FindVariable() function is called on the lldb.frame object to provide the dictionary variable as a Python variable: root = lldb.frame.FindVariable ("dictionary") Copy to clipboard This line is executed in the Python script interpreter in LLDB, and it requests the current frame to find the variable named dictionary and return it. The returned value is stored in the Python variable named root. This answers the question of how to get the variable, but it still does not explain what is put into root. In the LLDB API, observe that the SBFrame method FindVariable() returns an object of type SBValue. SBValue objects are used, among other things, to wrap up program variables and values. There are many useful methods defined in the SBValue class that allow you to get information or children values out of SBValues. The SBValue methods used in the DFS function are GetChildMemberWithName(), GetSummary(), and GetValue(). Note For more information on SBValues, see the SBValue.h header file. ## Explanation of the DFS script Before describing the example code in detail, following is a high-level overview of what the code does. The nodes in the binary search tree are of type tree\_node \*, which is defined as follows: typedef struct tree_node { const char *word; struct tree_node *left; struct tree_node *right; } tree_node; Copy to clipboard - Lines 2 to 11 of the DFS function involve getting data out of the current tree node and getting ready to do the actual search. - Lines 2 to 4 of the function get the word, left, and right fields out of the current node and store them in Python variables. - Because root\_word\_ptr is a pointer to the word, and the word itself is required, line 5 calls GetSummary() to get a string containing the value out of the pointer. - Because GetSummary() adds quotes around its result, lines 6 to 11 strip the surrounding quotes off the word. - Lines 12 to 25 are the actual depth-first search. - Line 12 checks to see if the word in the current node is the one being searched for. If so, the search is completed. - Line 13 returns the current path. - Otherwise, line 14 checks to see if the search should go left (that is, the search word comes before the current word). - If the search goes left, line 15 checks to see if the left pointer child is NULL (None is the Python equivalent of NULL). - If the left pointer is NULL, then the word is not in this tree and the function returns an empty path (line 16). - Otherwise, it adds an L to the end of the current path string to indicate that the search is going left (line 18), and then recursively searches on the left child (line 19). - Lines 20-25 are the same as lines 14-19, except that the search goes right instead of left. It can be difficult to type all the DFS function code directly into the interpreter, because if you make a single typing mistake, you must completely start over. Therefore, we recommend you write longer, more complicated script functions in a separate file (in this case, tree\_utils.py), and then import the file into the LLDB Python interpreter. ## Use the DFS script Start by running the dictionary program in the Hexagon simulator, and assign it a unique port number: hexagon-sim -G 54321 dictionary -- Romeo_and_Juliet.txt hexagon-sim INFO: The rev_id used in the simulation is 0x00008d68(v68n_1024) hexagon-sim INFO: Setting up debug server on port 54321 Copy to clipboard Port 54321 is used in this example, but you can use any available, valid port number. Next, launch LLDB in a separate terminal, and use `gdb-remote` to attach to the dictionary program running on the simulator: hexagon-lldb dictionary Hexagon utilities (pagetable, tlb, pv) loaded (lldb) target create "dictionary" Current executable set to '/local/mnt/workspace/dictionary' (hexagon). (lldb) gdb-remote 54321 Process 1 stopped * thread #1, name = 'T1', stop reason = signal SIGTRAP frame #0: 0x00000000 dictionary`_start dictionary`_start: -> 0x0 <+0>: { jump 0x98 } 0x4 <+4>: { jump 0x80 } 0x8 <+8>: { jump 0x8c } 0xc: (lldb) breakpoint set -n find_word Breakpoint 1: where = dictionary`find_word + 12 at dictionary.c:108:6, address = 0x0000644c (lldb) c Process 1 resuming Copy to clipboard Back in the simulator’s terminal, wait for the message, `Dictionary loaded. Enter search word:`, to appear, and then enter `Romeo`. In the LLDB terminal, you will see the breakpoint at find\_word and the program is stopped. At this point, the DFS function is ready to determine whether the word, *Romeo*, is stored in the tree. To use the function in LLDB on the dictionary program, do something like the following: Process 1 stopped * thread #1, name = 'T1', stop reason = breakpoint 1.1 frame #0: 0x0000644c dictionary`find_word(dictionary=0x000141c0,word="romeo") at dictionary.c:108:6 105 in the binary search tree. */ 106 107 int find_word(tree_node *dictionary, char *word) { -> 108 if (!word || !dictionary) 109 return 0; 110 111 int compare_value = strcmp(word, dictionary->word); (lldb) script Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D. >>> import tree_utils >>> root = lldb.frame.FindVariable ("dictionary") >>> current_path = "" >>> path = tree_utils.DFS (root, "Romeo", current_path) >>> print path LLRRL >>> exit (lldb) Copy to clipboard The first bit of code shows starting LLDB, attaching to the dictionary program, and getting to the find\_word function in the LLDB. In this example, the interesting part begins when the script command drops the program into the embedded interactive Python interpreter. This code is described line-by-line. - The first line, import tree\_utils: Imports the file containing the DFS function (tree\_utils.py) into Python. When importing the file, the .py extension is omitted. You can call any function in the imported file after assigning it the prefix tree\_utils so Python knows where to look for the function. - The line, root = lldb.frame.FindVariable (“dictionary”): Gets the program variable dictionary, which contains the binary search tree and stores it in the Python variable root. (For details on how this works, see Work with program variables in Python <script-examples-work-with-program-variables-in-python>.) - The line, current\_path = “”: Initializes current\_path from the root of the tree to the current node. Because the search starts at the root of the tree, the current path starts as an empty string. While searching goes right and left through the tree, the DFS function appends an R or an L to the current path, as appropriate. - The line, path = tree\_utils.DFS (root, “Romeo”, current\_path): Calls the DFS function (prefixing it with the module name so Python can find it). The function is passed three parameters: the binary tree stored in the variable root, the word being searched for, and the current path. The path that the DFS function returns is assigned to the Python variable path. - The last line, print path: Indicates whether the word was found in the tree, and if found, the path from the tree root to the word. In this code example, the word, Romeo, was found in the tree; the search path through the tree is left-left-right-right-left. ## Use breakpoint command scripts At this point, you are halfway through the scripting example. The word is confirmed to be in the binary tree, and its location in the tree is known. The next task is to determine why the binary search algorithm is not finding the word. Do this with breakpoint command scripts. The binary search algorithm has two main decision points: the decision to follow the right branch, and the decision to follow the left branch. Set a breakpoint at each of these decision points, and attach a Python breakpoint command script to each breakpoint. The breakpoint commands use the global path Python variable that you got from the DFS function. Each time one of these decision breakpoints is hit, the script compares the actual decision with the decision that the front of the path variable indicates should be made (that is, the first character in the path). If the actual decision and the path agree, the front character is stripped off the path, and execution is resumed. In this case, you never see the breakpoint being hit. But if the decision differs from what the path says it should be, the script prints a message and does not resume execution, leaving you at the first point in the program where a wrong decision is being made. ## Python breakpoint command scripts Python breakpoint command scripts are not what they seem. When you enter a Python breakpoint command in LLDB, it appears that you are entering one or more plain lines of Python. But LLDB then takes what you entered and wraps it into a Python function (just like using the def Python command). It automatically gives the function an obscure, unique, hard- to-stumble-across function name, and gives it two parameters: frame and bp\_loc. When the breakpoint is hit, LLDB wraps up the frame object where the breakpoint was hit, and the breakpoint location object for the breakpoint that was hit, and puts them into Python variables for you. It then calls the Python function that was created for the breakpoint command, and passes in the frame and breakpoint location objects. What does this mean for you when writing the Python breakpoint commands? It means that there are two things to keep in mind: - To access any Python variables created outside your script, you must declare such variables to be global. If you do not declare them as global, the Python function will treat them as local variables, and you will get unexpected behavior. - All Python breakpoint command scripts automatically have a frame and bp\_loc variable. LLDB preloads the variables with the correct context for the breakpoint. You are not required to use these variables, but they are available. ## Decision point breakpoint commands Following is what the Python breakpoint command script looks like for the decision to go right: global path if path[0] == 'R': path = path[1:] thread = frame.GetThread() process = thread.GetProcess() process.Continue() else: print "Here is the problem; going right, should go left!" Copy to clipboard As a reminder, LLDB takes this script and wraps it in a function, like this example: def some_unique_and_obscure_function_name (frame, bp_loc): global path if path[0] == 'R': path = path[1:] thread = frame.GetThread() process = thread.GetProcess() process.Continue() else: print "Here is the problem; going right, should go left!" Copy to clipboard LLDB calls the function, passing in the correct frame and breakpoint location whenever the breakpoint is hit. Note **Notes about this function:** - First, it is accessing and updating a piece of state (the path variable), and conditioning its behavior based upon this variable. Because the variable is defined outside of the script (and therefore outside of the corresponding function), Python must know that it is accessing a global variable. That is what the first line of the script does. - Next, the function checks where the path says the search should go, and compares it to the actual decision (recall that the program is at the breakpoint for the decision to go right). If the path agrees with the decision, the first character is stripped off of the path. - Because the decision matched the path, the program should resume execution by making use of the frame parameter that LLDB guarantees will be available. The LLDB API functions are used to get the current thread from the current frame, and then to get the process from the thread. Once the process is gotten, use it to resume the execution (using the Continue() function). - If the decision to go right does not agree with the path, execution is not resumed. The breakpoint is allowed to remain stopped (by doing nothing), and a message is printed noting that the problem has been found and explaining what the problem is. ## Use breakpoint commands Here is what happens when using the breakpoint commands in the program. The command source list -n find\_word displays the function containing the two decision points. In the code below, the breakpoints should be set on lines 113 and 115: (lldb) source list -n find_word File: /Volumes/Data/HD2/carolinetice/Desktop/LLDB-Web-Examples/dictionary.c. 101 102 int 103 find_word (tree_node *dictionary, char *word) 104 { 105 if (!word || !dictionary) 106 return 0; 107 108 int compare_value = strcmp (word, dictionary->word); 109 110 if (compare_value == 0) 111 return 1; 112 else if (compare_value < 0) 113 return find_word (dictionary->left, word); 114 else 115 return find_word (dictionary->right, word); 116 } 117 Copy to clipboard Set the breakpoints, enter the breakpoint command scripts, then see what happens: (lldb) breakpoint set -l 113 Breakpoint created: 2: file ='dictionary.c', line = 113, locations = 1, resolved = 1 (lldb) breakpoint set -l 115 Breakpoint created: 3: file ='dictionary.c', line = 115, locations = 1, resolved = 1 (lldb) breakpoint command add -s python 2 Enter your Python command(s). Type 'DONE' to end. > global path > if (path[0] == 'L'): > ... path = path[1:] > ... thread = frame.GetThread() > ... process = thread.GetProcess() > ... process.Continue() > else: > ... print "Here is the problem. Going left, should go right!" > ... DONE (lldb) breakpoint command add -s python 3 Enter your Python command(s). Type 'DONE' to end. > global path > if (path[0] == 'R'): > ... path = path[1:] > ... thread = frame.GetThread() > ... process = thread.GetProcess() > ... process.Continue() > ... else: > ... print "Here is the problem. Going right, should go left!" > ... DONE (lldb) **continue** Process 696 resuming Here is the problem. Going right, should go left! Process 696 stopped * thread #1: tid = 0x2d03, 0x000000010000189f dictionary`find_word + 127 at dictionary.c:115, stop reason = breakpoint 3.1 frame #0: 0x000000010000189f dictionary`find_word + 127 at dictionary.c:115 112 else if (compare_value < 0) 113 return find_word (dictionary->left, word); 114 else -> 115 return find_word (dictionary->right, word); 116 } 117 118 void (lldb) Copy to clipboard After setting the breakpoints, adding the breakpoint commands, and continuing, the program runs and then hits one of the breakpoints, printing out the error message from the breakpoint command. At this point in the tree, the search algorithm decided to go right, but the path says the node should go to the left. Examining the word at the node where the program stopped, and the search word, reveals the following: (lldb) expr dictionary->word (const char *) $1 = 0x0000000100100080 "dramatis" (lldb) exprword (char *) $2 = 0x00007fff5fbff108 "romeo" Copy to clipboard In this example, the word at the current node is dramatis, while the word being searched for is romeo, which comes after dramatis, alphabetically. Thus, going right seems to be the correct decision. Use Python to see what it thinks the path from the current node to the word is: (lldb) script print path LLRRL Copy to clipboard According to Python, the search needed to go left-left-right-right-left from the current node to find the word being searched for. Double-check the tree to see what word it has at that node: (lldb) expr dictionary->left->left->right->right->left->word (const char *) $4 = 0x0000000100100880 "Romeo" Copy to clipboard In this example, the word being searched for is romeo, while the word at the DFS location is Romeo. Here is the source of the problem: one word is uppercase, while the other word is lowercase. There appears to be a case conversion problem somewhere in the program. This is the end of the example showing how you can use Python scripting in LLDB to help find bugs in your program. ## Source files for the example The Python script used in this example is written for Python 2.7. The code used in this example—including the DFS function, Dictionary program (with case- conversion bug), and other Python script examples (tree\_utils.py)—is available in the following files from [https://lldb.llvm.org/use/python.html#source-files-for-the-example](https://lldb.llvm.org/use/python.html#source-files-for-the-example): - tree\_utils.py Example Python functions using the LLDB API (including DFS) - dictionary.c Sample dictionary program, with a bug You can get the text for *Romeo and Juliet* from the Gutenberg Project ([http://www.gutenberg.org](http://www.gutenberg.org/)). Last Published: Oct 06, 2026 [Previous Topic Create new command using a Python function](https://docs.qualcomm.com/bundle/publicresource/80-N3040-23/topics/python-scripting.md) [Next Topic Debug with the booter image](https://docs.qualcomm.com/bundle/publicresource/80-N3040-23/topics/debug-with-the-booter-image.md)