문제

저는 더 큰 응용 프로그램에 Delphi 2007을 사용하는 팀원이며 다른 설명이없는 이상한 버그가 있기 때문에 힙 부패가 의심됩니다. 컴파일러의 rangechecking 옵션은 배열에만 해당된다고 생각합니다. 응용 프로그램에서 할당되지 않은 메모리 주소에 쓰기가있을 때 예외 또는 로그를 제공하는 도구를 원합니다.

문안 인사

편집하다: 오류는 유형입니다.

오류 : Module 'BoatlogisticsAmcattracsserver.exe'의 주소 00404E78에서 액세스 위반. 주소 ffffffdd를 읽으십시오

edit2: 모든 제안에 감사드립니다. 불행히도 솔루션이 그보다 더 깊다 고 생각합니다. 우리는 소스를 소유하고있는 델파이 용 굵은 버전을 사용합니다. 아마도 대담한 프램 워크에 몇 가지 오류가 도입 될 것입니다. 예, JCL에서 처리하는 Callstack과 추적 메시지가있는 로그가 있습니다. 따라서 예외가있는 콜 스택은 다음과 같이 잠글 수 있습니다.

20091210 16:02:29 (2356) [EXCEPTION] Raised EBold: Failed to derive ServerSession.mayDropSession: Boolean
OCL expression: not active and not idle and timeout and (ApplicationKernel.allinstances->first.CurrentSession <> self)
Error: Access violation at address 00404E78 in module 'BoatLogisticsAMCAttracsServer.exe'. Read of address FFFFFFDD. At Location BoldSystem.TBoldMember.CalculateDerivedMemberWithExpression (BoldSystem.pas:4016)

Inner Exception Raised EBold: Failed to derive ServerSession.mayDropSession: Boolean
OCL expression: not active and not idle and timeout and (ApplicationKernel.allinstances->first.CurrentSession <> self)
Error: Access violation at address 00404E78 in module 'BoatLogisticsAMCAttracsServer.exe'. Read of address FFFFFFDD. At Location BoldSystem.TBoldMember.CalculateDerivedMemberWithExpression (BoldSystem.pas:4016)
Inner Exception Call Stack:
 [00] System.TObject.InheritsFrom (sys\system.pas:9237)

Call Stack:
 [00] BoldSystem.TBoldMember.CalculateDerivedMemberWithExpression (BoldSystem.pas:4016)
 [01] BoldSystem.TBoldMember.DeriveMember (BoldSystem.pas:3846)
 [02] BoldSystem.TBoldMemberDeriver.DoDeriveAndSubscribe (BoldSystem.pas:7491)
 [03] BoldDeriver.TBoldAbstractDeriver.DeriveAndSubscribe (BoldDeriver.pas:180)
 [04] BoldDeriver.TBoldAbstractDeriver.SetDeriverState (BoldDeriver.pas:262)
 [05] BoldDeriver.TBoldAbstractDeriver.Derive (BoldDeriver.pas:117)
 [06] BoldDeriver.TBoldAbstractDeriver.EnsureCurrent (BoldDeriver.pas:196)
 [07] BoldSystem.TBoldMember.EnsureContentsCurrent (BoldSystem.pas:4245)
 [08] BoldSystem.TBoldAttribute.EnsureNotNull (BoldSystem.pas:4813)
 [09] BoldAttributes.TBABoolean.GetAsBoolean (BoldAttributes.pas:3069)
 [10] BusinessClasses.TLogonSession._GetMayDropSession (code\BusinessClasses.pas:31854)
 [11] DMAttracsTimers.TAttracsTimerDataModule.RemoveDanglingLogonSessions (code\DMAttracsTimers.pas:237)
 [12] DMAttracsTimers.TAttracsTimerDataModule.UpdateServerTimeOnTimerTrig (code\DMAttracsTimers.pas:482)
 [13] DMAttracsTimers.TAttracsTimerDataModule.TimerKernelWork (code\DMAttracsTimers.pas:551)
 [14] DMAttracsTimers.TAttracsTimerDataModule.AttracsTimerTimer (code\DMAttracsTimers.pas:600)
 [15] ExtCtrls.TTimer.Timer (ExtCtrls.pas:2281)
 [16] Classes.StdWndProc (common\Classes.pas:11583)

내부 예외 부분은 현재 예외가 발생하는 순간 콜 스택입니다.

edit3 : 지금 이론은 VMT (Virtual Memory Table)가 어떻게 든 깨 졌다는 것입니다. 이런 일이 발생하면 그 징후가 없습니다. 메소드가 예외라고 불리는 경우에만 (언제나 주소에서 ffffffdd, -35 decimal) 그러나 너무 늦었습니다. 당신은 오류의 실제 원인을 모릅니다. 이런 버그를 잡는 방법에 대한 힌트는 정말 감사합니다 !!! 우리는 SAFEMM으로 시도했지만 문제는 3GB 플래그를 사용하는 경우에도 메모리 소비가 너무 높다는 것입니다. 그래서 이제 나는 SO 커뮤니티에 현상금을 주려고 노력합니다. :)

edit4 : 한 가지 힌트는 로그에 따라이 전에 종종 (또는 항상) 예외가 있다는 것입니다. 예를 들어 데이터베이스의 낙관적 잠금 일 수 있습니다. 우리는 강제로 예외를 제기하려고 노력했지만 테스트 환경에서는 잘 작동합니다.

edit5 : 이야기는 계속됩니다 ... 나는 지난 30 일 동안 로그를 검색했습니다. 결과:

  • "주소 ffffffdb 읽기"0
  • "주소 FFFFFFDC에 대한 읽기"24
  • "주소 ffffffdd의 읽기"270
  • "주소 FFFFFFDE 읽기"22
  • "주소 ffffffdf 읽기"7
  • "주소 ffffffe0"20
  • "주소 ffffffe1을 읽으십시오"0

따라서 현재 이론은 열거 (대담한 많은 사람들)가 포인터를 덮어 쓰는 것입니다. 위의 주소로 5 개의 히트를 얻었습니다. 이는 열거가 가장 많이 사용되는 5 개의 값을 보유하고 있음을 의미 할 수 있습니다. 예외가있는 경우 데이터베이스에 대한 롤백이 발생하고 Boldobjects가 파괴되어야합니다. 어쩌면 모든 것이 파괴되지 않고 열거가 여전히 주소 위치에 쓸 수있는 기회가있을 수 있습니다. 이것이 사실이라면 5 값의 열거에 대해 regexpr로 코드를 검색 할 수 있습니까?

edit6 : 요약하면, 아직 문제에 대한 해결책은 없습니다. 나는 콜 스택으로 당신을 조금 오도 할 수 있다는 것을 알고 있습니다. 예, 타이머가 있지만 타이머가없는 다른 콜 스택이 있습니다. 그 죄송합니다. 그러나 두 가지 공통 요소가 있습니다.

  • 주소 ffffffxx를 읽은 예외.
  • Callstack 상단은 System.tobject.inheritsfrom (sys system.pas : 9237)입니다.

이것은 나를 확신합니다 Vockek 문제를 가장 잘 설명하십시오. 또한 문제가 대담한 프레임 워크 어딘가에 있다고 확신합니다. 하지만 질문은 어떻게 이와 같은 문제를 해결할 수 있습니까? 같은 주장을하는 것만으로는 충분하지 않습니다 Vockek 피해가 이미 발생하고 콜 스택이 그 순간에 사라졌기 때문에 제안하십시오. 따라서 오류의 원인이 무엇인지에 대한 내 견해를 설명하기 위해 :

  1. 어딘가에 포인터에는 불량 값이 할당되지만 0, 2, 3 등도 될 수 있습니다.
  2. 해당 포인터에 객체가 할당됩니다.
  3. 객체베이스 클래스에는 메소드 호출이 있습니다. 이 원인 원인 메소드 tobject.inheritsform을 호출하고 주소 FFFFFFDD에 예외가 나타납니다.

이 3 가지 이벤트는 코드에서 함께 할 수 있지만 나중에 훨씬 나중에 사용될 수도 있습니다. 나는 이것이 마지막 메소드 호출에 사실이라고 생각합니다.

edit7 : 우리는 Bold Jan Norden의 저자와 긴밀히 협력했으며 최근 OCL-Evaluator에서 Bold Framework에서 버그를 발견했습니다. 이것이 고정되었을 때 이러한 종류의 예외는 많이 줄어들었지만 여전히 때때로옵니다. 그러나 이것이 거의 해결 된 것은 큰 안도감입니다.

도움이 되었습니까?

해결책

해결책이 없지만 해당 특정 오류 메시지에 대한 단서가 있습니다.

System.tobject.inherits에서 셀프 포인터 (클래스)에서 상수 vmtparent를 빼서 부모 클래스의 주소에 대한 포인터를 얻습니다.

Delphi 2007에서 vmtparent는 정의됩니다.

vmtparent = -36;

따라서 오류 $ ffffffdd (-35)는 클래스 포인터가 1 인 것처럼 들립니다.

다음은 재생산을위한 테스트 사례입니다.

procedure TForm1.FormCreate(Sender: TObject);
var
  I : integer;
  O : tobject;
begin
  I := 1;
  O := @I;
  O.InheritsFrom(TObject);
end;

vmtparent가 델파이 버전마다 다르기 때문에 Delphi 2010에서 시도하고 '주소 FFFFFFD1을 읽습니다'.

문제는 이것이 대담한 프레임 워크 내부에서 깊이 발생하므로 응용 프로그램 코드에서이를 보호하는 데 어려움을 겪을 수 있다는 것입니다.

dmattracstimers 코드에서 사용되는 객체에서 이것을 시도 할 수 있습니다 (응용 프로그램 코드라고 가정) :

Assert(Integer(Obj.ClassType)<>1,'Corrupt vmt');

다른 팁

당신은 당신이 거기에 예외가 있기를 원한다고 씁니다.

응용 프로그램에서 할당되지 않은 메모리 주소에 쓰기가 있습니다.

그러나 어쨌든 그것은 모두 발생합니다 하드웨어 그리고 OS 그것을 확인하십시오.

응용 프로그램의 할당 된 주소 범위에서 유효하지 않은 메모리 쓰기를 확인하고 싶다면 할 수있는 일이 너무 많습니다. 당신은 사용해야합니다 FASTMM4, 응용 프로그램의 디버그 모드에서 가장 장황하고 편집증 설정과 함께 사용하십시오. 이것은 많은 유효하지 않은 글을 잡을 것이며, 이미 릴리스 된 메모리 등에 대한 액세스가되지만 모든 것을 잡을 수는 없습니다. 큰 문자열의 중간 또는 플로트 값의 배열과 같은 다른 쓰기 가능한 메모리 위치를 가리키는 매달려있는 포인터를 고려하십시오. 입장.

객체 인스턴스 데이터의 메모리 손상이있는 것 같습니다.

VMT 자체는 손상되지 않습니다. FWIW : VMT는 (일반적으로) 실행 파일에 저장되며 그에 매핑되는 페이지는 읽기 전용입니다. 오히려, Vovelek이 말했듯이, 그것은 당신의 사례의 인스턴스 데이터의 첫 번째 필드가 값 1을 가진 32 비트 정수로 덮어 쓰인 것처럼 보입니다. 이것은 쉽게 확인할 수 있습니다. 메소드 호출이 실패한 객체의 인스턴스 데이터를 확인하십시오 첫 번째 dword가 00000001인지 확인하십시오.

실제로 손상된 인스턴스 데이터의 VMT 포인터 인 경우,이를 손상시키는 코드를 찾는 방법은 다음과 같습니다.

  1. 사용자 입력이 필요하지 않은 문제를 재현하는 자동화 된 방법이 있는지 확인하십시오. Windows가 메모리를 배치하는 방법으로 인해 재생산 간 재부팅이없는 단일 시스템에서만 문제가 재현 가능할 수 있습니다.

  2. 문제를 재현하고 메모리가 손상된 인스턴스 데이터의 주소를 기록하십시오.

  3. 두 번째 재생산을 다시 시작하고 확인하십시오. 두 번째 실행에서 손상된 인스턴스 데이터의 주소가 첫 번째 실행의 주소와 동일했는지 확인하십시오.

  4. 이제 세 번째 실행에 들어가서 이전 두 실행이 표시 한 메모리 섹션에 4 바이트 데이터 중단 점을 넣습니다. 요점은이 메모리에 대한 모든 수정을 중단하는 것입니다. VMT 포인터를 채우는 TOBJECT.INITINSTANCE 호출이어야합니다. 메모리 할당 자와 같은 인스턴스 구성과 관련된 다른 것이있을 수 있습니다. 그리고 최악의 경우, 관련 인스턴스 데이터는 이전 인스턴스에서 메모리로 재활용되었을 수 있습니다. 필요한 스텝핑량을 줄이려면 데이터 브레이크 포인트를 통화 스택으로 로그인하지만 실제로는 깨지지 않습니다. 가상 호출이 실패한 후 통화 스택을 확인하면 잘못된 쓰기를 찾을 수 있어야합니다.

Mghie는 물론 옳습니다. (Fastmm4는 플래그 FullDebugmode 또는 그와 비슷한 것을 호출합니다).

이는 일반적으로 정기적으로 점검되는 힙 할당 직전과 직후에 장벽과 함께 작동합니다 (모든 heapmgr 액세스에 따라).

이것은 두 가지 결과가 있습니다.

  • Fastmm이 오류를 감지하는 장소는 발생하는 지점에서 벗어날 수 있습니다.
  • 총 무작위 쓰기 (기존 할당의 오버플로 아님)는 감지되지 않을 수 있습니다.

그래서 다음은 다음과 같이 생각해야 할 것들이 있습니다.

  • 런타임 확인을 활성화합니다
  • 컴파일러의 모든 경고를 검토하십시오.
  • 다른 델파이 버전 또는 FPC로 컴파일하십시오. 다른 컴파일러/rtls/heapmanagers는 레이아웃이 다르므로 오류가 더 쉽게 잡힐 수 있습니다.

모든 것이 없으면 응용 프로그램이 사라질 때까지 응용 프로그램을 단순화하십시오. 그런 다음 가장 최근의 댓글/IFDEFED 부분을 조사하십시오.

내가 가장 먼저 할 일은 응용 프로그램에 madexcept를 추가하고 정확한 호출 트리를 인쇄하는 스택 트레이스 백을 얻는 것입니다. 임의의 예외와 이진/16 진수 메모리 주소 대신 스택에서 모든 매개 변수와 로컬 변수의 값이있는 호출 트리를 볼 필요가 있습니다.

응용 프로그램의 핵심 구조에서 메모리 손상이 의심되는 경우 종종이 버그를 추적 할 수 있도록 추가 코드를 작성합니다.

예를 들어, 메모리 구조 (클래스 또는 레코드 유형)에서 처음에는 Magic1 : Word와 Magic2 : 각 레코드가 메모리의 끝에있는 단어를 갖도록 배열 될 수 있습니다. 무결성 점검 함수는 각 레코드 Magic1과 Magic2가 생성자에서 설정 한 내용에서 변경되지 않았 음을 확인하여 해당 구조의 무결성을 확인할 수 있습니다. 소멸자는 Magic1과 Magic2를 $ FFFF와 같은 다른 값으로 변경합니다.

또한 내 응용 프로그램에 추적 로깅을 추가하는 것을 고려할 것입니다. Trace Logging in Delphi 응용 프로그램에서 종종 TMEMO가있는 Traceform 형식을 선언하는 것으로 시작되며 TraceForm.Trace (MSG : String) 함수는 "memo1.lines.add (MSG)"로 시작됩니다. 내 응용 프로그램이 성숙함에 따라 추적 로깅 시설은 동작의 전반적인 패턴과 오작동에 대한 응용 프로그램을 실행하는 방법입니다. 그런 다음 "설명 없음"이 발생한 "무작위"충돌 또는 메모리 손상이 발생하면 다시 통과 하여이 특정 사례로 이어진 것을 볼 수있는 추적 로그가 있습니다.

때로는 메모리 손상이 아니지만 간단한 기본 오류 (x가 할당 된 지 확인하는 것을 잊어 버린 다음 x.dosomething (...)이 할당되었다고 가정하지만 그렇지 않습니다.

타이머가 스택 추적에 있음을 알았습니다.
나는 원인이 내가 자유롭게하지 않은 양식 후에 타이머 이벤트가 발사되는 많은 이상한 오류를 보았다.
그 이유는 타이머 이벤트가 메시지 큐에 올려지고 Noge는 다른 구성 요소의 파괴를 위해 BR을 처리하기 때문입니다.
이 문제를 해결하는 한 가지 방법은 형태의 파괴의 첫 번째 항목으로 타이머를 비활성화하는 것입니다. Time Call Application.processMessages를 비활성화 한 후 구성 요소를 파괴하기 전에 모든 타이머 이벤트가 처리됩니다.
또 다른 방법은 Timerevent에서 양식이 파괴되는지 확인하는 것입니다. (ComponentState에서 CSDESTROYING).

이 절차의 sourcecode를 게시 할 수 있습니까?

boldsystem.tboldmember.calcatederivedmemberwithexpression (boldsystem.pas : 4016)

그래서 우리는 4016 행에서 무슨 일이 일어나고 있는지 알 수 있습니다.

또한이 기능의 CPU보기?
(이 절차의 4016 행에 중단 점을 설정하고 실행하고 중단 점에 도달하면 CPU보기 내용을 복사+붙여 넣으십시오).
따라서 어떤 CPU 명령이 주소 00404E78에 있는지 확인할 수 있습니다.

재진입 코드에 문제가있을 수 있습니까?

TTIMER 이벤트 핸들러 코드 주위에 가드 코드를 넣으십시오.

procedure TAttracsTimerDataModule.AttracsTimerTimer(ASender: TObject);
begin
  if FInTimer then
  begin
    // Let us know there is a problem or log it to a file, or something. 
    // Even throw an exception
    OutputDebugString('Timer called re-entrantly!'); 
    Exit; //======> 
  end;

  FInTimer := True;
  try

    // method contents

  finally
    FInTimer := False;
  end;
end;

N@

또 다른 가능성이 있다고 생각합니다. 타이머가 발사되어 "매달린 로그온 세션"이 있는지 확인합니다. 그런 다음 tlogonsession 객체에서 호출을 수행하여 삭제 될 수 있는지 확인합니다 (_getMayDropsession). 그러나 대상이 이미 파괴되면 어떻게됩니까? 스레드 안전 문제 또는 단지 .Free 호출로 인해 FreeAndnil 호출이 아니기 때문에 (따라서 변수는 여전히 <> nil) 등입니다. 얼마 후 변수를 ACCE를 시도하면 임의의 오류를 얻을 수 있습니다 ...

An example:

procedure TForm11.Button1Click(Sender: TObject);
var
  c: TComponent;
  i: Integer;
  p: pointer;
begin
  //create
  c := TComponent.Create(nil);
  //get size and memory
  i := c.InstanceSize;
  p := Pointer(c);
  //destroy component
  c.Free;
  //this call will succeed, object is gone, but memory still "valid"
  c.InheritsFrom(TObject);
  //overwrite memory
  FillChar(p, i, 1);
  //CRASH!
  c.InheritsFrom(TObject);
end;

Module 'Project10.exe'의 주소 004619D9에서 액세스 위반. 주소 01010101 읽기.

"_getMayDropsession"이 해제 된 세션 변수를 참조하는 문제가 아닙니까?

나는 객체가 해제되고 onchange 등에서 언급 된 TMS에서 이런 종류의 오류를 보았습니다 (일부 상황에서만 재생산하기가 매우 어렵거나 불가능한 오류가 발생한 경우에만 TMS :-)). 또한 리브 젝트 세션을 사용하면 비슷한 것을 얻었습니다 (나 자신의 나쁜 프로그래밍 버그로 인해).

세션 클래스에 더미 변수를 추가하고 가치를 확인하려고합니다.

  • 공개 변수 imagicnumber : 정수;
  • 생성자 생성 : imagicNumber : = 1234567;
  • 파괴자 파괴 : imagicnumber : = -1;
  • "기타 절차": Assert (imagicnumber = 1234567)
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top