Debugging techniques

From Inforail
Jump to: navigation, search

Developing software is much more pleasant if you've made sure that debugging is easy. Without that, each test requires a lot of effort, so people are likely to be sloppy or skip that part entirely, thus:

  • there are more bugs
  • extending the system is difficult, because verifying whether the new features didn't break the old ones takes time

Ultimately, the psychological factor plays a very important role, you end up hating the system you maintain. That's a bad thing - you failed to take care of the problem, so the problem took care of you.

To speed up the development process and make the whole experience enjoyable, you have to ensure your debugging environment is ergonomic. This page summarizes various debugging techniques you can rely on to make life easier.


Todo

  • make sure the debug build works
  • using relative paths
  • use version control, for zebra's sake!

Language specific

Python

  • if __name__ == "__main__" can be used in modules used by other files. When the file is used as a standalone program, it will go inside the if and execute the instructions in it. Those instructions can be
    • examples of function calls - such that someone can see how the functions should be used, which output is produced by certain input, etc
    • tests - when you make a change, you can see how the function works, without involving external modules, which would normally call the function from another place
  • import traceback and call traceback.format_exc() inside an exception handler to get a nice call stack (to be used in production systems that need to notify an admin about errors)

Task specific

Windows

Kernel mode drivers
  • That's pretty deep, so MessageBoxes are not going to work
  • Logging is the way to go, but if you need more output, you'll have to rebuild the module, reboot the OS, etc
  • Connecting to a target system in debug mode via a COM port may be a better idea, as you can go through the code step by step
  • VisualDDK - a way to develop and debug drivers using a fully blown Visual Studio IDE
Strong authentication in Windows

This applies to

  • Credential providers in Vista and above
  • GINA.dll in NT/2000/XP/2003
  • MessageBoxes work
  • So does logging
  • But if you want to go through it step by step, use watches - you'll need to connect to a remote system
  • You can also start your debugger in the secure desktop, no need to use serial cables, other computers (real or virtual)

Apache

  • Make every module run as a standalone module and test them separately
    • Use input replicators
  • Otherwise you may have to use modules like Xdebug, which are not easy to set up, or may require privileges you don't have

Generic methods

  • MessageBox debug
  • printf debug
  • Printing to places other than stdout
    • OutputDebugString
    • DbgPrint
    • KdPrint
  • How to debug a DLL
  • Connecting to an OS booted in debug-mode via a serial cable and using WinDbg
  • IFEO - Image file execution options, how to debug a process that crashes too fast to allow you to attach to it.

Best practices

  • Logging
  • Collect meta-data about the error, to make it easy for the programmer to replicate the problem in the given module, without having to replicate the entire infrastructure.
    • Text of the exception, error message
    • File name and line which produces the error
    • Input data
    • Call stack
  • Automatic notifications for errors that occur in [relatively] autonomous systems
    • Email - for stuff that contains a lot of metadata
    • SMS or Gtalk - when speed of response matters the most, and not much data is included in the error report
  • Purposeful errors - force the compiler to produce an error, so the programmer will definitely look at the line you want and read a comment, which you naively expected them to be aware of after RTFMing
    • GUIDs in Wix
    • Smartkey magic number for device handlers
  • Test tools
    • Input replicators facilitate simulating the context in which a problem occurs. For example, if you have to test a web-based system that handles a POST request, it is a good idea to have a function that will do the POST for you, so you don't have to start a browser, go to a URL and set a bunch of checkboxes and have the browser perform the POST.
    • Predefined input removes the need to type text by hand or enter weird characters




Examples

Using a main function in Python

#Normally fixurl is called from within other modules, but if you want 
#to test it separately, just run this file independently and see it in action.
#It contains examples of function calls with wicked input data, so you won't 
#have to type these characters by hand

def FixUrl(url):
	"""Takes a URL that contains Unicode characters and converts it into ASCII"""
	pass

if __name__ == "__main__":
	print FixUrl (u"Hugo Kołłątaja")
	print FixUrl('http://\xe2\x9e\xa1.ws/\xe2\x99\xa5')
	print FixUrl('http://\xe2\x9e\xa1.ws/\xe2\x99\xa5/%2F')
	print FixUrl(u'http://Åsa:abc123@➡.ws:81/admin')

Purposeful errors

This one will compile just fine, it is unlikely that the comment will be noticed

//WARNING!! you must change this to the number of schmidgets in the vorgaar!
//The number cannot be computed dynamically, so it needs to be hard-coded
#define MAGIC 45


You can be sure the programmer will read this comment, because the compiler will point them to the exact line where something is wrong. In this case, we've used haha - which is not a valid keyword.

//WARNING!! you must change this to the number of schmidgets in the vorgaar!
//The number cannot be computed dynamically, so it needs to be hard-coded
haha
#define MAGIC 45

You can also do this only for debug builds

//WARNING!! you must change this to the number of schmidgets in the vorgaar!
//The number cannot be computed dynamically, so it needs to be hard-coded
#ifdef DEBUG
haha
#endif
#define MAGIC 45

Only use this as a last resort. In an ideal case, the number would be determined dynamically.