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