Call Stack for debugging skills

Source: Internet
Author: User
Tags line editor

In computer science, callstack refers to the stack that stores information about the running functions of a program. A call stack is composed of stack frames. Each stack frame corresponds to an unfinished function.

In today's popular computer system architecture, most computer parameters are transmitted, and local Variable Allocation and release are implemented by manipulating the program stack. Stack is used to pass function parameters, store returned value information, and store registers to restore the status of the called pre-processing machine. Each time a function is called, stack space is allocated for the function instance called. The part of stack space allocated to a single function is called stack frame. That is to say, stack frame is mainly used to describe the function call relationship.

The importance and function of the stack frame organization mode are reflected in two aspects: First, it allows the caller and the called to reach an agreement. This Convention defines the transfer method of function parameters when a function is called, the return method of the function return value, and how the registers are shared between the caller and the called. Second, it defines how the caller uses its own stack frame to store and use local variables.

Brief Introduction
Debugging is essential for program developers. If you do not need to debug the program, once the program you write has a problem, you will not be able to start. I have summarized 10 years of VC experience and made a rough introduction to debugging skills. Hope to help you.

Today, we will briefly introduce the call stack. The call stack is introduced in the VC debugging entry in my column, but it is not described in detail.

First, let's talk about the call stack. Suppose we have several functions: function1, function2, function3, funtion4, function1, function2, function2, function3, and function3. During function4 running, we can learn from the current stack of the thread who called the functions. Looking at the order of functions, function4, function3, function2, and function1 present a "stack" feature, and the called functions appear at the top of the list. Therefore, this relationship is called call stack ).

When a fault occurs, if the program is interrupted, we can only see the function with the last error. Using call stack, we can know who calls the function with an error. Sometimes, we can guess the cause of the error. The assert macro causes the interruption.

When the program is interrupted, the second button to the right of the debug toolbar is generally the call stack button. After this button is pressed, you can see the current call stack.

Example 1: Introduction
Let's first demonstrate the call stack. First, create a dialog project named debug. After the project is created, double-click the OK button to create the message ing function and add the following code:

Void cdebugdlg: onok ()
{

// Todo: add extra validation here
Assert (false );

}

Press F5 To Start program debugging. After the program runs, click OK to interrupt the program. View the call Stack window and you will find the following content:

Cdebugdlg: onok () line 176 + 34 bytes
_ Random (cinclutarget * 0x0012fe74 {cdebugdlg}, unsigned int 1, int 0, void (void) * 0x5f402a00 'vcall' (void), void * 0x00000000, unsigned int 12, afx_cmdhandlerinfo * 0x00000000) line 88
C0000target: on0000msg (unsigned int 1, int 0, void * 0x00000000, afx_cmdhandlerinfo * 0x00000000) line 302 + 39 bytes
Cdialog: on1_msg (unsigned int 1, int 0, void * 0x00000000, afx_cmdhandlerinfo * 0x00000000) line 97 + 24 bytes
Cwnd: oncommand (unsigned int 1, long 656988) line 2088
Cwnd: onwndmsg (unsigned int 273, unsigned int 1, long 656988, long * 0x0012f83c) line 1597 + 28 bytes
Cwnd: windowproc (unsigned int 273, unsigned int 1, long 656988) line 1585 + 30 bytes
Afxcallwndproc (cwnd * 0x0012fe74 {cdebugdlg hwnd = ???}, Hwnd _ * 0x001204b0, unsigned int 273, unsigned int 1, long 656988) line 215 + 26 bytes
Afxwndproc (hwnd _ * 0x001204b0, unsigned int 273, unsigned int 1, long 656988) line 368
Afxwndprocbase (hwnd _ * 0x001204b0, unsigned int 273, unsigned int 1, long 656988) line 220 + 21 bytes
USER32! 77d48709 ()
USER32! 77d487eb ()
USER32! 77d4b368 ()
USER32! 77d4b3b4 ()
Ntdll! 7c90eae3 ()
USER32! 77d4b7ab ()
USER32! 77d7fc9d ()
USER32! 77d76530 ()
USER32! 77d58386 ()
USER32! 77d5887a ()
USER32! 77d48709 ()
USER32! 77d487eb ()
USER32! 77d489a5 ()
USER32! 77d489e8 ()
USER32! 77d6e819 ()
USER32! 77d65ce2 ()
Cwnd: isdialogmessagea (tagmsg * 0x004167d8 {MSG = 0x00000202 Wp = 0x00000000 Lp = 0x000f001c}) line 182
Cwnd: pretranslateinput (tagmsg * 0x004167d8 {MSG = 0x00000202 Wp = 0x00000000 Lp = 0x000f001c}) line 3424
Cdialog: pretranslatemessage (tagmsg * 0x004167d8 {MSG = 0x00000202 Wp = 0x00000000 Lp = 0x000f001c}) line 92
Cwnd: Define pretranslatetree (hwnd _ * 0x001204b0, tagmsg * 0x004167d8 {MSG = 0x00000202 Wp = 0x00000000 Lp = 0x000f001c}) line 2667 + 18 bytes
Cwinthread: pretranslatemessage (tagmsg * 0x004167d8 {MSG = 0x00000202 Wp = 0x00000000 Lp = 0x000f001c}) line 665 + 18 bytes
Cwinthread: pumpmessage () line 841 + 30 bytes
Cwnd: runmodalloop (unsigned long 4) line 3478 + 19 bytes
Cdialog: domodal () line 536 + 12 bytes
Cdebugapp: initinstance () line 59 + 8 bytes
Afxwinmain (hinstance _ * 0x00400000, hinstance _ * 0x00000000, char * 0x00141f00, int 1) line 39 + 11 bytes
Winmain (hinstance _ * 0x00400000, hinstance _ * 0x00000000, char * 0x00141f00, int 1) line 30
Winmaincrtstartup () line 330 + 54 bytes
Kernel32! 7c816d4f ()

Here, cdebugdialog: onok appears at the top of the call stack as the final called function in the entire call chain, and the startup function Kernel32 of the program in the kernel! 7c816d4f () appears at the bottom of the stack.

Example 2: learning methods
Microsoft provides the MDI/SDI model to provide a recommended structure for document processing. Sometimes, you want to control a link. For example, we want to bring up our own file opening dialog box, but do not want to open the entire document on our own, and prefer MFC to do other work. However, we do not know how MFC processes the document, nor how to insert custom code.

Fortunately, we know that when a document is opened, the system will call the serialize function of the cdocument derived class. We can use this function to track the processing of MFC.

Create a default SDI project test1, add a breakpoint at the beginning of the ctest1doc: serialize function, run the program, and open a file. At this time, we can see that the call stack is (I only intercept a section of interest ):

Ctest1doc: serialize (carchive & {...}) line 66
Cdocument: onopendocument (const char * 0x0012f54c) line 714
Csingledoctemplate: opendocumentfile (const char * 0x0012f54c, int 1) line 168 + 15 bytes
Cdocmanager: opendocumentfile (const char * 0x0042241c) line 953
Cwinapp: opendocumentfile (const char * 0x0042241c) line 93
Cdocmanager: onfileopen () line 841
Cwinapp: onfileopen () line 37
_ Afxdispatch1_msg (c0000target * 0x0000007f0 class ctest1app theapp, unsigned int 57601, int 0, void (void) * 0x00402898 cwinapp: onfileopen, void * 0x00000000, unsigned int 12, afx_cmdhandlerinfo * 0x00000000) line 88
C0000target: on0000msg (unsigned int 57601, int 0, void * 0x00000000, afx_cmdhandlerinfo * 0x00000000) line 302 + 39 bytes
Cframewnd: on1_msg (unsigned int 57601, int 0, void * 0x00000000, afx_cmdhandlerinfo * 0x00000000) line 899 + 33 bytes
Cwnd: oncommand (unsigned int 57601, long 132158) line 2088
Cframewnd: oncommand (unsigned int 57601, long 132158) line 317


From the above call stack, this process is triggered by a wm_command message (because we use the menu to open the file), and The cwinapp: onfileopen starts the actual processing process first. This function calls cdocmanager :: onfileopen open the document.

First, double-click cwinapp: onfileopen () line 37 to open cwinapp: onfileopen. The processing process is as follows:

Assert (m_pdocmanager! = NULL );
M_pdocmanager-> onfileopen ();

M_pdocmanager is an instance pointer of the cdocmanager class. We double-click the cdocmanager: onfileopen line to see the implementation of this function:

Void cdocmanager: onfileopen ()
{
// Prompt the user (with all document templates)
Cstring newname;
If (! Dopromptfilename (newname, afx_ids_openfile,
Ofn_hidereadonly | ofn_filemustexist, true, null ))
Return; // open canceled
Afxgetapp ()-> opendocumentfile (newname );
// If returns NULL, the user has already been alerted
}

Obviously, this function first calls the dopromptfilename function to obtain a file name, and then continues the subsequent opening process.

Along with this clue, we will be able to find the location of the dialog box for inserting our file. As this is not the focus of our research, I will not detail the subsequent analysis.

Example 3: out-of-bounds memory access
In the VC program of the debug version, the program will reserve several bytes for each new memory for cross-border detection. When the memory is released, the system checks these bytes to determine whether memory access is out of bounds.

We use the previous instance program to add the following lines of code at the beginning of ctest1app: initinstance:

Char * P = new char [10];
Memset (p, 0,100 );
Delete [] P;
Return false;

Obviously, this Code applies for 10 bytes of memory, but uses 100 bytes. We add a breakpoint in the memset (p, 0,100); line, and then execute the program. After the breakpoint arrives, we observe the memory value pointed to by P (using the memory function of the debug toolbar ), it can be found that its value is:

CD
CD FD
00 00 00 00 00 00 00
......

According to experience, P is actually allocated 16 bytes, And the last 6 bytes are used for protection. When we press F5 to execute the program at full speed, the following error message is displayed:

Debug error!
Program: C:/temp/test1/debug/test1.exe
Damage: after normal block (#55) at 0x00421ab0
Press retry to debug the application

This information prompts that the memory after the normal memory block 0x00421ab0 is damaged (memory access out of bounds). We click Retry to enter the debugging status and find that the call stack is:

_ Free_dbg_lk (void * 0x00421ab0, int 1) line 1033 + 60 bytes
_ Free_dbg (void * 0x00421ab0, int 1) line 970 + 13 bytes
Operator Delete (void * 0x00421ab0) line 351 + 12 bytes
Ctest1app: initinstance () line 54 + 15 bytes

Obviously, this error occurs when the delete operation is called. It appears in ctest1app: initinstance () line 54 + 15 bytes. We can easily find out which memory to release based on this information. Then, we only need to determine the Error Based on the memory access process, which will greatly reduce the debugging difficulty.

Example 4: subclass
Subclass is a common method for modifying an existing control to implement new functions. We use the debug dialog box project in instance 1 to demonstrate a story of learning subclass in the past. We create a default dialog box project named debug and instantiate it as follows:

Add an edit control to the resource in the dialog box.
Use Class Wizard to derive a class cmyedit for cedit (this class is not modified because we do not care about the details of subclass today)
For the Edit Control, add a control type variable m_edit, whose type is cmyedit
Add the following statement to oninitdialog:

M_edit.subclassdlgitem (idc_edit1, this );

When we run this program, we will encounter the following error:


Debug assertion failed!
Application: C:/temp/debug/debug.exe
File: wincore. cpp
Line: 311

For information on how your program can cause an assertion failure, see visual c ++ documentation on asserts.

(Press retry to debug the Application)

Click Retry to enter the debugging status. We can see that the call stack is:

Cwnd: attach (hwnd _ * 0x000205a8) line 311 + 28 bytes
Cwnd: subclasswindow (hwnd _ * 0x000205a8) line 3845 + 12 bytes
Cwnd: subclassdlgitem (unsigned int 1000, cwnd * 0x0012fe34 {cdebugdlg hwnd = 0x001d058a}) line 3883 + 12 bytes
Cdebugdlg: oninitdialog () line 120

It can be seen that there is a problem with the attach handle. The code of the problematic line is:

Assert (m_hwnd = NULL );

This indicates that we should not bind the control to the subclass. we delete the following line in cdebugdialog: dodataexchange:

Ddx_control (PDX, idc_edit1, m_edit );

The problem is solved.

Summary
In short, call stack is a technology that must be mastered during debugging, but programmers need a wealth of experience to master and use it well. You not only need to be familiar with the c ++ syntax, but also need to have a certain understanding of related platforms and software design ideas. My article can only be a rough introduction. After all, I am not a master in this regard. We hope this will be helpful for new customers.


Programming preparation for debugging

For a programmer, it is very easy to learn a language and an algorithm (excluding those who spend a lot of time playing at school and who say they don't have time at work ). However, any program may be defective, especially those who have experience in collaborative programming.


In my previous article on debugging, I focused on some basic skills required for setting information and debugging in the VC integration environment. However, knowing this is not enough. The beginning of a successful debugging is the preparation in programming.

Separation Error
Many programmers like to write the following formula:

Cleftview * pview =
(Cframewnd *) afxgetapp ()-> m_pmainwnd)-> m_wndsplitterwnd.getpane (0, 0 );

If everything goes well, there is no problem with such a formula. However, as a programmer, you should always remember that any call may fail in some special circumstances. Once the above formula fails, the whole cascade may fail, it's hard for you to figure out what went wrong. The result of such a formula is often: It saves 2 minutes of coding time, and debugging time is several weeks later.

For the formula above, we should try our best to break down the formula into independent function calls, so that we can determine which function call has a problem at any time, and reduce the scope of import to be checked.

Check return values
Checking the return value seems a hassle for many programmers. However, if you can check the return value for every function call that may have an error, you can immediately know the function that has the error.

Some people have realized the importance of checking the return value, but remember that it is not enough to check whether the function fails. We need to know the exact cause of the function failure. For example, the following code:

If (connect (sock, (const sockaddr *) & ADDR, sizeof (ADDR) = socket_error)
{
Afxmessagebox ("Connect failed ");
}

Although the returned value has been checked here, it does not actually help much. Just as many people ask questions on vckbase, they can only shout "why is the connection failed ?". In this case, you can only guess the cause of failure. Even the experts cannot tell the cause of failure accurately.

Add diagnostic information
If you know the error, you should try to tell the test and user more information as much as possible to understand the cause of the failure. If the programmer can provide the following error information, it is very helpful for diagnosing errors:

Error file: We can use the macros this_file and _ file __. Note that this_file is manually defined in the CPP file, while _ file _ is defined by the compiler. When the function that records errors is defined in. H, it is better to use this_file, because it can indicate in which CPP to call and cause failure.
Error line: We can use the help macro _ line __
Wrong function: If the design is good, the above two items are sufficient. Of course, we can print out the wrong function or expression directly, so that it is useful to search in a large pile of code (especially in the go to line editor not supported. For more information, see my article workshop.
Cause of the error: many causes of the error can only be provided by the program itself. If you only ask someone else when an error occurs, you will never be a qualified Program designer. Errno is set when many functions fail. We can use getlasterror to get the error code and print the text description of the specific error through formatmessage.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.