2009-07-18 4 views
2

У меня была тема 1-2 недели назад о «повреждении заголовка блока» и «Блокировка была изменена после освобождения».Выделение памяти для динамического массива - заголовок блока был поврежден (FastMM4)

Кто-то дал мне хороший совет (спасибо Александру) о том, как установить FullDebugModeScanMemoryPoolBeforeEveryOperation в 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: Ошибка появляется только при попытке открыть/загрузить очень специфический набор (двух) файлов. Для всех других файлов ошибка не отображается.

+0

Что такое объявление TDiskTrc? Является ли размер элемента достаточно большим (не менее 2) или вы, возможно, перезаписываете память, пытаясь прочитать слишком много (2) байтов в каждый элемент? Кроме того, что делают Swap и Unpack? –

+0

Hi TOndrej. Я обновил свое сообщение, чтобы показать Распаковать. TDiskTrc может содержать эти два байта. Swap определяется в Delphi.system. – Ampere

+0

BTW, было бы неплохо добавить ссылку на этот вопрос для старого и наоборот. – Alex

ответ

1

Решено. Сегодня я провел день вручную, проверяя каждую строку кода. Я сделал немало изменений и, наконец, проблема исчезла. Я не пытался понять, какая конкретная строка вызвала проблему.

Большое спасибо всем тела за помощь !!!

+1

Вы уверены, что эта проблема действительно исчезает? Может быть, вы просто спрячете его под изменениями в подблоке. Это случается очень много. – Alex

+0

Я постараюсь сегодня запустить его с помощью FullDebugModeScanMemoryPoolBeforeEveryOperation. Принимает sometginh, как 40 минут. Я действительно надеюсь, что он будет решен сейчас! – Ampere

1

Я думаю, что у вас есть коррупция в другом месте, и то, что похоже на обычный вызов, просто разоблачает проблему. Точки, на которые вы указываете, не выглядят вероятными для памяти. Не смотря на код подробно, изолируя процедуры и тщательно их проверяя, очень сложно догадаться, в чем проблема. Получаете ли вы какие-либо предупреждения при компиляции?

+0

Предупреждений нет. FastMM не смог предоставить какую-либо реальную информацию, пока я не включил функцию FullDebugModeScanMemoryPoolBeforeEveryOperation var. Но то, что он показывает сейчас, кажется реалистичным. – Ampere

1

Я думаю, ошибка не иона код, показанный, но есть что-то вы действительно должны проверить:

SetLength(DiskTrc1, H.NrOfSamples+1); 

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

Это работает благодаря +1 в SetLength, но ты в курсе, что вы выделяя 1 дополнительный элемент в DiskTrc1 [0], который никогда не используется?

Я подозреваю, что вы смешиваете это с 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

Typo? Обычный шаблон имеет индекс, начинающийся с 0, а не 1, и цикл до Length - 1. – mghie

+0

mghie: Да, опечатка. Исправленный. –

1

Вы должны показать больше кода

Несколько точек (как, как TDiskTrc определено?):

Помните, что динамические массивы являются ориентировочными -counted, вы пробовали работать с Оптимизациями и без них?

Перед использованием любого из этих пунктов вы можете утверждать:

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

Есть ли код (Своп, распакуйте, ...), которая хранит ссылку на массив?

Повторно ли вы используете объекты SC SC? (Подсказка: нет)

Вы очистки любых массивов (например, DiskTrc1: = ноль или SetLength (DiskTrc1, 0)

+0

1. Я обновил свое сообщение для Unpack. Swap определяется в Delphi.system. 2. Я не повторно использую SC, но у меня есть процедура Clear, которую я вызываю перед чтением данных с диска, поэтому я думаю, что объект может быть повторно использован. 3. Нет. Я не вручную очищаю этот массив. ------------ процедура TScObj.Clear; начало Windows.FillMemory (@H, SizeOf (H), 0); FFileName: = ''; Объявления: = ''; Комментарии: = ''; Предупреждения: = ''; PrivateData: = ''; конец; – Ampere

+0

Надеюсь, что H не содержит Dyn массивов? –

+0

H - это упакованная запись, содержащая много кардиналов и 3 статических массива символов. – Ampere

0

Ваших массивов 0 на основе или 1 на основе

Multiple? раз запускать петли так:

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

вы уверены, что это не читает в конце прошлого DiskTrc1

+0

1-based. Просто выполнил поиск в этом блоке для «-1», и строка не найдена. Я уже проверил это примерно в 1000 раз. Но ты прав. Это устаревший код. Я изменю код, чтобы сделать его 0-based. :) – Ampere

+0

PS: FastMM настроен на режим супер-агрессивной отладки. Я не помню точно, как это звучит сообщение об ошибке, которое оно бросает, когда вы пытаетесь прочитать/записать за границей массива, но не является «поврежденным заголовком». Также у меня есть «Проверка диапазона» ON – Ampere

1

Все, кажется, пропуская важную ошибку в начале:

Компания FastMM обнаружила ошибку во время операции сканирования свободного блока. FastMM обнаружил, что блок был изменен после освобождения.

Старый указатель используется где-то.

Поскольку у вас нет подсказки по ошибке указателя, которую вы ищете, я бы забыл ее сейчас и поеду искать другую - они могут оказаться связанными.

+0

Привет Лорен. Я не совсем понимаю, что вы имеете в виду. Можете ли вы добавить несколько подробностей? Вы имеете в виду, что «старый указатель» является указателем на объект SC? Или указатель на массив «trace»? Объект SC имеет короткий срок службы (менее 100 мс), и я использую объект только один раз для загрузки/декодирования файла с диска. Есть несколько других объектов, похожих на SC, для чтения похожих форматов файлов с диска. Основываясь на запросе пользователя, программа загружает файлы с диска в эти специализированные объекты, затем я переношу данные в объединяющий объект (ы) (TCub), который живет для всего диапазона программы. Все остальные объекты-загрузчики работают. – Ampere

+0

FastMM жалуется, что вы выпустили указатель, а затем изменили память, на которую он указывал. В то время как это опасно, вы обычно избегаете его, чтобы он, вероятно, не выходил из строя. Вы все равно должны это исправить. –

+0

В блоке указан фрагмент кода для этой ошибки. – Ampere

2

Вы пытались запустить этот код с включенным FullDebugModeScanMemoryPoolBeforeEveryOperation? Вы пытались вызвать ScanMemoryPoolForCorruptions при запуске TScObj.ReadData?

Если это не помогает - попробуйте войти в этот вызов проблемы (GetMem?) И следуйте за кодом FastMM, чтобы увидеть адрес этого поврежденного заголовка. Просто запишите его на бумаге и перезапустите программу. Есть очень большие шансы, что адрес этого блока будет таким же.

Установите точку останова в безопасном месте - то есть прямо перед «плохими вещами». После этого остановите его - затем установите новую точку останова на место памяти - прямо на этом заголовке, который позже будет поврежден (не забудьте установить слишком рано).

Затем просто запустите свою программу - отладчик остановится прямо на этом плохом коде, который пытается изменить заголовок.

Смежные вопросы