2009-07-18 15 views
2

1-2週間前に「ブロックヘッダーが破損しました」と「ブロックが解放された後に変更されました」というエラーが表示されました。ダイナミックアレイ用メモリの割り当て - ブロックヘッダーが破損しています(FastMM4)

誰かが、FullDebugModeScanMemoryPoolBeforeEveryOperationをtrueに設定することについての良いアドバイス(感謝のアレクサンダー)を与えてくれました。そして、最後に、エラーの真の位置がどこにあるかについていくつかの兆候があります。

エラーログは、TScObjオブジェクトを指します。私はこの1つに非常に似た2番目のオブジェクトを持っています。私が使用すると、エラーは表示されません。したがって、これは何らかの形でエラーがこの特定のオブジェクト(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。 Unpackを表示するために投稿を更新しました。 TDiskTrcは、これらの2バイトを保持できます。スワップはDelphi.systemで定義されています。 – Ampere

+0

ところで、古いものとその逆のためのこの質問へのリンクを追加するとよいでしょう。 – 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で+1のおかげで動作しますが、しかし、あなたがいることを知っていますDiskTrc1 [0]に余分なアイテムを1つ割り当てることはありません。

私はこれをSetlength(xx、N)と混在させると思われます(CMXはどのように作成/寸法されていますか?)。

動的配列が参照されていることに注意してください:通常のパターンは、あなたがより多くのコード(?のように、どのようにTDiskTrcが定義されている)

いくつかのポイントを表示する必要が

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

Typo?通常のパターンはインデックスが0から始まり1ではなく、長さ-1まで繰り返されます。 – mghie

+0

mghie:うん、タイプミス。修正されました。 –

1

であることを

注意最適化の有無にかかわらず試してみましたか?

これらの項目のいずれかを使用する前に、あなたは主張したいことがあります。

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

配列への参照を格納するコード(スワップ、開梱、...)はありますか?

thos SCオブジェクトを再利用していますか? (ヒント:しない)

あなたはDiskTrc1のように(任意の配列をクリアしています:?= nilのかはsetLength(DiskTrc1、0)

+0

1. Unpackの投稿を更新しました。スワップはDelphi.systemで定義されています。 2.私はSCを再利用しませんが、ディスクからデータを読み込む前に呼び出すClearプロシージャがあるので、オブジェクトを再利用できると思います。 3.いいえ、私は手動でアレイをクリーンアップしません。 ------------ プロシージャTScObj.Clear; begin Windows.FillMemory(@H、SizeOf(H)、0); FFileName:= ''; 広告:= ''; コメント:= ''; 警告:= ''; PrivateData:= ''; end; – Ampere

+0

私はHにDyn Arraysが含まれていないと信じていますか? –

+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

PS:FastMMは超攻撃的なデバッグモードに設定されています。私は、配列の境界を越えて読み書きしようとするときにスローするエラーメッセージがどのように聞こえるか正確には覚えていませんが、 "ヘッダが壊れている"というエラーはありません。また、私は "レンジチェック"を持っています – Ampere

1

誰もが開始時に重要なエラーをスキップしているようだ:

フリーブロックスキャン操作中にFastMMがエラーを検出しました。 FastMMは、ブロックが解放された後に変更されたことを検出しました。

古いポインタがどこかで使用されています。

あなたが探しているポインタのエラーに手がかりがないので、私は今のところそれを忘れて、他のものを探すでしょう - 彼らは関連すると分かるかもしれません。

+0

ハイローレン。私はあなたが何を意味しているのかよく分かりません。あなたはいくつかの詳細を追加できますか?あなたは "古いポインタ"がSCオブジェクトへのポインタであることを意味しますか?または "トレース"配列へのポインタ? SCオブジェクトの寿命は100ミリ秒以下です。ディスクからファイルをロード/デコードするには、オブジェクトを1回だけ使用します。ディスクと同様のファイル形式を読み取るために、SCに似た他のオブジェクトはほとんどありません。ユーザーの要求に基づいて、プログラムはディスクからファイルを読み込み、プログラムのスパン全体に存在する統一オブジェクト(TCub)内のデータを転送します。 他のすべてのローダーオブジェクトが機能します。 – Ampere

+0

FastMMは、ポインタをリリースし、それが指していたメモリを変更したと不平を言っています。これは危険ですが、通常は逃げるので、おそらくクラッシュは発生していません。とにかくそれを修正する必要があります。 –

+0

ログには、このエラーのコードが元の投稿に表示されます。 – Ampere

2

FullDebugModeScanMemoryPoolBeforeEveryOperationを有効にしてこのコードを実行しようとしましたか? TScObj.ReadDataの開始時にScanMemoryPoolForCorruptionsを呼び出そうとしましたか?

それでも解決できない場合は、その問題の呼び出し(GetMem?)に進み、FastMMのコードに従って、破損したヘッダーのアドレスを確認してください。紙に書き留めて、プログラムを再起動してください。このブロックのアドレスは同じである可能性が非常に高いです。

安全な場所にブレークポイントを設定します。つまり、「悪いもの」が発生する直前に設定します。いったん停止すると、メモリの場所に新しいブレークポイントを設定します。このヘッダーはすぐに破損します(あまりにも早く設定しないでください)。

あなたのプログラムを実行するだけで、デバッガはこの悪いコードですぐに停止し、ヘッダを修正しようとします。

関連する問題