2009-07-18 5 views
2

1-2 주 전에 "블록 헤더가 손상되었습니다."및 "블록이 해제 된 후 수정되었습니다."라는 오류가있었습니다.동적 배열을위한 메모리 할당 - 블록 헤더가 손상되었습니다. (FastMM4)

Somebody는 FullDebugModeScanMemoryPoolBeforeEveryOperation을 true로 설정하는 것에 대한 좋은 팁 (Alexander에게 감사함)을 주었고, 마지막으로 오류의 TRUE 위치가 어디인지에 대한 몇 가지 표시를했습니다.

오류 로그는 TScObj 개체를 가리 킵니다. 두 번째 객체는이 객체와 매우 유사하며 사용하면 오류가 나타나지 않습니다. 그래서 이것은 어떻게 든이 오류가이 특정 객체 (TScObj)에 있음을 확인합니다.

로그는 다음과 같이이다 :

FastMM has detected an error during a free block scan operation. 
FastMM detected that a block has been modified after being freed. 
Modified byte offsets (and lengths): 15656(1) 

The previous block size was: 15672 
This block was previously allocated by thread 0xC88, and the stack trace (return addresses) at the time was: 
402EC9 [System][@ReallocMem] 
40666C [System][DynArraySetLength] 
40A17D [FastMM4][UpdateHeaderAndFooterCheckSums] 
40674E [System][@DynArraySetLength] 
4CE329 [ReadSC.pas][ReadSC][TScObj.ReadData][239] 
4CDD0C [ReadSC.pas][ReadSC][TScObj.LoadFromFile][168] 
4D013E [SmplCubImport.pas][SmplCubImport][TCubImport.ImportSample][164] 
40461A [System][@AfterConstruction] 
4DC151 [UnitAsmJob.pas][UnitAsmJob][TAsmJob.LoadSample][960] 

The allocation number was: 78709 
The block was previously freed by thread 0xC88, and the stack trace (return addresses) at the time was: 
402E6F [System][@FreeMem] 
4068A8 [System][@DynArrayClear] 
405DF9 [System][@FinalizeArray] 
4CE9F9 [ReadSC.pas][ReadSC][TScObj.ReadData][298] 
4CDD0C [ReadSC.pas][ReadSC][TScObj.LoadFromFile][168] 
4D013E [SmplCubImport.pas][SmplCubImport][TCubImport.ImportSample][164] 
40461A [System][@AfterConstruction] 
4DC151 [UnitAsmJob.pas][UnitAsmJob][TAsmJob.LoadSample][960] 

것은 내가 내가 부당하게 메모리를 할당 할 수 있습니다 내 코드에서 어떤 장소를 볼 수 없다는 것입니다. 나중에 "부하 디스크에서"절차 동안

type 
TWordTrace = array of Word; 
TDiskTrc = array of Smallint; 
var Tracea,Tracec: TWordTrace; 

procedure TScObj.ReadData; 
Var i: Integer; 
    DiskTrc1: TDiskTrc; 
    DiskTrc2: TDiskTrc; 
    DiskTrc3: TDiskTrc; 
    DiskTrc4: TDiskTrc; 
begin 
SetLength(DiskTrc1, H.NrOfSamples+1); 
SetLength(DiskTrc2, H.NrOfSamples+1); 
SetLength(DiskTrc3, H.NrOfSamples+1); 
SetLength(DiskTrc4, H.NrOfSamples+1); <------ log shows error here. <- on DynArraySetLength 

FStream.Seek(H.SOffset, soFromBeginning); 

if H.SampleSize = 1 then 
    begin 
    for i:= 1 TO H.NrOfSamples DO 
    FStream.Read(DiskTrc1[i], 1); 
    Unpack(DiskTrc1); 

    for i:= 1 TO H.NrOfSamples DO 
    FStream.Read(DiskTrc2[i], 1); 
    Unpack(DiskTrc2); 

    etc... 
    end 

else 
    begin 
    for i:= 1 TO H.NrOfSamples DO 
    begin 
    FStream.Read(DiskTrc1[i], 2); 
    DiskTrc1[i]:= Swap(DiskTrc1[i]); 
    end; 
    Unpack(DiskTrc1); 

    for i:= 1 TO H.NrOfSamples DO 
    begin 
    FStream.Read(DiskTrc2[i], 2); 
    DiskTrc2[i]:= Swap(DiskTrc2[i]); 
    end; 
    Unpack(DiskTrc2); 

    etc... 
    end; 

SetLength(Tracea, H.NrOfSamples+1); 
SetLength(Tracec, H.NrOfSamples+1); 
SetLength(Traceg, H.NrOfSamples+1); 
SetLength(Tracet, H.NrOfSamples+1); <------ log shows error here. <- on FinalizeArray 

for i:=1 to H.NrOfSamples DO 
    begin 
    if DiskTrc1[i]< 0 
    then Tracea[i]:= 0 
    else Tracea[i]:= DiskTrc1[i]; 

    if DiskTrc2[i]< 0 
    then Tracec[i]:= 0 
    else Tracec[i]:= DiskTrc2[i]; 

    etc... 
    end; 
end; 


procedure TScObj.Unpack(VAR DiskTrc: TDiskTrc); 
var i: integer; 
    Prev: Integer; 
    Recover: Integer; 
begin 
Prev:= 0; 
for i:= 1 to H.NrOfSamples do 
    begin 
    Recover := DiskTrc[i] + Prev; 
    if (Recover> 32767) OR (Recover< -32768) 
    then Recover:= 0; 
    DiskTrc[i]:= Recover; 
    Prev:= DiskTrc[i]; 
    end; 
Prev:= 0; 
for i:= 1 to H.NrOfSamples do 
    begin 
    Recover := DiskTrc[i] + Prev; 
    if (Recover> 32767) OR (Recover< -32768) 
    then Recover:= 0; 
    DiskTrc[i]:= Recover; 
    Prev:= DiskTrc[i]; 
    end; 
end; 

는 임시 로더 객체 (SC)의 정보는 다음과 같이 좀 더 "확실한"개체로 옮겨진된다

TSam = class 
etc...             
for i:= 1 to NrOfSamples DO 
    begin 
    CMX[i].Tracea:= SC.Tracea[i]; 
    CMX[i].Tracec:= SC.Tracec[i]; 
    etc... 
    end; 

편집 2 : 이 버그는 특정 (2 개) 파일 세트를 열거 나로드하려고 시도 할 때만 나타납니다. 다른 모든 파일의 경우 버그가 표시되지 않습니다.

+0

TDiskTrc의 선언은 무엇입니까? 요소 크기가 충분히 크거나 (적어도 2) 아니면 각 요소에 너무 많은 (2) 바이트를 읽으려고 메모리를 덮어 쓰고 있습니까? 또한, 스왑 및 언팩은 무엇을합니까? –

+0

안녕하세요. TOndrej. 포장을 풀기 위해 내 게시물을 업데이트했습니다. TDiskTrc는이 두 바이트를 저장할 수 있습니다. 스왑은 Delphi.system에서 정의됩니다. – Ampere

+0

BTW, 이전 질문과 비자에 대한 링크를 추가하는 것이 좋습니다. – Alex

답변

1

해결. 오늘 나는 코드의 각 라인을 수동으로 검사하는 데 보냈다. 나는 거의 변화를 만들지 않았고 마침내 문제는 사라졌습니다. 어떤 특정 라인이 문제를 일으켰는지 확인하려하지 않았습니다.

도움을 얻기 위해 모든 몸에 감사드립니다.

+1

정말로, 그 문제는 정말로 사라 집니까? sublecode 변경으로 숨길 수도 있습니다. 그 일은 많이 발생합니다. – Alex

+0

오늘 FullDebugModeScanMemoryPoolBeforeEveryOperation으로 실행 해 보겠습니다. sometginh 40 분처럼 걸립니다. 나는 정말로 그것이 지금 해결되기를 바란다! – Ampere

1

다른 곳에서 부패가 발생했다고 생각합니다. 일상적인 통화처럼 보이는 것은 문제를 푸는 것입니다. 가리키고있는 위치가 메모리를 손상시키지 않는 것 같습니다. 코드를 자세히 보지 않고 절차를 격리하고 신중하게 테스트하지 않고 문제가있는 곳을 추측하는 것은 대단히 어렵습니다. 컴파일 할 때 경고 메시지가 나옵니까?

+0

경고가 표시되지 않습니다. FastMM은 FullDebugModeScanMemoryPoolBeforeEveryOperation 변수를 활성화하지 않을 때까지 실제 정보를 제공 할 수 없었습니다. 그러나 그것이 현재 보여주고있는 것은 현실적인 것처럼 보입니다. – Ampere

1

나는 오류 코드가 표시 이온되지 생각하고 있어요,하지만 당신이 정말로 확인해야 무언가가 :

SetLength(DiskTrc1, H.NrOfSamples+1); 

    for i:= 1 TO H.NrOfSamples DO 
    FStream.Read(DiskTrc1[i], 1); 

이 SetLength를의 한 덕분에 작동합니다,하지만 당신은 알고 있습니다 DiskTrc1 [0]에 여분의 항목 1 개를 할당하지 않았습니까?

어딘가에 Setlength (xx, N)와 혼합한다고 생각합니다 (CMX의 생성/치수 지정 방법은 무엇입니까?). 정상적인 패턴 당신은 더 많은 코드 보여줄 필요가

SetLength(DiskTrc1, H.NrOfSamples); 

    for i:= 0 TO H.NrOfSamples-1 DO 
    FStream.Read(DiskTrc1[i], 1); 
+0

감사합니다. 나는 +1에 대해 절대적으로 알고있다. CMX는 NrOfSamples + 1을 기반으로 초기화됩니다. 몇 가지 이유 때문에 저는 그 +1로 오래전부터 시작 했었습니다. 이제는 많은 코드가 그것에 기초하고 있습니다. 나는 전체 코드를 신중하게 수정해야한다. 아마 오늘 해야겠다. – Ampere

+0

오타? 일반 패턴은 인덱스가 0에서 시작하고 1에서 시작하지 않고 길이가 1까지 반복됩니다. – mghie

+0

mghie : 네, 오타입니다. 수정 됨. –

1

것을

몇 점 (같은 방법 TDiskTrc 정의를?) :

가 동적 배열은 참조 기억하십시오 최적화하지 않고 실행 해 보았습니까?

이러한 항목 중 하나를 사용하기 전에 당신이 주장 할 수 있습니다 :

assert(CMX <> nil); 
    assert(Length(CMX) = NrOfSamples+1); 

배열에 대한 참조를 저장하는 코드 (스왑, 포장을 풀고, ...)이 있습니까?

thos SC 개체를 다시 사용하고 있습니까?

당신이 DiskTrc1처럼 (모든 배열을 삭제 위치 : (힌트하지 않습니다) = 무기 호 또는 SetLength를 (DiskTrc1, 0)

+0

1. Unpack을 위해 내 게시물을 업데이트했습니다. 스왑은 Delphi.system에서 정의됩니다. 2. SC를 재사용하지는 않지만 디스크에서 데이터를 읽기 전에 호출하는 Clear (클리어) 절차가 있으므로 개체를 다시 사용할 수 있다고 생각합니다. 3. 아니요. 해당 배열을 수동으로 정리하지 않습니다. ------------ procedure TScObj.Clear; begin Windows.FillMemory (@H, SizeOf (H), 0); FFileName : = ''; 광고 : = ''; 댓글 : = ''; 경고 : = ''; PrivateData : = ''; 끝; – Ampere

+0

H가 Dyn Array를 포함하지 않는다고 나는 믿는다. –

+0

H는 많은 카디날과 3 개의 정적 char 배열을 포함하는 압축 된 레코드입니다. – Ampere

0

는 0 기반 또는 1 기반

여러 귀하의 배열 있습니까? 시간이 같은 루프를 실행

if H.SampleSize = 1 then 
begin 
    for i:= 1 TO H.NrOfSamples DO 
    FStream.Read(DiskTrc1[i], 1); 
    Unpack(DiskTrc1); 
    ... 

당신이 DiskTrc1의 끝을지나 읽기되지 확실

+0

1 기반. 해당 단위에서 "-1"에 대한 검색을 수행했는데 문자열을 찾을 수 없습니다. 이미 약 1000 번 확인했습니다. 그러나 당신 말이 맞습니다. 그것이 레거시 코드입니다. 코드를 0 기반으로 변경합니다. :) – Ampere

+0

추 신 : FastMM은 매우 적극적인 디버깅 모드로 설정됩니다. 정확히 어떻게 배열의 테두리를 지나서 읽거나 쓰려고 할 때 throw되는 오류 메시지가 들리지만 "헤더가 손상되었습니다"라는 메시지는 기억 나지 않습니다. 또한 나는 "Range check"를 가지고있다. – Ampere

1

모두는 시작에 중요한 오류를 건너 뛰는 것 같다 :

FreeMM은 여유 블록 스캔 작업 중에 오류를 감지했습니다. FastMM은 해제 된 후 블록이 수정되었음을 감지했습니다.

이전 포인터가 어딘가에서 사용되고 있습니다.

포인터 오류에 대한 단서가 없으므로 지금은 잊어 버리고 다른 하나를 찾으십시오.

+0

안녕하세요. 나는 당신이 의미하는 것을 이해하지 못합니다. 몇 가지 세부 사항을 추가 할 수 있습니까? "이전 포인터"가 SC 객체에 대한 포인터라는 것을 의미합니까? 또는 "추적"배열에 대한 포인터? SC 개체의 수명이 100ms 미만이며 디스크에서 파일을로드/디코딩 할 때 한 번만 개체를 ​​사용합니다. 디스크와 비슷한 파일 형식을 읽으려면 SC와 비슷한 다른 개체가 거의 없습니다. 사용자의 요청에 따라 프로그램은 디스크에서 파일을로드하여 해당 특수 객체에 저장 한 다음 전체 프로그램 범위에 해당하는 통합 객체 (TCub)로 데이터를 전송합니다. 다른 모든 로더 개체가 작동합니다. – Ampere

+0

FastMM은 포인터를 놓은 다음 포인터를 가리키는 메모리를 수정했다는 불평을합니다. 이것은 위험하지만 보통 충돌을 일으키지 않을 정도로 위험에 빠져 있습니다. 어쨌든 그것을 고쳐야합니다. –

+0

로그에이 오류 코드가 원래 게시물에 표시됩니다. – Ampere

2

FullDebugModeScanMemoryPoolBeforeEveryOperation이 활성화 된 상태에서이 코드를 실행하려고 시도 했습니까? TScObj.ReadData를 시작할 때 ScanMemoryPoolForCorruptions를 호출하려고 했습니까?

그래도 도움이되지 않는 경우 - 문제가있는 전화 (GetMem?)를 시도하고 FastMM의 코드를 따라 해당 손상된 헤더의 주소를 확인하십시오. 그냥 종이에 적어두고 프로그램을 다시 시작하십시오. 이 블록의 주소가 동일 할 가능성이 매우 높습니다.

안전한 위치, 즉 "나쁜 것"이 발생하기 직전에 중단 점을 설정하십시오. 일단 멈 추면 - 메모리 위치에 새로운 중단 점을 설정하십시오 -이 헤더 바로 다음에 손상 될 것입니다 (너무 일찍 설정하지 않도록하십시오).

그러면 프로그램을 실행하십시오. 디버거가 헤더를 수정하려고하는이 잘못된 코드에서 바로 멈출 것입니다.