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