`
oliver_peng
  • 浏览: 44254 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

一个C++调试工具类

阅读更多
可以捕捉所有未知异常和生成包含程序名的Core dump 文件。
/*
 * DebugUtility.h
 *
 *  Created on: Jun 4, 2014
 *      Author: root
 *
 *  Use following two ways to help debugging application when application is crashed:
 *
 *    1 Turn on core dump and generate core dump file by forking child process and rename the child process dump
 *      file to new name. Including application name in core dump file is more easy to identify then default
 *      file name.
 *    2 Overwrite terminate() to handle un-handled exception and print backtrace before abort.
 *      Use gdb to get backtrace and save into log file /tmp/[app_name]_backtrace
 *
 *  Requriement:
 *    Linux, gcc, gdb
 *
 *  How to use it:
 *
 *    Add following lines at the beginning of main function to initialize:
 *    Be attention: app_name can't include path.
 *
 *    char * app_name;
 *	  if ( (app_name = strrchr(argv[0], '/')) == NULL )
 *	  {
 *		DebugUtility::GetInstance().Setup(argv[0], true);
 *	  }
 *	  else
 *	  {
 *		DebugUtility::GetInstance().Setup(app_name + 1, true);
 *	  }
 *
 *    To generate core dump file, you need to call DebugUtility::GetInstance().GenerateCoreDump() in signal
 *    handler code.
 *
 *    For example:
 *
 *    void signal_handler(int signal, siginfo_t * p_signinfo, void * p_context)
 *	  {
 *	    DebugUtility::GetInstance().GenerateCoreDump();
 *	    exit(0);
 *	  }
 *
 *    In main function:
 *
 *    	struct sigaction sa;
 *      sa.sa_sigaction = signal_handler;
 *      sigemptyset(&sa.sa_mask);
 *      sa.sa_flags = SA_RESTART | SA_SIGINFO;
 *      sigaction(SIGTERM, &sa, (struct sigaction *) NULL);
 *
 *    For application name test, if it crashed, you should be able to find core dump file core.test.[pid].
 *
 *    For application name test, if it quit because of raised exception, you should be able to find backtrace
 *    log file /tmp/test_backtrace
 */

#ifndef DEBUGUTILITY_H_
#define DEBUGUTILITY_H_

#include <stdlib.h>
#include <string>
#include <sys/time.h>
#include <sys/resource.h>
#include <stdio.h>
#include <sys/wait.h>
#include <iostream>
#include <exception>

using namespace std;

class DebugUtility
{
public:

	// Overwrite default global terminate handler to handle all un-handled exceptions
	// Print backtrace before calling abort()
	static void my_terminate()
	{
		DebugUtility::GetInstance().PrintBacktrace();

		abort();
	}

	// Use singleton pattern to be sure only one DebugUtility object
	static DebugUtility & GetInstance()
	{
		static DebugUtility instance;

		return instance;
	}

	// Setup debug environment
	// Parameters:
	//   app_name: application name which will be part of core dump and backtrace file.
	//   core_dump_enabled: whether enable core dump, by default true.
	void Setup(string app_name, bool core_dump_enabled = true)
	{
		m_app_name = app_name;

		m_core_dump_enabled = core_dump_enabled;

		re_throw_flag = true;

		if (m_core_dump_enabled)
			EnableCoreDump();

		std::set_terminate(DebugUtility::my_terminate);
	}

	// Generate core dump file based on application name
	// For example application name test and pid is 12345, the core dump file is core.test.12345
	void GenerateCoreDump()
	{
		try
		{
			if (m_core_dump_enabled)
			{
				// Fork child process to generate core dump file
				// Then rename file name with application name

				pid_t childpid = fork();

				// child process generates core dump
				if (childpid == 0)
				{
					// Send SIGABRT signal to terminate child process and generate core dump
					abort();
				}

				if (childpid > 0)
				{
					waitpid(childpid, 0, 0);
				}

				char core_dump_file_name[1000];
				char child_core_dump_file_name[1000];

				// Rename the core dump name.
				sprintf(core_dump_file_name, "core.%s.%d", m_app_name.c_str(), getpid());
				sprintf(child_core_dump_file_name, "core.%d", childpid);

				// try with core.pid
				int rename_rval = rename(child_core_dump_file_name, core_dump_file_name);

				if (rename_rval == -1)
				{
					// try with just core which may happen on HP server
					rename_rval = rename("core", core_dump_file_name);
				}
			}
		} catch (...)
		{
			// Catch all exceptions
		}
	}

	// Try to re-throw exception again and print error message to std:cerr
	// Generate back trace by using gdb and save under /tmp
	// For example application name test, the backtrace file is /tmp/test_backtrace
	void PrintBacktrace()
	{
		try
		{
			// try once to re-throw currently active exception
			if (re_throw_flag)
			{
				re_throw_flag = false;
				throw;
			}
		} catch (const std::exception &e)
		{
			std::cerr << __FUNCTION__ << " caught unhandled exception. what(): " << e.what() << std::endl;
		} catch (...)
		{
			std::cerr << __FUNCTION__ << " caught unknown/unhandled exception." << std::endl;
		}

		try
		{
			char command_line[1000];
			snprintf(command_line, sizeof(command_line), "gdb 2>/dev/null --batch -n -ex thread -ex bt full --exec=%s --pid=%d > /tmp/%s_backtrace",
					m_app_name.c_str(), getpid(), m_app_name.c_str());
			system(command_line);
		} catch (...)
		{
			// Catch all exceptions
		}
	}

	~DebugUtility()
	{
	}

private:

	DebugUtility()
	{

	}

	// Enable core dump
	void EnableCoreDump()
	{
		m_core_dump_enabled = true;
		// Set core dump limit to unlimited to enable core dump
		rlimit core_limit =
		{ RLIM_INFINITY, RLIM_INFINITY };
		setrlimit(RLIMIT_CORE, &core_limit);
	}

	// Don't forget to declare these two. You want to make sure they
	// are unaccessable otherwise you may accidently get copies of
	// your singleton appearing.

	DebugUtility(DebugUtility const&); // Don't Implement
	void operator=(DebugUtility const&); // Don't implement

	// Application name which will be used in backtrace file name and core dump file name
	string m_app_name;

	// Whether generate core dump file
	bool m_core_dump_enabled;

	// Re-throw flag
	bool re_throw_flag;
};

#endif /* DEBUGUTILITY_H_ */



使用范例代码:

#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "DebugUtility.h"
#include <exception>
#include <stdexcept>
#include <iostream>

using namespace std;

void signal_handler(int signal, siginfo_t * p_signinfo, void * p_context)
{
	DebugUtility::GetInstance().GenerateCoreDump();
	exit(0);
}

void func1()
{

	throw std::runtime_error("test");
}

void func2()
{
	func1();
}

int main(int argc, char* argv[])
{
	cout << "!!!Hello World!!!" << endl; // prints !!!Hello World!!!

	char * app_name;
	if ( (app_name = strrchr(argv[0], '/')) == NULL )
	{
		DebugUtility::GetInstance().Setup(argv[0], true);
	}
	else
	{
		DebugUtility::GetInstance().Setup(app_name + 1, true);
	}

	struct sigaction sa;
	sa.sa_sigaction = signal_handler;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = SA_RESTART | SA_SIGINFO;

	sigaction(SIGTERM, &sa, (struct sigaction *) NULL);
	sigaction(SIGINT, &sa, (struct sigaction *) NULL);

	func2();

	return 0;
}

分享到:
评论

相关推荐

    C++文件操作工具类

    "C++文件操作工具类"是一个专门为C++开发者设计的实用工具,它简化了对文件进行读写、创建、删除等操作的过程。 首先,我们要理解C++中的文件操作基本概念。C++通过标准库中的`fstream`头文件提供了一套接口,允许...

    c++日志工具类 c++实现

    本篇文章将详细介绍如何在C++中实现一个基础的日志工具类。 首先,我们需要定义日志级别。这些级别可以是枚举类型,例如: ```cpp enum class LogLevel { DEBUG, INFO, WARN, ERROR, FATAL }; ``` 接下来,...

    Visual C++ 调试器

    VC6 调试器是一个功能强大且易于使用的调试工具。通过了解和掌握这些调试技巧,我们可以更好地解决问题,提高我们的调试效率。 调试器分类 在 Windows 平台上,调试器可以分为两大类:用户模式调试器和内核模式...

    Visual C++开发工具与调试技巧

    这似乎是目前这个 Visual C++ 6.0 版本的一个 bug,可以按照以下步骤使其正常:关闭 Project,删除“工程名.ncb”文件,然后重新打开工程。 8. 如何将一个通过 ClassWizard 生成的类彻底删除? 首先在工作区的 ...

    VC/C++调试工具

    总之,“VC/C++调试工具”是一个面向C++开发者的实用工具,通过控制台显示和日志生成,它能帮助开发者有效地定位和解决问题,提高开发效率。在没有Visual C++环境或需要轻量级调试方案的情况下,这款工具将发挥巨大...

    windows visual studio C++ 蓝牙BLE客户端(蓝牙调试工具)的完整源码和例子,可用于调试蓝牙BLE的开发

    在Windows平台上,Visual Studio是一个优秀的集成开发环境(IDE),支持C++开发,并提供了丰富的调试和代码编辑功能。 BLE技术是蓝牙技术的一个分支,旨在实现低功耗、短距离的无线通信。它特别适合于物联网(IoT)...

    源码 C++ MFC 源代码 串口调试助手

    这个源码包名为“源码 C++ MFC 源代码 串口调试助手”,显然是一款基于C++和MFC的串口通信调试工具。 串口通信是计算机通信技术中的一个重要部分,特别是在嵌入式系统和设备调试中。通过串行端口,设备可以相互通信...

    c、c++调试软件

    "c、c++调试软件"便是这样一类工具,它们为程序员提供了一个直观的环境,以便检查程序的运行状态,找出潜在的bug。 Microsoft Visual Studio是其中的一款杰出代表,它是一个全面的集成开发环境(IDE),不仅支持C++...

    Android 学习笔记——利用JNI技术在Android中调用、调试C++代码

    - **使用Android Studio的C++调试器**:在Android Studio中设置断点,然后启动带有NDK调试配置的运行/调试会话。 - **使用GDB**:可以通过NDK提供的GDB工具进行远程调试,需要在应用中开启调试标志,并通过ADB连接...

    C++中英文敏感词检测工具类

    本项目提供了一个C++实现的中英文敏感词检测工具类,该工具能够帮助开发者检查文本中是否存在特定的关键字,并在找到时进行替换,以保护数据的安全性和合规性。下面我们将详细探讨这个工具类的实现原理、功能特性...

    C++实现 蓝牙BLE客户端(蓝牙调试工具)

    windows visual studio C++ 蓝牙BLE客户端(蓝牙调试工具)的完整源码和例子,可用于调试蓝牙BLE的开发板,比如esp32。自己用了很多年,稳定,代码结构清晰 //注册通知回调 RegisterBleDeviceRecvData(call_back); ...

    Android JNI 断点调试C++

    在Android开发中,JNI(Java Native Interface)是一个关键的组件,允许Java代码调用本地(C/C++)代码,反之亦然。这对于性能敏感的应用、底层库的集成以及利用现有C/C++库是非常有用的。本教程将聚焦于如何在...

    0061+TCP+UDP网络调试助手含源码.zip_UDP c++源码_UDP 调试助手_tcp 网络 c#_udp 网络调试_

    调试工具通常包括了显示网络流量、捕获和解析网络包、模拟不同网络条件等功能,帮助开发者排查网络问题。 在实际开发中,理解和掌握TCP与UDP的特性及其在C++和C#中的应用至关重要。TCP的可靠性保证了数据的正确传输...

    串口调试助手c++源码

    总的来说,这个C++源码项目为学习和实践串口通信提供了一个实例,可以帮助开发者深入理解串口工作的原理,以及如何在Windows环境下用C++编写串口通信程序。对于从事硬件开发、嵌入式系统或物联网应用的人来说,这是...

    TCP.rar_c++ 网络调试_c# TCP_tcp调试_网络调试_调试 tcp

    此外,C++和C#都有丰富的调试工具,如GDB和Visual Studio,它们可以帮助我们步进执行代码,观察变量状态,找出可能导致问题的源头。 总的来说,TCP网络调试是一项涉及网络基础、套接字编程和问题排查技能的工作。...

    Java转C++代码工具 J2C

    Java转C++代码工具J2C是一个专门用于将Java源代码转换为等效C++代码的工具。这个工具对于那些需要在不支持Java或者需要利用C++特定性能特性的环境中迁移Java项目的人来说非常有用。在深入探讨J2C之前,我们先理解...

    c++2.0编程工具

    6. **调试工具**:Turbo C++自带了一个简单的调试器,允许程序员设置断点,单步执行代码,查看变量值等,帮助调试程序。 7. **代码管理**:在DOS下,没有现代的版本控制系统,程序员可能需要手动管理源代码文件,...

    visual C++ 6.0开发工具与调试

    《Visual C++ 6.0开发工具与调试详解》 Visual C++ 6.0是一款经典且强大的集成开发环境(IDE),它为程序员提供了丰富的工具和功能,支持C++编程语言,尤其在Windows应用程序开发中占据重要地位。本文将深入探讨其...

    C++学习工具

    最后,一个全面的C++学习工具还应该包括调试工具。学会如何设置断点、查看变量状态、单步执行代码以及理解调用堆栈是成为一名熟练C++程序员的必要技能。 总之,C++学习工具是掌握这种语言的关键。它不仅要有完善的...

Global site tag (gtag.js) - Google Analytics