[C#] C# Debugging
Categories: CS
Tags: Debugging
📋 This is my note-taking from what I learned in the class “Programming 1 - COMP 100-002”
Objectives
- What are bugs and how do you get them?
- Why do you need to debug?
-
- Types of errors
-
- Syntax
-
- Semantic
-
- Runtime → Reproducible & Non-reproducible
-
- Implementation
- Third party debuggers
- Test Driven Development
- Profilers
- 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
- Debugging with third party tools
- Not Just My Code: How to debug third party libraries without decompiling them
- Debugging Third-Party Code in Visual Studio
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}!");
Leave a comment