Abnormal Termination Firebird 1.5.3.4870

Access Violation, некорректное выполнение запросов или вызовов API, ошибки утилит командной строки, в общем все, что вам мешает работать

Модераторы: kdv, dimitr

Valmir
Сообщения: 21
Зарегистрирован: 14 сен 2006, 10:59

Abnormal Termination Firebird 1.5.3.4870

Сообщение Valmir » 14 сен 2006, 12:06

Есть база на firebird 1.5.3.4870 и прога-клиент; одна из процедур базы использует функцию, реализованную в dll; естестенно dll лежит в папке UDF и все параметры функции описаны, а именно:
DECLARE EXTERNAL FUNCTION CALCPRICE
VARCHAR(255),
DOUBLE PRECISION,
DOUBLE PRECISION,
DOUBLE PRECISION,
DOUBLE PRECISION,
DOUBLE PRECISION,
DOUBLE PRECISION,
INTEGER
RETURNS DOUBLE PRECISION FREE_IT
ENTRY_POINT 'CalcPrice' MODULE_NAME 'MyDLL.dll'.
На моем компе или если подключаться с других компов, вызов вышеупомянутой процедуры проходит корректно. Но на серваке клиентов, время от времени при вызове процедуры сервак firebird сваливается.
В логах firebird написано следующее:
ARCTURUS (Server) Thu Sep 07 14:47:22 2006
Access violation.
The code attempted to access a virtual
address without privilege to do so.
This exception will cause the Firebird server
to terminate abnormally.

ARCTURUS (Client) Thu Sep 07 14:47:22 2006
C:\Program Files\Firebird\Firebird_1_5\bin\fbserver.exe: terminated abnormally (4294967295)
В журнале событий винды такоесообщение:
The description for Event ID ( 281 ) in Source ( FirebirdGuardianDefaultInstance ) cannot be found. The local computer
may not have the necessary registry information or message DLL files to display messages from a remote computer. You may
be able to use the /AUXSOURCE= flag to retrieve this description; see Help and Support for details. The following
information is part of the event: Abnormal Termination: C:\Program Files\Firebird\Firebird_1_5\bin\fbserver.exe:
terminated abnormally (4294967295)r.exe

При чем заметили такую особенность, при первом вызове процедуры с клиента, почти 100%, что сервак свалиться, если потом сделать на клиенте реконект, то процедура с теми же параметрами выполняется без ошибок, и дальнейшие вызовы проходят нормально. Потом, если отключиться клиентом от базы, подождать какое-то время и подключится и снова выполнить процедуру снова ошибка.
Пожет кто подсказать в чем дело? Вобще впечатление, что винда или фаерволы/антивири, время от времени не дают firebird получить доступ к dll или самой dll не дают нормально работать с ыделением памяти.

dimitr
Разработчик Firebird
Сообщения: 888
Зарегистрирован: 26 окт 2004, 16:20

Re: Abnormal Termination Firebird 1.5.3.4870

Сообщение dimitr » 14 сен 2006, 12:09

Valmir писал(а):RETURNS DOUBLE PRECISION FREE_IT
это шутка такая?

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 14 сен 2006, 12:13

если обращение к udf валит сервер, то udf написана криво. про free_it для double уже сказали, это как минимум...

Valmir
Сообщения: 21
Зарегистрирован: 14 сен 2006, 10:59

Сообщение Valmir » 14 сен 2006, 12:38

Может я чего-то недопонимаю...
Заголовок в функции dll следующий:

Код: Выделить всё

function CalcPrice(Formula: PChar; var PriceDiler, PriceIn, RateDiler,
    RateIn, RateOut, Coef: double; var PriceType: integer): PDouble; cdecl; export;
когда возвращал просто Double, то выдавалось левое число.
free it сдела так как PDouble, а не Double.
даже если допустить, что dll написана криво, почему тогда на моем компе все нормально работает и ничего не падает. а на другом падает, но только иногда?

dimitr
Разработчик Firebird
Сообщения: 888
Зарегистрирован: 26 окт 2004, 16:20

Сообщение dimitr » 14 сен 2006, 12:45

ты в своей UDF память для результата выделяешь? Если нет, то нафига тогда FREE_IT? Если да, то показывай, как именно.

Valmir
Сообщения: 21
Зарегистрирован: 14 сен 2006, 10:59

Сообщение Valmir » 14 сен 2006, 12:58

dimitr писал(а):ты в своей UDF память для результата выделяешь? Если нет, то нафига тогда FREE_IT? Если да, то показывай, как именно.
конечно выделяю:

Код: Выделить всё

New(Result);
...
Result^ := RoundReal(Result^/RateOut, 2);
я почитал статью "Как научиться писать UDF для InterBase за 21 мин",
там прямо написано

Код: Выделить всё

function Add_B(var iSmall: SmallInt; var iLong: Integer): PInteger; cdecl; export;

замечание: с серверами архитектуры SuperServer нельзя использовать такие udf. потому что udf будет вызываться в контексте одного процесса для разных пользователей (threads), и соответственно в глобальной переменной ResultInteger будет мешанина из конкурентных значений. В SuperServer значения можно возвращать только по значению, по FREE_IT, или через входной параметр
потому и сделал по ссылке и FREE_IT

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 14 сен 2006, 13:03

когда возвращал просто Double, то выдавалось левое число.
этот код восстановить нельзя?
New(Result);
статью ты видно НЕ ЧИТАЛ. Какой еще new для free_it ???

Valmir
Сообщения: 21
Зарегистрирован: 14 сен 2006, 10:59

Сообщение Valmir » 14 сен 2006, 13:12

kdv писал(а):
когда возвращал просто Double, то выдавалось левое число.
этот код восстановить нельзя?
New(Result);
статью ты видно НЕ ЧИТАЛ. Какой еще new для free_it ???
код восстановить можно, но говорю ж возвращалось левое число
NEW(result) - выделяет память под PDouble и делает result ссылкой на этот адрес памяти, да в статье сделана глобальная переменная, а значение функции становится ссылкой на эту переменную, только какой смысл, чем плох New ?
может я недопонимаю смысл Free_it? в статье не описано, что реально происходит....

dimitr
Разработчик Firebird
Сообщения: 888
Зарегистрирован: 26 окт 2004, 16:20

Сообщение dimitr » 14 сен 2006, 13:21

FREE_IT заставляет сервер освобождать за тобой память. Неужто трудно догадаться, что он это делает отнюдь не дельфовым менеджером памяти? Для FREE_IT ты обязан выделять память *только* через ib_util_malloc().

Valmir
Сообщения: 21
Зарегистрирован: 14 сен 2006, 10:59

Сообщение Valmir » 14 сен 2006, 13:25

хорошо Free_it освобождает память, тогда чем плох мой вариант?
функция в dll выделила под свой результат память, вернула firebird значение buReference, т.е. ссылкой на адрес этой памяти, что тут плохого? в чем некорректность dll?

dimitr
Разработчик Firebird
Сообщения: 888
Зарегистрирован: 26 окт 2004, 16:20

Сообщение dimitr » 14 сен 2006, 13:30

в том, что память выделена в дельфовой куче, а сервер ее освобождает через сишную free().

Valmir
Сообщения: 21
Зарегистрирован: 14 сен 2006, 10:59

Сообщение Valmir » 14 сен 2006, 13:42

dimitr писал(а):в том, что память выделена в дельфовой куче, а сервер ее освобождает через сишную free().
это понятно, но тогда пожалуйста дайте ответ на встречные вопросы
1. допустим делаем так: функция dll возвращает тип Double, тогда можно ложить значение прямо в Result функции или необходимо делать глобальную переменную?
2. Если делать глобальную переменную, то как будет работать в многопоточности? т.к. в статье как-то неоднозначно сказано:

Код: Выделить всё

с серверами архитектуры SuperServer нельзя использовать такие udf. потому что udf будет вызываться в контексте одного процесса для разных пользователей (threads), и соответственно в глобальной переменной ResultInteger будет мешанина из конкурентных значений. В SuperServer значения можно возвращать только по значению, по FREE_IT, или через входной параметр
- вобще исходя из данного куска складывается мнение, что если делаем byReference, то надо делать Free_it, чтобы был корректный резальтат в многопоточности
3. если вобще не делать Free_it, то тогда как будет освобождаться память из дэлфевой кучи после многократного вызова функции из dll

Valmir
Сообщения: 21
Зарегистрирован: 14 сен 2006, 10:59

Сообщение Valmir » 14 сен 2006, 13:47

Наверное уже достал, но надеюсь посдледний вопрос:
будет ли корректно работать в многопоточности и корректно выделяться/освобождаться память если делать так:

Код: Выделить всё

function CalcPrice(Formula: PChar; var PriceDiler, PriceIn, RateDiler,
    RateIn, RateOut, Coef: double; var PriceType: integer): Double; cdecl; export;
в теле функции не делаем никакого выделения памяти, и значение ложим прямо в результат.
в базе делаем возврат byValue и не делаем Free_if?
такой вариант корректный во всех ракурсах?

Dimitry Sibiryakov
Заслуженный разработчик
Сообщения: 1436
Зарегистрирован: 15 сен 2005, 09:05

Сообщение Dimitry Sibiryakov » 14 сен 2006, 13:53

Да, будет корректно работать в многопоточности.
Если не будете выделять память то не будет и ошибок с ее освобождением.
Основные баги обычно кроются не в заголовке функции а в ее коде.

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 14 сен 2006, 15:25

про free_it дополнительно написано в faq.
www.ibase.ru/ibfaq.htm#free_it

но в данном случае тебе free_it не нужен.

Valmir
Сообщения: 21
Зарегистрирован: 14 сен 2006, 10:59

Сообщение Valmir » 14 сен 2006, 16:12

а сервак все равно продолжает падать
текст функции в dll:

Код: Выделить всё

function CalcPrice(Formula: PChar; var PriceDiler, PriceIn, RateDiler,
    RateIn, RateOut, Coef: double; var PriceType: integer): Double;
  var
    i, j: integer;
    KeyLen: Word absolute Formula;
    KeyStr: PChar;
    s: String;
  begin
    PriceDiler := PriceDiler*RateDiler;
    PriceIn := PriceIn*RateIn;
    case PriceType of
      -2: Result := PriceDiler*Coef;
      -3: Result := PriceIn*Coef;
      else
      begin
        KeyStr := AllocMem(KeyLen + 1);
        Move((Formula + 2)^, KeyStr^, KeyLen);
        s := KeyStr;
        s := AnsiReplaceText(AnsiLowerCase(s), '[pricediler]', FloatToStr(PriceDiler));
        s := AnsiReplaceText(s, '[pricein]', FloatToStr(PriceIn));
        s := AnsiReplaceText(s, ',', '.');
        Result := CalcFunction(s, 0, 0, 0);
        FreeMem(KeyStr, KeyLen+1);
      end;
    end;
    Result := RoundReal(Result/RateOut, 2);
  end;
память если и выделяется дэлфей, то тут же освобождается
в базе функция определена так:

Код: Выделить всё

DECLARE EXTERNAL FUNCTION CALCPRICE
    VARCHAR(255),
    DOUBLE PRECISION,
    DOUBLE PRECISION,
    DOUBLE PRECISION,
    DOUBLE PRECISION,
    DOUBLE PRECISION,
    DOUBLE PRECISION,
    INTEGER
RETURNS DOUBLE PRECISION BY VALUE
ENTRY_POINT 'CalcPrice' MODULE_NAME 'MyDll.dll'
при первом вызове сервак падает, после реконекта все хорошо работает. если отключится выждать время 1-4 мин, то опять при первом обращении падает. такая ситуация именно на серваке клиентов. при тестировании на наших компах все нормально. в чем же все таки дело?

dimitr
Разработчик Firebird
Сообщения: 888
Зарегистрирован: 26 окт 2004, 16:20

Сообщение dimitr » 14 сен 2006, 16:56

Valmir писал(а):текст функции в dll:

Код: Выделить всё

function CalcPrice(Formula: PChar; var PriceDiler, PriceIn, RateDiler,
    RateIn, RateOut, Coef: double; var PriceType: integer): Double;
cdecl ты из вредности убрал?

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 14 сен 2006, 16:57

при первом вызове сервак падает
в конце статьи www.ibase.ru/devinfo/udf_ok.htm есть описание как отлаживать UDF в Delphi. Тем более, если сервак валится при первом же вызове.

Кстати, а зачем вот это AllocMem, Move и т.п. ? Да еще блин с такими опасными древностями типа word absolute formula...

Valmir
Сообщения: 21
Зарегистрирован: 14 сен 2006, 10:59

Сообщение Valmir » 15 сен 2006, 07:30

To dimitr: cdecl есть в заголовке процедуры в начале юнита, я запостил код реализации потому и нету cdecl и export;
To kdv:
1. может и древности, но работающие корректно или вы хотите сказат что такая реализация некорректная?
2. читайте внимательно я же четко написал: на моем компьютере все работает корректно, ничего не сваливается, функция возвращает правильный результат. Но когда тестируем программу на серваке клиентов, то только тогда сервак firebird падает, при чем только первый раз, после реконекта все работает нормально. Я именно это не могу понять, если предположить что dll написана криво, то почему у меня ничего не падает, а у них падает но иногда?

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 15 сен 2006, 10:42

Valmir. ТАК уже никто не пишет. Так когда-то писали под досом, где шансов получить AV почти не было, и утечки памяти никого не интересовали.

читать внимательно нужно вам - я объяснил, как отлаживать udf, что настоятельно рекомендую сделать, как и переписать этот странный и местами лишний код. Именно потому что у вас на сервере эта udf падает.

не хотите чинить - это ваша проблема.
Я именно это не могу понять, если предположить что dll написана криво, то почему у меня ничего не падает, а у них падает но иногда?
не надо ничего тут понимать. Возьмите программу с утечками памяти и неверной адресацией, и она будет глючить на двух компьютерах по разному.
И кривые udf именно так и ведут себя. они могут глючить при первом запуске, при десятом, или вообще на специфических данных.

Ответить