forked from ascholerChemeketa/ThinkCPP2
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdebugging_runtime-errors.ptx
More file actions
221 lines (176 loc) · 8.47 KB
/
Copy pathdebugging_runtime-errors.ptx
File metadata and controls
221 lines (176 loc) · 8.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
<section xml:id="debugging_runtime-errors">
<title>Run-Time Errors</title>
<introduction>
<p>
It's not always clear what causes a run-time error, but you can often figure things out by adding print statements to your program.
</p>
</introduction>
<subsection xml:id="debugging_my-program-hangs">
<title>My program hangs</title>
<introduction>
<p>
<idx><h>hanging</h></idx>
<idx><h>infinite loop</h></idx>
If a program stops and seems to be doing nothing, we say it is <q>hanging</q>.
Often that means it is caught in an infinite loop or an infinite recursion.
<ul>
<li><p>If you suspect that a particular loop is the problem, add a print statement immediately before the loop that says <c>"entering the loop"</c> and another immediately after that says <c>"exiting the loop"</c>.
Run the program.
If you get the first message and not the second, you know where the program is getting stuck.
Go to the section titled <q>Infinite loop</q>.
<idx><h>StackOverflowError</h></idx></p></li>
<li><p>Most of the time, an infinite recursion will cause the program to run for a while and then produce a <c>StackOverflowError</c>.
If that happens, go to the section titled <q>Infinite recursion</q>.
If you are not getting a <c>StackOverflowError</c>, but you suspect there is a problem with a recursive method, you can still use the techniques in the infinite recursion section.</p></li>
<li><p>If neither of the previous suggestions helps, you might not understand the flow of execution in your program.
Go to the section titled <q>Flow of execution</q>.</p></li>
</ul>
</p>
</introduction>
<subsubsection xml:id="infloop">
<title>Infinite loop</title>
<p>
If you think you have an infinite loop and you know which loop it is, add a print statement at the end of the loop that displays the values of the variables in the condition, and the value of the condition.
</p>
<p>
For example:
</p>
<program>
while (x > 0 && y < 0) {
// do something to x
// do something to y
System.out.println("x: " + x);
System.out.println("y: " + y);
System.out.println("condition: " + (x > 0 && y < 0));
}
</program>
<p>
Now when you run the program, you see three lines of output for each time through the loop.
The last time through the loop, the condition should be <c>false</c>.
If the loop keeps going, you will see the values of <c>x</c> and <c>y</c>, and you might figure out why they are not getting updated correctly.
</p>
</subsubsection>
<subsubsection xml:id="infrec">
<title>Infinite recursion</title>
<p>
<idx><h>recursion</h><h>infinite</h></idx>
<idx><h>infinite recursion</h></idx>
Most of the time, an infinite recursion will cause the program to throw a <c>StackOverflowError</c>.
But if the program is slow, it may take a long time to fill the stack.
</p>
<p>
If you know which method is causing an infinite recursion, check that there is a base case.
There should be a condition that makes the method return without making a recursive invocation.
If not, you need to rethink the algorithm and identify a base case.
</p>
<p>
If there is a base case, but the program doesn't seem to be reaching it, add a print statement at the beginning of the method that displays the parameters.
</p>
<p>
Now when you run the program, you see a few lines of output every time the method is invoked, and you can see the values of the parameters.
If the parameters are not moving toward the base case, you might see why not.
</p>
</subsubsection>
<subsubsection xml:id="flowexec">
<title>Flow of execution</title>
<p>
<idx><h>flow of execution</h></idx>
<idx><h>tracing</h></idx>
If you are not sure how the flow of execution is moving through your program, add print statements to the beginning of each method with a message like <c>"entering method foo"</c>, where <c>foo</c> is the name of the method.
Now when you run the program, it displays a trace of each method as it is invoked.
</p>
<p>
You can also display the arguments each method receives.
When you run the program, check whether the values are reasonable, and check for one of the most common errors<mdash/>providing arguments in the wrong order.
</p>
</subsubsection>
</subsection>
<subsection xml:id="debugging_when-i-run-program-i-get-exception">
<title>When I run the program, I get an exception</title>
<p>
<idx><h>exception</h></idx>
<idx><h>stack trace</h></idx>
When an exception occurs, Java displays a message that includes the name of the exception, the line of the program where the exception occurred, and a stack trace.
The stack trace includes the method that was running, the method that invoked it, the method that invoked that one, and so on.
</p>
<p>
The first step is to examine the place in the program where the error occurred and see if you can figure out what happened:
<ul>
<li>
<title>NullPointerException:</title>
<p>
<idx><h>NullPointerException</h></idx>
You tried to access an instance variable or invoke a method on an object that is currently <c>null</c>.
You should figure out which variable is <c>null</c> and then figure out how it got to be that way.
Remember that when you declare a variable with an array type, its elements are initially <c>null</c> until you assign a value to them.
For example, this code causes a <c>NullPointerException</c>:
\begin{code}
int[] array = new Point[5];
System.out.println(array[0].x);
\end{code}</p></li>
<li>
<title>ArrayIndexOutOfBoundsException:</title>
<p>
<idx><h>ArrayIndexOutOfBoundsException</h></idx>
The index you are using to access an array is either negative or greater than <c>array.length - 1</c>.
If you can find the site where the problem is, add a print statement immediately before it to display the value of the index and the length of the array.
Is the array the right size?
Is the index the right value?
Now work your way backward through the program and see where the array and the index come from.
Find the nearest assignment statement and see if it is doing the right thing.
If either one is a parameter, go to the place where the method is invoked and see where the values are coming from.</p></li>
<li>
<title>StackOverflowError:</title>
<p>
<idx><h>StackOverflowError</h></idx>
See <q>Infinite recursion</q> on <xref ref="infrec">page</xref>.</p></li>
<li>
<title>FileNotFoundException:</title>
<p>
<idx><h>FileNotFoundException</h></idx>
This means Java didn't find the file it was looking for.
If you are using a project-based development environment like Eclipse, you might have to import the file into the project.
Otherwise, make sure the file exists and that the path is correct.
This problem depends on your filesystem, so it can be hard to track down.</p></li>
<li>
<title>ArithmeticException:</title>
<p>
<idx><h>ArithmeticException</h></idx>
Something went wrong during an arithmetic operation; for example, division by zero.</p></li>
</ul>
</p>
</subsection>
<subsection xml:id="debugging_i-added-so-many-print-statements-i-get-inundated-with-output">
<title>I added so many print statements I get inundated with output</title>
<p>
<idx><h>print statement</h></idx>
<idx><h>statement</h><h>print</h></idx>
One of the problems with using print statements for debugging is that you can end up buried in output.
There are two ways to proceed: either simplify the output or simplify the program.
</p>
<p>
To simplify the output, you can remove or comment out print statements that aren't helping, or combine them, or format the output so it is easier to understand.
As you develop a program, you should write code to generate concise, informative traces of what the program is doing.
</p>
<p>
To simplify the program, scale down the problem the program is working on.
For example, if you are sorting an array, sort a <em>small</em> array.
If the program takes input from the user, give it the simplest input that causes the error.
</p>
<p>
<idx><h>nested</h></idx>
Also, clean up the code.
Remove unnecessary or experimental parts, and reorganize the program to make it easier to read.
For example, if you suspect that the error is in a deeply nested part of the program, rewrite that part with a simpler structure.
If you suspect a large method, split it into smaller methods and test them separately.
</p>
<p>
The process of finding the minimal test case often leads you to the bug.
For example, if you find that a program works when the array has an even number of elements, but not when it has an odd number, that gives you a clue about what is going on.
</p>
<p>
Reorganizing the program can help you find subtle bugs.
If you make a change that you think doesn't affect the program, and it does, that can tip you off.
</p>
</subsection>
</section>