English 中文(简体)
Continue to debug after failed assertion on Linux?
原标题:

When an assertion fails with Visual C++ on Windows, the debugger stops, displays the message, and then lets you continue (or, if no debugging session is running, offers to launch visual studio for you).

On Linux, it seems that the default behavior of assert() is to display the error and quit the program. Since all my asserts go through macros, I tried to use signals to get around this problem, like

#define ASSERT(TEST) if(!(TEST)) raise(SIGSTOP);

But although GDB (through KDevelop) stops at the correct point, I can t seem to continue past the signal, and sending the signal manually within GDB just leaves me hanging, with control of neither GDB nor the debugged process.

最佳回答

You really want to recreate the behavior of DebugBreak. This stops the program in the debugger.

My googling of "DebugBreak linux" has turned up several references to this piece of inline assembly which is supposed to do the same.

#define DEBUG_BREAK asm("int $3")

Then your assert can become

#define ASSERT(TEST) if(!(TEST)) asm("int $3");

According to Andomar int 3 causes the cpu to raise interrupt 3. According to drpepper a more portable way to do this would be to call:

 raise(SIGTRAP);
问题回答

You can configure gdb to handle specific signals in a different way. For example, the following will cause SIGSTOP not to be treated as a stoppable event.

handle SIGSTOP nostop noprint pass

help handle within gdb will give you more information.

Even better usability is achieved with

/*!
 * file: assert_x.h
 * rief: Usability Improving Extensions to assert.h.
 * author: Per Nordlöw
 */

#pragma once

#include <errno.h>
#include <signal.h>
#include <assert.h>

#ifdef __cplusplus
extern "C" {
#endif

#if !defined(NDEBUG)
#  define passert(expr)                                                 
  if (!(expr)) {                                                        
    fprintf(stderr, "%s:%d: %s: Assertion `%s  failed.",                
            __FILE__, __LINE__, __ASSERT_FUNCTION, __STRING(expr)); raise(SIGTRAP); 
  }
#  define passert_with(expr, sig)                                       
  if (!(expr)) {                                                        
    fprintf(stderr, "%s:%d: %s: Assertion `%s  failed.",                
            __FILE__, __LINE__, __ASSERT_FUNCTION, __STRING(expr)); raise(sig); 
  }
#  define passert_eq(expected, actual)                                  
  if (!(expected == actual)) {                                          
    fprintf(stderr, "%s:%d: %s: Assertion `%s  == `%s  failed.",        
            __FILE__, __LINE__, __ASSERT_FUNCTION, __STRING(expected), __STRING(actual)); raise(SIGTRAP); 
  }
#  define passert_neq(expected, actual)                                 
  if (!(expected != actual)) {                                          
    fprintf(stderr, "%s:%d: %s: Assertion `%s  != `%s  failed.",        
            __FILE__, __LINE__, __ASSERT_FUNCTION, __STRING(expected), __STRING(actual)); raise(SIGTRAP); 
  }
#  define passert_lt(lhs, rhs)                                          
  if (!(lhs < rhs)) {                                                   
    fprintf(stderr, "%s:%d: %s: Assertion `%s  < `%s  failed.",         
            __FILE__, __LINE__, __ASSERT_FUNCTION, __STRING(lhs), __STRING(rhs)); raise(SIGTRAP); 
  }
#  define passert_gt(lhs, rhs)                                          
  if (!(lhs > rhs)) {                                                   
    fprintf(stderr, "%s:%d: %s: Assertion `%s  < `%s  failed.",         
            __FILE__, __LINE__, __ASSERT_FUNCTION, __STRING(lhs), __STRING(rhs)); raise(SIGTRAP); 
  }
#  define passert_lte(lhs, rhs)                                         
  if (!(lhs <= rhs)) {                                                  
    fprintf(stderr, "%s:%d: %s: Assertion `%s  <= `%s  failed.",        
            __FILE__, __LINE__, __ASSERT_FUNCTION, __STRING(lhs), __STRING(rhs)); raise(SIGTRAP); 
  }
#  define passert_gte(lhs, rhs)                                         
  if (!(lhs >= rhs)) {                                                  
    fprintf(stderr, "%s:%d: %s: Assertion `%s  >= `%s  failed.",        
            __FILE__, __LINE__, __ASSERT_FUNCTION, __STRING(lhs), __STRING(rhs)); raise(SIGTRAP); 
  }
#  define passert_zero(expr)                                            
  if (!(expr == 0)) {                                                   
    fprintf(stderr, "%s:%d: %s: Assertion `%s  is zero failed.",        
            __FILE__, __LINE__, __ASSERT_FUNCTION, __STRING(expr)); raise(SIGTRAP); 
  }
#else
#  define passert(expr)
#  define passert_with(expr, sig)
#  define passert_eq(expected, actual)
#  define passert_lt(lhs, rhs)
#  define passert_gt(lhs, rhs)
#  define passert_lte(lhs, rhs)
#  define passert_gte(lhs, rhs)
#  define passert_zero(expr)
#endif

#ifdef __cplusplus
}
#endif

Have you tried to send a SIGCONT signal to the process?

kill -s SIGCONT <pid>

You can replace assert with your own version which calls pause() instead of abort(). When the assertion fails, the program will pause and you can run gdb --pid $(pidof program) to examine the callstack and variables. An advantage of this approach is that the program does not need to be started under GDB.

Header file (based on /usr/include/assert.h):

#include <assert.h>

#ifndef NDEBUG
    void assert_fail(const char *assertion, const char *file, unsigned line, const char *function)
    __attribute__ ((noreturn));
    #undef assert
    #define assert(expr)            
        ((expr)                     
        ? __ASSERT_VOID_CAST (0)    
        : assert_fail (__STRING(expr), __FILE__, __LINE__, __ASSERT_FUNCTION))
#endif /* NDEBUG */

Implementation of assert_fail (based on assert.c in glibc):

#include <stdio.h>   /* for stderr, fprintf() */
#include <stdlib.h>  /* for abort() */
#include <unistd.h>  /* for pause() */

void assert_fail(const char *assertion, const char *file, unsigned line, const char *function) {
    extern const char *__progname;
    fprintf(stderr, "%s%s%s:%u: %s%sAssertion `%s  failed.
",
        __progname,
        __progname[0] ? ": " : "",
        file,
        line,
        function ? function : "",
        function ? ": " : "",
        assertion
    );
    pause();
    abort();
}




相关问题
Undefined reference

I m getting this linker error. I know a way around it, but it s bugging me because another part of the project s linking fine and it s designed almost identically. First, I have namespace LCD. Then I ...

C++ Equivalent of Tidy

Is there an equivalent to tidy for HTML code for C++? I have searched on the internet, but I find nothing but C++ wrappers for tidy, etc... I think the keyword tidy is what has me hung up. I am ...

Template Classes in C++ ... a required skill set?

I m new to C++ and am wondering how much time I should invest in learning how to implement template classes. Are they widely used in industry, or is this something I should move through quickly?

Print possible strings created from a Number

Given a 10 digit Telephone Number, we have to print all possible strings created from that. The mapping of the numbers is the one as exactly on a phone s keypad. i.e. for 1,0-> No Letter for 2->...

typedef ing STL wstring

Why is it when i do the following i get errors when relating to with wchar_t? namespace Foo { typedef std::wstring String; } Now i declare all my strings as Foo::String through out the program, ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

Window iconification status via Xlib

Is it possible to check with the means of pure X11/Xlib only whether the given window is iconified/minimized, and, if it is, how?

热门标签