Fixed #60, elaborating on debugging chapter.

This commit is contained in:
Amy J. Ko 2020-09-18 14:11:18 -07:00
parent c42921fde7
commit 7c43479126
3 changed files with 90 additions and 63 deletions

4
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,4 @@
{
"editor.fontFamily": "Lora, Menlo, Monaco, 'Courier New', monospace",
"editor.fontSize": 15
}

View file

@ -1,34 +1,34 @@
{
"title": "Cooperative Software Development",
"authors": ["[Amy J. Ko|https://faculty.washington.edu/ajko/]"],
"contributors": ["[Benjamin Xie|http://benjixie.com]"],
"license": "[Creative Commons Attribution-NoDeriviatives 4.0|https://creativecommons.org/licenses/by-nd/4.0/]",
"cover": ["cover.jpg", "A photograph of a racially and gender diverse team of six making decisions.", "Software engineering is inherently social.", "Shutterstock"],
"unknown": ["error.png", "A screen shot of a Linux operating system kernel panic", "Uh oh, something went wrong.", "William Pina"],
"description": "This book is an introduction to the many human, social, and political aspects of software engineering. It's unique in two ways. First, unlike many software engineering books, it explictly avoids centering technical questions about software engineering, instead focusing on the many ways that software engineering work is cognitive, social, and organizational. Second, it does so by engaging extensively with academic research literature, summarizing key findings, but also questioning them, opening a dialog about the nature of software engineering work and the many factors that shape it. Anyone that reads it will be better prepared to critically engage in creating software in teams.\n\nThis book is a living document. Do you see ways to improve it? [Submit an issue|https://github.com/amyjko/cooperative-software-development/issues] or a [pull request|https://github.com/amyjko/cooperative-software-development/pulls] to its [GitHub repository|https://github.com/amyjko/cooperative-software-development].\n\n_This material is based upon work supported by the National Science Foundation under Grant No. 0952733. Any opinions, findings, and conclusions or recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of the National Science Foundation._",
"chapters": [
["History", "history", "Hamilton.jpg", "Margaret Hamilton working on the Apollo flight software.", "Margaret Hamilton working on the Apollo flight software.", "NASA"],
["Organizations", "organizations", "team.jpg", "A software team hard at work", "Early days at the author's software startup in 2012.", "Amy J. Ko"],
["Communication", "communication", "communication.png", "A man and a woman having a conversation", "Clear and timely communication is at the heart of effective software engineering.", "Public domain"],
["Productivity", "productivity", "productivity.jpg", "A women working at a laptop", "Productivity isn't just about working fast.", "Creative Commons CC0"],
["Quality", "quality", "zoho.jpg", "A screenshot of the Zoho issue tracker.", "Software quality is multidimensional and often coursely measured through issue trackers like this one.", "Zoho, Inc."],
["Requirements", "requirements", "scaffolding.jpg", "An architectural structure showing the framework of a glass structure", "Requirements specify what software must do, constraining, focusing, and defining it's successful functioning.", "Public domain"],
["Architecture", "architecture", "church.jpg", "A photograph of a church hallway with arches.", "Architecture is how code is organized", "Creative Commons 0"],
["Specifications", "specifications", "blueprint.jpg", "A blueprint for an architectural plan", "Specifications add a layer of detail onto architectural plans.", "Public domain"],
["Process", "process", "flow.jpg", "A photograph of a river", "Good process is like a river, seamlessly flowing around obstacles", "Public domain"],
["Comprehension", "comprehension", "network.png", "A visualization of many interconnected node", "Program comprehension is about understanding dependencies", "Public domain"],
["Verification", "verification", "check.png", "A check mark", "Have you met your requirements? How do you know?", "Public domain"],
["Monitoring", "monitoring", "monitoring.jpg", "A photograph of a lifeguide monitoring a beach.", "It's not always easy to see software fail.", "Public domain"],
["Evolution", "evolution", "atsign.png", "Four variations on an at sign.", "Software changes and that requires planning.", "Public domain"],
["Debugging", "debugging", "swatter.png", "An illustration of a fly swatter.", "Debugging is inevitable because defects are inevitable", "Public domain"]
"title": "Cooperative Software Development",
"authors": ["[Amy J. Ko|https://faculty.washington.edu/ajko/]"],
"contributors": ["[Benjamin Xie|http://benjixie.com]"],
"license": "[Creative Commons Attribution-NoDeriviatives 4.0|https://creativecommons.org/licenses/by-nd/4.0/]",
"cover": ["cover.jpg", "A photograph of a racially and gender diverse team of six making decisions.", "Software engineering is inherently social.", "Shutterstock"],
"unknown": ["error.png", "A screen shot of a Linux operating system kernel panic", "Uh oh, something went wrong.", "William Pina"],
"description": "This book is an introduction to the many human, social, and political aspects of software engineering. It's unique in two ways. First, unlike many software engineering books, it explictly avoids centering technical questions about software engineering, instead focusing on the many ways that software engineering work is cognitive, social, and organizational. Second, it does so by engaging extensively with academic research literature, summarizing key findings, but also questioning them, opening a dialog about the nature of software engineering work and the many factors that shape it. Anyone that reads it will be better prepared to critically engage in creating software in teams.\n\nThis book is a living document. Do you see ways to improve it? [Submit an issue|https://github.com/amyjko/cooperative-software-development/issues] or a [pull request|https://github.com/amyjko/cooperative-software-development/pulls] to its [GitHub repository|https://github.com/amyjko/cooperative-software-development].\n\n_This material is based upon work supported by the National Science Foundation under Grant No. 0952733. Any opinions, findings, and conclusions or recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of the National Science Foundation._",
"chapters": [
["History", "history", "Hamilton.jpg", "Margaret Hamilton working on the Apollo flight software.", "Margaret Hamilton working on the Apollo flight software.", "NASA"],
["Organizations", "organizations", "team.jpg", "A software team hard at work", "Early days at the author's software startup in 2012.", "Amy J. Ko"],
["Communication", "communication", "communication.png", "A man and a woman having a conversation", "Clear and timely communication is at the heart of effective software engineering.", "Public domain"],
["Productivity", "productivity", "productivity.jpg", "A women working at a laptop", "Productivity isn't just about working fast.", "Creative Commons CC0"],
["Quality", "quality", "zoho.jpg", "A screenshot of the Zoho issue tracker.", "Software quality is multidimensional and often coursely measured through issue trackers like this one.", "Zoho, Inc."],
["Requirements", "requirements", "scaffolding.jpg", "An architectural structure showing the framework of a glass structure", "Requirements specify what software must do, constraining, focusing, and defining it's successful functioning.", "Public domain"],
["Architecture", "architecture", "church.jpg", "A photograph of a church hallway with arches.", "Architecture is how code is organized", "Creative Commons 0"],
["Specifications", "specifications", "blueprint.jpg", "A blueprint for an architectural plan", "Specifications add a layer of detail onto architectural plans.", "Public domain"],
["Process", "process", "flow.jpg", "A photograph of a river", "Good process is like a river, seamlessly flowing around obstacles", "Public domain"],
["Comprehension", "comprehension", "network.png", "A visualization of many interconnected node", "Program comprehension is about understanding dependencies", "Public domain"],
["Verification", "verification", "check.png", "A check mark", "Have you met your requirements? How do you know?", "Public domain"],
["Monitoring", "monitoring", "monitoring.jpg", "A photograph of a lifeguide monitoring a beach.", "It's not always easy to see software fail.", "Public domain"],
["Evolution", "evolution", "atsign.png", "Four variations on an at sign.", "Software changes and that requires planning.", "Public domain"],
["Debugging", "debugging", "swatter.png", "An illustration of a fly swatter.", "Debugging is inevitable because defects are inevitable", "Public domain"]
],
"revisions": [
["September 2020", "Migrated to [Peruse|https://github.com/amyjko/peruse]."],
["July 2020", "Revised all chapters to address racism, sexism, and ableism in software engineering."],
["July 2019", "Incorporated newly published work from ICSE, ESEC/FSE, SIGCSE, TSE, and TOSEM."],
["July 2018", "Incorporated newly published work from ICSE, ESEC/FSE, SIGCSE, TSE, and TOSEM."],
["July 2017", "First draft of the book release."]
],
["September 2020", "Migrated to [Peruse|https://github.com/amyjko/peruse]."],
["July 2020", "Revised all chapters to address racism, sexism, and ableism in software engineering."],
["July 2019", "Incorporated newly published work from ICSE, ESEC/FSE, SIGCSE, TSE, and TOSEM."],
["July 2018", "Incorporated newly published work from ICSE, ESEC/FSE, SIGCSE, TSE, and TOSEM."],
["July 2017", "First draft of the book release."]
],
"references": {
"abbate12": "Abbate, Janet (2012). [Recoding Gender: Women's Changing Participation in Computing|https://mitpress.mit.edu/books/recoding-gender]. The MIT Press.",
"abdalkareem17": "Rabe Abdalkareem, Olivier Nourry, Sultan Wehaibi, Suhaib Mujahid, and Emad Shihab. 2017. [Why do developers use trivial packages? An empirical case study on npm|https://doi.org/10.1145/3106237.3106267]. In Proceedings of the 2017 11th Joint Meeting on Foundations of Software Engineering (ESEC/FSE 2017). ACM, New York, NY, USA, 385-395.",
@ -37,6 +37,7 @@
"akers09": "David Akers, Matthew Simpson, Robin Jeffries, and Terry Winograd. 2009. [Undo and erase events as indicators of usability problems|http://dx.doi.org/10.1145/1518701.1518804]. In Proceedings of the SIGCHI Conference on Human Factors in Computing Systems (CHI '09). ACM, New York, NY, USA, 659-668.",
"albaik15": "Al-Baik, O., & Miller, J. (2015). [The kanban approach, between agility and leanness: a systematic review|https://doi.org/10.1007/s10664-014-9340-x]. Empirical Software Engineering, 20(6), 1861-1897.",
"aranda09": "Jorge Aranda and Gina Venolia. 2009. [The secret life of bugs: Going past the errors and omissions in software repositories|http://dx.doi.org/10.1109/ICSE.2009.5070530]. In Proceedings of the 31st International Conference on Software Engineering (ICSE '09). IEEE Computer Society, Washington, DC, USA, 298-308.",
"arnold96": "Arnold, R. S. (1996). Software change impact analysis. IEEE Computer Society Press.",
"atwood16": "Software Engineering Daily. [The State of Programming with Stack Overflow Co-Founder Jeff Atwood|https://softwareengineeringdaily.com/2016/03/14/state-programming-jeff-atwood/].",
"bacchelli13": "Alberto Bacchelli and Christian Bird. 2013. [Expectations, outcomes, and challenges of modern code review|https://doi.org/10.1109/ICSE.2013.6606617]. In Proceedings of the 2013 International Conference on Software Engineering (ICSE '13). IEEE Press, Piscataway, NJ, USA, 712-721.",
"baecker88": "R. Baecker. 1988. [Enhancing program readability and comprehensibility with tools for program visualization|https://doi.org/10.1109/ICSE.1988.93716]. In Proceedings of the 10th international conference on Software engineering (ICSE '88). IEEE Computer Society Press, Los Alamitos, CA, USA, 356-366.",
@ -122,10 +123,12 @@
"kim16": "Miryung Kim, Thomas Zimmermann, Robert DeLine, and Andrew Begel. 2016. [The emerging role of data scientists on software development teams|https://doi.org/10.1145/2884781.2884783]. In Proceedings of the 38th International Conference on Software Engineering (ICSE '16). ACM, New York, NY, USA, 96-107.",
"ko04": "Ko, A. J., Myers, B. A., & Aung, H. H. (2004, September). [Six learning barriers in end-user programming systems|http://ieeexplore.ieee.org/abstract/document/1372321/]. In Visual Languages and Human Centric Computing, 2004 IEEE Symposium on (pp. 199-206). IEEE.",
"ko05": "Amy J. Ko, Htet Htet Aung, Brad A. Myers (2005). Eliciting Design Requirements for Maintenance-Oriented IDEs: A Detailed Study of Corrective and Perfective Maintenance Tasks. International Conference on Software Engineering (ICSE), 126-135.",
"ko05b": "Ko, A. J., & Myers, B. A. (2005). [A framework and methodology for studying the causes of software errors in programming systems|https://doi.org/10.1016/j.jvlc.2004.08.003]. Journal of Visual Languages & Computing, 16(1-2), 41-84.",
"ko07": "Amy J. Ko, Rob DeLine, and Gina Venolia (2007). [Information needs in collocated software development teams|https://doi.org/10.1109/ICSE.2007.45]. In 29th International Conference on Software Engineering, 344-353.",
"ko08": "Amy J. Ko and Brad A. Myers. 2008. [Debugging reinvented: asking and answering why and why not questions about program behavior|http://dx.doi.org/10.1145/1368088.1368130]. In Proceedings of the 30th international conference on Software engineering (ICSE '08). ACM, New York, NY, USA, 301-310.",
"ko09": "Amy J. Ko and Brad A. Myers (2009). [Finding causes of program output with the Java Whyline|https://doi.org/10.1145/1518701.1518942]. In Proceedings of the SIGCHI Conference on Human Factors in Computing Systems (pp. 1569-1578).",
"ko17": "Ko, Amy J. (2017). [A Three-Year Participant Observation of Software Startup Software Evolution|https://faculty.washington.edu/ajko/papers/Ko2017AnswerDashReflection.pdf]. International Conference on Software Engineering, Software Engineering in Practice, 3-12.",
"ko19": "Ko, A. J., LaToza, T. D., Hull, S., Ko, E. A., Kwok, W., Quichocho, J., ... & Pandit, R. (2019). [Teaching explicit programming strategies to adolescents|https://doi.org/10.1145/3287324.3287371]. In Proceedings of the 50th ACM Technical Symposium on Computer Science Education (pp. 469-475).",
"kocaguneli13": "Ekrem Kocaguneli, Thomas Zimmermann, Christian Bird, Nachiappan Nagappan, and Tim Menzies. 2013. [Distributed development considered harmful?|https://doi.org/10.1109/ICSE.2013.6606637]. In Proceedings of the 2013 International Conference on Software Engineering (ICSE '13). IEEE Press, Piscataway, NJ, USA, 882-890.",
"koide05": "Amy J. Ko, Htet Aung, and Brad A. Myers. 2005. [Eliciting design requirements for maintenance-oriented IDEs: a detailed study of corrective and perfective maintenance tasks|http://ieeexplore.ieee.org/abstract/document/1553555/]. In Proceedings of the 27th international conference on Software engineering (ICSE '05). ACM, New York, NY, USA, 126-135.",
"kononenko16": "Oleksii Kononenko, Olga Baysal, and Michael W. Godfrey. 2016. [Code review quality: how developers see it|https://doi.org/10.1145/2884781.2884840]. In Proceedings of the 38th International Conference on Software Engineering (ICSE '16). ACM, New York, NY, USA, 1028-1038.",
@ -133,6 +136,7 @@
"latoza06": "Thomas D. LaToza, Gina Venolia, and Robert DeLine. 2006. [Maintaining mental models: a study of developer work habits|http://dx.doi.org/10.1145/1134285.1134355]. In Proceedings of the 28th international conference on Software engineering (ICSE '06). ACM, New York, NY, USA, 492-501.",
"latoza07": "Thomas D. LaToza, David Garlan, James D. Herbsleb, and Brad A. Myers. 2007. [Program comprehension as fact finding|http://dx.doi.org/10.1145/1287624.1287675]. In Proceedings of the the 6th joint meeting of the European software engineering conference and the ACM SIGSOFT symposium on The foundations of software engineering (ESEC-FSE '07). ACM, New York, NY, USA, 361-370.",
"latoza10": "Thomas D. LaToza and Brad A. Myers. 2010. [Developers ask reachability questions|http://dx.doi.org/10.1145/1806799.1806829]. In Proceedings of the 32nd ACM/IEEE International Conference on Software Engineering - Volume 1 (ICSE '10), Vol. 1. ACM, New York, NY, USA, 185-194.",
"latoza20": "LaToza, T. D., Arab, M., Loksa, D., & Ko, A. J. (2020). [Explicit programming strategies|https://doi.org/10.1007/s10664-020-09810-1]. Empirical Software Engineering, 1-34.",
"lavallee15": "Mathieu Lavallee and Pierre N. Robillard. 2015. [Why good developers write bad code: an observational case study of the impacts of organizational factors on software quality|http://dl.acm.org/citation.cfm?id=2818754.2818837]. In Proceedings of the 37th International Conference on Software Engineering - Volume 1 (ICSE '15), Vol. 1. IEEE Press, Piscataway, NJ, USA, 677-687.",
"lawrie06": "Lawrie, D., Morrell, C., Feild, H., & Binkley, D. (2006). [What's in a name? A study of identifiers|https://doi.org/10.1109/ICPC.2006.51]. IEEE International Conference on Program Comprehension, 3-12.",
"lee03": "Lee, G. K., & Cole, R. E. (2003). [From a firm-based to a community-based model of knowledge creation: The case of the Linux kernel development|http://pubsonline.informs.org/doi/abs/10.1287/orsc.14.6.633.24866]. Organization science, 14(6), 633-649.",
@ -230,6 +234,7 @@
"ye03": "Yunwen Ye and Kouichi Kishida (2003). [Toward an understanding of the motivation Open Source Software developers|http://dl.acm.org/citation.cfm?id=776867]. In Proceedings of the 25th International Conference on Software Engineering, 419-429.",
"yin11": "Zuoning Yin, Ding Yuan, Yuanyuan Zhou, Shankar Pasupathy, and Lakshmi Bairavasundaram. 2011. [How do fixes become bugs?|http://dx.doi.org/10.1145/2025113.2025121] In Proceedings of the 19th ACM SIGSOFT symposium and the 13th European conference on Foundations of software engineering (ESEC/FSE '11). ACM, New York, NY, USA, 26-36.",
"zeller02": "Andreas Zeller. 2002. [Isolating cause-effect chains from computer programs|http://dx.doi.org/10.1145/587051.587053]. In Proceedings of the 10th ACM SIGSOFT symposium on Foundations of software engineering (SIGSOFT '02/FSE-10). ACM, New York, NY, USA, 1-10.",
"zeller02b": "Zeller, A., & Hildebrandt, R. (2002). [Simplifying and isolating failure-inducing input|https://doi.org/10.1109/32.988498]. IEEE Transactions on Software Engineering, 28(2), 183-200.",
"zeller09": "Zeller, A. (2009). [Why programs fail: a guide to systematic debugging|https://www.google.com/books/edition/_/_63Bm4LAdDIC]. Elsevier.",
"zhou11": "Minghui Zhou and Audris Mockus. 2011. [Does the initial environment impact the future of developers?|https://doi.org/10.1145/1985793.1985831] In Proceedings of the 33rd International Conference on Software Engineering (ICSE '11). ACM, New York, NY, USA, 271-280."
}

View file

@ -1,53 +1,71 @@
Despite all of your hard work at design, implementation, and verification, your software has failed. Somewhere in its implementation there's a line of code, or multiple lines of code, that, given a particular set of inputs, causes the program to fail. How do you find those defective lines of code? You debug, and when you're doing debugging right, you do it systematically<zeller09>. And yet, despite decades of research and practice, most developers have weak debugging skills, don't know how to properly use debugging tools, and still rely in basic print statements<beller18>.
Despite all of your hard work at design, implementation, and verification, your software has failed. Somewhere in its implementation there's a line of code, or multiple lines of code, that, given a particular set of inputs, causes the program to fail. *Debugging* is the activity of finding those causes and identifying changes to code that will prevent those failures. Of course, because defects are inenvitable in code that human developers write, debugging is no niche process in software engineering: it is a central, critical, and challenging activity that is part of nearly all aspects of creating software.
# What is debugging?
Before we can talk about debugging, it's first important to consider what counts as a "bug". This term, which according to computer science mythology, first emerged from actual insects finding their way into the vacuum tubes of mainframes, is actually quite vague. Is the "bug" the incorrect code? Is it the faulty behavior that occurs at runtime when that incorrect code executes? Is it the problem that occurs in the world when the software misbehaves, such as a program crashing, or an operating system hanging? "*Bug*" actually refers to all of things things, which makes it a colloquial, but imprecise term.
To clarify things, consider four definitions<ko05b>.
To begin, let's consider *program behavior*, which we will define as any program output, at either a point in time, or over time, that is percevied or processed by a person or other software. Behavior, in this sense, is what we see programs do: they crash, hang, retrieve incorrect information, show error codes, compute something incorrectly, exhibit incomprehensible behavior, and so on. Program behavior is what [requirements|requirements] attempt to constraint (e.g., "the program should always finish in less than one second" is a statement about the program's behavior over time.)
Given this definition of behavior, we can then define a *defect* is some set of program fragments that may cause program behavior that is inconsistent a program's requirements. Note that this definition actually has some non-obvious implications. First, defects do not necessarily cause problems; may defects may actually never be executed, or never executed with inputs that cause a program to misbehave. Second, defects can only be defined as such to the extent that requirements are clear. If you haven't written those requirements down in an unambiguous way, there will be debate about whether something is defect. Take, for example, a web application that has SQL injection security vulnerabilites, but the for the purpose of learning how to identify such vulnerabilities. Those aren't defects because they are their intentionally.
A *fault* is a program state caused by a defect that may result in a program behavior inconsistent with a program's requirements. For example, imagine a program that is supposed to count from 1 to 10 using a variable to track and increment the current number, but with a defect that causes it to start at 0. The fault is the value of that variable when it is set to 0. When it is set to 1 through 10, there's nothing faulty about program behavior. Faults, like defects, do not necessarily cause problems. For example, imagine that the same program prints out the current value, but has another defect that unintentionally skips printing the first value. There would be two defects, a fault on the first number, but no undesirable program behavior, because it would still print 1 to 10.
Finally, a *failure* is a program behavior that is inconsistent with a program's requirements. Failures are what we report in bug reports, what we often mean when we say "bug", and ultimately what matters in the world, as program behavior is what programs do to the world. To use our terminology then, we would say that "_defects may cause faults, faults may cause failures, and failures may cause consequences in the world_"
What then, is debugging, using this terminology? *Debugging* is any activity that, given a report of a failure, seeks to identify the one or more defects that caused one or more faults, which caused the failure, and then making changes to a program to eliminate the associated defects. How to do this, of course, is the hard part. Therefore, debugging is inherently a process of searching—for faults that cause failures, and defects that cause faults. What's being searched when debugging is the thousands, millions, or perhaps even billions of instructions that are executed when a program executes (causing faults), and the thousands, or even millions of lines of code that might have have caused those faults.
# Findings defects
To start, you have to *reproduce* the failure. Failure reproduction is a matter of identifying inputs to the program (whether data it receives upon being executed, user inputs, network traffic, or any other form of input) that causes the failure to occur. If you found this failure while _you_ were executing the program, then you're lucky: you should be able to repeat whatever you just did and identify the inputs or series of inputs that caused the problem, giving you a way of testing that the program no longer fails once you've fixed the defect. If someone else was the one executing the program (for example, a user, or someone on your team), you better hope that they reported clear steps for reproducing the problem. When bug reports lack clear reproduction steps, bugs often can't be fixed<bettenburg08>.
Research and practice broadly agree: finding defects quickly and successfully requires systematic, and sometimes scientific investigations of causality<zeller09>. And yet, despite decades of research and practice, most developers never learn the skills for debugging systematically and don't know how to properly use debugging tools to support systematic debugging. In fact, most still rely in basic print statements, partly because they are the most available and flexible tool<beller18>. Despite this, debugging sysemtatically has a few essential phases.
If you can reproduce the problem, the next challenge is to *localize* the defect, trying to identify the cause of the failure in code. There are many different strategies for localizing defects. At the highest level, one can think of this process as a hypothesis testing activity<gilmore91>.
The first phase is *reproducing* the failure, so that the program may be inspected for faults, which can be traced back to defects. Failure reproduction is a matter of identifying inputs to the program (whether data it receives upon being executed, user inputs, network traffic, or any other form of input) that causes the failure to occur. If you found this failure while _you_ were executing the program, then you're lucky: you should be able to repeat whatever you just did and identify the inputs or series of inputs that caused the problem, giving you a way of testing that the program no longer fails once you've fixed the defect. If someone else was the one executing the program (for example, a user, or someone on your team), you better hope that they reported clear steps for reproducing the problem. When bug reports lack clear reproduction steps, bugs often can't be fixed<bettenburg08>.
* Observe failure
* Form hypothesis of cause of failure
* Devise a way to test hypothesis, such as analyzing the code you believe caused it or executing the program with the reproduction steps and stopping at the line you believe is wrong.
* If the hypothesis was supported (meaning the program failed for the reason you thought it did), stop. Otherwise, return to 1.
Once you can reproduce a failure, the next phase is to minimize the failure-inducing input<zeller02b>. Imagine, for example, a program that, given a string `"abcdefg"`, is supposed to print all of the vowels in the program in the sequence they appear (`"ae"`), but instead produces just `"a"`. The intuition behind minimizing failure-inducing inputs is that they reduce the compexity of the search space in debugging. For example, in our example, we might find that entering the string `"abcde"` causes the same failed output of `"a"`, or even shorter, that just the string `"ae"` causes the failure. To minimize that failure-inducing input, one could just randomly remove parts of the input to find the smallest input that still causes the problem. More effective is to search the input space systematically, perhaps by using knowledge of the program behavior (e.g., vowels are what matter, so get rid of the consonants, as we did above), or even more systematic, doing something like a binary search of the input (e.g., trying successively smaller halves of the string until finding the smallest string that still causes the problem). Note that minimizing failure-inducing input applies to _any_ kind of input, not just data: you can also minimize a program, excluding lines you belive are irrelevant to the failure, finding the smallest possible program that still causes the failure.
The problems with the strategy above are numerous. First, what if you can't think of a possible cause? Second, what if your hypothesis is way off? You could spend _hours_ generating hypotheses that are completely off base, effectively analyzing all of your code before finding the defect.
Once you have your minimized program and input, the next phase is to *localize* the defect, trying to identify the cause of the failure in code. There are many different strategies for localizing defects. One of the simplest strategies is to work forward:
1. Set a breakpoint to the beginning of the program.
2. Reproduce the failure. (If the program doesn't pause, then either the line with the breakpoint doesn't execute, the debugger is broken, or you didn't reproduce the failure).
3. Step forward one instruction at a time until the program deviates from intended behavior, monitoring program state and control flow after each step.
4. This step that deviates or one of the previous steps caused the failure.
This process, while straightforward, is the slowest, requiring a long, vigilant search. A more efficient scientific strategy can leverage your knowledge of the program by guiding the search with hypotheses you generate<gilmore91>:
1. Observe the failure
2. Form a hypothesis about what caused the failure
3. Identify ways of observing program behavior to test your hypothesis.
4. Analyzing the data from your observations
5. If you've identified the defect, move on to the repair phase; if not, return to step 2.
The problems with the strategy above are numerous. First, what if you can't generate a hypothesis? What if you can, but testing the hypothesis is slow or impossible? You could spend _hours_ generating hypotheses that are completely off-base, effectively analyzing all of your code and it's executions before finding the defect.
Another strategy is working backwards<ko08>.
* Observe failure
* Identify the line of code that caused the failing output
* Identify the lines of code that caused the line of code in step 2 and any data used on the line in step 2
* Repeat three recursively, analyzing all lines of code for defects along the chain of causality
1. Observe the failure
2. Identify the line of code that caused the failing output
3. Identify the lines of code that caused the line of code in step 2 and any data used on the line in step 2
4. Repeat three recursively, analyzing all lines of code for defects along the upstream chain of causality until finding the defect.
The nice thing about this strategy is that you're _guaranteed_ to find the defect if you can accurately identify the causes of each line of code contributing to the failure. It still requires you to analyze each line of code and potentially execute to it in order to inspect what might be wrong, but it requires potentially less work than guessing. My dissertation work investigated how to automate this strategy, allowing you to simply click on the fault output and then immediately see all upstream causes of it<ko08>.
This strategy _guarantees_ that you will find the defect if you systematically check all of the upstream causes of the failure. It still requires you to analyze each line of code and potentially execute to it in order to inspect what might be wrong, but it requires potentially less work than guessing. As we discussed in the [Comprehension|comprenshion] chapter, tools can automate some of this process<ko08>.
Yet another strategy called _delta debugging_ is to compare successful and failing executions of the program<zeller02>.
* Identify a successful set of inputs
* Identify a failing set of inputs
* Compare the differences in state from the successful and failing executions
* Identify a change to input that minimizes the differences in states between the two executions
* Variables and values that are different in these two executions contain the defect
1. Identify a successful set of inputs and minimize them
2. Identify a failing set of inputs and minimie them
3. Compare the differences in program state from the successful and failing executions during execution
4. Identify a change to input that minimizes the differences in states between the two executions
5. Variables their values that are different in these two executions contain the defect
This is a powerful strategy, but only when you have successful inputs and when you can automate comparing runs and identifying changes to inputs.
One of the simplest strategies is to work forward:
* Execute the program with the reproduction steps
* Step forward one instruction at a time until the program deviates from intended behavior
* This step that deviates or one of the previous steps caused the failure
This strategy is easy to follow, but can take a _long_ time because there are so many instructions that can execute.
For particularly complex software, it can sometimes be necessary to debug with the help of teammates, helping to generate hypotheses, identify more effective search strategies, or rule out the influence of particular components in a bug<aranda09>.
For particularly complex software, it can sometimes be necessary to debug with the help of teammates, helping to generate hypotheses, identify more effective search strategies, or rule out the influence of particular components in a bug<aranda09>. In fact, some work shows that following systematic debugging strategies can make novice developers just as effective as experienced ones<latoza20>, but that many novices struggle to use them, either because they are overconfident, they struggle to self-regulate their work, or because they lack sufficient prior knowledge to execute the strategies, and have to revert to simpler ones that require less knowledge.
Ultimately, all of these strategies are essentially search algorithms, seeking the events that occurred while a program executed with a particular set of inputs that caused its output to be incorrect. Because programs execution millions and potentially billions of instructions, these strategies are necessary to reduce the scope of your search. This is where debugging *tools* come in: if you can find a tool that supports an effective strategy, then your work to search through those millions and billions of instructions will be greatly accelerated. This might be a print statement, a breakpoint debugger, a performance profiler, or one of the many advanced debugging tools beginning to emerge from research.
# Fixing defects
Once you've found the defect, what do you do? It turns out that there are usually many ways to repair a defect. How professional developers fix defects depends a lot on the circumstances: if they're near a release, they may not even fix it if it's too risky; if there's no pressure, and the fix requires major changes, they may refactor or even redesign the program to prevent the failure<murphyhill13>. This can be a delicate, risky process: in one study of open source operating systems bug fixes, 27% of the incorrect fixes were made by developers who had never read the source code files they changed, suggesting that key to correct fixes is a deep comprehension of exactly how the defective code is intended to behave<yin11>.
Once you've found the defect, what do you do? It turns out that there are usually many ways to repair a defect. How developers fix defects depends a lot on the circumstances: if they're near a release, they may not even fix it if it's too risky; if there's no pressure, and the fix requires major changes, they may refactor or even redesign the program to prevent the failure<murphyhill13>. This can be a delicate, risky process: in one study of open source operating systems bug fixes, 27% of the incorrect fixes were made by developers who had never read the source code files they changed, suggesting that key to correct fixes is a deep comprehension of exactly how the defective code is intended to behave<yin11>.
This risks suggest the importance of *impact analysis*, the activity of systematically and precisely analyzing the consequences of some proposed fix. This can involve analyzing dependencies that are affected by a bug fix, re-running manual and automated tests, and perhaps even running users tests to ensure that the way in which you fixed a bug does not inadvertently introduce problems with usability or workflow. Debugging is therefore like surgery: slow, methodical, purposeful, and risk-averse.
This risks suggest the importance of *impact analysis*<arnold96>, the activity of systematically and precisely analyzing the consequences of some proposed fix. This can involve analyzing dependencies that are affected by a bug fix, re-running manual and automated tests, and perhaps even running users tests to ensure that the way in which you fixed a bug does not inadvertently introduce problems with usability or workflow. Debugging is therefore like surgery: slow, methodical, purposeful, and risk-averse.