【转】C++异常中的堆栈跟踪

浅析C++ StackTrace 堆栈轨迹 堆栈轨迹:如果你需要打印出某个时间的调用堆栈状态,你将产生一个堆栈轨迹。 stack trace 中包括三部分,分别为:.bss .text .data bss: 表示程序中未初始化的全局变量的一块内存区域 text: 表示程序中已初始化的全局变量的一块内存区域 data:表示存放程序执行代码的一块内存区域      我们在学习函数调用时,都知道每个函数都拥有自己的栈空间。一个函数被 阅读详情

C++异常中的堆栈跟踪


C++语言的运行时环境是基于栈的环境,堆栈跟踪(trace stack)就是程序运行时能够跟踪并打印所调用的函数、变量及返回地址等,C++异常中的堆栈跟踪就是当程序抛出异常时,能够把导致抛出异常的语句所在的文件名和行号打印出来,以及把调用抛出异常的语句的函数以及其它上层函数信息都打印出来。
1. 为什么需要堆栈跟踪
当你在开发程序时,你是否曾遇到过程序运行过程中突然当机,而你不知道哪一行代码出的问题;你是否曾遇到过程序调试过程中突然抛出异常,而你不知道哪一行代码出的问题;你是否曾遇到过当你在单步调试时突然抛出异常而你却忘了单步执行到哪一步时抛出的异常,于是你只好重来一次。Beta程序在客户那里试运行当中,突然当机,而你不能调试,只能依据客户报告的一些信息来找bug,而客户大多不熟悉程序开发,所以他们报告的信息太少使你感觉无从下手、一筹莫展。
如果你碰到过以上情况,你就只好痛苦地一条一条单步执行语句,看抛出异常的语句在哪,检查非法访问内存的语句在哪里,糟糕的是根据海森堡不确定原理,有时当你调试时又不出问题了。所以幸运的话,你能很快就找到bug,不幸的话,几小时或几天都不能找出问题所在,并将成为你的梦魇。我在程序开发过程中,就经常碰到以上这些情况。
众所周知,在程序开发中发现一个bug将比改正这个bug难度大很多。所以如果有一个方法能够在程序出错时把出错信息打印出来,这样将大大方便找到bug,加快程序开发速度,提高程序的质量。这样,当客户报告程序出错时,你只需要客户把日志发送给你,你根据这个日志里的异常堆栈信息就能轻松发现问题所在。
在java中就有堆栈跟踪功能,它能在程序抛出异常时,能够打印出能够把导致抛出异常的语句所在的文件名和行号,C#中也有这个功能。很多人认为用java开发程序比用C++开发程序要快,我认为java有抛出异常时能够跟踪堆栈这个功能是其中的一个重要原因。

2. 如何实现C++异常中的堆栈跟踪
要实现堆栈跟踪,必须依赖于底层机制即操作系统或虚拟平台,java与jvm虚拟平台绑定,C#与.NET虚拟平台绑定,它们都提供了堆栈跟踪的功能,而C++与操作系统或平台无关,所以没有提供这个功能,但是否能够利用操作系统的系统函数实现这个功能呢?下面简要介绍如何在Windows2000下实现C++异常中的堆栈跟踪。
在Windows中,C++异常底层的实现是通过Windows中的结构化异常SEH来实现的,结构化异常包括如除0溢出、非法内存访问、堆栈溢出等,虽然用catch( … )能够捕获结构化异常,但不能知道是哪种结构化异常,所以第一步就是要把结构化异常转化为C++异常,Windows中的_set_se_translator()函数可以实现这个功能。先建立一个转化函数:void _cdecl TranslateSEHtoCE( UINT code, PEXCEPTION_POINTERS pep ) ;在这个转化函数中抛出一个继承C++标准异常的类,如CRecoverableSEHException(可以恢复的结构化异常类)和CUnRecoverableSEHException(不可以恢复的结构化异常类),这两个类继承CSEHException,CSEHException继承标准C++异常的基类exception。然后在main函数开始处调用 _set_se_translator(TranslateSEHtoCE ),这样就可以把结构化异常转换为C++异常。
另外,由于VC中默认new失败时并不抛出异常,所以需要让new失败时抛出异常,这样可以统一处理,可以使用WINDOWS中的_set_new_handler( )转化,让new失败时抛出异常。同上,先建立一个转化函数 int NewHandler( size_t size ),在这个转化函数中抛出C++标准异常的类bad_alloc,在main函数开始处调用 _set_new_handler (NewHandler)。
接着在CSEHException的构造函数中跟踪堆栈,把导致抛出结构化异常的语句所在的文件名和行号打印出来,调用void ShowStack( HANDLE hThread, CONTEXT& c )。ShowStack函数封装了跟踪堆栈所需调用的各种系统API。它的功能就是根据参数c(线程的上下文),得到当前程序的路径,枚举所调用的系统动态连接库,然后按照从里到外的顺序打印出所有执行的函数名及其所在的文件名和行号。
创建自己的异常类使其具有堆栈跟踪的功能,定义自己使用的异常基类如CMyException(当然,如果你愿意,你可以修改其命名),令其继承标准C++异常类domain_error(当然也可以继承exception),然后在CMyException的构造函数中调用void ShowStack( HANDLE hThread, CONTEXT& c ),这样就可以实现堆栈跟踪,其它自定义的异常继承CMyException,就自动获得堆栈跟踪的功能。这样就形成了一个完整的类层次。

exception

logic_error runtime_error?
length_error
out_of_range bad_alloc bad_cast range_error
invalid_argument bad_exception overflow_error
domain_error ios_base:failure underflow_error

CMyException(自定义异常基类) CSEHException(结构化异常基类)?

CRecoverableSEHException CUnRecoverableSEHException

CSocketException(与socket相关的异常)?
CConfigException(与配置文件相关的异常)
注:CMyException上面的异常类均为标准C++的异常类。
注:以上异常类的基类均为exception。


本人实现的具有堆栈跟踪的C++异常类库和测试程序可以从www.smiling.com.cn中的umlchina小组中下载StackTraceInC.zip文件。
3. 如何使用C++异常中的堆栈跟踪类库
下载的文件包括Exception.h Exception.cpp(具有堆栈跟踪功能的异常类库), main.cpp, Test1.h, Test1.cpp (测试代码)。
让我们先感受一下堆栈跟踪的威力,运行下载的示例程序,将打印出如下结果(因为输出太长,所以只节选了其中一部分)。主程序为:
void main(){

// 在每个线程函数的入口加上以下语句。
// 检查内存泄露。
CWinUtil::vCheckMemoryLeak();
// 使new函数失败时抛出异常。
CWinUtil::vSetThrowNewException();?
// 把WINDOWS中的结构化异常转化为C++异常。
CWinUtil::vMapSEHtoCE();
// 初始化。
CWinUtil::vInitStackEnviroment();
try {
// 捕获非法访问内存的结构化异常。
int* pInt; // 故意不分配内存
*pInt = 5; // 应该显示出这一行出错。
}
// 捕获可恢复的结构化异常。
catch ( const CRecoverableSEHException &bug ) {
cout << bug.what() << endl;
}
// 捕获不可恢复的结构化异常。
catch ( const CUnRecoverableSEHException &bug ) {
cout << bug.what() << endl;
}
// 捕获标准C++异常。
catch ( const exception& e ) {
cout << e.what() << endl;
}
// 捕获其它不是继承exception的异常。
catch ( ... ) {
cout << " else exception." << endl;
}
try {
// 捕获自定义的异常。
throw CMyException( " my exception" ); // 应该显示出这一行出错。
}
catch ( const CRecoverableSEHException &bug ) {
cout << bug.what() << endl;
}
catch ( const CUnRecoverableSEHException &bug ) {
cout << bug.what() << endl;
}
catch ( const exception& e ) {
cout << e.what() << endl;
}
catch ( ... ) {
cout << " else exception." << endl;
}
try {
// 捕获函数中的异常。
vDivideByZero(); // 应该显示出这个函数抛出的异常。
int i = 1;
}
catch ( const CRecoverableSEHException &bug ) {
cout << bug.what() << endl;
}
catch ( const CUnRecoverableSEHException &bug ) {
cout << bug.what() << endl;
}
catch ( const exception& e ) {
cout << e.what() << endl;
}
catch ( ... ) {
cout << " else exception." << endl;
}
try {
// 捕获另一源文件Test1.cpp中的函数抛出的异常。
vTestVectorThrow();// 应该显示出在这个函数抛出的异常。
int i = 1;
}
catch ( const CRecoverableSEHException &bug ) {
cout << bug.what() << endl;
}
catch ( const CUnRecoverableSEHException &bug ) {
cout << bug.what() << endl;
}
catch ( const exception& e ) {
cout << e.what() << endl;
}
catch ( ... ) {
cout << "else exception." << endl;
}
int i;
cin >> i; // 防止无意中按键使程序退出。
}
对于第1个异常输出为:
0 .V 004066d5 0040779f 0012ff70 00000000 _main + 85 bytes
Sig: _main
Decl: _main
Line: H:\C++ Test\StackWalk\Test\main.cpp(50) + 3 bytes
Mod: Test[H:\C++ Test\StackWalk\Test\Debug\Test.exe], base: 0x00400000h
Sym: type: PDB, file: H:\C++ Test\StackWalk\Test\Debug\Test.exe
从上面第4行可以知道在main.cpp文件的第50行抛出的异常,找到这一行就是*pInt = 5;然后检查上下文,哦,没有分配内存,于是“臭名昭著”的非法内存访问就轻易发现了!Is it powerful?
对于第2个异常输出为:
1 .V 0040683d 0040779f 0012ff70 00000000 _main + 445 bytes
Sig: _main
Decl: _main
Line: H:\C++ Test\StackWalk\Test\main.cpp(72) + 49 bytes
Mod: Test[H:\C++ Test\StackWalk\Test\Debug\Test.exe], base: 0x00400000h
Sym: type: PDB, file: H:\C++ Test\StackWalk\Test\Debug\Test.exe
从上面第4行可以知道在main.cpp文件的第72行抛出的异常,找到这一行就是throw CMyException( " my exception" ); 哦,是自定义异常。
对于第3个异常输出为:
0 .V 004065f5 0040697c 0012fe98 00000000 void __cdecl vDivideByZero(void) + 37 b
ytes
Sig:
Decl: void __cdecl vDivideByZero(void)
Line: H:\C++ Test\StackWalk\Test\main.cpp(26) + 6 bytes
Mod: Test[H:\C++ Test\StackWalk\Test\Debug\Test.exe], base: 0x00400000h
Sym: type: PDB, file: H:\C++ Test\StackWalk\Test\Debug\Test.exe

1 .V 0040697c 0040779f 0012ff70 00000000 _main + 764 bytes
Sig: _main
Decl: _main
Line: H:\C++ Test\StackWalk\Test\main.cpp(100) + 0 bytes
Mod: Test[H:\C++ Test\StackWalk\Test\Debug\Test.exe], base: 0x00400000h
Sym: type: PDB, file: H:\C++ Test\StackWalk\Test\Debug\Test.exe
从上面第5行可以知道在main.cpp文件的第26行抛出的异常,找到这一行就是int iRet = 5 / iZero; 哦,是除零异常。然后从上面第12行可以知道调用这个函数是在main.cpp文件的第100行,就是vDivideByZero();的下一行(注意因为vDivideByZero();函数已经调用了,所以显示的行数都是它的下一行)。这样,我们就可以知道一个异常发生的完整过程。
对于第4个异常输出为:
0 .V 004070ca 00406ab9 0012fe98 00000000 void __cdecl vTestVectorThrow(void) + 7
4 bytes
Sig:
Decl: void __cdecl vTestVectorThrow(void)
Line: h:\c++ test\stackwalk\test\test1.cpp(13) + 10 bytes
Mod: Test[H:\C++ Test\StackWalk\Test\Debug\Test.exe], base: 0x00400000h
Sym: type: PDB, file: H:\C++ Test\StackWalk\Test\Debug\Test.exe

1 .V 00406ab9 0040779f 0012ff70 00000000 _main + 1081 bytes
Sig: _main
Decl: _main
Line: H:\C++ Test\StackWalk\Test\main.cpp(118) + 0 bytes
Mod: Test[H:\C++ Test\StackWalk\Test\Debug\Test.exe], base: 0x00400000h
Sym: type: PDB, file: H:\C++ Test\StackWalk\Test\Debug\Test.exe
从上面第5行可以知道在test1.cpp文件的第13行抛出的异常,找到这一行就是 vectInt[ 3 ] = 100; 检查上下文,发现没有给vectInt分配空间。然后从上面第12行可以知道调用这个函数是在main.cpp文件的第118行,就是vTestVectorThrow();的下一行。
那么如何使用这个类库呢?对于新工程,首先把exception.h和exception.cpp加入工程,你需要把自定义的异常类继承自CMyException,这样自定义的异常类就具有堆栈跟踪功能,其次在每个线程的入口函数加上以下几个函数调用(注意:必须在每个线程的入口都要调用,当然如CWinUtil::vInitStackEnviroment();不需要,只需要在main入口即可,但如果在每个线程的入口都要调用也不会有副作用):
` // 在每个线程函数的入口加上以下语句。
// 检查内存泄露。
CWinUtil::vCheckMemoryLeak();
// 使new函数失败时抛出异常。
CWinUtil::vSetThrowNewException();?
// 把WINDOWS中的结构化异常转化为C++异常。
CWinUtil::vMapSEHtoCE();
// 初始化。
CWinUtil::vInitStackEnviroment();
然后如下所示捕获异常:
try {
vTest();// 假设要捕获vTest()函数可能抛出的异常。
}
catch ( const CRecoverableSEHException &bug ) {// 用于捕获可恢复的结构化异常。
cout << bug.what() << endl;
}
catch ( const CUnRecoverableSEHException &bug ) {// 用于捕获不可恢复的结构化异常。
cout << bug.what() << endl;
}
catch ( const exception& e ) {// 用于捕获标准C++异常及其子类。
cout << e.what() << endl;
}
catch ( ... ) { // 用于捕获那些抛出非结构化异常和不是继承exception的异常。
cout << " else exception." << endl;
}
当然你对于结构化异常没有其它特别的处理策略,也可以简化为:
try {
vTest();// 假设要捕获vTest()函数可能抛出的异常。
}
// 用于捕获标准C++异常及其子类。因为结构化异常继承自exception,所以这里也能捕获// 结构化异常。
catch ( const exception& bug ) {
cout << bug.what() << endl;
}

对于已有的工程,首先把exception.h和exception.cpp加入工程,把原来的自定义的异常类继承自CMyException,然后同上的方法捕获异常,每个线程入口增加初始化函数即可,可以与你原来的异常处理完美集成。
对于在MFC中的用法,可以按如下方式捕获异常:
try {
vTest();
}
catch ( const exception& e ) {
cout << e.what() << endl;
}?
// CException是MFC中异常基类,MFC中的异常通常从堆中分配,所以应通过指针捕获,// 而且使用完之后还应该调用delete函数清除内存。
catch ( CException* e ) {
// hadle exception
e->delete();?
}
即在MFC异常上加一层捕获标准C++异常和结构化异常以及自定义异常。另外由于MFC中已经自动有了处理内存泄露的机制,所以需要删除exception.h文件的第34行到第40行(有关内存泄露的说明见下面),由于在MFC中每个.cpp文件开始处都要包含stdafx.h,所以还需要在exception.cpp文件开始处加上#include “stdafx.h”,不然会编译不通过。
如果你希望把堆栈信息输出在文件中,以防丢失,可以使用IO重定向功能(有关IO重定向可参考Jim Hyslop and Herb Sutter的文http://www.cuj.com/experts/1903/hyslop.htm ),即在main()函数开头加入以下语句:
ofstream ofLog( "exception.txt", ios_base::app );
streambuf *outbuf = cout.rdbuf( ofLog.rdbuf() );
这样,所有输出到console的信息就重定向到exception.txt文件中了。如果你想恢复,则可以加入以下语句:
// restore the buffers
cout.rdbuf( outbuf );
对于release版本,如果你运行,你会发现程序不能捕获非法内存访问、除零等结构化异常,这是因为VC在release版默认是同步异常,不捕获结构化异常,只能捕获C++的异常,所以你需要修改编译选项,采用异步异常模型,在project->setting->c/c++->project options框中增加/EHa的编译选项。另外,release版默认不生成调试符号文件,这样你就不能不能打印出抛出异常的代码的行号等信息,所以你需要修改编译配置,方法如下:Project->Settings->c/c++页中的debug info列表选项中选择program database项。这样release版本也能实现堆栈跟踪。当然,这样会使release版本减慢速度,而且还要带一个debug info文件,因为有些bug只有在release版本中才会出现,而且release版是真正给客户使用的,所以必须测试release版,可以考虑release的beta1和beta2版本带这些调试信息,这样的话,因为debug版和release版都测试通过,发行给客户的最终正式版可以通过设置一个宏注释掉这些调试信息,恢复成同步异常模型,即恢复成VC默认的release版配置。
4. 其他需要注意的问题
本类库还有检查内存泄露的功能,只要你在每个.cpp文件的所有#include之后,加上以下语句:
// 以下几行是能够定义到发生内存泄露的代码行。在每个.cpp文件都应该声明。
#include “Exception.h”
#ifdef _DEBUG?
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
然后以debug方式启动程序,当程序正常退出或关闭时(注意不能用stop debug的命令停止,否则将不会打印出内存泄露的信息),在VC的debug窗口将会打印出有可能产生内存泄露的源代码信息,包括文件名和行号。由于MFC程序自动会生成这些代码,所以在MFC程序中不需要手工添加这些代码。例如,当你以debug方式运行下载的测试程序,当程序正常退出后,在debug窗口会显示如下语句:
Detected memory leaks!
Dumping objects ->
H:\C++ Test\StackWalk\Test\main.cpp(88) : {184} normal block at 0x00632D50, 100 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD?
Object dump complete.
从上面第3行可以知道在main.cpp文件的第88行产生了内存泄露,找到这一行就是 char* pcLeak = new char[ 100 ]; 检查上下文,发现果然没有释放内存。
当然如同你使用Purify、BoundsChecker等工具检查内存泄露一样,它也会谎报军情,有些不会内存泄露的地方,它也告诉你内存泄露了,尤其当你使用了大量STL类库时,这就需要你细心检查上下文,以确定是否是内存泄露了。
本类库由于使用了一些VC中特有调试符号特性,所以可能不能在其它编译器下通过。另外,本文讨论的堆栈跟踪实现都是基于Windows 2000以上,Win98和Win95将不能输出导致抛出异常的语句所在的文件名和行号。本类库也不能在Unix或Linux下运行。有在Unix或Linux平台工作的读者朋友如果有兴趣,可以实现一个在Unix或Linux平台运行的C++异常堆栈跟踪类库。
本类库不能跟踪标准C++异常及其它你不能控制的异常的堆栈信息,即当这些异常抛出时,不能输出抛出异常语句的文件名和行号信息。这是因为标准C++异常是语言内置的,而其它类库的异常你不能控制其构造函数。这是一个小小的遗憾,不过你可以通过异常说明知道它是哪一种异常,可能在程序的哪一些C++函数中抛出,这样你也能很快地找到错误之处。
有了完善的异常类层次,你可以在程序中干任何事,异常过滤机制和堆栈跟踪会忠实的记录任何错误,除了你在析构函数抛出异常以及重复删除不为NULL的指针,这两种情况下程序还是会当掉,而且不能记录堆栈信息。当然,对于缓冲区溢出、堆写越界等情况,本类库还是无能为力,不过,你可以借助Purify、BoundsChecker等工具来检查程序运行中是否有这类问题。
5. 小结
谁说C++就不能有堆栈跟踪的功能。有了这个具有堆栈跟踪功能的异常类库,你将如虎添翼,必将加快你开发程序的进程。
Enjoy!

http://hi.baidu.com/barrypp/item/6d63573c580995637d034bc2
C++异常中的堆栈跟踪 C++异常中的堆栈跟踪 C++异常中的堆栈跟踪就是当程序抛出异常时,能够把抛出异常的语句所在的文件名、函数以及其它上层函数信息都打印出来。 堆栈跟踪意义重大:在实际的生产过程中,发现代码中bug要比解决bug更加费事费力,而发生异常时的堆栈信息可以帮助我们更快的发现程序中的问题,有助于加快开发。 C++不像java,在异常类中有提供堆栈跟踪功能,C++标准库中的std::exception及其子类... 阅读详情

相关推荐

C++异常中的堆栈跟踪 [转载]

C++异常中的堆栈跟踪C++语言的运行时环境是基于栈的环境,堆栈跟踪(trace stack)就是程序运行时能够跟踪并打印所调用的函数、变量及返回地址等,C++异常中的堆栈跟踪就是当程序抛出异常时,能够把导致抛出异常的语句所在的文件名和行号打印出来,以及把调用抛出异常的语句的函数以及其它上层函数信息都打印出来。1. 为什么需要堆栈跟踪当你在开发程序时,你是否曾遇到过程序运行

wuhf的专栏 1697

VC++ 崩溃处理以及打印调用堆栈

我们在程序发布后总会面临崩溃的情况,这个时候一般很难重现或者很难定位到程序崩溃的位置,之前有方法在程序崩溃的时候记录dump文件然后通过windbg来分析。那种方法对开发人员的要求较高,它需要程序员理解内存、寄存器等等一系列概念还需要手动加载对应的符号表。Java、Python等等语言在崩溃的时候都会打印一条异常的堆栈信息并告诉用户那块出错了,根据这个信息程序员可以很容易找到对应的代码位置并进行处...

Masimaro的专栏 3919

C++ 捕获异常崩溃全流程

在程序崩溃后,程序的状态是不确定的,可能会存在一些问题,如内存泄漏、资源未释放等。因此,直接在崩溃后的程序中上传服务器可能会存在风险,可能会加重程序的负担,导致程序异常退出。另外,对于一些轻微的错误或者性能要求比较高的代码,使用返回错误码的方式也可以提高程序的执行效率。需要注意的是,exec() 系列函数执行成功后,将不会返回,因此在调用 execl() 之后的代码将不会被执行。同时,在上传过程中要注意数据的传输安全,以及上传数据的完整性和准确性,以确保开发者能够及时准确地获取到程序崩溃的信息。

milan-xiao-tiejiang的博客 6674

[转]C++异常中的堆栈跟踪

<br />C++语言的运行时环境是基于栈的环境,堆栈跟踪(trace stack)就是程序运行时能够跟踪并打印所调用的函数、变量及返回地址等,C++异常中的堆栈跟踪就是当程序抛出异常时,能够把导致抛出异常的语句所在的文件名和行号打印出来,以及把调用抛出异常的语句的函数以及其它上层函数信息都打印出来。<br />    1. 为什么需要堆栈跟踪<br />    当你在开发程序时,你是否曾遇到过程序运行过程中突然当机,而你不知道哪一行代码出的问题;你是否曾遇到过程序调试过程中突然抛出异常,而你不知道哪一行代

游戏人生的专栏 1532

解决堆栈报错问题的编程学习

在编程中,堆栈报错指的是当程序执行过程中发生错误时,系统会生成一个堆栈跟踪,其中包含了错误发生的位置和调用堆栈上的函数调用关系。堆栈报错通常以异常的形式抛出,提供了一种调试和定位问题的方法。当程序发生错误时,它会引发一个异常。异常包含有关错误的信息,例如错误类型、错误消息和堆栈跟踪。堆栈跟踪是一个记录函数调用关系的列表,它从错误发生的地方开始,一直追踪到程序的起点。

database_bug768的博客 965

StackWalkerExeption:C ++中的堆栈跟踪

StackWalkerExeption C ++中的堆栈跟踪 此自由使用了并添加了在引发异常后从异常访问堆栈跟踪的功能。 用法 在将StackWalker librery添加到您的项目中之后,添加Exception.h和Exception.cpp并包含头文件。 #include "Exception.h" try { throw Exception("Example exception") } catch(Exception& ex) { cout << ex.what() << endl; cout << ex.getStackTrace() << endl; } 它仍在进行中。 将会增加更多的老师,希望Windows和Linux上的GCC支持。

堆栈跟踪

C++语言的运行时环境是基于栈的环境,堆栈跟踪(trace stack)就是程序运行时能够跟踪并打印所调用的函数、变量及返回地址等,C++异常中的堆栈跟踪就是当程序抛出异常时,能够把导致抛出异常的语句所在的文件名和行号打印出来,以及把调用抛出异常的语句的函数以及其它上层函数信息都打印出来。 1. 为什么需要堆栈跟踪     当你在开发程序时,你是否曾遇到过程序运行过程中突然当机,而你不知道

n567656756765676567的专栏 2298

解决创建conda环境时Solving environment: failed 和 ResolvePackageNotFound 的错误

环境配置文件是在别的机器上导出的,文件里的软件包是按照导出文件的机器配置的,因此导出的时候,把这些包的详细信息也导了出来,即第二个等号后面跟着的信息,当我们按照这个文件创建环境时,conda就会根据我们机器的配置信息,按照配置文件里包的详细信息,去找这个包,那当然找不到,结果发现“查无此包”,因此也就会报上面的错误。上面找到了问题的所在之处,解决起来就很简单了,只需要在配置文件 environment.yml 中将包的详细信息删除,也就是第二个等号后面的内容。然而升级超级慢不说,也没有解决我的问题。...

番茄炒蛋三分糖 4万+

backward-cpp:C++开发者必备的堆栈跟踪美化神器

在C++开发过程中,当程序崩溃或抛出异常时,传统的堆栈跟踪信息往往难以阅读和理解。backward-cpp正是为解决这一问题而生的强大工具,它能够将晦涩的堆栈跟踪信息转换为清晰、美观、易于分析的格式,显著提升调试效率。 ## 核心概念:从混乱到清晰 back栈跟踪是调试过程中最重要的信息来源之一,它展示了程序崩溃时函数调用的完整路径。然而,标准的堆栈跟踪输出通常包含内存地址、未解析的函数符号和

gitblog_00693的博客 534

笔记汇总 | 斯坦福 CS229 机器学习

本文为斯坦福大学 2014 秋季 CS229 机器学习课程学习笔记本文主体部分转载自黄海广博士,文末已给出,大家有兴趣可以直接访问笔记首页,下载对应课程资料及作业代码。

ReturnTmp的博客 2463

【论文速读】MLDT:基于开源大语言模型的复杂长时机器人任务规划多层次分解方法

—在数据驱动的人工智能技术领域,开源大语言模型(LLMs)在机器人任务规划中的应用是一个重要的里程碑。近期基于开源大语言模型的机器人任务规划方法通常利用大量的任务规划数据集来提升模型的规划能力。虽然这些方法展现出了一定的前景,但它们在处理复杂长时任务时存在困难,因为这类任务需要理解更多的上下文信息并生成更长的动作序列。本文提出多层次分解任务规划方法(MLDT)来解决这一局限性。该方法创新性地在目标级、任务级和动作级对任务进行分解,以应对复杂长时任务带来的挑战。

muyulingfengye的博客 1645

[Backward-cpp] 优雅地打印堆栈跟踪信息——Backward-cpp

C/C++编程的同学经常会遇到程序出现段错误而 Backward 会将堆栈信息打印出来,帮助定位。有点像gdb时的backtrace只有当源文件可访问时,它才能显示代码片段。所有以管道“|”为前缀的“源”行和代码片段都是内联下一帧的帧。项目源码本质上只有文件,集成到自己的程序中非常方便,如果加入文件一起编译,则自己代码中不需要调用Backward-cpp中的函数,非常方便。

qq_43331089的博客 180

【Creo】关于Creo一换网络就报错许可证丢失问题解决方案

依照我的个人理解,Creo只要一更换网络,则其许可证里面的网卡参数和现在的网络就对不上了,所以就会出现报错,并且有可能是报错代码(-9),提示许可证激活有问题。

weixin_52002180的博客 6037
上一篇: Net::Telnet使用小记
下一篇: 如何识别代码是否被inline了?
fibbery
博客等级 码龄25年 304粉丝 32原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值