1edb874b2SJonas DevliegherePython Scripting 2edb874b2SJonas Devlieghere================ 3edb874b2SJonas Devlieghere 4edb874b2SJonas DevlieghereLLDB has been structured from the beginning to be scriptable in two 5edb874b2SJonas Devlieghereways -- a Unix Python session can initiate/run a debug session 6edb874b2SJonas Devliegherenon-interactively using LLDB; and within the LLDB debugger tool, Python 7edb874b2SJonas Devliegherescripts can be used to help with many tasks, including inspecting 8edb874b2SJonas Devlieghereprogram data, iterating over containers and determining if a breakpoint 9edb874b2SJonas Devlieghereshould stop execution or continue. This document will show how to do 10edb874b2SJonas Devliegheresome of these things by going through an example, explaining how to use 11edb874b2SJonas DevliegherePython scripting to find a bug in a program that searches for text in a 12edb874b2SJonas Devliegherelarge binary tree. 13edb874b2SJonas Devlieghere 14edb874b2SJonas DevlieghereThe Test Program and Input 15edb874b2SJonas Devlieghere-------------------------- 16edb874b2SJonas Devlieghere 17edb874b2SJonas DevlieghereWe have a simple C program (dictionary.c) that reads in a text file, 18edb874b2SJonas Devlieghereand stores all the words from the file in a Binary Search Tree, sorted 19edb874b2SJonas Devliegherealphabetically. It then enters a loop prompting the user for a word, 20edb874b2SJonas Devliegheresearching for the word in the tree (using Binary Search), and reporting 21edb874b2SJonas Devlieghereto the user whether or not it found the word in the tree. 22edb874b2SJonas Devlieghere 23edb874b2SJonas DevlieghereThe input text file we are using to test our program contains the text 24edb874b2SJonas Devliegherefor William Shakespeare's famous tragedy "Romeo and Juliet". 25edb874b2SJonas Devlieghere 26edb874b2SJonas DevlieghereThe Bug 27edb874b2SJonas Devlieghere------- 28edb874b2SJonas Devlieghere 29edb874b2SJonas DevlieghereWhen we try running our program, we find there is a problem. While it 30edb874b2SJonas Devliegheresuccessfully finds some of the words we would expect to find, such as 31edb874b2SJonas Devlieghere"love" or "sun", it fails to find the word "Romeo", which MUST be in 32edb874b2SJonas Devliegherethe input text file: 33edb874b2SJonas Devlieghere 34edb874b2SJonas Devlieghere:: 35edb874b2SJonas Devlieghere 3659c954f7SShivam Gupta $ ./dictionary Romeo-and-Juliet.txt 37edb874b2SJonas Devlieghere Dictionary loaded. 38edb874b2SJonas Devlieghere Enter search word: love 39edb874b2SJonas Devlieghere Yes! 40edb874b2SJonas Devlieghere Enter search word: sun 41edb874b2SJonas Devlieghere Yes! 42edb874b2SJonas Devlieghere Enter search word: Romeo 43edb874b2SJonas Devlieghere No! 44edb874b2SJonas Devlieghere Enter search word: ^D 4559c954f7SShivam Gupta $ 46edb874b2SJonas Devlieghere 47edb874b2SJonas DevlieghereUsing Depth First Search 48edb874b2SJonas Devlieghere------------------------ 49edb874b2SJonas Devlieghere 50edb874b2SJonas DevlieghereOur first job is to determine if the word "Romeo" actually got inserted 51edb874b2SJonas Devlieghereinto the tree or not. Since "Romeo and Juliet" has thousands of words, 52edb874b2SJonas Devliegheretrying to examine our binary search tree by hand is completely 53edb874b2SJonas Devlieghereimpractical. Therefore we will write a Python script to search the tree 54edb874b2SJonas Devliegherefor us. We will write a recursive Depth First Search function that 55edb874b2SJonas Devliegheretraverses the entire tree searching for a word, and maintaining 56edb874b2SJonas Devlieghereinformation about the path from the root of the tree to the current 57edb874b2SJonas Devliegherenode. If it finds the word in the tree, it returns the path from the 58edb874b2SJonas Devlieghereroot to the node containing the word. This is what our DFS function in 59edb874b2SJonas DevliegherePython would look like, with line numbers added for easy reference in 60edb874b2SJonas Devliegherelater explanations: 61edb874b2SJonas Devlieghere 62edb874b2SJonas Devlieghere:: 63edb874b2SJonas Devlieghere 64edb874b2SJonas Devlieghere 1: def DFS (root, word, cur_path): 65edb874b2SJonas Devlieghere 2: root_word_ptr = root.GetChildMemberWithName ("word") 66edb874b2SJonas Devlieghere 3: left_child_ptr = root.GetChildMemberWithName ("left") 67edb874b2SJonas Devlieghere 4: right_child_ptr = root.GetChildMemberWithName ("right") 68edb874b2SJonas Devlieghere 5: root_word = root_word_ptr.GetSummary() 69edb874b2SJonas Devlieghere 6: end = len (root_word) - 1 70edb874b2SJonas Devlieghere 7: if root_word[0] == '"' and root_word[end] == '"': 71edb874b2SJonas Devlieghere 8: root_word = root_word[1:end] 72edb874b2SJonas Devlieghere 9: end = len (root_word) - 1 73edb874b2SJonas Devlieghere 10: if root_word[0] == '\'' and root_word[end] == '\'': 74edb874b2SJonas Devlieghere 11: root_word = root_word[1:end] 75edb874b2SJonas Devlieghere 12: if root_word == word: 76edb874b2SJonas Devlieghere 13: return cur_path 77edb874b2SJonas Devlieghere 14: elif word < root_word: 78*58611451SEisuke Kawashima 15: if left_child_ptr.GetValue() is None: 79edb874b2SJonas Devlieghere 16: return "" 80edb874b2SJonas Devlieghere 17: else: 81edb874b2SJonas Devlieghere 18: cur_path = cur_path + "L" 82edb874b2SJonas Devlieghere 19: return DFS (left_child_ptr, word, cur_path) 83edb874b2SJonas Devlieghere 20: else: 84*58611451SEisuke Kawashima 21: if right_child_ptr.GetValue() is None: 85edb874b2SJonas Devlieghere 22: return "" 86edb874b2SJonas Devlieghere 23: else: 87edb874b2SJonas Devlieghere 24: cur_path = cur_path + "R" 88edb874b2SJonas Devlieghere 25: return DFS (right_child_ptr, word, cur_path) 89edb874b2SJonas Devlieghere 90edb874b2SJonas Devlieghere 91edb874b2SJonas DevlieghereAccessing & Manipulating Program Variables 92edb874b2SJonas Devlieghere------------------------------------------ 93edb874b2SJonas Devlieghere 94edb874b2SJonas DevlieghereBefore we can call any Python function on any of our program's 95edb874b2SJonas Devliegherevariables, we need to get the variable into a form that Python can 96edb874b2SJonas Devlieghereaccess. To show you how to do this we will look at the parameters for 97edb874b2SJonas Devliegherethe DFS function. The first parameter is going to be a node in our 98edb874b2SJonas Devliegherebinary search tree, put into a Python variable. The second parameter is 99edb874b2SJonas Devliegherethe word we are searching for (a string), and the third parameter is a 100edb874b2SJonas Devliegherestring representing the path from the root of the tree to our current 101edb874b2SJonas Devliegherenode. 102edb874b2SJonas Devlieghere 103edb874b2SJonas DevlieghereThe most interesting parameter is the first one, the Python variable 104edb874b2SJonas Devliegherethat needs to contain a node in our search tree. How can we take a 105edb874b2SJonas Devliegherevariable out of our program and put it into a Python variable? What 106edb874b2SJonas Devliegherekind of Python variable will it be? The answers are to use the LLDB API 107edb874b2SJonas Devliegherefunctions, provided as part of the LLDB Python module. Running Python 108edb874b2SJonas Devliegherefrom inside LLDB, LLDB will automatically give us our current frame 109edb874b2SJonas Devlieghereobject as a Python variable, "lldb.frame". This variable has the type 110a58aceffSRaphael Isemann`SBFrame` (see the LLDB API for more information about `SBFrame` 111edb874b2SJonas Devlieghereobjects). One of the things we can do with a frame object, is to ask it 112edb874b2SJonas Devlieghereto find and return its local variable. We will call the API function 113a58aceffSRaphael Isemann`SBFrame.FindVariable` on the lldb.frame object to give us our dictionary 114edb874b2SJonas Devliegherevariable as a Python variable: 115edb874b2SJonas Devlieghere 116edb874b2SJonas Devlieghere:: 117edb874b2SJonas Devlieghere 118edb874b2SJonas Devlieghere root = lldb.frame.FindVariable ("dictionary") 119edb874b2SJonas Devlieghere 120edb874b2SJonas DevlieghereThe line above, executed in the Python script interpreter in LLDB, asks the 121edb874b2SJonas Devliegherecurrent frame to find the variable named "dictionary" and return it. We then 122edb874b2SJonas Devliegherestore the returned value in the Python variable named "root". This answers the 123edb874b2SJonas Devliegherequestion of HOW to get the variable, but it still doesn't explain WHAT actually 124edb874b2SJonas Devliegheregets put into "root". If you examine the LLDB API, you will find that the 125a58aceffSRaphael Isemann`SBFrame` method "FindVariable" returns an object of type `SBValue`. `SBValue` 126edb874b2SJonas Devlieghereobjects are used, among other things, to wrap up program variables and values. 127a58aceffSRaphael IsemannThere are many useful methods defined in the `SBValue` class to allow you to get 128edb874b2SJonas Devlieghereinformation or children values out of SBValues. For complete information, see 129a58aceffSRaphael Isemannthe header file SBValue.h. The `SBValue` methods that we use in our DFS function 130edb874b2SJonas Devlieghereare ``GetChildMemberWithName()``, ``GetSummary()``, and ``GetValue()``. 131edb874b2SJonas Devlieghere 132edb874b2SJonas Devlieghere 133edb874b2SJonas DevlieghereExplaining DFS Script in Detail 134edb874b2SJonas Devlieghere------------------------------- 135edb874b2SJonas Devlieghere 136edb874b2SJonas DevlieghereBefore diving into the details of this code, it would be best to give a 137edb874b2SJonas Devliegherehigh-level overview of what it does. The nodes in our binary search tree were 138edb874b2SJonas Devliegheredefined to have type ``tree_node *``, which is defined as: 139edb874b2SJonas Devlieghere 140edb874b2SJonas Devlieghere:: 141edb874b2SJonas Devlieghere 142edb874b2SJonas Devlieghere typedef struct tree_node 143edb874b2SJonas Devlieghere { 144edb874b2SJonas Devlieghere const char *word; 145edb874b2SJonas Devlieghere struct tree_node *left; 146edb874b2SJonas Devlieghere struct tree_node *right; 147edb874b2SJonas Devlieghere } tree_node; 148edb874b2SJonas Devlieghere 149edb874b2SJonas DevlieghereLines 2-11 of DFS are getting data out of the current tree node and getting 150edb874b2SJonas Devlieghereready to do the actual search; lines 12-25 are the actual depth-first search. 151edb874b2SJonas DevlieghereLines 2-4 of our DFS function get the word, left and right fields out of the 152edb874b2SJonas Devliegherecurrent node and store them in Python variables. Since root_word_ptr is a 153edb874b2SJonas Devliegherepointer to our word, and we want the actual word, line 5 calls GetSummary() to 154edb874b2SJonas Devlieghereget a string containing the value out of the pointer. Since GetSummary() adds 155edb874b2SJonas Devliegherequotes around its result, lines 6-11 strip surrounding quotes off the word. 156edb874b2SJonas Devlieghere 157edb874b2SJonas DevlieghereLine 12 checks to see if the word in the current node is the one we are 158edb874b2SJonas Devliegheresearching for. If so, we are done, and line 13 returns the current path. 159edb874b2SJonas DevlieghereOtherwise, line 14 checks to see if we should go left (search word comes before 160edb874b2SJonas Devliegherethe current word). If we decide to go left, line 15 checks to see if the left 161edb874b2SJonas Devliegherepointer child is NULL ("None" is the Python equivalent of NULL). If the left 162edb874b2SJonas Devliegherepointer is NULL, then the word is not in this tree and we return an empty path 163edb874b2SJonas Devlieghere(line 16). Otherwise, we add an "L" to the end of our current path string, to 164edb874b2SJonas Devlieghereindicate we are going left (line 18), and then recurse on the left child (line 165edb874b2SJonas Devlieghere19). Lines 20-25 are the same as lines 14-19, except for going right rather 166edb874b2SJonas Devliegherethan going left. 167edb874b2SJonas Devlieghere 168edb874b2SJonas DevlieghereOne other note: Typing something as long as our DFS function directly into the 169edb874b2SJonas Devlieghereinterpreter can be difficult, as making a single typing mistake means having to 170edb874b2SJonas Devliegherestart all over. Therefore we recommend doing as we have done: Writing your 171edb874b2SJonas Devliegherelonger, more complicated script functions in a separate file (in this case 172edb874b2SJonas Devliegheretree_utils.py) and then importing it into your LLDB Python interpreter. 173edb874b2SJonas Devlieghere 174edb874b2SJonas Devlieghere 175edb874b2SJonas DevlieghereThe DFS Script in Action 176edb874b2SJonas Devlieghere------------------------ 177edb874b2SJonas Devlieghere 178edb874b2SJonas DevlieghereAt this point we are ready to use the DFS function to see if the word "Romeo" 179edb874b2SJonas Devlieghereis in our tree or not. To actually use it in LLDB on our dictionary program, 180edb874b2SJonas Devlieghereyou would do something like this: 181edb874b2SJonas Devlieghere 182edb874b2SJonas Devlieghere:: 183edb874b2SJonas Devlieghere 18459c954f7SShivam Gupta $ lldb 185edb874b2SJonas Devlieghere (lldb) process attach -n "dictionary" 186edb874b2SJonas Devlieghere Architecture set to: x86_64. 187edb874b2SJonas Devlieghere Process 521 stopped 188edb874b2SJonas Devlieghere * thread #1: tid = 0x2c03, 0x00007fff86c8bea0 libSystem.B.dylib`read$NOCANCEL + 8, stop reason = signal SIGSTOP 189edb874b2SJonas Devlieghere frame #0: 0x00007fff86c8bea0 libSystem.B.dylib`read$NOCANCEL + 8 190edb874b2SJonas Devlieghere (lldb) breakpoint set -n find_word 191edb874b2SJonas Devlieghere Breakpoint created: 1: name = 'find_word', locations = 1, resolved = 1 192edb874b2SJonas Devlieghere (lldb) continue 193edb874b2SJonas Devlieghere Process 521 resuming 194edb874b2SJonas Devlieghere Process 521 stopped 195edb874b2SJonas Devlieghere * thread #1: tid = 0x2c03, 0x0000000100001830 dictionary`find_word + 16 196edb874b2SJonas Devlieghere at dictionary.c:105, stop reason = breakpoint 1.1 197edb874b2SJonas Devlieghere frame #0: 0x0000000100001830 dictionary`find_word + 16 at dictionary.c:105 198edb874b2SJonas Devlieghere 102 int 199edb874b2SJonas Devlieghere 103 find_word (tree_node *dictionary, char *word) 200edb874b2SJonas Devlieghere 104 { 201edb874b2SJonas Devlieghere -> 105 if (!word || !dictionary) 202edb874b2SJonas Devlieghere 106 return 0; 203edb874b2SJonas Devlieghere 107 204edb874b2SJonas Devlieghere 108 int compare_value = strcmp (word, dictionary->word); 205edb874b2SJonas Devlieghere (lldb) script 206edb874b2SJonas Devlieghere Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D. 207edb874b2SJonas Devlieghere >>> import tree_utils 208edb874b2SJonas Devlieghere >>> root = lldb.frame.FindVariable ("dictionary") 209edb874b2SJonas Devlieghere >>> current_path = "" 210edb874b2SJonas Devlieghere >>> path = tree_utils.DFS (root, "Romeo", current_path) 211edb874b2SJonas Devlieghere >>> print path 212edb874b2SJonas Devlieghere LLRRL 213edb874b2SJonas Devlieghere >>> ^D 214edb874b2SJonas Devlieghere (lldb) 215edb874b2SJonas Devlieghere 216edb874b2SJonas DevlieghereThe first bit of code above shows starting lldb, attaching to the dictionary 217edb874b2SJonas Devlieghereprogram, and getting to the find_word function in LLDB. The interesting part 218edb874b2SJonas Devlieghere(as far as this example is concerned) begins when we enter the script command 219edb874b2SJonas Devlieghereand drop into the embedded interactive Python interpreter. We will go over this 220edb874b2SJonas DevliegherePython code line by line. The first line 221edb874b2SJonas Devlieghere 222edb874b2SJonas Devlieghere:: 223edb874b2SJonas Devlieghere 224edb874b2SJonas Devlieghere import tree_utils 225edb874b2SJonas Devlieghere 226edb874b2SJonas Devlieghere 227edb874b2SJonas Devlieghereimports the file where we wrote our DFS function, tree_utils.py, into Python. 228edb874b2SJonas DevlieghereNotice that to import the file we leave off the ".py" extension. We can now 229edb874b2SJonas Devliegherecall any function in that file, giving it the prefix "tree_utils.", so that 230edb874b2SJonas DevliegherePython knows where to look for the function. The line 231edb874b2SJonas Devlieghere 232edb874b2SJonas Devlieghere:: 233edb874b2SJonas Devlieghere 234edb874b2SJonas Devlieghere root = lldb.frame.FindVariable ("dictionary") 235edb874b2SJonas Devlieghere 236edb874b2SJonas Devlieghere 237edb874b2SJonas Devliegheregets our program variable "dictionary" (which contains the binary search tree) 238edb874b2SJonas Devlieghereand puts it into the Python variable "root". See Accessing & Manipulating 239edb874b2SJonas DevlieghereProgram Variables in Python above for more details about how this works. The 240edb874b2SJonas Devliegherenext line is 241edb874b2SJonas Devlieghere 242edb874b2SJonas Devlieghere:: 243edb874b2SJonas Devlieghere 244edb874b2SJonas Devlieghere current_path = "" 245edb874b2SJonas Devlieghere 246edb874b2SJonas DevlieghereThis line initializes the current_path from the root of the tree to our current 247edb874b2SJonas Devliegherenode. Since we are starting at the root of the tree, our current path starts as 248edb874b2SJonas Devliegherean empty string. As we go right and left through the tree, the DFS function 249edb874b2SJonas Devliegherewill append an 'R' or an 'L' to the current path, as appropriate. The line 250edb874b2SJonas Devlieghere 251edb874b2SJonas Devlieghere:: 252edb874b2SJonas Devlieghere 253edb874b2SJonas Devlieghere path = tree_utils.DFS (root, "Romeo", current_path) 254edb874b2SJonas Devlieghere 255edb874b2SJonas Devliegherecalls our DFS function (prefixing it with the module name so that Python can 256edb874b2SJonas Devliegherefind it). We pass in our binary tree stored in the variable root, the word we 257edb874b2SJonas Devlieghereare searching for, and our current path. We assign whatever path the DFS 258edb874b2SJonas Devliegherefunction returns to the Python variable path. 259edb874b2SJonas Devlieghere 260edb874b2SJonas DevlieghereFinally, we want to see if the word was found or not, and if so we want to see 261edb874b2SJonas Devliegherethe path through the tree to the word. So we do 262edb874b2SJonas Devlieghere 263edb874b2SJonas Devlieghere:: 264edb874b2SJonas Devlieghere 265edb874b2SJonas Devlieghere print path 266edb874b2SJonas Devlieghere 267edb874b2SJonas DevlieghereFrom this we can see that the word "Romeo" was indeed found in the tree, and 268edb874b2SJonas Devliegherethe path from the root of the tree to the node containing "Romeo" is 269edb874b2SJonas Devlieghereleft-left-right-right-left. 270edb874b2SJonas Devlieghere 271edb874b2SJonas DevlieghereUsing Breakpoint Command Scripts 272edb874b2SJonas Devlieghere-------------------------------- 273edb874b2SJonas Devlieghere 274edb874b2SJonas DevlieghereWe are halfway to figuring out what the problem is. We know the word we are 275edb874b2SJonas Devliegherelooking for is in the binary tree, and we know exactly where it is in the 276edb874b2SJonas Devliegherebinary tree. Now we need to figure out why our binary search algorithm is not 277edb874b2SJonas Devliegherefinding the word. We will do this using breakpoint command scripts. 278edb874b2SJonas Devlieghere 279edb874b2SJonas DevlieghereThe idea is as follows. The binary search algorithm has two main decision 280edb874b2SJonas Devliegherepoints: the decision to follow the right branch; and, the decision to follow 281edb874b2SJonas Devliegherethe left branch. We will set a breakpoint at each of these decision points, and 282edb874b2SJonas Devlieghereattach a Python breakpoint command script to each breakpoint. The breakpoint 283edb874b2SJonas Devliegherecommands will use the global path Python variable that we got from our DFS 284edb874b2SJonas Devliegherefunction. Each time one of these decision breakpoints is hit, the script will 285edb874b2SJonas Devliegherecompare the actual decision with the decision the front of the path variable 286edb874b2SJonas Devliegheresays should be made (the first character of the path). If the actual decision 287edb874b2SJonas Devlieghereand the path agree, then the front character is stripped off the path, and 288edb874b2SJonas Devlieghereexecution is resumed. In this case the user never even sees the breakpoint 289edb874b2SJonas Devliegherebeing hit. But if the decision differs from what the path says it should be, 290edb874b2SJonas Devliegherethen the script prints out a message and does NOT resume execution, leaving the 291edb874b2SJonas Devlieghereuser sitting at the first point where a wrong decision is being made. 292edb874b2SJonas Devlieghere 293edb874b2SJonas DevliegherePython Breakpoint Command Scripts Are Not What They Seem 294edb874b2SJonas Devlieghere-------------------------------------------------------- 295edb874b2SJonas Devlieghere 296edb874b2SJonas DevlieghereWhat do we mean by that? When you enter a Python breakpoint command in LLDB, it 297edb874b2SJonas Devlieghereappears that you are entering one or more plain lines of Python. BUT LLDB then 298edb874b2SJonas Devliegheretakes what you entered and wraps it into a Python FUNCTION (just like using the 299edb874b2SJonas Devlieghere"def" Python command). It automatically gives the function an obscure, unique, 300edb874b2SJonas Devliegherehard-to-stumble-across function name, and gives it two parameters: frame and 301edb874b2SJonas Devliegherebp_loc. When the breakpoint gets hit, LLDB wraps up the frame object where the 302edb874b2SJonas Devliegherebreakpoint was hit, and the breakpoint location object for the breakpoint that 303edb874b2SJonas Devliegherewas hit, and puts them into Python variables for you. It then calls the Python 304edb874b2SJonas Devliegherefunction that was created for the breakpoint command, and passes in the frame 305edb874b2SJonas Devlieghereand breakpoint location objects. 306edb874b2SJonas Devlieghere 307edb874b2SJonas DevlieghereSo, being practical, what does this mean for you when you write your Python 308edb874b2SJonas Devliegherebreakpoint commands? It means that there are two things you need to keep in 309edb874b2SJonas Devliegheremind: 1. If you want to access any Python variables created outside your 310edb874b2SJonas Devliegherescript, you must declare such variables to be global. If you do not declare 311edb874b2SJonas Devliegherethem as global, then the Python function will treat them as local variables, 312edb874b2SJonas Devlieghereand you will get unexpected behavior. 2. All Python breakpoint command scripts 313edb874b2SJonas Devlieghereautomatically have a frame and a bp_loc variable. The variables are pre-loaded 314edb874b2SJonas Devlieghereby LLDB with the correct context for the breakpoint. You do not have to use 315edb874b2SJonas Devliegherethese variables, but they are there if you want them. 316edb874b2SJonas Devlieghere 317edb874b2SJonas DevlieghereThe Decision Point Breakpoint Commands 318edb874b2SJonas Devlieghere-------------------------------------- 319edb874b2SJonas Devlieghere 320edb874b2SJonas DevlieghereThis is what the Python breakpoint command script would look like for the 321edb874b2SJonas Devliegheredecision to go right: 322edb874b2SJonas Devlieghere 323edb874b2SJonas Devlieghere:: 324edb874b2SJonas Devlieghere 325edb874b2SJonas Devlieghere global path 326edb874b2SJonas Devlieghere if path[0] == 'R': 327edb874b2SJonas Devlieghere path = path[1:] 328edb874b2SJonas Devlieghere thread = frame.GetThread() 329edb874b2SJonas Devlieghere process = thread.GetProcess() 330edb874b2SJonas Devlieghere process.Continue() 331edb874b2SJonas Devlieghere else: 332edb874b2SJonas Devlieghere print "Here is the problem; going right, should go left!" 333edb874b2SJonas Devlieghere Just as a reminder, LLDB is going to take this script and wrap it up in a function, like this: 334edb874b2SJonas Devlieghere 335edb874b2SJonas Devlieghere 336edb874b2SJonas Devlieghere def some_unique_and_obscure_function_name (frame, bp_loc): 337edb874b2SJonas Devlieghere global path 338edb874b2SJonas Devlieghere if path[0] == 'R': 339edb874b2SJonas Devlieghere path = path[1:] 340edb874b2SJonas Devlieghere thread = frame.GetThread() 341edb874b2SJonas Devlieghere process = thread.GetProcess() 342edb874b2SJonas Devlieghere process.Continue() 343edb874b2SJonas Devlieghere else: 344edb874b2SJonas Devlieghere print "Here is the problem; going right, should go left!" 345edb874b2SJonas Devlieghere 346edb874b2SJonas DevlieghereLLDB will call the function, passing in the correct frame and breakpoint 347edb874b2SJonas Devliegherelocation whenever the breakpoint gets hit. There are several things to notice 348edb874b2SJonas Devlieghereabout this function. The first one is that we are accessing and updating a 349edb874b2SJonas Devliegherepiece of state (the path variable), and actually conditioning our behavior 350edb874b2SJonas Devliegherebased upon this variable. Since the variable was defined outside of our script 351edb874b2SJonas Devlieghere(and therefore outside of the corresponding function) we need to tell Python 352edb874b2SJonas Devliegherethat we are accessing a global variable. That is what the first line of the 353edb874b2SJonas Devliegherescript does. Next we check where the path says we should go and compare it to 354edb874b2SJonas Devlieghereour decision (recall that we are at the breakpoint for the decision to go 355edb874b2SJonas Devlieghereright). If the path agrees with our decision, then we strip the first character 356edb874b2SJonas Devlieghereoff of the path. 357edb874b2SJonas Devlieghere 358edb874b2SJonas DevlieghereSince the decision matched the path, we want to resume execution. To do this we 359edb874b2SJonas Devliegheremake use of the frame parameter that LLDB guarantees will be there for us. We 360edb874b2SJonas Devlieghereuse LLDB API functions to get the current thread from the current frame, and 361edb874b2SJonas Devliegherethen to get the process from the thread. Once we have the process, we tell it 362edb874b2SJonas Devlieghereto resume execution (using the Continue() API function). 363edb874b2SJonas Devlieghere 364edb874b2SJonas DevlieghereIf the decision to go right does not agree with the path, then we do not resume 365edb874b2SJonas Devlieghereexecution. We allow the breakpoint to remain stopped (by doing nothing), and we 366edb874b2SJonas Devlieghereprint an informational message telling the user we have found the problem, and 367edb874b2SJonas Devliegherewhat the problem is. 368edb874b2SJonas Devlieghere 369edb874b2SJonas DevlieghereActually Using The Breakpoint Commands 370edb874b2SJonas Devlieghere-------------------------------------- 371edb874b2SJonas Devlieghere 372edb874b2SJonas DevlieghereNow we will look at what happens when we actually use these breakpoint commands 373edb874b2SJonas Devlieghereon our program. Doing a source list -n find_word shows us the function 374edb874b2SJonas Devliegherecontaining our two decision points. Looking at the code below, we see that we 375edb874b2SJonas Devliegherewant to set our breakpoints on lines 113 and 115: 376edb874b2SJonas Devlieghere 377edb874b2SJonas Devlieghere:: 378edb874b2SJonas Devlieghere 379edb874b2SJonas Devlieghere (lldb) source list -n find_word 380edb874b2SJonas Devlieghere File: /Volumes/Data/HD2/carolinetice/Desktop/LLDB-Web-Examples/dictionary.c. 381edb874b2SJonas Devlieghere 101 382edb874b2SJonas Devlieghere 102 int 383edb874b2SJonas Devlieghere 103 find_word (tree_node *dictionary, char *word) 384edb874b2SJonas Devlieghere 104 { 385edb874b2SJonas Devlieghere 105 if (!word || !dictionary) 386edb874b2SJonas Devlieghere 106 return 0; 387edb874b2SJonas Devlieghere 107 388edb874b2SJonas Devlieghere 108 int compare_value = strcmp (word, dictionary->word); 389edb874b2SJonas Devlieghere 109 390edb874b2SJonas Devlieghere 110 if (compare_value == 0) 391edb874b2SJonas Devlieghere 111 return 1; 392edb874b2SJonas Devlieghere 112 else if (compare_value < 0) 393edb874b2SJonas Devlieghere 113 return find_word (dictionary->left, word); 394edb874b2SJonas Devlieghere 114 else 395edb874b2SJonas Devlieghere 115 return find_word (dictionary->right, word); 396edb874b2SJonas Devlieghere 116 } 397edb874b2SJonas Devlieghere 117 398edb874b2SJonas Devlieghere 399edb874b2SJonas Devlieghere 400edb874b2SJonas DevlieghereSo, we set our breakpoints, enter our breakpoint command scripts, and see what happens: 401edb874b2SJonas Devlieghere 402edb874b2SJonas Devlieghere:: 403edb874b2SJonas Devlieghere 404edb874b2SJonas Devlieghere (lldb) breakpoint set -l 113 405edb874b2SJonas Devlieghere Breakpoint created: 2: file ='dictionary.c', line = 113, locations = 1, resolved = 1 406edb874b2SJonas Devlieghere (lldb) breakpoint set -l 115 407edb874b2SJonas Devlieghere Breakpoint created: 3: file ='dictionary.c', line = 115, locations = 1, resolved = 1 408edb874b2SJonas Devlieghere (lldb) breakpoint command add -s python 2 409edb874b2SJonas Devlieghere Enter your Python command(s). Type 'DONE' to end. 410edb874b2SJonas Devlieghere > global path 411edb874b2SJonas Devlieghere > if (path[0] == 'L'): 412edb874b2SJonas Devlieghere > path = path[1:] 413edb874b2SJonas Devlieghere > thread = frame.GetThread() 414edb874b2SJonas Devlieghere > process = thread.GetProcess() 415edb874b2SJonas Devlieghere > process.Continue() 416edb874b2SJonas Devlieghere > else: 417edb874b2SJonas Devlieghere > print "Here is the problem. Going left, should go right!" 418edb874b2SJonas Devlieghere > DONE 419edb874b2SJonas Devlieghere (lldb) breakpoint command add -s python 3 420edb874b2SJonas Devlieghere Enter your Python command(s). Type 'DONE' to end. 421edb874b2SJonas Devlieghere > global path 422edb874b2SJonas Devlieghere > if (path[0] == 'R'): 423edb874b2SJonas Devlieghere > path = path[1:] 424edb874b2SJonas Devlieghere > thread = frame.GetThread() 425edb874b2SJonas Devlieghere > process = thread.GetProcess() 426edb874b2SJonas Devlieghere > process.Continue() 427edb874b2SJonas Devlieghere > else: 428edb874b2SJonas Devlieghere > print "Here is the problem. Going right, should go left!" 429edb874b2SJonas Devlieghere > DONE 430edb874b2SJonas Devlieghere (lldb) continue 431edb874b2SJonas Devlieghere Process 696 resuming 432edb874b2SJonas Devlieghere Here is the problem. Going right, should go left! 433edb874b2SJonas Devlieghere Process 696 stopped 434edb874b2SJonas Devlieghere * thread #1: tid = 0x2d03, 0x000000010000189f dictionary`find_word + 127 at dictionary.c:115, stop reason = breakpoint 3.1 435edb874b2SJonas Devlieghere frame #0: 0x000000010000189f dictionary`find_word + 127 at dictionary.c:115 436edb874b2SJonas Devlieghere 112 else if (compare_value < 0) 437edb874b2SJonas Devlieghere 113 return find_word (dictionary->left, word); 438edb874b2SJonas Devlieghere 114 else 439edb874b2SJonas Devlieghere -> 115 return find_word (dictionary->right, word); 440edb874b2SJonas Devlieghere 116 } 441edb874b2SJonas Devlieghere 117 442edb874b2SJonas Devlieghere 118 void 443edb874b2SJonas Devlieghere (lldb) 444edb874b2SJonas Devlieghere 445edb874b2SJonas Devlieghere 446edb874b2SJonas DevlieghereAfter setting our breakpoints, adding our breakpoint commands and continuing, 447edb874b2SJonas Devliegherewe run for a little bit and then hit one of our breakpoints, printing out the 448edb874b2SJonas Devlieghereerror message from the breakpoint command. Apparently at this point in the 449edb874b2SJonas Devliegheretree, our search algorithm decided to go right, but our path says the node we 450edb874b2SJonas Devliegherewant is to the left. Examining the word at the node where we stopped, and our 451edb874b2SJonas Devliegheresearch word, we see: 452edb874b2SJonas Devlieghere 453edb874b2SJonas Devlieghere:: 454edb874b2SJonas Devlieghere 455edb874b2SJonas Devlieghere (lldb) expr dictionary->word 456edb874b2SJonas Devlieghere (const char *) $1 = 0x0000000100100080 "dramatis" 457edb874b2SJonas Devlieghere (lldb) expr word 458edb874b2SJonas Devlieghere (char *) $2 = 0x00007fff5fbff108 "romeo" 459edb874b2SJonas Devlieghere 460edb874b2SJonas DevlieghereSo the word at our current node is "dramatis", and the word we are searching 461edb874b2SJonas Devliegherefor is "romeo". "romeo" comes after "dramatis" alphabetically, so it seems like 462edb874b2SJonas Devliegheregoing right would be the correct decision. Let's ask Python what it thinks the 463edb874b2SJonas Devliegherepath from the current node to our word is: 464edb874b2SJonas Devlieghere 465edb874b2SJonas Devlieghere:: 466edb874b2SJonas Devlieghere 467edb874b2SJonas Devlieghere (lldb) script print path 468edb874b2SJonas Devlieghere LLRRL 469edb874b2SJonas Devlieghere 470edb874b2SJonas DevlieghereAccording to Python we need to go left-left-right-right-left from our current 471edb874b2SJonas Devliegherenode to find the word we are looking for. Let's double check our tree, and see 472edb874b2SJonas Devliegherewhat word it has at that node: 473edb874b2SJonas Devlieghere 474edb874b2SJonas Devlieghere:: 475edb874b2SJonas Devlieghere 476edb874b2SJonas Devlieghere (lldb) expr dictionary->left->left->right->right->left->word 477edb874b2SJonas Devlieghere (const char *) $4 = 0x0000000100100880 "Romeo" 478edb874b2SJonas Devlieghere 479edb874b2SJonas DevlieghereSo the word we are searching for is "romeo" and the word at our DFS location is 480edb874b2SJonas Devlieghere"Romeo". Aha! One is uppercase and the other is lowercase: We seem to have a 481edb874b2SJonas Devliegherecase conversion problem somewhere in our program (we do). 482edb874b2SJonas Devlieghere 483edb874b2SJonas DevlieghereThis is the end of our example on how you might use Python scripting in LLDB to 484edb874b2SJonas Devliegherehelp you find bugs in your program. 485edb874b2SJonas Devlieghere 486edb874b2SJonas DevlieghereSource Files for The Example 487edb874b2SJonas Devlieghere---------------------------- 488edb874b2SJonas Devlieghere 489edb874b2SJonas DevlieghereThe complete code for the Dictionary program (with case-conversion bug), the 490edb874b2SJonas DevlieghereDFS function and other Python script examples (tree_utils.py) used for this 491edb874b2SJonas Devlieghereexample are available below. 492edb874b2SJonas Devlieghere 493edb874b2SJonas Devliegheretree_utils.py - Example Python functions using LLDB's API, including DFS 494edb874b2SJonas Devlieghere 495edb874b2SJonas Devlieghere:: 496edb874b2SJonas Devlieghere 497edb874b2SJonas Devlieghere """ 498edb874b2SJonas Devlieghere # ===-- tree_utils.py ---------------------------------------*- Python -*-===// 499edb874b2SJonas Devlieghere # 500c874dd53SChristopher Di Bella # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 501c874dd53SChristopher Di Bella # See https://llvm.org/LICENSE.txt for license information. 502c874dd53SChristopher Di Bella # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 503edb874b2SJonas Devlieghere # 504c874dd53SChristopher Di Bella # ===----------------------------------------------------------------------===// 505edb874b2SJonas Devlieghere 506edb874b2SJonas Devlieghere tree_utils.py - A set of functions for examining binary 507edb874b2SJonas Devlieghere search trees, based on the example search tree defined in 508edb874b2SJonas Devlieghere dictionary.c. These functions contain calls to LLDB API 509edb874b2SJonas Devlieghere functions, and assume that the LLDB Python module has been 510edb874b2SJonas Devlieghere imported. 511edb874b2SJonas Devlieghere 512edb874b2SJonas Devlieghere For a thorough explanation of how the DFS function works, and 513edb874b2SJonas Devlieghere for more information about dictionary.c go to 514edb874b2SJonas Devlieghere http://lldb.llvm.org/scripting.html 515edb874b2SJonas Devlieghere """ 516edb874b2SJonas Devlieghere 517edb874b2SJonas Devlieghere 518edb874b2SJonas Devlieghere def DFS(root, word, cur_path): 519edb874b2SJonas Devlieghere """ 520edb874b2SJonas Devlieghere Recursively traverse a binary search tree containing 521edb874b2SJonas Devlieghere words sorted alphabetically, searching for a particular 522edb874b2SJonas Devlieghere word in the tree. Also maintains a string representing 523edb874b2SJonas Devlieghere the path from the root of the tree to the current node. 524edb874b2SJonas Devlieghere If the word is found in the tree, return the path string. 525edb874b2SJonas Devlieghere Otherwise return an empty string. 526edb874b2SJonas Devlieghere 527edb874b2SJonas Devlieghere This function assumes the binary search tree is 528edb874b2SJonas Devlieghere the one defined in dictionary.c It uses LLDB API 529edb874b2SJonas Devlieghere functions to examine and traverse the tree nodes. 530edb874b2SJonas Devlieghere """ 531edb874b2SJonas Devlieghere 532edb874b2SJonas Devlieghere # Get pointer field values out of node 'root' 533edb874b2SJonas Devlieghere 534edb874b2SJonas Devlieghere root_word_ptr = root.GetChildMemberWithName("word") 535edb874b2SJonas Devlieghere left_child_ptr = root.GetChildMemberWithName("left") 536edb874b2SJonas Devlieghere right_child_ptr = root.GetChildMemberWithName("right") 537edb874b2SJonas Devlieghere 538edb874b2SJonas Devlieghere # Get the word out of the word pointer and strip off 539edb874b2SJonas Devlieghere # surrounding quotes (added by call to GetSummary). 540edb874b2SJonas Devlieghere 541edb874b2SJonas Devlieghere root_word = root_word_ptr.GetSummary() 542edb874b2SJonas Devlieghere end = len(root_word) - 1 543edb874b2SJonas Devlieghere if root_word[0] == '"' and root_word[end] == '"': 544edb874b2SJonas Devlieghere root_word = root_word[1:end] 545edb874b2SJonas Devlieghere end = len(root_word) - 1 546edb874b2SJonas Devlieghere if root_word[0] == '\'' and root_word[end] == '\'': 547edb874b2SJonas Devlieghere root_word = root_word[1:end] 548edb874b2SJonas Devlieghere 549edb874b2SJonas Devlieghere # Main depth first search 550edb874b2SJonas Devlieghere 551edb874b2SJonas Devlieghere if root_word == word: 552edb874b2SJonas Devlieghere return cur_path 553edb874b2SJonas Devlieghere elif word < root_word: 554edb874b2SJonas Devlieghere 555edb874b2SJonas Devlieghere # Check to see if left child is NULL 556edb874b2SJonas Devlieghere 557edb874b2SJonas Devlieghere if left_child_ptr.GetValue() is None: 558edb874b2SJonas Devlieghere return "" 559edb874b2SJonas Devlieghere else: 560edb874b2SJonas Devlieghere cur_path = cur_path + "L" 561edb874b2SJonas Devlieghere return DFS(left_child_ptr, word, cur_path) 562edb874b2SJonas Devlieghere else: 563edb874b2SJonas Devlieghere 564edb874b2SJonas Devlieghere # Check to see if right child is NULL 565edb874b2SJonas Devlieghere 566edb874b2SJonas Devlieghere if right_child_ptr.GetValue() is None: 567edb874b2SJonas Devlieghere return "" 568edb874b2SJonas Devlieghere else: 569edb874b2SJonas Devlieghere cur_path = cur_path + "R" 570edb874b2SJonas Devlieghere return DFS(right_child_ptr, word, cur_path) 571edb874b2SJonas Devlieghere 572edb874b2SJonas Devlieghere 573edb874b2SJonas Devlieghere def tree_size(root): 574edb874b2SJonas Devlieghere """ 575edb874b2SJonas Devlieghere Recursively traverse a binary search tree, counting 576edb874b2SJonas Devlieghere the nodes in the tree. Returns the final count. 577edb874b2SJonas Devlieghere 578edb874b2SJonas Devlieghere This function assumes the binary search tree is 579edb874b2SJonas Devlieghere the one defined in dictionary.c It uses LLDB API 580edb874b2SJonas Devlieghere functions to examine and traverse the tree nodes. 581edb874b2SJonas Devlieghere """ 582edb874b2SJonas Devlieghere if (root.GetValue is None): 583edb874b2SJonas Devlieghere return 0 584edb874b2SJonas Devlieghere 585edb874b2SJonas Devlieghere if (int(root.GetValue(), 16) == 0): 586edb874b2SJonas Devlieghere return 0 587edb874b2SJonas Devlieghere 588edb874b2SJonas Devlieghere left_size = tree_size(root.GetChildAtIndex(1)) 589edb874b2SJonas Devlieghere right_size = tree_size(root.GetChildAtIndex(2)) 590edb874b2SJonas Devlieghere 591edb874b2SJonas Devlieghere total_size = left_size + right_size + 1 592edb874b2SJonas Devlieghere return total_size 593edb874b2SJonas Devlieghere 594edb874b2SJonas Devlieghere 595edb874b2SJonas Devlieghere def print_tree(root): 596edb874b2SJonas Devlieghere """ 597edb874b2SJonas Devlieghere Recursively traverse a binary search tree, printing out 598edb874b2SJonas Devlieghere the words at the nodes in alphabetical order (the 599edb874b2SJonas Devlieghere search order for the binary tree). 600edb874b2SJonas Devlieghere 601edb874b2SJonas Devlieghere This function assumes the binary search tree is 602edb874b2SJonas Devlieghere the one defined in dictionary.c It uses LLDB API 603edb874b2SJonas Devlieghere functions to examine and traverse the tree nodes. 604edb874b2SJonas Devlieghere """ 605edb874b2SJonas Devlieghere if (root.GetChildAtIndex(1).GetValue() is not None) and ( 606edb874b2SJonas Devlieghere int(root.GetChildAtIndex(1).GetValue(), 16) != 0): 607edb874b2SJonas Devlieghere print_tree(root.GetChildAtIndex(1)) 608edb874b2SJonas Devlieghere 609edb874b2SJonas Devlieghere print root.GetChildAtIndex(0).GetSummary() 610edb874b2SJonas Devlieghere 611edb874b2SJonas Devlieghere if (root.GetChildAtIndex(2).GetValue() is not None) and ( 612edb874b2SJonas Devlieghere int(root.GetChildAtIndex(2).GetValue(), 16) != 0): 613edb874b2SJonas Devlieghere print_tree(root.GetChildAtIndex(2)) 614edb874b2SJonas Devlieghere 615edb874b2SJonas Devlieghere 616edb874b2SJonas Devliegheredictionary.c - Sample dictionary program, with bug 617edb874b2SJonas Devlieghere 618edb874b2SJonas Devlieghere:: 619edb874b2SJonas Devlieghere 620edb874b2SJonas Devlieghere //===-- dictionary.c ---------------------------------------------*- C -*-===// 621edb874b2SJonas Devlieghere // 622c874dd53SChristopher Di Bella // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 623c874dd53SChristopher Di Bella // See https://llvm.org/LICENSE.txt for license information. 624c874dd53SChristopher Di Bella // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 625edb874b2SJonas Devlieghere // 626c874dd53SChristopher Di Bella //===----------------------------------------------------------------------===// 627edb874b2SJonas Devlieghere #include <ctype.h> 628edb874b2SJonas Devlieghere #include <stdio.h> 629edb874b2SJonas Devlieghere #include <stdlib.h> 630edb874b2SJonas Devlieghere #include <string.h> 631edb874b2SJonas Devlieghere 632edb874b2SJonas Devlieghere typedef struct tree_node { 633edb874b2SJonas Devlieghere const char *word; 634edb874b2SJonas Devlieghere struct tree_node *left; 635edb874b2SJonas Devlieghere struct tree_node *right; 636edb874b2SJonas Devlieghere } tree_node; 637edb874b2SJonas Devlieghere 638edb874b2SJonas Devlieghere /* Given a char*, returns a substring that starts at the first 639edb874b2SJonas Devlieghere alphabet character and ends at the last alphabet character, i.e. it 640edb874b2SJonas Devlieghere strips off beginning or ending quotes, punctuation, etc. */ 641edb874b2SJonas Devlieghere 642edb874b2SJonas Devlieghere char *strip(char **word) { 643edb874b2SJonas Devlieghere char *start = *word; 644edb874b2SJonas Devlieghere int len = strlen(start); 645edb874b2SJonas Devlieghere char *end = start + len - 1; 646edb874b2SJonas Devlieghere 647edb874b2SJonas Devlieghere while ((start < end) && (!isalpha(start[0]))) 648edb874b2SJonas Devlieghere start++; 649edb874b2SJonas Devlieghere 650edb874b2SJonas Devlieghere while ((end > start) && (!isalpha(end[0]))) 651edb874b2SJonas Devlieghere end--; 652edb874b2SJonas Devlieghere 653edb874b2SJonas Devlieghere if (start > end) 654edb874b2SJonas Devlieghere return NULL; 655edb874b2SJonas Devlieghere 656edb874b2SJonas Devlieghere end[1] = '\0'; 657edb874b2SJonas Devlieghere *word = start; 658edb874b2SJonas Devlieghere 659edb874b2SJonas Devlieghere return start; 660edb874b2SJonas Devlieghere } 661edb874b2SJonas Devlieghere 662edb874b2SJonas Devlieghere /* Given a binary search tree (sorted alphabetically by the word at 663edb874b2SJonas Devlieghere each node), and a new word, inserts the word at the appropriate 664edb874b2SJonas Devlieghere place in the tree. */ 665edb874b2SJonas Devlieghere 666edb874b2SJonas Devlieghere void insert(tree_node *root, char *word) { 667edb874b2SJonas Devlieghere if (root == NULL) 668edb874b2SJonas Devlieghere return; 669edb874b2SJonas Devlieghere 670edb874b2SJonas Devlieghere int compare_value = strcmp(word, root->word); 671edb874b2SJonas Devlieghere 672edb874b2SJonas Devlieghere if (compare_value == 0) 673edb874b2SJonas Devlieghere return; 674edb874b2SJonas Devlieghere 675edb874b2SJonas Devlieghere if (compare_value < 0) { 676edb874b2SJonas Devlieghere if (root->left != NULL) 677edb874b2SJonas Devlieghere insert(root->left, word); 678edb874b2SJonas Devlieghere else { 679edb874b2SJonas Devlieghere tree_node *new_node = (tree_node *)malloc(sizeof(tree_node)); 680edb874b2SJonas Devlieghere new_node->word = strdup(word); 681edb874b2SJonas Devlieghere new_node->left = NULL; 682edb874b2SJonas Devlieghere new_node->right = NULL; 683edb874b2SJonas Devlieghere root->left = new_node; 684edb874b2SJonas Devlieghere } 685edb874b2SJonas Devlieghere } else { 686edb874b2SJonas Devlieghere if (root->right != NULL) 687edb874b2SJonas Devlieghere insert(root->right, word); 688edb874b2SJonas Devlieghere else { 689edb874b2SJonas Devlieghere tree_node *new_node = (tree_node *)malloc(sizeof(tree_node)); 690edb874b2SJonas Devlieghere new_node->word = strdup(word); 691edb874b2SJonas Devlieghere new_node->left = NULL; 692edb874b2SJonas Devlieghere new_node->right = NULL; 693edb874b2SJonas Devlieghere root->right = new_node; 694edb874b2SJonas Devlieghere } 695edb874b2SJonas Devlieghere } 696edb874b2SJonas Devlieghere } 697edb874b2SJonas Devlieghere 698edb874b2SJonas Devlieghere /* Read in a text file and storea all the words from the file in a 699edb874b2SJonas Devlieghere binary search tree. */ 700edb874b2SJonas Devlieghere 701edb874b2SJonas Devlieghere void populate_dictionary(tree_node **dictionary, char *filename) { 702edb874b2SJonas Devlieghere FILE *in_file; 703edb874b2SJonas Devlieghere char word[1024]; 704edb874b2SJonas Devlieghere 705edb874b2SJonas Devlieghere in_file = fopen(filename, "r"); 706edb874b2SJonas Devlieghere if (in_file) { 707edb874b2SJonas Devlieghere while (fscanf(in_file, "%s", word) == 1) { 708edb874b2SJonas Devlieghere char *new_word = (strdup(word)); 709edb874b2SJonas Devlieghere new_word = strip(&new_word); 710edb874b2SJonas Devlieghere if (*dictionary == NULL) { 711edb874b2SJonas Devlieghere tree_node *new_node = (tree_node *)malloc(sizeof(tree_node)); 712edb874b2SJonas Devlieghere new_node->word = new_word; 713edb874b2SJonas Devlieghere new_node->left = NULL; 714edb874b2SJonas Devlieghere new_node->right = NULL; 715edb874b2SJonas Devlieghere *dictionary = new_node; 716edb874b2SJonas Devlieghere } else 717edb874b2SJonas Devlieghere insert(*dictionary, new_word); 718edb874b2SJonas Devlieghere } 719edb874b2SJonas Devlieghere } 720edb874b2SJonas Devlieghere } 721edb874b2SJonas Devlieghere 722edb874b2SJonas Devlieghere /* Given a binary search tree and a word, search for the word 723edb874b2SJonas Devlieghere in the binary search tree. */ 724edb874b2SJonas Devlieghere 725edb874b2SJonas Devlieghere int find_word(tree_node *dictionary, char *word) { 726edb874b2SJonas Devlieghere if (!word || !dictionary) 727edb874b2SJonas Devlieghere return 0; 728edb874b2SJonas Devlieghere 729edb874b2SJonas Devlieghere int compare_value = strcmp(word, dictionary->word); 730edb874b2SJonas Devlieghere 731edb874b2SJonas Devlieghere if (compare_value == 0) 732edb874b2SJonas Devlieghere return 1; 733edb874b2SJonas Devlieghere else if (compare_value < 0) 734edb874b2SJonas Devlieghere return find_word(dictionary->left, word); 735edb874b2SJonas Devlieghere else 736edb874b2SJonas Devlieghere return find_word(dictionary->right, word); 737edb874b2SJonas Devlieghere } 738edb874b2SJonas Devlieghere 739edb874b2SJonas Devlieghere /* Print out the words in the binary search tree, in sorted order. */ 740edb874b2SJonas Devlieghere 741edb874b2SJonas Devlieghere void print_tree(tree_node *dictionary) { 742edb874b2SJonas Devlieghere if (!dictionary) 743edb874b2SJonas Devlieghere return; 744edb874b2SJonas Devlieghere 745edb874b2SJonas Devlieghere if (dictionary->left) 746edb874b2SJonas Devlieghere print_tree(dictionary->left); 747edb874b2SJonas Devlieghere 748edb874b2SJonas Devlieghere printf("%s\n", dictionary->word); 749edb874b2SJonas Devlieghere 750edb874b2SJonas Devlieghere if (dictionary->right) 751edb874b2SJonas Devlieghere print_tree(dictionary->right); 752edb874b2SJonas Devlieghere } 753edb874b2SJonas Devlieghere 754edb874b2SJonas Devlieghere int main(int argc, char **argv) { 755edb874b2SJonas Devlieghere tree_node *dictionary = NULL; 756edb874b2SJonas Devlieghere char buffer[1024]; 757edb874b2SJonas Devlieghere char *filename; 758edb874b2SJonas Devlieghere int done = 0; 759edb874b2SJonas Devlieghere 760edb874b2SJonas Devlieghere if (argc == 2) 761edb874b2SJonas Devlieghere filename = argv[1]; 762edb874b2SJonas Devlieghere 763edb874b2SJonas Devlieghere if (!filename) 764edb874b2SJonas Devlieghere return -1; 765edb874b2SJonas Devlieghere 766edb874b2SJonas Devlieghere populate_dictionary(&dictionary, filename); 767edb874b2SJonas Devlieghere fprintf(stdout, "Dictionary loaded.\nEnter search word: "); 768edb874b2SJonas Devlieghere while (!done && fgets(buffer, sizeof(buffer), stdin)) { 769edb874b2SJonas Devlieghere char *word = buffer; 770edb874b2SJonas Devlieghere int len = strlen(word); 771edb874b2SJonas Devlieghere int i; 772edb874b2SJonas Devlieghere 773edb874b2SJonas Devlieghere for (i = 0; i < len; ++i) 774edb874b2SJonas Devlieghere word[i] = tolower(word[i]); 775edb874b2SJonas Devlieghere 776edb874b2SJonas Devlieghere if ((len > 0) && (word[len - 1] == '\n')) { 777edb874b2SJonas Devlieghere word[len - 1] = '\0'; 778edb874b2SJonas Devlieghere len = len - 1; 779edb874b2SJonas Devlieghere } 780edb874b2SJonas Devlieghere 781edb874b2SJonas Devlieghere if (find_word(dictionary, word)) 782edb874b2SJonas Devlieghere fprintf(stdout, "Yes!\n"); 783edb874b2SJonas Devlieghere else 784edb874b2SJonas Devlieghere fprintf(stdout, "No!\n"); 785edb874b2SJonas Devlieghere 786edb874b2SJonas Devlieghere fprintf(stdout, "Enter search word: "); 787edb874b2SJonas Devlieghere } 788edb874b2SJonas Devlieghere 789edb874b2SJonas Devlieghere fprintf(stdout, "\n"); 790edb874b2SJonas Devlieghere return 0; 791edb874b2SJonas Devlieghere } 792edb874b2SJonas Devlieghere 793edb874b2SJonas Devlieghere 794edb874b2SJonas DevlieghereThe text for "Romeo and Juliet" can be obtained from the Gutenberg Project 795edb874b2SJonas Devlieghere(http://www.gutenberg.org). 796edb874b2SJonas Devlieghere 797