2011-01-17 3 views
14

우리 소프트웨어에서 이상한 충돌이 발생하고 디버깅하는 데 많은 어려움을 겪고 있습니다. 따라서 이 문제를 해결하는 방법에 대한 조언을 구하고 있습니다. $ 00CF0041에서유용한 호출 스택이없는 재생산하기 어려운 크래시를 어떻게 디버깅합니까?

첫 번째 예외 :

충돌은 NULL 포인터를 읽기 액세스 위반이다. 예외 클래스 $ C0000005 메시지가 '0x00cf0041에서 액세스 위반 : 주소 0x00000000의 읽기'.

'가끔'만 발생합니다. 나는 어떤 운이나 이유를 알아낼 수 없었지만, 언제나 메인 스레드에서만 가능합니다. 메인 스레드에 대한

Call stack with one line, Classes::TList::Get, address 0x00cf0041

,이 인은 다른 항목의 전체 큰 스택을 표시해야합니다 : 그것은 발생하면 호출 스택은 하나 개의 잘못된 항목이 포함되어 있습니다.

이 시점에서 다른 모든 스레드는 비활성 상태입니다 (주로 WaitForSingleObject 또는 이와 유사한 함수에 있음).이 충돌은 주 스레드에서만 발생하는 것으로 나타났습니다. 동일한 주소에서 동일한 메소드에서 항상 동일한 항목의 호출 스택을가집니다. 이 방법은 관련이있을 수도 있고 아닐 수도 있습니다. 우리는 우리의 어플리케이션에서 VCL을 사용합니다. 내 베팅은 스택 (stack)을 손상시키고있는 무언가 (아마도 꽤 오래 전부터)가 충돌하는 주소가 무작위 적이라는 것입니다. 하지만 여러 빌드에서 동일한 주소가 사용되었지만 실제로는 무작위가 아닐 수도 있습니다. 여기

내가 무엇을 시도했다입니다 :

  • 가 특정 지점에서 안정적으로 재현하려고합니다. 나는 매번 그것을 재현하는 것을 발견하지 못했고, 명백한 이유가없는 가끔은 두 가지를하거나하지 않았다. 이것들은 코드의 특정 섹션으로 좁힐 수있는 충분한 '협의'액션이 아닙니다. 그것은 타이밍과 관련이있을 수 있지만, IDE가 깨지면 다른 스레드는 일반적으로 아무것도하지 않습니다. 스레딩 문제는 배제 할 수는 없지만 가능성은 희박하다고 생각합니다.
  • 추가 디버깅 문으로 작성 (추가 디버그 정보, 추가 어설 션 등) 이렇게하면 충돌이 발생하지 않습니다.
  • Codeguard이있는 건물. 이렇게하면 충돌이 발생하지 않으며 Codeguard는 오류를 표시하지 않습니다.

내 질문 :

1. 어떻게 충돌의 원인 코드를 찾을 수 있습니까? 스택을 걷는 것과 동일한 조치를 수행하려면 어떻게해야합니까?

2.이 충돌의 원인을 추적하는 방법에 대한 일반적인 조언은 무엇입니까? 내가 Embarcadero RAD Studio 2010을 사용하고

은 (프로젝트는 대부분 C++ 빌더 코드와 델파이의 소량이 포함되어 있습니다.)

편집 : 내가 실제로 원인을 추가해야합니다 생각했다. ReadDirectoryChangesW을 호출 한 스레드가 있고 GetOverlappedResult을 사용하여 계속 이벤트를 기다렸다가 변경 사항을 수행합니다.이벤트는 또한 상태 플래그를 설정 한 후 스레드를 종료하기 위해 신호를 보냈습니다. 문제는 스레드가 종료되었을 때 결코 CancelIO을 호출하지 않는다는 것입니다. 결과적으로 Windows는 여전히 변경 내용을 추적하고 있었고 버퍼가 겹쳐진 구조와 이벤트가 더 이상 존재하지 않거나 (심지어는 만든 스레드 컨텍스트가 아니더라도) 디렉터리가 변경되었을 때 버퍼에 계속 쓰고있었습니다. CancelIO이 호출되었을 때 , 더 이상의 충돌은 없었다.

+0

저는 CodeGaurd에 익숙하지 않습니다. 스택 카나리아 및 유효성 검사도 소개합니까? 나는 C++과 Delphi를 혼합하고 있기 때문에 물어 봅니다 - 이것은 당신이 그것을 깨닫지 못하고 호출 규칙을 혼합 할 수 있음을 의미합니다. 그렇게하면 매우 빨리 스택을 망가뜨릴 수 있습니다.이 스택은 손상된 호출 스택이있는 메인 스레드에서 무작위로 충돌하는 것처럼 보입니다. –

+0

Codeguard는 스택의 초기화되지 않은 부분을 바이트 패턴으로 채 웁니다. 또한 해제 된 메모리에 액세스하는 것, 할당 된 메모리에 오버런하는 것과 같은 것들을 확인하려고 시도합니다. 호출 규칙을 잘못 지정하면 분명히 이와 같은 문제가 발생할 수 있습니다 (예를 들어 제안에 감사드립니다). 그렇다면 어디에 있는지 잘 모릅니다. : C++ Builder는 Delphi 코드와 상호 운용되도록 설계되었으므로 선언 어딘가에서 오류를 만들어야하며 대부분 IDE 또는 컴파일러가 관리합니다. 중요한 질문은 다음과 같습니다. 잘못 선언 된 메소드를 찾는 방법은 무엇입니까? –

+0

막연한 답변이기 때문에 다른 디버거를 사용해 볼 수도 있습니다. 예를 들어 WinDbg는 손상되거나 혼동 된 경우 실제 콜 스택을 재구성하기위한 힌트 (또는 모든 것)를 제공합니다. –

답변

14

IDE 제공 스택 추적이 아직 완전하지 않더라도 스택에 유용한 정보가 아직 남아 있지 않다는 의미는 아닙니다. CPU 뷰를 열고 스택 창을 체크 아웃합니다. 모든 CALL 연산 코드에 대해 반환 주소가 스택에 푸시됩니다. 스택이 아래쪽으로 커지기 때문에 스택 주소창에서 위쪽으로 스크롤하여 현재 스택 위치 위에 이러한 반환 주소를 찾을 수 있습니다.

메인 스레드의 스택은 $ 00120000 또는 $ 00180000 정도입니다 (Vista의 주소 공간 랜덤 화 이상으로 랜덤하게 만들었습니다). 주 실행 파일의 코드는 어딘가 $ 00400000 정도입니다. 스택 엔트리를 마우스 오른쪽 버튼으로 클릭하고 팔로우 -> 근시 코드을 선택하면 정수 데이터 (낮은 값) 또는 스택 주소 ($ 00120000 + 범위)와 유사하지 않은 스택의 요소를 추측하여 조사 할 수 있습니다. 디스 어셈블 창에서 해당 코드 주소로 이동합니다. 잘못된 코드처럼 보이면 스택 추적에서 유효한 항목이 아닐 수도 있습니다. 그것이 유효한 코드라면 OS 코드 (자주 $ 77000000 이상)가 될 수 있습니다.이 경우에는 의미있는 기호는 없지만 실제로는 적절한 스택 항목에 도달하게됩니다.

이 기술은 다소 힘들지만 디버거가 모든 것을 추적 할 수 없을 때 의미있는 스택 추적 정보를 얻을 수 있습니다.ESP (스택 포인터)가 망가 졌다고해도 도움이되지 않습니다. 다행히도, 그것은 꽤 드물다. 의

+0

감사합니다 배리! 이것은 매우 도움이되며 어쨌든 일반적으로 알고있는 매우 유용한 정보입니다. –

+0

이것은 방금 버그 (또는 다른 - 어느 쪽이든, 그것은 매우 도움이되었습니다.) 내가 최근에 어떤 임의의 코드를 밝혀 냈습니다 해결했습니다!) 시간을내어 답변 주셔서 감사합니다 - 난 그냥 질문에 대답해라. –

2

스레딩이 여기에 해당 될 수 있습니다. 일반적인 용의자는 스택에 OVERLAPPED 구조를 사용하는 스레드와 스택에있는 객체에 대한 포인터를 다른 스레드에 보내는 스레드입니다.

Deubgging Tools For Windows을 사용하고 "dps"명령을 사용하면 부분 스택 정보를 복구 할 수 있습니다.

+0

감사합니다. 존, 그리고 이것을 살펴 봅니다. 나는 스레딩 코드의 대부분을 작성했으며, 동적으로 할당 된 객체를 전달합니다. 나는 아직도 두 번 체크 할 것이다! –

+0

Windows 용 디버깅 도구는 디버거 정보 형식을 사용하지 않고 Microsoft 컴파일러로 컴파일되지 않은 코드로 작동합니까? Embarcadero의 툴은 PDB 파일을 생성하지 않습니다. –

+1

Windows 도구에는 호환 가능한 기호 형식 (PDB가 선호되지만 DBG 파일도 작동)이 필요합니다. – John

2

나는 100 % 확실하지 않지만, 당신이 제공 한 이미지에서 나는 어딘가에 당신이 TList의 객체를 NULL에 접근하려고 시도하고 있다고 생각한다. 예 :

AList[Index].SomeProperty/SomeMethod/etc. <-- error if (AList[Index] == NULL) 

디버깅과 관련하여 예외가 발생합니다 실제 장소를 찾는이없는 많은 정보가있을 때 특히 쉬운 일이 결코 또는 내가 보통이 경우, 재현하기 어렵다 :

  • 기본 폼의 실행 단계에서 단계별로 이동하십시오 (아무 예외가없는 경우)

  • 단계별로 진행하면서 안전하지 않은 코드를 찾으면 try ... except와 인덱스 조건 (if 배열, 목록, 예상 값이 있습니다. 일부 라이브러리는

  • 사용 유레카 로그를 실패하는 경우는 위의 확인, 문제를 발견하지 못할 경우

  • ) 등을 전달하는, 때로는뿐만 아니라 (아주 몇 번 실패)하지만 일반적으로 점 올바른 방향으로 당신은

나는 당신과 비슷한 수많은 문제가 있었 문제가 해결하기 위해 거의 매우 쉽다고 오류 팝, 내가하지 않았을 때 나는 "근처 지적하지만, 말할 수 " 오류.

+0

코드가 TList에 액세스하는 것처럼 보입니다.하지만 그렇지 않을 수도 있습니다. 스택이 깨 졌으므로 스택의 일부라도 유효한지 누가 알 수 있습니다. 유레카 로그 (Eureka Log)는 재미있는 제안입니다. –

+0

@David M 그래, 너는 시간을 절약해야한다. 처음 들어 들었을 때 나는 회의적이었다. 그러나 몇 가지 테스트를 한 후에 나는 그것이 얼마나 많은 시간을 절약하는지에 대해 매우 감명을 받았다. 유레카는 실패하지만 이들은 거의 없습니다. – ComputerSaysNo

4

내가 정상 스택 추적 할 수없는 경우는 전체 스택을 보여줍니다 그래서, 원시 스택 추적과 스택을 표시 할 수 있습니다 http://code.google.com/p/asmprofiler/wiki/ProcessStackViewer

:-) 프로세스 스택 뷰어를 만든 이유입니다.
원시 스택 추적은 "오 탐지"를 표시합니다! 함수 이름을 찾을 수있는 스택의 모든 주소가 나열됩니다. 업로드 된 새 버전, 웹 사이트의 이전 버전 없었다 : 나는 같은 문제에 실행했을 때 당신

편집 (유효하지 않은 스택 상태로 델파이 가능 걷고에는 일반 스택)로

그것은 나에게 여러 번 도움 (새로운 버전을 많이 사용합니다.) http://asmprofiler.googlecode.com/files/AsmProfiler_Sampling%20v1.0.7.13.zip

+0

rrrr 너무 최근에 바쁜, 그것에 대해 모든 것을 가지고있어, 상기시켜 주셔서 감사합니다! – ComputerSaysNo

+0

+1 내가 설명한 절차를 자동화 한 것 같습니다. –

+0

매우 유용한 사운드! 나는 그것을 밖으로 시도 할 것이다. –

관련 문제