[C#] C# Debugging

Date:     Updated:

Categories:

Tags:

📋 This is my note-taking from what I learned in the class “Programming 1 - COMP 100-002”


Objectives

  1. What are bugs and how do you get them?
  2. Why do you need to debug?
  3. Types of errors
    • Syntax
    • Semantic
    • Runtime → Reproducible & Non-reproducible
    • Implementation
  4. Third party debuggers
  5. Test Driven Development
  6. Profilers
  7. Summary


1. What are bugs and how do you get them?

  • Anything that prevents the system from functioning as it should.
    • Code that does not conform to the language rules will not compile.
    • Incorrectly implemented code will do the task correctly.
  • These may occur anything in the software development life cycle.
    • During coding (easiest to find and fix)
    • At compilation (fairly easy to find and fix)
    • During execution (most difficult to find and fix)
  • The IDE goes a long way in making the coding experience a better one.
  • The runtime can identify suspected statement during execution.


2. Why do you have to debug?

  • No one is perfect, you will make mistakes.
  • It is probably impossible to have a reasonable complex program completely free of bugs.
  • We want to reduce undesirable artifacts during the executable of the program.
  • In some non-critical systems, some amount of deviations may be tolerated.
    • Games, direction findings in the malls, and so on.
  • In high-risk systems, even the tiniest error can have dire outcomes.
    • Radiation therapy, autonomous vehicles, air traffic control.
  • Neither high-risk or non-critical.
    • Threat detection, inventory system, flight reservation.

Preventing bugs

  • Write clean code
    • Go for a simple style rather than a complicated or smart style.
    • Choose meaningful identifiers.
    • Use camel-casing for variable (and pascal-casing for method).
    • Use named constants instead of magic number.
    • One statement per line. → It is ok to declare (and initialize) multiple variables of the same type in one line.
  • The design of a language also helps to decrease the chance of writing buggy code.
    • Dynamically typed scripting language are more prone to bugs.
    • Statically typed compiled languages generally have less bugs.
  • Adding documentation
    • Although C# is a relatively descriptive language, you should include an appropriate amount of documentation in your code.

What is debugging?

  • Is figuring out what is wrong with your code.
    • Implies that you are crystal clear about the problem being solved.
    • You must be able to predict the state of your system at any point in execution.
  • What could be wrong about your code?
    • It does not compile (very easy to fix)
    • It does not produce the intending results → Consistently OR Intermittently (extremely difficult to fix)
  • Sometimes it can as simply as running your code and examine the result.
    • Your if statement is giving the reverse of the intended logic.
    • You are not capturing the boundary condition of your loop.


3. Types of errors

Syntax Errors

  • These are grammatical errors that occurs while coding.
  • They manifest as typos and are the easiest to find and fix (you have been doing this since week 1).
  • The IDE flags these as red squiggly lines. Yellow squiggles are warning and green squiggles are for information.
  • If you hover the mouse over the squiggles, the IDE will tell you what is wrong and even suggest corrections (which are mostly correct).
  • These are the earliest errors you will encounter and the often the simplest to correct. Common errors are:
    • Unbalanced brackets, missing or miss-placed semi-colons, using the assignment operator for comparison, forgetting to delimit string literals by double quotes.
  • Subtly errors like the use of unassigned variable are only slightly more difficult to resolved.

Semantic Errors

  • These are mostly logic errors, some of which can be caught be the compiler.
  • The compiler will give you the line and column number of the suspected error.
  • When you run the program, you don’t get the output that you expect.
    • Expecting a double in integer division.
  • Sometimes the error can be fixed by just examining the output.
    • E.g. one off errors in looping.
    • You can also put print statement to check the value of suspect variable.
  • If you are not able to find the error with the above techniques.
  • You will be to execute your code in single-step mode.

Runtime time Errors

  • These are the most difficulty to track down and fix.
  • These are mainly logic errors.
  • If it is easily reproducible, then it is possible to be located and possibly fixed.
  • If it is not reproducible, or intermittent, then it is very hard to locate and fix.
  • A common technique is use print statement to try to zoom in on the error source. You might have to modify your code to print finer level of details.
  • If this does not work, then setting a breakpoint before the suspect code statement and running the code in single step execution mode.

Implementation Errors

  • When a feature or functionality is not implement correctly.
  • The only fix is a new release of the software.
  • Relying of a service or API that has changed or is no longer available.
    • Payment service
    • Database
  • This are relatively easy to detect. Fixing might or might not be difficult.


4. Third party debuggers


Single-step execution mode

  • This allows you to process a line at a time.
  • You may add a breakpoint (or a number of points). The program will run until it reaches a breakpoint and then it will enter single-step mode.
  • You may inspect the value of a variable simply by hovering the mouse over the variable.
  • You may also set up a set of variables (a watch list), that you may follow while the code in executing.
  • You also have the option of stepping over a block of code or entering the block.
  • If you are in a code block you may also step out of the block.
  • Looping errors are very likely to be detected using this technique.


Tasks

  • Set a break point -> Right click the line and select Insert Breakpoint
  • Run the program in Debug-mode -> F5
  • This will bring up the Debug Toolbar (you can also use the following keyboard shortcuts)
  • Stop Debugging -> Shift+F5
  • Restart -> Ctrl+Shift+F5
  • Show Next Statement -> Alt + Num *
  • Step Into -> F11
  • Step Over -> F10
  • Step Out -> Shift+F11
  • Step Forward -> Alt + ]
  • Step Backward -> Alt + [


5. Test Driven Development

  • TDD is an attractive paradigm that is used in modern software development.
  • Essentially to write a test case for each feature of your software.
  • Then you write code to remove all the errors one at a time.
  • When all the errors are remove, your software is ready to be shipped.
  • This topic will be further explored in Programming 2 and in other courses in later semester.


6. Profilers

  • A profiler is a tool that monitors the execution of another application.
  • The foremost objective is the measurement of speed, it can be used to gather information about the following:
    • Loading and unloading of assemblies, modules, classes, or methods.
    • Thread creation and destruction.
    • Transitions between unmanaged and managed code.
    • Jit compilation.
    • Memory heap and garbage collection.
  • It can be done in sampling and non-sampling mode.
  • Although, your IDE comes with a profiler, at this stage of your learning, we will not focus on speed or efficiency of your code, but on clarity and robustness.


7. Summary

  • Debugging is a very important part of the production of a software system.
  • A developer’s philosophy should be to write simple and clean code.
  • This will mitigate bugs later
  • You are likely go through the process three times:
  • Before compilation
    • Generally easy to fix.
  • During compilation
    • Reasonable difficult to fix.
  • After compilation
    • Very difficult to fix.


Error to Fixed

Logic error

//following code has a logic error

for(int c = 0; c <= 100; c++)
{
  Console.WriteLine($"{c,5:F} {9 / 5 * c + 32,6:F}");
}
---------------------------------------------------------------

//code is fixed

for(int c = 0; c <= 100; c++)
{
  Console.WriteLine($"{c,5:F} {9.0 / 5 * c + 32,6:F}");
}

Decomposing a complex math operation

//following code has a logic error. (trying to print y = x^2 – x - 6) cols: | x | x^2 | –x | -6 | y |

for (double x= 0; x < 1; x += 0.1)
{
  Console.WriteLine($"{x,4:F1} {x*x,6:F1} {-x,6:F1} {-6,6:F1} {x*x + x - 6,6:F1}");
}
---------------------------------------------------------------

//code is fixed

for (double x= 0; x < 1; x += 0.1)
{
  double first = x * x; //this can be verified in the debugger
  double second = -x; //this can be verified in the debugger
  double y = first + second - 6; //this can be verified in the debugger

  Console.WriteLine($"{x,4:F1} {first,6:F1} {second,6:F1} {-6,6:F1} {y,6:F1}");
}

Un-initialize variable

// Let assume that there are only two type of student: International and Domestic

double fee;

if (studentType == "domestic")
{
  fee = 349.99;
}
if (studentType == "international")
{
  fee = 1395.99;
}

Console.WriteLine($"For {studentType} students the fee is {fee:C}!");
---------------------------------------------------------------

// Now fee will always have a value

// alternately you may initialize fee to a sensible value
// Let assume that there are only two type of student: International and Domestic

double fee;

if (studentType == "domestic")
{
  fee = 349.99;
}
else
{
  fee = 1395.99;
}

Console.WriteLine($"For {studentType} students the fee is {fee:C}!");




Back to Top

See other articles in Category CS

Leave a comment