forum.boolean.name

forum.boolean.name (http://forum.boolean.name/index.php)
-   FAQ (http://forum.boolean.name/forumdisplay.php?f=15)
-   -   "DebugLog и память" или "юзаем аккуратно" (http://forum.boolean.name/showthread.php?t=541)

impersonalis 10.01.2006 18:14

"DebugLog и память" или "юзаем аккуратно".
На создание этой заметки меня сподвиг факт существования культа авторитета и культа мнения большинства.
Или как называл эти тормозящие процесс познания явления Френсис Бэкон: призраки рынка и призраки театра.
Но не будем вдаваться в подробности поиска метода познания в период Нового Времени и вернёмся к b3d.
Помнится многие рьяно уверяли меня в том, что b3d адекватно отображает кирилический текст произвольной длины командой Text...
Пока Morpher и Tormoz не подтвердили этот обидный недостаток... Часто проблема обнаружения уязвимости заключается в сложности провоцирования ошибки.
На этот раз сбой при выполнении команды Debuglog.
К сожалению чётко сформулировать условия сбоя пока не удалось, но происходит он при
многократном вызове функций ( в т.ч. рекурсивно) и юзании конструкций типа:
Код:

Function a(x)
        DebugLog x
        Return x
End Function

DebugLog a(x)

В приведённом ниже примере, уменьшение кол-ва вызовов команды debuglog ведёт к повышению устойчивости, а запуск программы вне отладочного режима ( что эквивалентно полному избавлению кода от команд отладки) даёт 100%-правильный результат вычилений при любом кол-ве вызовов.
Собственно, сам пример:
В примере набо функций и тип для выичсления определителя кв.матрицы произвольного ограниченного размера.
Конкретно считается вот такой определитель:

Нетрудно заметить пропорциональность строк, на основании чего сделать вывод - матрица имеет нулевой
детерминант.
Вот b3d реализация:
Код:

Graphics 800,600
SetFont LoadFont("arial cur",20)
Const TMatrixMaxIndex2D=99
Type TMatrix
        Field Buffer#[TMatrixMaxIndex2D]
        Field StringSize
        Field CSize
End Type

Function TMatrix_Create.TMatrix(size,size2=-1)
        M.TMatrix=New TMatrix
        M\StringSize=size
        M\CSize=size2
        If M\CSize=-1 M\CSize=size
        Return M
End Function

Function TMatrix_IN(M.TMatrix,SNumber,CNumber,Z$)
        Local Index2d=(SNumber-1)*M\StringSize+(CNumber-1)
        M\Buffer[Index2d]=Z$
End Function


Function TMatrix_OUT(M.TMatrix,SNumber,CNumber)
        Local Index2d=(SNumber-1)*M\StringSize+(CNumber-1)
        Return M\Buffer[Index2d]
End Function

Function TMatrix_Delete(M.TMatrix)
        Delete M
End Function

Function TMatrix_Determinant#(M.TMatrix)

        SS=M\StringSize
        CS=M\CSize
       
        If SS=1 And CS=1
        DebugLog "determ ="+M\buffer[0]
        Return Float(M\buffer[0])
        EndIf
        Local DET#=0
        For i=1 To SS
 *MINOR.TMATRIX=TMatrix_minor(M,1,i)
 *DebugLog ""+DET+"+"+((-1)^(i+1))+"*"+TMatrix_Determinant(MINOR)+"*"+TMatrix_OUT(M,1,i)
 *DET=DET+(-1)^(i+1)*TMatrix_Determinant(MINOR)*Float(TMatrix_OUT(M,1,i))
 *DebugLog "="+DEt
 *TMatrix_Delete(MINOR)
        Next
        DebugLog "determ ="+DET
        Return DET
End Function

Function TMatrix_minor.TMatrix(M.TMatrix,NS,NC)
        minSS=M\StringSize-1
        minCS=M\CSize-1
        Minor.TMatrix=TMATRIX_Create(minSS,minCS)
        Local Smin=1
        For S=1 To M\CSize
 *Local Cmin=1
 *For C=1 To M\StringSize
 *        If C=NC
 *        Cmin=Cmin-1
 *        Else
 *        TMatrix_IN(Minor,Smin,Cmin,TMatrix_OUT(M,S,C))
 *        EndIf
 *        Cmin=Cmin+1
 *Next
 *If S=NS
 *Smin=Smin-1
 *EndIf
 *Smin=Smin+1
        Next
        Return Minor
End Function

a.tmatrix=tmatrix_create(3,3)
 TMatrix_IN(a,1,1,1)
 TMatrix_IN(a,1,2,2)
 TMatrix_IN(a,1,3,3)

 TMatrix_IN(a,2,1,4)
 TMatrix_IN(a,2,2,5)
 TMatrix_IN(a,2,3,6)

 TMatrix_IN(a,3,1,7)
 TMatrix_IN(a,3,2,8)
 TMatrix_IN(a,3,3,9)
For i=1 To 25
dd=TMatrix_Determinant(a)
DebugLog dd
Print "test#: "+i+"; * 0="+dd
Next
WaitKey()
End

Код вычурно перегружен отладочными командами и вызовами функций с целью показть уязвимость в действии.
Запустите его в ___ОТЛАДОЧНОМ____ режиме.
И понаблюдайте - сколько раз из 25 b3d докажет что 0<>0.
Теперь отключите отладку и сравните результаты. =)
Положив 2 часа на отладку полностью рабочего кода ( постоянно добавляя отладочные команды - чем ещё более усугублял совё положение) я перепробовал различные версии b3d - все подвержены данной ошибке, которая, судя по всему, заключается в затирании уже используемых пространств памяти.

alex-mad 10.01.2006 18:25

гы... когда я услышал по скайпу ответ b3d этой матрицы (за минуту до этого проверил формулы написанные Импером) я почти упал со стула!
будьте осторожны!

AsmLover 10.01.2006 22:06

Цитата:

Originally posted by impersonalis@Jan 10 2006, 05:14 PM
Код вычурно перегружен отладочными командами и вызовами функций с целью показть уязвимость в действии.
Запустите его в ___ОТЛАДОЧНОМ____ режиме.
И понаблюдайте - сколько раз из 25 b3d докажет что 0<>0.
Теперь отключите отладку и сравните результаты. =)
Положив 2 часа на отладку полностью рабочего кода ( постоянно добавляя отладочные команды - чем ещё более усугублял совё положение) я перепробовал различные версии b3d - все подвержены данной ошибке, которая, судя по всему, заключается в затирании уже используемых пространств памяти.

8)
К сожалению, это не баг, а всего лишь неграмотное применение отладчика в примере, который привел Impersonalis. Отлачик в функции TMatrix_Determinant рекурсивно вызывает эту же функцию, что, естественно, неизбежно вызывает исчерпание стека (хорошо еще, что встроенные отладчики имеют свою стековую машину, а то бы вообще все рунуло).

Правильно делать так:
Код:

Function TMatrix_Determinant#(M.TMatrix)
  SS=M\StringSize
  CS=M\CSize

  If SS=1 And CS=1
    DebugLog "determ ="+M\buffer[0]
    Return Float(M\buffer[0])
  EndIf
 
  Local DET#=0
  For i=1 To SS
    MINOR.TMATRIX=TMatrix_minor(M,1,i)
    TMD#=TMatrix_Determinant(MINOR)
    DebugLog ""+DET+"+"+((-1)^(i+1))+"*"+TMD+"*"+TMatrix_OUT(M,1,i)
    DET=DET+(-1)^(i+1)*TMD*Float(TMatrix_OUT(M,1,i))
    DebugLog "="+DEt
    TMatrix_Delete(MINOR)
  Next
  DebugLog "determ ="+DET
  Return DET
End Function

Теперь никаких ошибок не будет.

В нормальном режиме и дебаггера достаточно ресурсов для своей работы.
Собственно, DebugLog предназначен для формирования строки вывода и имеет буфер строки более 16 Кбайт, это легко проверить с помощью
Код:

a$=""
For j=1 To 16000
  For i=0 To 9
  a$=a$+i
  Next
Next

DebugLog a$

WaitKey

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

В Help-файле написано, что аргументом DebugLog может быть текстовая переменная, строка, числовое значение. К сожалению, структура справки в целом нечеткая, поэтому трудно судить, запрещалось ли изначально в ней вычисление выражения. Но я пытался без рекурсии свалить стек, но при корректных вычислениях у меня не получилось. Но в любом случае, для вычисления выражений в приведенном Impersonalisom примере ограничивается стековое пространство при рекурсии.

Но есть программы, которые ведут себя странно со включенным Debug , причем DebugLog вообще там не используется, например партикловая система Lotus.

impersonalis 11.01.2006 01:22

Ну в общем то целью заметки было предотвратить неосторожное юзание debuglog.
Ведь это сложнонаходимо.
Если переполнить стек программы она либо закрешится, либо молча зкроется - а тут всё считается.. но не правильно.

То что код так не пишут я знаю =)
Это пример.

В любом случае - спибо за автороитетное мнение и уточнение деталей.

AsmLover 11.01.2006 03:06

Цитата:

Originally posted by impersonalis@Jan 11 2006, 12:22 AM
Ну в общем то целью заметки было предотвратить неосторожное юзание debuglog.
Ведь это сложнонаходимо.
Если переполнить стек программы она либо закрешится, либо молча зкроется - а тут всё считается.. но не правильно.

То что код так не пишут я знаю =)
Это пример.

В любом случае - спибо за автороитетное мнение и уточнение деталей.

1) Хорошо сказал - неосторожное юзанье! :super:
DebugLog можно и нужно использовать. :super:
2)Насчет сложнонаходимо - у меня это заняло 1 минуту с проверкой. А для того, чтобы потренироваться со стеком - надо на Форте попробовать попрограммировать - там одновременно несколько стеков используется, вот где раздолье! :bravo:
3)Во-первых тут стек не программы, а отладчика. Отладчик работает, как вариант, сохраняя кадр программы и переключая регистр BP на другую область стека. А во-вторых, так как DebugLog чисто информационный, из-за переполнения стека может быть получен только недостоверный результат, поскольку вычисление функции может прерваться в любом месте, но при этом происходит восстановление кадра программы (всех регистров, стека и флагов). То есть краха программы не будет. :P
Поэтому считается все правильно, только в последней рекурсии, на которой выделенный стек загнулся, вычисление обрывается с итоговым неправильным состоянием регистров микропроцессора, которое выдаст в неправильный результат DET. :wallbash:

AsmLover 11.01.2006 03:15

P.S. Естественно, под BP подразумевается EBP или RBP.

AsmLover 11.01.2006 03:27

Имеется в виду, что ESP юзается обычно в связки с EPB. Вообщем, это уже не Бейсик.
Что-то я размазался по трем постам.... Зарегиться, что ли, чтобы править можно было... :)

impersonalis 11.01.2006 04:14

Цитата:

Originally posted by AsmLover@Jan 11 2006, 02:27 AM
Зарегиться, что ли, чтобы править можно было... :)
ага :)
давно ждём, столь опытного кодера в своих рядах ;)


Часовой пояс GMT +4, время: 17:08.

vBulletin® Version 3.6.5.
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Перевод: zCarot