前几天我在代码中遇到了一个奇怪的错误,显然变量pi
(见下文)的值为0,因为行lat += 0.5 * pi
什么也没做,但lat += 1
工作了正如所料。更改编译器版本后(从CentOS 6上的GCC 5.3.1到GCC 6.2.1),这个bug就消失了,但最让我感兴趣的是当使用gdb时,到达该位置的设定断点时,{{1}输出预期的print pi
,但仍然3.141
没有以任何方式更改lat += 0.5 * pi
的值。在内部,不知何故,gdb中lat
的值与程序中的值不匹配。
在更改我的工具链时,错误消失并且我不知道错误来自哪里(可能是我做错了或者至少以不好的方式),这是非常不满意的。但是,在什么情况下我不能相信gdb中给我的价值观? pi
的外部链接是否可以用于调试目的?
pi
Code.cpp是main.cpp链接的静态库的一部分。
编辑:使用的Makefile是
// Constant.h
#pragma once
namespace abc {
const double pi = 3.141;
}
// Code.h
#pragma once
namespace abc {
extern const double pi;
int lat_func(double& lat);
}
// Code.cpp
#include "Constant.h"
#include "Code.h"
#include <iostream>
namespace abc {
int lat_func(double& lat) {
std::cout << "pi: " << pi << std::endl; // `pi: 0`
// When setting a breakpoint here, `p pi` returns `3.141` in gdb
lat += 0.5 * pi; // lat is unchanged
lat += 1; // 1 is added to lat
return 0;
}
}
// main.cpp
#include "Code.h"
#include <iostream>
int main()
{
double lat = 0.5;
abc::lat_func(lat);
std::cout << lat << std::endl;
}
编辑:正如评论中所建议的那样,这是行INCLUDES = -I.
LIBS = -L. -lcode
CC = g++
CCFLAGS = -g -O0 -Wall -Wextra -pedantic -Wno-write-strings -Wno-unknown-pragmas -Wall -Wextra -pedantic -std=c++11
all: libcode.a main
main: main.o
$(CC) -o $@ $^ $(LIBS)
libcode.a: Code.o
ar cr $@ $^
%.o: %.cpp
$(CC) $(CCFLAGS) $(INCLUDES) -fPIC -c -o $@ $<
的反汇编代码。两者之间的差异(据我所知)是在GCC 5.3.1中,lat += 0.5 * pi
在一个点被调用,而在GCC 6.2.1中,这变为pxor %xmm2,%xmm2
。
GGC 5.3.1
movsd 0xcfee57d(%rip),%xmm2
GCC 6.2.1
lat += 0.5 * pi;
=> 0x0000000000f1c22c <+312>: mov -0x20(%rbp),%rax
0x0000000000f1c230 <+316>: movsd (%rax),%xmm1
0x0000000000f1c234 <+320>: pxor %xmm2,%xmm2
0x0000000000f1c238 <+324>: movsd 0x1269a0(%rip),%xmm0 # 0x1042be0
0x0000000000f1c240 <+332>: mulsd %xmm2,%xmm0
0x0000000000f1c244 <+336>: addsd %xmm1,%xmm0
0x0000000000f1c248 <+340>: mov -0x20(%rbp),%rax
0x0000000000f1c24c <+344>: movsd %xmm0,(%rax)
答案 0 :(得分:1)
GDB和GCC肯定存在错误(必须提供正确的DWARF信息,否则GDB将无效)。
您是如何确定执行lat += 0.5 * pi;
语句的?经常发生的事情(至少在优化的代码中)是你跨过这样的一条线,但是GDB只执行它的部分(比如,只是乘法,或地址的计算),并最终在几次之后返回它脚步。然后可以执行添加,然后执行添加。
像这样的复杂语句会导致多个机器指令,GCC会独立地对它们进行调度,并将它们与其他指令交错。通过GCC当前为优化代码生成调试信息的方式,无法以使用GDB进行调试的方式来表示这一点。
可以说,这是一个GCC / GDB错误,但它并不容易修复。最近的博客文章Statement Frontier Notes and Location Views解释了GNU工具链人员如何解决这个问题。
编辑在考虑了这个之后,最可能的解释是,这只是一个GCC错误,它只是不能正确处理pi
变量。 GDB查看内存中的变量,并查找存储在那里的预期值,但由于某种原因,生成的机器代码GCC无法正确加载它。如果这是一个编译器错误,那么推测GDB行为是没用的,因为任何事情都可能发生在编译器错误上(当遇到这样的错误时,你可以做的唯一调试就是查看生成的机器代码和转储包含中间编译器信息的文件。)