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