6.005 Elements of Software Construction Fall 2008

6.005 Elements of Software Construction Fall 2008

MIT OpenCourseWare http://ocw.mit.edu 6.005 Elements of Software Construction Fall 2008 For information about citing these materials or our Terms of Use, visit: http://ocw.mit.edu/terms. 10/15/2008 Today’s Topics how to avoid debugging ¾assertions ¾code reviews how to do it when you have to ¾reducing test cases ¾hypothesis-driven debugging ¾binary search Debugging very hard bugs Rob Miller ¾Heisenbugs Fall 2008 © Robert Miller 2008 © Robert Miller 2008 Defensive Programming First Defense: Impossible By Design first defense against bugs is to make them impossible in the language ¾Java makes buffer overflow bugs impossible ¾automatic array bounds checking make buffer overflow bugs impossible second defense against bugs is to not make them ¾static typing eliminates many runtime type errors ¾correctness: get things riihght first time in the protocols/libraries/modules third defense is to make bugs easy to find ¾TCP/IP guarantees that data is not reordered ¾local visibility of errors: if things fail, we'd rather they fail loudly and ¾BigInteger guarantees that there will be no overflow immediately – e.g. with assertions in self-imposed conventions fourth defense is extensive testing ¾immutable objects can be passed around and shared without fear ¾uncover as many bugs as possible ¾caution: you have to keep the discipline last resort is dbidebugging • get the language to hel p you as much as possible , e.g. with pritivate and final ¾needed when effect of bug is distant from cause © Robert Miller 2008 © Robert Miller 2008 1 10/15/2008 Second Defense: Correctness Third Defense: Immediate Visibility get things right the first time if we can't prevent bugs, we can try to localize them to ¾don’t code before you think! Think before you code. a small part of the program • do your thinking in design; use a pattern to map that design to code ¾fail fast: the earlier a problem is observed, the easier it is to fix especially true when debugging is going to be hard ¾assertions: catch bugs early, before failure has a chance to contaminate (and be obscured by) further computation ¾concurrency • in Java: assert boolean-expression simplicity is key • note that you must enable assertions with -ea ¾modularity ¾unit testing: when you test a module in isolation, you can be confident that • divide program into chunks that are easy to understand any bug you find is in that unit (or in the test driver) • use abstract data types with well-defined interfaces ¾regression testing: run tests as often as possible when changing code. • avoid rep exposure • if a test fails, the bug is probably in the code you just changed ¾specification when localized to a single method or small module, • write specs for all modules, so that an explicit, well-defined contract bugs can be found simply by studying the program text exists between each module and its client © Robert Miller 2008 © Robert Miller 2008 Example: Assertions Code Review /* other eyes looking at the code can find bugs * Returns n!, the number of permutations of n objects. * n must be nonnegative. */ code review public static int fact(int n) { where would ¾carefu l, systematic study of source code by others (not origi nal author) if (n == 0) return 1; assertions be ¾analogous to proofreading an English paper else return n * fact(n-1); usefully added } to this code? ¾look for bugs, poor style, design problems, etc. ¾formal inspection: several people read code separately, then meet to /* discuss it * Returns (n choose k), the number of distinct subsets ¾lightweight methods: over-the-shoulder walkthrough, or by email * of size k in a set of size n. * Requires 0 <= k <= n. ¾manyygpq dev groups require a code review before commit */ code review complements other techniques public static int combinations(int n, int k) { ¾code reviews can find many bugs cheaply return fact(n) / (fact(k) * fact(n-k)); } ¾also test the understandability and maintainability of the code ¾three proven techniques for reducing bugs: reasoning, code reviews, testing © Robert Miller 2008 © Robert Miller 2008 2 10/15/2008 Let’s Review Some Code How to Debug public class PigLatin { 1) reproduce the bug with a small test case static String[] words; ¾find a small, repeatable test case that produces the failure (may take effort, public static String toPigLatin(String s) { but helps clarify the bug, and also gives you something for regression) words = s.split(" "); ¾don't move on to next step until you have a repeatable test String result = ""; 2) find the cause for (int i = 0; i <= words.length; ++i) { piggify(i); ¾narrow down location and proximate cause result += words[i]; ¾study the data / hypothesize / experiment / repeat } ¾may change code to get more information return result; } ¾don't move on to next step until you understand the cause public static void piggify(int i) { 3) fix the bug if (words[i].startsWith("a") || words[i].startsWith("e") || ...) { ¾is it a simple typo, or is it a design flaw? does it occur elsewhere? words[i] += "yay"; } else { 4) add test case to regression tests words[i] = words[i].substring(1); ¾then run regression tests to ensure that the bug appears to be fixed, and words[i] += words[i].charAt(0) + "ay"; no new bugs have been introduced by the fix } } © Robert Miller 2008 © Robert Miller 2008 } Reducing to a Simple Test Case Example find simplest input that will provoke bug /** * Returns true if and only if s contains t as a substring, ¾usually not the input that originally revealed existence of the bug * e.g. contains("hello world", "world") == true. ¾start with data that revealed bug */ ¾keep paring it down (binary search can help) public static boolean contains(String s, String t) { ... } ¾often leads directly to an understanding of the cause same idea is useful at many levels of a system ¾a user discovers that ¾method arguments contains("Life is wonderful! I am so very very happy all the time“, "very happy") ¾input files incorrectly returns false ¾keystrokes and mouse clicks in a GUI wronggpp approach: ¾try to trace the execution of contains() for this test case right approach: ¾first try to reduce the size of the test case ¾even better: bracket the bug with a test case that fails and similar test cases that succeed © Robert Miller 2008 © Robert Miller 2008 3 10/15/2008 Code for contains() Finding the Cause /** exploit modularity * Returns true if and only if s contains t as a substring, * e.g. contains("hello world", "world") == true. ¾start with everything, take away pieces until bug goes */ ¾start with nothing, add pieces back in until bug appears public static boolean contains(String s, String t) { take advantage of modular reasoning search: for (int i = 0; i < s.length(); ++i) { ¾trace through program, viewing intermediate results for (int j = 0; j < t.length(); ++j, ++i) { ¾insert assertions targeted at the bug if (s.charAt(i) != t.charAt(j)) continue search; ¾design all data structures to be printable (i.e., implement toString()) } ¾println is a surprisingly useful and universal tool return true; } • in large systems, use a logging infrastructure instead of println return false; use binary search to speed things up } ¾bug happens somewhere between first and last statement ¾so do binary search on the ordered set of statements © Robert Miller 2008 © Robert Miller 2008 Example: Finding a Sudoku Bug Regression Testing suppose a Sudoku solver produces the wrong answer whenever you find and fix a bug ¾store the input that elicited the bug 3.... 7.... Sudoku ¾store the correct output ...... ...... solver ¾add it to your test suite ...... ...... why regression tests help ¾helps to populate test suite with good test cases • remember that a test is good if it elicits a bug – and every regression parse make SAT SAT interpret print test did in one version of your code input formula solver assignment output ¾protects against reversions that reintroduce bug ¾the buggy may be an easy error to make ( since it happ ened once already) test-first debugging ¾when a bug arises, immediately write a test case for it that elicits it Note that this isn’t a state machine diagram or a module dependence diagram; it ¾once you find and fix the bug, the test case will pass, and you’ll be done shows data flow, which is often useful for thinking about bugs. © Robert Miller 2008 © Robert Miller 2008 4 10/15/2008 The Ugliest Bugs Example of a heisenbug we’ve had it easy so far public class Bank { int balance; ¾sequential, deterministic programs have repeatable bugs but the real world is not that nice… public Bank(int balance) { ¾tiiiming depen denc ies this.balance = balance; } ¾unpredictable network delays ¾varying processor loads public void deposit(int amount) { ¾concurrency balance += amount; heisenbugs } ¾nondeterministic, hard to reproduce public void withdraw(int amount) { ¾may even disappear when you try to loo k at it with printl n or deb ugger! balance -= amount; one approach } ¾build a lightweight event log (circular buffer) public int getBalance() { ¾log events during execution of program as it runs at speed return balance; ¾when you detect the error, stop program and examine logs } } © Robert Miller 2008 © Robert Miller 2008 Example of a heisenbug Summary // our bank account starts with $100 avoid debugging final Bank account = new Bank(100); // start a bunch of threads ¾it’s not fun and not productive List<Thread> threads = new ArrayList<Thread>(); ¾many of the techniques of this class are designed to save you from bugs for (int i = 0; i < 10; ++i) { approach it systematically Thread t = new Thread(new Runnable() { public void run() { ¾simplify test cases // each thread does a bunch of bank transactions ¾find cause before trying to fix for (int i = 0; i < 10000; ++i) { account.deposit(1); // put a dollar in account.withdraw(1); // take it back out }}}); t.

View Full Text

Details

  • File Type
    pdf
  • Upload Time
    -
  • Content Languages
    English
  • Upload User
    Anonymous/Not logged-in
  • File Pages
    6 Page
  • File Size
    -

Download

Channel Download Status
Express Download Enable

Copyright

We respect the copyrights and intellectual property rights of all users. All uploaded documents are either original works of the uploader or authorized works of the rightful owners.

  • Not to be reproduced or distributed without explicit permission.
  • Not used for commercial purposes outside of approved use cases.
  • Not used to infringe on the rights of the original creators.
  • If you believe any content infringes your copyright, please contact us immediately.

Support

For help with questions, suggestions, or problems, please contact us