1 / 65

Writing Solid Code

Writing Solid Code. Zubin Huang. Reference. Writing Solid Code, Steve Maguire, Microsoft Press, 1993, ISBN 1-55615-551-4. Mission. BUGS, BUGS and BUGS … Who is responsible for the BUGGY Program Find and fixing bugs was DEVELOPER ’s responsibility Zero defects? Can we success? How?.

esme
Télécharger la présentation

Writing Solid Code

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Writing Solid Code Zubin Huang

  2. Reference • Writing Solid Code, Steve Maguire, Microsoft Press, 1993, ISBN 1-55615-551-4

  3. Mission • BUGS, BUGS and BUGS … • Who is responsible for the BUGGY Program • Find and fixing bugs was DEVELOPER’s responsibility • Zero defects? Can we success? How?

  4. Feasibility “I don’t expect programmers to write flawless code every time they sit at the keyboard, but I do believe that it’s possible to keep bugs out of the master sources.” -- Steve Maguire

  5. Every Bug Found • How could I have automatically detected this bug? • How could I have prevented this bug? Better Testing? NO!

  6. Use tools to find bugs • Enable all optional compiler warnings • Use Tools (Purify, BoundCheck, lint, mportal)to catch bugs that your compiler may miss • If you have unit tests, use them

  7. Assert Yourself • Undefined vs Steer Clear • Use assertions to catch illegal uses of undefined behavior • Get bugs in assertion? • Should fix the program? • Fix the assertion? • Document unclear assertion.

  8. Assert Yourself … /* Check both outputs for validity */ ASSERT( pcRet == pcDisasmAlt(pc, &opc)); ASSERT( compare_opc(popcRet, &opc) ==SAME); • DO NOT disturb the surrounding code in an unexpected way in ASSERT

  9. FORTIFY YOUR SUBSYSTEMS How are programmers going to misuse this subsystem, and how can I detect these problems automatically

  10. There’s No Need to Know • Suppose a new programmer creates a lost memory block? • The checks kick in • New Programmer (NP) not know what a lost memory block is • NP not need to know that for the checks to work • NP can track down the failure • Guideline: Strive to implement transparent integrity checks

  11. You Don’t Ship the Debug Version • You use the debug version to find bugs • You use the release version to please customers

  12. • MS result: • the debug version of your application – packed with assertions and subsystem tests – should run at about half the speed of your ship version. • Guideline: Don’t apply ship version constraints to the debug version. Trade size and speed for error detection.

  13. Step Through Your Code

  14. Best Way to Find BUG • Execute the code and somehow spot them • By EYE • By using automated tests such as assertions and subsystem integrity checks • Step though all NEW or MODIFIED code to watch it execute

  15. Gain Confidence in Your Code • RPG • A went to B office and said: “I think I found a bug in the code you just finished” • B load the code into an editor and A show the problem. B surprised. • B: “You’re right; the code is definitely wrong. I wonder why my test cannot catch the bug.” • A: “How exactly did you test the code?” • B explained the test to A, and it seems as if it should have caught the bug. • A: “Let’s set a breakpoint on the function and step through the code” • B set the breakpoint and hit “Run”, the test ran to completion. NEVER hit the breakpoint!

  16. Best Way • The best way to test your code is by stepping through it and taking a microscopic look at the intermediate result. • Step though every Code path • Focus on Data flow • Are you missing something

  17. A Fork in Your Code • Step through every instruction of your code at least once to verify that it works correctly. • Guideline: Step through every code path.

  18. Data Flow, the Lifeblood of Code • As you step through code, focus on data flow • Overflow and underflow bugs • Data conversion bugs • Off-by-one bugs • NULL pointer bugs • Using garbage memory (0xA3 bugs) • Assignment bugs in which you’ve used = instead of == • Precedence bugs • Logic bugs

  19. Are You Missing Something • Anytime you step to a compound conditional that use && and || operators, scan the list to verify that they are “spelled” correctly. • Use the debugger to display the result for each side of the expression • Guideline: Source code debuggers can hide execution details. Step through critical code at the instruction level.

  20. Candy-Machine Interfaces

  21. getchar()? • Two samples: 1. char c; c = getchar(); if (c == EOF) … 2. /*strdup – allocate a duplicate of a string */ char *strdup(char *str) { char *strNew; strNew = (char *)malloc(strlen(str)+1); strcpy(strNew, str); return (strNew); }

  22. What’s the problem? • The problem with these functions is not that they return errors, but that they bury those errors in normal return values where it’s easy for programmers to overlook them. • The reality is that combining exclusive outputs into a single return value is a carryover from assembly language, where you have a limited number of machine registers to manipulate and pass data • Guideline: Make it hard to ignore error conditions. Don’t bury error codes in return values.

  23. Read Between The Lines • Examine the interfaces from the point of view of the caller. • Ex: #define BASE10 1 #define BASE16 0 UnsignedToStr(u, str, BASE10); UnsignedToStr(u, str, BASE16); void UnsignedToStr(unsigned u, char *str, flag fDecimal);  void UnsignedToStr(unsigned u, char *str, unsigned base); • Guideline: Make the code intelligible at the point of call; Avoid boolean arguments.

  24. Performance, Memory Footprint & Risky • If you were to put a programmer at the top of a cliff and give him/her a rope and a hang glider, how do you think he’d get to the bottom? • Climb down the rope • Glide to the bottom • Jump to the bottom • Why programmers ignore risk? • They blindly assume that no matter how they implement their code, they’re going to implement it without bugs. • They have never been taught to ask question such as, • “How risky is this design?” • “How risky is this implementation?” • “Is there a safer way to write this expression?” • “Is it even possible to test this design?”

  25. How Long is a long for(int i=0; i<MAX_RANGE; i++) { … } • Guideline: Use well-defined data types. • Guideline: Always ask, “Can this variable or expression over- or underflow”

  26. Function Just “Doing Their Thing” • Don’t accept special purpose arguments such as the NULL pointer. • Implement your design, not something that approximates it. • Strive to make every function perform its task exactly one time.

  27. High Risk, No Return int function(int i) { if (i ==1) return 0;} int j = function(2); • Guideline: always return.

  28. Cost • Using bitwise operators to multiply, divide, and mod values by a power of 2? • Guideline: Avoid risky language idioms. • Don’t overestimate the cost!

  29. Inconsistency • Don’t mix operator types if you don’t have to. • If you must mix operator types, use parentheses to isolate those operations. • Don’t look up the precedence, if you have to, the code is not obvious; make it obvious.

  30. Failures • Don’t call functions that return errors. • If you repeatedly handle the same error condition throughout your program, isolate that error handling • How do you handle the error? Do you alert the user? Ignore the request and silently keep the old one?

  31. Example if (fResizeMemory(&pwnd->strWndTitle, strlen(strNewTitle)+1)) strcpy(pwn->strWndTitle, strNewTitle); else /* Unable to allocate space for the window title … */ void RenameWindow(window *pwnd, char *strNewTitle) { ASSERT(fValidWindow(pwnd)); ASSERT(strNewTitle != NULL); ASSERT(fValidPointer(pwnd->strWndTitle, sizeMaxWndTitle)); strcpy(pwnd->strWndTitle, strNewTitle); }

  32. The Need for Speed void *memchr(void *pv, unsigned char ch, size_t size) { unsigned char *pch = (unsigned char *)pv; while (size-- > 0) { if (*pch == ch) return (pch); pch++; } return (NULL); } make it faster?

  33. A “Faster” Version? void *memchr(void *pv, unsigned char ch, size_t size) { unsigned char *pch = (unsigned char *)pv; unsigned char *pchPlant; unsigned char chSave; /* pchPlant points to the first character following the memory run that memchr is searching. Plant ch at that location so that memchr is guaranteed to find ch even if it is not in the run. */ pchPlant = pch + size; chSave = *pchPlant; /*save the original char */ *pchPlant = ch; while (*pch != ch) pch++; *pchPlant = chSave; /*Now put the original back */ return ((pch == pchPlant) ? NULL : pch) ; } Clever?

  34. The bugs? • This version of memchr has more problems than Batman has gadgets: • pchPlant points to read-only memory • pchPlant points to memory-mapped I/O • pchPlant points to the last size byte of RAM • pchPlant points to data shared by concurrent processes • Guideline: Don’t reference memory that you don’t own.

  35. Take Only What You Need /* UnsToStr – convert an unsigned value to a string */ void UnsToStr(unsigned u, char *str) { char *strStart = str; do *str++ = (u % 10) + ‘0’; while (( u /= 10) > 0); *str = ‘\0’; ReverseStr(strStart); } Wastful calling ReverseStr()?

  36. Another version void UnsToStr(unsigned u, char *str) { char *pch; /* u out of range? Use UlongToStr … */ ASSERT(u <= 65535); /* Store the digits in str from back to front. Start storing the digits deep enough into the string to hold the largest possible value for u */ pch = &str[5]; *pch = ‘\0’; do *--pch = (u % 10) + ‘0’; while (( u /= 10 ) > 0 ); strcpy(str, pch); }

  37. How about this version? void UnsToStr(unsigned u, char *str) { char strDigits[6]; /* conversion buffer */ char *pch; /* u out of range? Use UlongToStr … */ ASSERT(u <= 65535); /* Store the digits in str from back to front. */ pch = &strDigits[5]; *pch = ‘\0’; do *--pch = (u % 10) + ‘0’; while (( u /= 10 ) > 0 ); strcpy(str, pch); } • Guideline: Don’t use output memory as workspace buffers

  38. Yet Another Version char *strFromUns(unsigned u) { static char *strDigits = “??????”; /*5 chars + ‘\0’ */ char *pch; ASSERT( u <= 65535 ); /* store the digits in strDigits from back to front */ pch = &pchDigits[5]; ASSERT(*pch == ‘\0’); do *--pch = (u % 10) + ‘0’; while (( u /= 10 ) > 0) ; return (pch); }

  39. Keep Private Things to Yourself • Don’t pass data in global buffers unless you absolutely have to. • Same rule apply to static memory. • Guideline: Don’t pass data in static (or global) memory.

  40. Functional Leeches • Passing data in public buffers is risky, but you can get away with it if you’re careful and a bit lucky. But writing parasite functions that rely on the internal workings of other function is not only risky, but also irresponsible: if you change the host function, you kill the parasite. • Guideline: Don’t write parasite functions.

  41. Example /* CMOVE – move memory using a head-to-head move */ void CMOVE(byte *pbFrom, byte *pbTo, size_t size) { while (size-- > 0) *pbTo++ = *pbFrom++; } /* FILL – fill a range of memory */ void FILL(byte *pb, size_t size, byte b) { if (size > 0) { *pb = b; CMOVE(pb, pb+1, size-1); } }

  42. Size? • There is a correlation between the size of your C code and the size of the corresponding machine code, but that correlation breaks down when you apply it to individual lines of code. • Guideline: Tight C does not guarantee efficient machine code.

  43. void *memmove(void *pvTo, void *pvFrom, size_t size) { byte *pbTo = (byte *)pvTo; byte *pbFrom = (byte *)pvFrom; ((pbTo>pbFrom)?tailmove:headmove)(pbTo, pbFrom, size); return (pvTo); } No Hoity-Toity Programming void *memmove(void *pvTo, void *pvFrom, size_t size) { byte *pbTo = (byte *)pvTo; byte *pbFrom = (byte *)pvFrom; if (pbTo>pbFrom) tailmove(pbTo, pbFrom, size); else headmove(pbTo, pbFrom, size); } • Guideline: Write code for the “average” programmer

  44. The Rest Is Attitude

  45. For My Next Trick, Disappearing Bugs • 3 reasons bugs disappear: • The bug report was wrong • The bug has been fixed by another programmer • The bug still exists but isn’t apparent • Professional programmer: it is your job to determine which of the 3 cases described my disappearing bug and to act accordingly. • Guideline: Bugs don’t just “go away”.

  46. Save Time • You don’t save time by fixing bugs late in the product cycle. In fact, you lose time because it’s often harder to fix bugs in code you wrote a year ago than you wrote days ago. • Fixing bugs “as you go” provides damage control because the earlier you learn of your mistakes, the less likely you are to repeat them. • Bugs are a form of negative feedback that keep fast but sloppy programmers in check. • By keeping the bug count near zero, you have a much easier time predicting when you’ll finish the product. • Guideline: Don’t fix bugs later, fix them now.

  47. Bug-Doctor to the Rescue! • Like a doctor, programmers are sometimes so busy “healing” bugs that they never stop to figure out what’s causing them. … if (pb == NULL) return (FALSE); … • Guideline: Fix the cause, not the symptom.

  48. Are You a Code Meddler? char chGetNext(void) { int ch; /*ch *must* be an int */ ch = getchar(); return (chRemapChar(ch)); } char chGetNext(void) { return (chRemapChar(getchar()); } • Guideline: Don’t clean up code unless the clean-up is critical to the product’s success.

  49. “Cool” Feature Cool? • Don’t write (or change) code if you don’t have to. • Put “cool” features into cold storage. • Guideline: Don’t implement non-strategic features.

  50. Free Lunch? • “Free” features are another source of unnecessary bugs. • They fall out of existing designs with little or no effort. • Almost never critical to the success of the product. • Fallacy: free features may not cost the programmer much, but there is more to a feature than code. Somebody has to write document for the feature. Somebody has to test the feature. And of course somebody has to fix any bugs that show up with the feature. • Guideline: There are no free lunch.

More Related