📜 ⬆️ ⬇️

11 tips to help save time when debugging programs

Programming is not only when you write code, run it and observe your job with satisfaction, because it often works not at all like we expect! There is a need for effective debugging of applications, and this, it turns out, is a whole art! In this article, I provide my own list of tips that I hope will help you in debugging native code.



The original article was written by Bartlomiej Filipek and published on his blog .

Auxiliary means


Each programmer must know how to start the debugger, set breakpoints, resume code execution, enter and exit functions (using the keyboard!). Here are some simple tips to make debugging easier based on basic techniques.

1. Add error location information to the debugger report (LinePos)


Regardless of your skill level, you will probably still use one of the basic debugging methods: track certain values ​​using functions and macros printf , TRACE , outputDebugString , etc. and check the debugger report. In Visual Studio, you can do one interesting trick that allows you to quickly move from the output window to a specific line of code.
')
To do this, you just need to set the following output format:

"%s(%d): %s", file, line, message 

However, remember that the file and line values ​​must be taken according to their actual position in the source file, and not in the recording function, so you probably need a macro like this:
 #define MY_TRACE(msg, ...) MyTrace(__LINE__, __FILE__, msg, __VA_ARGS__) // usage: MY_TRACE("hello world %d", 5); 

Note that __LINE__ and __FILE__ are standard ANSI-compatible predefined preprocessor macros that can be recognized by the compiler. See Predefined Macros, MSDN .

Also, do not forget to use the OutputDebugString function so that the message is displayed in the output window and not in the console.

Now, when a message appears in the VS output window, you can double-click on it with the mouse to go to the specified file and line. In the same way, you can work with warnings or error messages during compilation. Once I received an error message, but I could not determine its exact location in the code, which is why I lost a lot of time. That time I had to find a string, and this is a long and laborious process. The described way to go to the code by double-clicking allows you to do this in a matter of milliseconds.

By the way, if you have another IDE (not Visual Studio), is there such an opportunity in it? Post in the comments, I would be interested to know.

And here is a simple example of code where you can try the described technique: github.com/fenbf/DebuggingTipsSamples .

Update: as indicated by jgalowicz in his commentary, if you want the file names to be displayed in abbreviated form, make the macro __SHORT_FILE__ : for details of implementation, see the note in his blog . However, in Visual Studio , the / FC compiler option is disabled by default, so usually abbreviated file names are usually displayed (referring only to the solution directory).

2. Get a simple static variable to control one option or another.


 // change while debugging if needed static bool bEnableMyNewFeature = true; 

The Edit And Continue function in Visual studio is a very powerful tool, but you can get by with a simplified, “manual” version of it. It is not so elegant, but it does its job. You just need to have a static variable, with which you will manage this or that program option. To do this, you can use the usual Boolean flag or an integer type variable. During debugging, the value of this variable can be changed, which allows you to examine the operation of the option without restarting or rebuilding the program.

How to change the value during debugging? Go to the watch window for variables ( Watch window ) or just move the cursor over a variable - an edit box should appear in which you can set the desired value.

And do not forget to disable / remove this terrible variable in the final assemblies and commits!

3. Conditional test points


I hope you already use conditional control points, but I still want to briefly tell you about the main ways of using them. As the name implies, the work of the debugger at these points is suspended when a certain condition is met (quite simple).


One little piece of advice: write your own reference point if you need to examine the workings of a certain section of code in more detail.

See the list of expressions used in the conditions: msdn: Expressions in the Debugger

But that is not all.

As you must have noticed in the screenshot, there is another useful condition for the control point: “Hit count”. With it, you can set the number of events after which the control point will be activated. This option is very useful when you need to track some kind of dynamic event or multiple objects.

4. Do not enter functions that you do not want to perform.


How many times have you been forced to enter a string-type constructor in order to quickly get out of it then? Or in a lot of small / library functions, before you could get to the desired method? In most cases, it is a waste of time.

Take a look at this example:

 void MyFunc(const string &one, const string &two) { auto res = one + two; std::cout << res << "\n"; } .... MyFunc("Hello ", "World"); 

And now try to enter the call of the MyFunc () function by pressing Ctrl + F11 . Where does the debugger go? This is what happens to me:


Moreover, if you exit this constructor and re-enter it ... you will enter the constructor of the second parameter. Now imagine what happens if there are several such parameters. You'll lose your mind until you get to the desired method!

As a rule, it is better to filter out such undesirable methods: it is unlikely that the problem lies in the constructor std :: string :)

How to filter out these basic functions? Starting from version 2012, in VS it is enough to edit the default.natstepfilter file.

For ways to filter functions in earlier versions of VS, see here: How to Not Step Into Functions using the Visual C ++ Debugger . There will have to tinker with the registry values.

As an additional incentive, I’ll let you know that Visual Assist has the same option and is much easier to work with. In debug mode, the VA Step Filter window is displayed: just tick or uncheck the appropriate method from the list of found methods. These settings can be applied both globally and locally for this project. Filtering settings in VA are custom and cannot be added to the default.natstepfilter file.

5. Add auxiliary variables for objects in debug mode


The more data the better! Unwanted messages can always be filtered, but you cannot get data from nothing. Depending on the specific goals, it may be useful to create several auxiliary variables in the objects: when debugging, these variables can provide very valuable information or simply facilitate the work process.

For example, when working with tree structures, you will probably often need to check the pNext and pPrev elements . These pointers are usually placed in some kind of base class like TreeNode , and if you are interested in MyTreeNode , which is three levels lower in the class hierarchy, checking pNext every time can be quite tiresome. And what if I update MyTreeNode , adding data from pNext to it ? Then it will not be necessary to pass each time through all levels of the hierarchy to check this element. The described method, however, has one drawback: you need to somehow fix this updated state. The value of pNext can easily change, so you have to implement additional logic to synchronize these changes. However, although this is true for most cases, a less elegant and effective solution will do for debugging tasks.

I will give an example.

I often have to work with tree structures representing text objects. The text object contains strings, and strings contain characters. It was very tiring to check every time what line I am at (i.e. what text it contains), because I had to return the first character of the line, and then watch the value on the pNext pointer to find out the second character and based on these two characters look what kind of string. How to simplify this process? I just started the strLine variable and added it to Line and periodically updated it. Let it be an imperfect source of information (data is lost if a character is added or deleted for one frame, but in the next frame it still appears), but at least I can quickly find out which line I'm at the moment. Just like that! And you can save a lot of time.

6. Write your own debug visualizers


This is an extensive topic, which I will only briefly introduce here:

If you do not like the way objects are displayed in the debugger, try writing your own visualizers.

Debug Visualizers in Visual C ++ 2015

In VS2015, a new built-in template has even appeared, which can be found in the Project → Add New Item → Visual C ++ → Utility → Debugger visualization file (.natvis) file .

Debugging tricks


Based on basic debugging techniques, you can build more complex ones.

7. Have to check a lot of objects?


If some code is called for a set of objects, it is very problematic to go through all these objects and check them line by line. And what if you use the unique value of some field as a hint about where to look for the error? To do this, you can create a conditional control point in which this value will be compared with a certain range. The smaller this range, the better.

Example: I often had to debug code that iterates through all the characters in a document. There was a problem with one (special) symbol. It’s impossible to debug all the characters individually, but I knew that this particular character was different from the rest of the bounding box, so I set a conditional control point at which the width value was checked, so I could access this symbol (condition width> usual_char_width ). Only two or three elements met this condition, so I quickly figured out the error.

Generally speaking, the already such ranges, the better: do not have to check dozens or hundreds of places.

8. Mouse events


Debugging mouse events is especially difficult, because most of these events disappear when the debugger is stopped!

Debugging mouse clicks is usually easy. For example, if you need to check which code was called by clicking on an object, you just need to create a control point at the entrance to the OnClick / onMouseDown method.

What about dragging objects? When the debugger is stopped, the dragging state is lost. In such situations, you can try the following tricks:


9. Create debug visualizers and other tools.


This tip assumes the further development of reception with the creation of simple auxiliary variables. If you are working with complex objects, it’s good to have tools that can more effectively track data. A basic set of similar tools can be found in Visual Studio and any other IDE or debugger, but since all projects are different, it’s better to develop your own solutions.

This situation, I think, is typical for games. You can, for example, create an additional interface layer and enable it during the game to see game statistics, performance information, and memory consumption. The amount of information displayed is limited only by your needs. So I strongly recommend taking the time to create such tools.

Other


10. Debugging in Release Mode


Release builds are faster because almost all code optimizations are already included. However, nothing prevents debugging in this case. How exactly can this be implemented? You need to do the following (for VS 2013 or VS 2015):


11. Accelerate the debug build!



Conclusion


In this article, I brought 11 tips to help you speed up the process of debugging programs. Which ones are most valuable? Perhaps these are points about conditional control points, debugging of a multitude of objects and acceleration of the debug version. However, the rest of the tips are important too, so I find it difficult to arrange them in any particular order. In addition, depending on the specific tasks often have to give preference to different techniques.

Moreover, this list is clearly not complete - there are many other techniques and practices. Perhaps you also have something to add?


Additional resources


Source: https://habr.com/ru/post/318658/


All Articles