Debugging: A Guide to Codes That Fly

Debugging 101 FunctionUp

The often asked question with debugging is whether debugging is an art or there is a scientific, systematic approach to it. Is it more intuitive and creative or is there a systematic approach to it? 

Well to put it simply, it is a balance of both and perhaps it is for this reason that there is a dearth of literature about it. 

There are definite approaches to debugging and knowing them and the tools that can be used to debug can be a life changer for novice coders and practitioners. While the professional coders and programmers may be aware of most of them, it proves beneficial to revisit the philosophy, approaches, and tools to identify the ones that work the best for the job they are doing. 

Let’s begin by understanding what debugging is?

Debugging is the process of identifying and removing errors from computer hardware or software.

Quoting the authors Norman Maltoff and Peter Jay Salzman from their book “The Art of Debugging with GDB, DDD, and Eclipse”,  

The Fundamental Principle of Confirmation Fixing a buggy program is a process of confirming, one by one, that the many things you believe to be true about the code actually are true. 

When you find that one of your assumptions is not true, you have found a clue to the location (if not the exact nature) of a bug. 

Another way of saying this is: Surprises are good!

When one of the things that you think is true about the program fails to confirm, you are surprised. But it’s a good surprise because this discovery can lead you to the location of a bug.

The core debugging process comprises 4 steps.

Before we get into other processes the first and the most widely used approach is and should be the empirical approach for debugging. 

According to the empirical approach, one observes and experiences a problem or a situation and is not guided by just theory or logic. Which in the case of software means actually observing how it behaves. But to be able to discover the bug this behavior will have to be repeated by the software which brings us to the next part of the discussion. 

Reproduce

Reproducing the problem is important to find the root cause and ultimately the solution. And therefore it becomes the first and most important step of the debugging process. Reproducing can be successfully done only by:

  • Identifying the variants relevant to the bug 
  • Figuring out the required settings they should be at 
  • Discovering a way to achieve those settings.  

Diagnose

Bring out your inner Sherlock with detective glasses and a hat because this step involves using your intellect and understanding of the software to diagnose the bug. Creativity, open-mindedness and methodical investigation will help you investigate the bug.  

There are two approaches to it:

  • Start with a hypothesis and create an experiment to test that hypothesis
  • Start with an observation that doesn’t fit with the current theory and modify the theory or completely replace it 

In most cases, the latter approach is adopted. There is a theory about how software should behave but then is disapproved by the observation of a bug and now the theory needs to be fixed. 

A stepwise approach to that will be:

  1. Observe the software’s behavior and then construct a hypothesis about it
  2. Design an experiment to test the hypothesis
  3. If the hypothesis is disapproved, construct a new one and start over again
  4. If the hypothesis is supported keep experimenting to ascertain the validity with certainty to consider it proven

Other general guidelines are to change one thing at a time and keep a track of the changes being made and ignore nothing. Developers have a “daybook” to keep a log of all the experiments conducted. 

Another good piece of advice is the oft-quoted Occam’s Razor which can be paraphrased as “All other things being equal, the simplest explanation is the best.”

Fix

This stage is more disciplined, structured, and accurate. A fair warning will include minding the entropy. When changes are introduced to a system, it is highly probable that new unexpected problems may emerge. 

Often represented by the formula 

E = I´C / S

Where I = The number of unexpected problems introduced during the last development iteration.

C = The perceived probability that implementing changes to the system now results in a new I´ > 0

S = The scope of the next development iteration.

The first recommended step is to start from a clean slate, i.e. cleaning up in order to differentiate between the various ad hoc changes made during the previous iteration.

The next step includes testing. An automated test framework and an extensive body of unit tests can help at this stage and help in ensuring that the fix addresses the problem and safeguards against regression. 

The proposed sequence could be:

  • Run the tests and ensure by demonstrating that they pass
  • Add a new test or fix the existing test to demonstrate the bug 
  • Fix the bug
  • Demonstrate that the bug is fixed
  • Demonstrate that no regressions are introduced ( none of the previously passed tests fail now)

However, this sequence may go for a toss while solving the actual problem which would involve going back and forth from constructing tests to modifying codes. It is also a good time to go back to the experiments, data files, and anything else created to reproduce and diagnose the problem. 

The tests will have to be performed manually which can be discarded later and there should be a set of regression tests handy too which will detect any regressions happening due to changes being made.

Leveraging source control and getting your code reviewed are the next steps to be considered. 

Reflect

This process is important to figure out what went wrong in the first place to reach the point of “how did it ever work?”

  • Analyzing the root cause gives information regarding at what point did the error arise
  • Identifying the error
  • Ensuring the same problem is not repeated
  • Using refractor codes to improve the incorrect usage
  • Taking feedback from colleagues to modify the process
  • Involving all stakeholders and closing the loop

A brief Introduction to Debugging Tools:

GDB

GDB, the GNU Project debugger, allows you to see what is going on ‘inside’ another program while it executes — or what another program was doing at the moment it crashed.

What Languages does GDB Support?

GDB supports the following languages 

  • Ada
  • Assembly
  • C
  • C++
  • D
  • Fortran
  • Go
  • Objective-C
  • OpenCL
  • Modula-2
  • Pascal
  • Rust

DDD

Not a debugger itself but Data Display Debugger acts as a frontend for several powerful debuggers such as GDB, XDB, and DBX. It allows developers to monitor the running programs along with features like

  • Machine-level debugging
  • Hypertext navigation
  • Debugging remote hosts

A GNU official project can be used without encountering common problems faced by other debuggers.

DDD can be used to debug programs written in C, C++, Java, Fortran, Perl, Python, Modula, Ada, and Linux Bash scripts.

IDE

An IDE is more than just a debugging tool; it integrates an editor, build tool, debugger, and other development aids into one package. However, it is not free, unlike GDB or DDD. The differentiating factor that IDE has are:

  • Multi-platform debugger makes it easy to maintain large-scale systems
  • It can produce assembly code from compiled binary files such as standard ELF executable
  • Can support a wide variety of computing hardware, including the Intel 8086 family, ARM, DEC Alpha & PDP-11, JVM, SPARC, and PowerPC
  • Comes with pre-built support for remote target debugging, code graphing, and flirt(fast library identification & recognition technology)
  • Its plugin-based architecture allows it to be extended in a very short time

With several other options like Ghidra, Nemiver, Valgrind, PurifyPlus, BASHDB, Bugzilla, TotalView, strace, LLDB, Electric Fence, Xdebug, KDbg, dbx, JSwat, Affinic Debugger, Insure++, D.U.M.A , developers are spoilt for choice but the GNU Debugger alongside LLDB and IDA are the most powerful ones. 

However, one may also choose from a number of tools built for solving language or system-specific programming problems too.

Join our Advanced Backend Development boot camp and crack a high-paying tech job in just 4 months

FunctionUp
We help you break the glass ceiling which blocks entry to tech. We are a team of IIT / ISB alumni who are passionate about solving the key problem of starting a career in tech. A lot of smart and handworking graduates find it difficult to crack high paying tech jobs either because of their background and their college not being able to help them get interviews. Hence, we started this placement bootcamp that hand holds people from skilling till employment.

Leave a Reply

Your email address will not be published. Required fields are marked *