GDB가 줄 사이를 예측할 수없이 점프하고 변수를 "<value optimize out>"으로 출력하는 이유는 무엇입니까?


84

누구든지 gdb의이 동작을 설명 할 수 있습니까?

900         memset(&new_ckpt_info,'\0',sizeof(CKPT_INFO));
(gdb)
**903         prev_offset   = cp_node->offset;**
(gdb)
**905         m_CPND_CKPTINFO_READ(ckpt_info,(char *)cb->shm_addr.ckpt_addr+sizeof(CKPT_** HDR),i_offset);
(gdb)
**903         prev_offset   = cp_node->offset;**
(gdb)
**905         m_CPND_CKPTINFO_READ(ckpt_info,(char *)cb->shm_addr.ckpt_addr+sizeof(CKPT_ HDR),i_offset);**
(gdb)
**908         bitmap_offset  = client_hdl/32;**
(gdb)
**910         bitmap_value = cpnd_client_bitmap_set(client_hdl%32);**
(gdb)
**908         bitmap_offset  = client_hdl/32;**
(gdb)
**910         bitmap_value = cpnd_client_bitmap_set(client_hdl%32);**
(gdb)
**908         bitmap_offset  = client_hdl/32;**
(gdb)
**910         bitmap_value = cpnd_client_bitmap_set(client_hdl%32);**
(gdb)
913         found = cpnd_find_exact_ckptinfo(cb , &ckpt_info , bitmap_offset , &offset , &prev_offset);
(gdb)
916         if(!found)
(gdb) p found
$1 = <value optimized out>
(gdb) set found=0
Left operand of assignment is not an lvalue.

903 행을 실행 한 후 905908910에 대해 동일한 작업을 다시 실행하는 이유는 무엇입니까?

또 다른 물건은 foundA는 bool, 타입 변수 왜 그것을 보여주고있다 value optimized out? 의 값도 설정할 수 없습니다 found.

이것은 컴파일러 최적화 인 것 같습니다 (이 경우에는 -O2). 여전히 값을 found어떻게 설정할 수 있습니까?


8
디버깅 할 때 일반적으로 -O0을 사용하여 컴파일하는 것이 좋습니다. 최적화하면 이러한 종류의 문제가 발생합니다.
LiraNuna

답변:


115

최적화 된 코드를 디버깅하려면 어셈블리 / 기계 언어를 배우십시오.

GDB TUI 모드를 사용하십시오. 마이너스와 Enter를 입력하면 GDB 사본이 활성화됩니다. 그런 다음 Cx 2를 입력합니다 (즉, Control을 누른 상태에서 X를 누른 다음 둘 다 놓은 다음 2를 누릅니다). 그러면 분할 소스 및 디스 어셈블리 디스플레이가됩니다. 그런 다음 및를 사용 stepi하여 nexti한 번에 하나의 기계 명령을 이동하십시오. Cx o를 사용하여 TUI 창 사이를 전환합니다.

CPU의 기계어 및 함수 호출 규칙에 대한 PDF를 다운로드하십시오. 함수 인수 및 반환 값으로 수행되는 작업을 빠르게 인식하는 방법을 배우게됩니다.

다음과 같은 GDB 명령을 사용하여 레지스터 값을 표시 할 수 있습니다. p $eax


여전히 "최적화"문제가 있고 변수 값이 다른 창에 표시되지 않지만 그래도 좋은 정보입니다. 감사합니다!
Tom Brito 2013

16
@TomBrito : 최적화는 변수가 메모리에 없다는 것을 의미합니다. 아마도 CPU 레지스터에만있을 것입니다. 즉, 디스 어셈블리를 읽고 레지스터 값을 인쇄해야 찾을 수 있습니다.
Zan Lynx

@Zan Lynx : 귀하의 분석에 동의하는지 잘 모르겠습니다. DWARF 심볼에는 레지스터에서 값을 추출하기에 충분한 정보가 있습니다. 아마도 여기서 의미하는 것은 컴파일러가 실행이 현재 줄에 도달 할 때까지 변수를 안전하게 버릴 수 있다고 결정했음을 의미합니다. 이 경우 변수가있는 저장소는 아마도 다른 용도로 재사용되었을 것입니다. 나는 이것이 일반적으로 변수가 등록 된 경우에만 발생한다는 것이 맞다고 생각합니다.
Ian Ni-Lewis

@ IanNi-Lewis : 어떤 버전의 DWARF를 사용 중인지 모르겠지만 제 경험상 GDB는 레지스터에 저장된 변수를 인쇄 할 수 없습니다.
Zan Lynx

나는 당신이 옳다고 확신합니다. DWARF에 대한 나의 경험은 gdb를 사용하는 것이 아니라 내 자신의 파서를 작성하는 데서 비롯 되었기 때문에 gdb가 무엇을 할 수 있는지 잘 모릅니다.
Ian Ni-Lewis

75

최적화없이 재 컴파일합니다 (gcc의 -O0).


17
-O0조차도 최적화 된 코드를 생성 할 수 있습니다 (지금 당장이 문제로 어려움을 겪고 있음). 이유는 모르겠지만.
Chris Gregg

@ChrisGregg 같은 문제가 생겼습니다! 문제가 무엇인지 알아 내셨습니까?
Paolo M

1
@paolom 그것은 clang 문제로 보일 수 있으므로 불행히도 디버깅 목적으로 g ++로 컴파일했습니다.
Chris Gregg

종종 이것은 해결책이 아닙니다. 특히 프로덕션에서 코어 덤프가 있거나 개발 환경에서 문제를 재현 할 수없는 경우에 그렇습니다.
smbear

39

발견 된 것을 "휘발성"으로 선언하십시오 . 이것은 컴파일러에게 최적화하지 않도록 지시해야합니다.

volatile int found = 0;

1
gdb 디버거에서 일부 변수를 "휘발성"으로 선언해도 최적화 된 변수로 표시됩니다! 이것에 더 이상 있습니까?
M.Rez

11

컴파일러는 최적화가 켜져있는 상태에서 매우 영리한 작업을 시작합니다. 디버거는 변수가 레지스터에 저장되는 최적화 된 방식으로 인해 앞뒤로 점프하는 코드를 많이 보여줍니다. 이것이 디버거가 액세스 할 수있는 직접 메모리 위치가 아닌 레지스터간에 영리하게 분산되어 있기 때문에 변수를 설정할 수없는 (또는 경우에 따라 값을 볼 수없는) 이유 일 것입니다.

최적화없이 컴파일 하시겠습니까?


6

일반적으로 이와 같이 계산 된 직후 분기에서 사용되는 부울 값은 실제로 변수에 저장되지 않습니다. 대신 컴파일러 는 이전 비교에서 설정된 조건 코드에서 직접 분기합니다 . 예를 들면

int a = SomeFunction();
bool result = --a >= 0; // use subtraction as example computation
if ( result ) 
{
   foo(); 
}
else
{
   bar();
}
return;

일반적으로 다음과 같이 컴파일됩니다.

call .SomeFunction  ; calls to SomeFunction(), which stores its return value in eax
sub eax, 1 ; subtract 1 from eax and store in eax, set S (sign) flag if result is negative
jl ELSEBLOCK ; GOTO label "ELSEBLOCK" if S flag is set
call .foo ; this is the "if" black, call foo()
j FINISH ; GOTO FINISH; skip over the "else" block
ELSEBLOCK: ; label this location to the assembler
call .bar
FINISH: ; both paths end up here
ret ; return

"bool"이 실제로 어디에도 저장되지 않는지 확인하십시오.


4

당신은 발견의 가치를 거의 설정할 수 없습니다. 최적화 된 프로그램을 디버깅하는 것은 문제의 가치가 거의 없습니다. 컴파일러는 소스 코드와 일치하지 않는 방식으로 코드를 재 배열 할 수 있습니다 (동일한 결과를 생성하는 것 제외). 따라서 디버거를 끝까지 혼란스럽게 만들 수 있습니다.


4

최적화 된 프로그램을 디버깅 할 때 (버그가 디버그 빌드에 나타나지 않는 경우 필요할 수 있음) 종종 생성 된 어셈블리 컴파일러를 이해해야합니다.

귀하의 특별한 경우,의 반환 값은 반환 값 cpnd_find_exact_ckptinfo을 위해 플랫폼에서 사용되는 레지스터에 저장됩니다. 에 ix86, 그 것이다 %eax. On x86_64: %rax, etc. 위의 경우에 해당하지 않는 경우 '[귀하의 프로세서] 프로 시저 호출 규칙'을 Google에 표시해야 할 수 있습니다.

해당 레지스터를 검사하고 GDB설정할 수 있습니다. 예 ix86:

(gdb) p $eax
(gdb) set $eax = 0 

0

gdb와 함께 QtCreator를 사용하고 있습니다.

첨가

QMAKE_CXXFLAGS += -O0
QMAKE_CXXFLAGS -= -O1
QMAKE_CXXFLAGS -= -O2
QMAKE_CXXFLAGS -= -O3

나를 위해 잘 작동

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.