Конференция "Основная" » Тихо умирающее приложение [D7, WinXP]
 
  • AlexNe (05.07.08 18:41) [0]
    Уважаемые эксперты! Похоже я своими стараниями попал
    в ситуацию из которой пока не вижу выхода.
    Суть проблемы:
    Имеется приложение, работающее с различными удаленными серверами через TSocketConnection, c базой данных Interbase, и с сервером реального времени, который крутится на DOS машине - соотв. через синхронный сокет TIdTCPClient.
    Для работы с каждым сервером имеется отдельный поток,
    причем поток сокета TIdTCPClient имеет наивысший приоритет.
    Приложение работает под WinXP,  работает без замечаний иногда 9 часов, иногда 3, затем элегантно, без каких либо сообщений схлопывается (а следом закрываются сервера).
    Отслеживание возникающих исключений
    (посылка сообщений из Application.OnException другой программе) не дало ничего - все чисто до самого схлопывания.
    Запустить программу надолго под отладчиком Delphi я не имею возможности.
    Безуспешные попытки найти причину доводят меня до отчаяния.
    Уже вырубил на всех TSocketConnection  свойство SupportCallBacks в False. Во всех Еxecute потоков защитил в
    try-except все что только возможно - не помогает!

    Может быть кто-нибудь сталкивался с похожей ситуацией при работе с потоками, или подскажет хотябы в какую сторону искать. Буду признателен за любые замечания
  • Loginov Dmitry © (05.07.08 18:59) [1]
    > или подскажет хотябы в какую сторону искать.


    Копать следует путем записи в лог всего и вся (все действия приложения, все сообщения об ошибках с указанием ID потока), а потом часами сидеть и анализировать зависимости между записанными в лог строчками.

    Загляни также в системный журнал работы ОС. Из него порой удается извлечь толк.
  • Сергей М. © (05.07.08 19:20) [2]

    > приложение, работающее с различными удаленными серверами .. схлопывается


    > следом закрываются сервера


    Полный трындец - рухнул кривой самопальный клиент, и в солидарность ему тут же рухнул сервер-бренд)

    Или нет ? Или совсем не так дело обстоит ?)
  • AlexNe (05.07.08 19:38) [3]
    Одна из возможных причин схлопывания, как мне думалось, возникновение Exceptions внутри execute потока.
    Действительно, когда снабдил потоки механизмом передачи логов - что-то вроде

    procedure TMainTread.Execure;
    var Hwnd: THandle;
     Txt: string;
     cds: CopyDataStruct;
    begin
    ..........
     try
     .....
     except
       Txt := 'Интересующие параметры';
       cds.cbData := length (Txt);
       cds.lpData := PChar (Txt);
       SendMessage (hwnd, wm_CopyData, 0, Integer (@cds));
     end;
    .........
    end;

    обнаружил, что в потоке образуется за секунду куча исключений, которые
    не выводятся на экран! Ну и через некоторое время операционка очевидно
    убивает приложение.
    Фишка в том, что эти ошибки я выловил и убрал, а проблема осталась!

    Системный журнал щас пойду копать, спасибо за совет.
  • AlexNe (05.07.08 19:41) [4]
    to Сергей М.
    не, сервера тоже самопальные :)  
    они и должны в случае пропадания всех клиентов закрываться.
    А трындец действительно полный.
  • Loginov Dmitry © (05.07.08 19:41) [5]
    > except
    >   Txt := 'Интересующие параметры';
    >   cds.cbData := length (Txt);
    >   cds.lpData := PChar (Txt);
    >   SendMessage (hwnd, wm_CopyData, 0, Integer (@cds));
    > end;


    А оригинальный текст сообщения об ошибке разве не интересует?
  • AlexNe (05.07.08 19:56) [6]
    Нет, ну конечно, там стоит Exception.Message когда оно есть, плюс номер тика, когда случилось. Или я чего-то не понял? В смысле засунуть туда HandleException?
  • AlexNe (05.07.08 20:03) [7]
    Просмотр системного журнала показал наличие серии ошибок Source="Application Error"   Category= "None" Event=1000 но с моментами вылетов эти серии слабо кореллируют
  • Loginov Dmitry © (05.07.08 20:19) [8]
    > Или я чего-то не понял?


    да фик тебя знает. Я имел ввиду следующее:

    try

    except
     on E: Exception do
       WriteToLog(E.Message);
    end;

  • Сергей М. © (05.07.08 21:03) [9]

    > не, сервера тоже самопальные


    Не, тады твоим потугам точно трындец)

    Бо ты нишиша не знаешь, что . где, когда и при каких обстоятельствах у тебя чего-то там "попадает".

    Ты отладчик-то пользуешь, чудо ?)
    Или на бубен уповаешь ?)
  • AlexNe (05.07.08 21:10) [10]
    to Loginov Dmitry
    прошу прощения, у меня уже из-за этой проблемы фокус сбивается...
    Может быть стоит. Только это ничего не даст - в програме после
    каждого  try except стоят WriteToLog (т.е SendMessage) , и еще
    тестировалось с обработчиком Application.OnException, который как раз
    записывал в лог E.Message. Так вот, за ночь ни одной записи об исключительных ситуациях и при этом два схлопывания. То есть, когда начинается разрушение, либо SendMessage не работает, либо черт знает вообще что....
  • AlexNe (05.07.08 21:12) [11]
    to Сергей М.
    на бубен, на бубен
  • Сергей М. © (05.07.08 21:17) [12]
    Ну тады тряси)
    Авось само рассосется)

    Небеса, панимаишь ..
  • Дмитрий Белькевич © (05.07.08 21:17) [13]
    Удалённую отладку используй. Гарантированно Excpeption'ы покажет, да еще и потрассировать даст. Очень помогает, бывает. Особенно, когда клиент на другой стороне шарика ;)
  • AlexNe (05.07.08 21:21) [14]
    Собственно проблема возникла после замены асинхронного, но кривого TClientSocket на не кривой, но синхронный  TIdTCPClient для работы с сервером под DOS. Соответственно пришлось засунуть TIdTCPClient  в отдельный поток, а потом и все остальные обращения к серверам рассовать по потокам. И в общем все стало работать много лучше, но в отместку этот глюк!
  • Дмитрий Белькевич © (05.07.08 21:21) [15]
    Даже мёртвый поток, кстати, после exception'а может OnTerminate вызвать, если его обработать, конечно. А (Sender as TThread).FatalException иногда интересные данные отдаёт.
  • Дмитрий Белькевич © (05.07.08 21:23) [16]
    Можно еще попробовать Indy обновить до актуальных.
  • Сергей М. © (05.07.08 21:30) [17]

    > кривого TClientSocket на не кривой, но синхронный  TIdTCPClient


    Эдаких клоунов, зафиксировавших сей факт,  - пруд пруди)
  • AlexNe (05.07.08 21:38) [18]
    to Дмитрий Белькевич
    Спасибо, попробую. Насколько понял из хелпа это для серверов под виндой. Есть еще Interbase, но он не падал и ничего особенного не ругался. Досовский сервер пишет подробный лог, пойду его копать.
    Про OnTerminate и (Sender as TThread).FatalException напиши завтра, т.к. систему забрали а работу и поэкспериментировать до утра не смогу.
  • Дмитрий Белькевич © (05.07.08 21:39) [19]
    Угу, всё в потоках замечательно работает. Сотни потоков.
  • AlexNe (05.07.08 21:40) [20]
    не напиши завтра а напишу завтра, конечно...
  • Германн © (06.07.08 02:34) [21]

    > AlexNe   (05.07.08 21:21) [14]
    >
    > Собственно проблема возникла после замены асинхронного,
    > но кривого TClientSocket на не кривой, но синхронный  TIdTCPClient

    Есть ещё ICS. Асинхронный и не очень кривой.
  • AlexNe (06.07.08 11:15) [22]
    to Германн
    Спасибо. Буду иметь в виду на будущее.
  • Slym © (07.07.08 05:15) [23]
    AlexNe   (05.07.08 21:21) [14]
    TClientSocket

    Самый прямой из простых оберток на WinSock... и не только асинхронный, но синхронный см ClientType:=ctBlocking;
  • AlexNe (08.07.08 11:58) [24]
    to Slym
    Cвязь через TClientSocket с сервером реального времени (на машине под DOS) у меня работала устойчиво только при условии установки наивысшего приоритета для всего клиентского Win приложения. Засунуть в специальный  поток его проблематично. При этом, если загрузить сильно клиента, например перерисовкой интерфейса, то часть приходящих с сервера пакетов TClientSocket просто терял...  
    TIdTCPClient в отдельном потоке теряет ничего.
  • Slym © (08.07.08 12:18) [25]
    AlexNe   (08.07.08 11:58) [24]
    Cвязь через TClientSocket

    покажи как делал
    AlexNe   (08.07.08 11:58) [24]
    Засунуть в специальный  поток его проблематично

    ничего проблемного:
    unit Unit2;
    interface
    uses
     Classes,Scktcomp,winsock;

    type
     TSockThread = class(TThread)
     private
     protected
       procedure Execute; override;
     end;

    implementation

    function SockWaitForData(ASocket:TSocket; ATimeout: Longint): Boolean;
    var
     FDSet: TFDSet;
     TimeVal: TTimeVal;
    begin
     TimeVal.tv_sec := ATimeout div 1000;
     TimeVal.tv_usec := (ATimeout mod 1000) * 1000;
     FD_ZERO(FDSet);
     FD_SET(ASocket, FDSet);
     Result := select(0, @FDSet, nil, nil, @TimeVal) > 0;
    end;

    function SockReadLn(ASocket:TSocket;ATerminator:char;AMaxLineLength:integer;ATimeOut:Long int):string;
    var
     Buf:array[byte] of char;
     SizeBuf:integer;
     r,i:integer;
     s:string;
    begin
     SizeBuf:=SizeOf(Buf);
     result:='';
     while SockWaitForData(ASocket,ATimeOut) do
     begin
       r:=recv(ASocket, Buf, SizeBuf, MSG_PEEK);
       if r=SOCKET_ERROR then RaiseSocketError(WSAGetLastError, 'recv');
       i:=LCharPos(@Buf,ATerminator,r);
       if i<>0 then r:=i;
       r:=recv(ASocket, Buf, r, 0);
       if r=SOCKET_ERROR then RaiseSocketError(WSAGetLastError, 'recv');
       SetString(s,Buf,r);
       result:=result+s;
       if i<>0 then Break;
     end;
     if length(result)=0 then exit;
     i:=length(result)-1;
     if (ATerminator=LF) and (result[i]=CR) then Dec(i);
     SetLength(result,i);
    end;

    { TSockThread }

    procedure TSockThread.Execute;
    var
     Socket:TClientWinSocket;
     s:string;
     Code:integer;
    begin
     Socket:=TClientWinSocket.Create(-1);
     try
       Socket.ClientType:=ctBlocking;
       Socket.Open('192.168.14.2','','',3128);
       Socket.SendText('CONNECT hostname HTTP/1.0'#13#10);
       Socket.SendText(#13#10);
       s:=SockReadLn(Socket.SocketHandle);
       Fetch(s,' ');
       Code:=StrToIntDef(Fetch(s,' '),200);
       if Code<>200 then raise Exception.CreateFmt('Bad HTTP status code: %d %s',[Code,s]);
       repeat until
         SockReadLn(PeerSocket.SocketHandle) = '';
       ...
     finally
       Socket.Free;
     end;
    end;

    end.

  • Дмитрий Белькевич © (08.07.08 12:25) [26]
    Что-то мне кажется, что решение задачи слишком усложнено. Какую проблему решает система?
  • Slym © (08.07.08 12:27) [27]
    Дмитрий Белькевич ©   (08.07.08 12:25) [26]
    Какую проблему решает система?

    2. что за DOS сервер, не порали найти/сделать win аналог?
  • Eraser © (08.07.08 15:02) [28]
    > [0] AlexNe   (05.07.08 18:41)


    > причем поток сокета TIdTCPClient имеет наивысший приоритет.

    сетевым потоком не нужно ставить наивысший приоритет, пустая трата процессорного времени.. работа с сетью - довольно медленная операция.
    проблема не в инди скорее всего, там tcp клиент/сервер довольно грамотно написаны. Если приложение падает и даже исключение не вылетает, то скорее всего где-то неверная работа с памятью, либо что-то не корректно вызывается и система сразу прихлопывает приложение. Сетевая часть думаю особо не при чем.
  • AlexNe (08.07.08 16:36) [29]
    Извиняюсь за задержку - у меня последний день командировки и рвут на части.
    to Slym ©  
    Возможно, что-то было с  TClientSocket в потоке сделано не так, нужно рыть старый исходник. Но сокет с DOS машиной через несколько минут 10Гц обмена вис, причем ситуация решалась только перезагрузкой DOS

    to Дмитрий Белькевич ©  
    Нет, не усложнено. Система решает задачу управления научным оборудованием, которое разбросано в радиусе 50 м и имеет ограничения по длине проводов связи - отсюда наличие множества серверов на разлинух компах.

    to Slym ©  
    >2. что за DOS сервер, не порали найти/сделать win аналог?
    Найти нельзя. Сделать - во- первых нужна система жесткого реального времени, тут скорее QNX. Но главное, то кол-во самодельных железок, для которых по мере развития системы писались дрова - все переписть в обночасье никак не получится, а останавливать ради этого систему никто не даст. Так что живем как живем.

    to Eraser ©
    ? - если кто-то приостановит обмен с DOS машиной на 0.5 с., сработает защита, которая останавливает плавное движение конструкции весом 3.5т.
    > Если приложение падает и даже исключение не вылетает, то скорее всего > где-то неверная работа с памятью
    что-то похожее на конфликт реентерабельности наблюдается по логам, - два раза произошел вылет при входе в Timer.OnTimer (там ничего нет, кроме обновления интерфейсных элементов).
    Поставил на тестирование - до вечера, последний шанс отловить барабашку.
  • Сергей М. © (08.07.08 17:25) [30]

    > синхронный  TIdTCPClient для работы с сервером под DOS


    Клиенту глубоко фиолетово, под какой ОС работает сервер.
    Равно как и наоборот.
    И синхронность или асинхронность тут совершенно ни причем.
  • AlexNe (08.07.08 19:18) [31]
    >Сетевая часть думаю особо не при чем.
  • AlexNe (08.07.08 19:22) [32]
    to Eraser ©
    Предположение появилось из-за чрезвычайной редкости события - 1-2 раза за сутки, при том что старая версия с теми же серверам, оборудованием и в том же режиме не падает (хотя и теряет иногда важную информацию)
  • Сергей М. © (08.07.08 19:26) [33]

    > AlexNe   (08.07.08 19:18) [31]


    Смотря что ты подразумеваешь под "сетевой частью".

    Подозреваю, что ты не имеешь ни малейшего представления о модели OSI.
  • AlexNe (08.07.08 22:10) [34]
    Случилось! Дебаггер указал на ошибку в потоке, в котором сидит IdTCPClient. Лог застопорился перед чтением свойства объекта-наследника соотв. потока. Т.е. какая-то аномалия при работе сокета DOS-Windows.
    К сожалению проверить и уточнить получится нескоро, командировка кончилась. Поскольку моя работа с системой прерывается, тему считаю временно закрытой. Всем спасибо за потраченное время и ценную информацию.
  • Дмитрий Белькевич © (08.07.08 22:37) [35]
    >Т.е. какая-то аномалия при работе сокета DOS-Windows.

    Наиболее вероятная аномалия - доступ к обломкам разрушенного объекта. Объектам после разрушения присваивай nil, может ошибку удастся 'проявить'. По AV доступа к 00000000.

    Еще немного информации. Если нужен хороший реалтайм, разумно выбросить весь дос и вообще все лишние писюки и, по возможности, самодельные железки вместе с дровами. Сделать промежуточный приём на однокристалках, постараться все данные собирать только ими, благо сейчас их разных есть, можно и абсолютный аналог, и дифференциальный, и цифру забирать, и по многим каналам сразу. Объединить их в сеть (есть железки с готовым железячным tcp стёком), и сливать информацию по мере доступности на писюк.

    Сделать единый движок для 'пропихивания' инфы в компьютер. И несколько разных драйверов для разных входных данных.

    Я, конечно, всех особенностей не знаю, но, скорее всего, должно работать без проблем.
  • SergeyIT © (09.07.08 00:02) [36]

    > Дмитрий Белькевич ©   (08.07.08 22:37) [35]
    > разумно выбросить весь дос и вообще все лишние писюки

    Легко говорить. А кто заплатит за такую переделку?
    Как говортся - хорошо быть здоровым и богатым.;)
  • Anatoly Podgoretsky © (09.07.08 00:31) [37]
    > Дмитрий Белькевич  (08.07.2008 22:37:35)  [35]

    Не надо nil, лучше $80808080
  • Дмитрий Белькевич © (09.07.08 12:24) [38]
    >Легко говорить. А кто заплатит за такую переделку?
    >Как говортся - хорошо быть здоровым и богатым.;)

    Лишние писюки - продать, купить однокристалок. Разницу (если останется) - пропить ;)
    Вообще, всё равно придётся им что-то менять, просто 'закопаются' со временем. Хотя, может быть я и не прав. Все обстоятельства мне неизвестны.

    >Не надо nil, лучше $80808080

    Почему, кстати? Нашел только, что FastMM в FullDebugMode атоматом забивает память разрушенных объектов и строк этим значением.

    Вот, кстати, еще одна идея: поставить FastMM в FullDebugMode.
  • Anatoly Podgoretsky © (09.07.08 13:02) [39]
    > Дмитрий Белькевич  (09.07.2008 12:24:38)  [38]

    Для того что бы отличить не инициализированое, от уничтоженого.
    Методика из FastMM
  • Сергей М. © (09.07.08 14:14) [40]

    > аномалия при работе сокета DOS-Windows


    Что это за сокет такой "DOS-Windows" ?

    Какое тебе вообще дело до сокета на другом конце соединения ?

    Если там что-то не в порядке, сокет на твоем конце соединения возбудит соответствующее исключение, которое ты должен грамотно обработать.
  • Alex_Ne (11.07.08 11:25) [41]
    to Дмитрий Белькевич ©  
    to Anatoly Podgoretsky ©


    > Наиболее вероятная аномалия - доступ к обломкам разрушенного
    > объекта. Объектам после разрушения присваивай nil, может
    > ошибку удастся 'проявить'. По AV доступа к 00000000.



    > Не надо nil, лучше $80808080


    Т.е. присвоение соотв. переменной $80808080 в OnTerminate или OnDestroy?
    (штатно все объекты всюду убиваются или методом Free или с последующей установкой в Nil соотв. переменной)
    Судя по последнему событию, которое удалось засечь перед отъездом -
    похоже рушится незаметно поток, в котором сидит IdTCPClient.  При этом событие потока OnTerminate не возникает, т.к. не приходит соотв. лог. Далее, обращение в основном потоке (в обработчике TTimer.OnTimer) к его св-ву обрушивает приложение. По какой причине все это происходит без AV - для меня загадка. Связь через IdTCPClient также тестировалась и на помехи и на приход "неправильного" пакета и на разрыв сети - все обрабатывается корректно. И вообще, первый раз наблюдаю такое неуловимое чудо за 9 лет работы с Delphi.

    Попробую промоделировать ситуацию дома. Если удастся найти разгадку - сообщу на форум. Спасибо еще раз за информацию.
  • Alex_Ne (11.07.08 11:29) [42]
    to Дмитрий Белькевич ©  

    > Если нужен хороший реалтайм, разумно выбросить весь дос
    > и вообще все лишние писюки и, по возможности, самодельные
    > железки вместе с дровами. Сделать промежуточный приём на
    > однокристалках, постараться все данные собирать только ими,
    >  благо сейчас их разных есть, можно и абсолютный аналог,
    >  и дифференциальный, и цифру забирать, и по многим каналам
    > сразу. Объединить их в сеть (есть железки с готовым железячным
    > tcp стёком), и сливать информацию по мере доступности на
    > писюк.


    Прямо бальзам! Вы как будто мысли читаете - на самом деле выкинуть
    нафиг DOS машину, или ограничить ее использование только как станции точного времени (10МГц таймер на основе PCI1780 и с привязкой времени по GPS приемнику)  - давняя мечта. Последние года два все новое именно и строится под децентрализованную архитектуру на отдельных программируемых платах - связь через tcp/ip или rs232. ИМХО более гибкий и технологичный с точки зрения развития/ремонта/обслуживания подход.  
    Но избавиться от прошлого не так просто - о чем уже написано выше.
  • Anatoly Podgoretsky © (11.07.08 11:29) [43]
    > Alex_Ne  (11.07.2008 11:25:41)  [41]

    Три значения для переменной объекта позволяют сократить зону поиска.

    nil - обращение не к инициализированому объекту
    80808080 - обращение к разрушеному объекту
    xxxxx - разрушение не контролируется тобой или забыл установить 80808080
  • Дмитрий Белькевич © (11.07.08 19:58) [44]
    >Т.е. присвоение соотв. переменной $80808080 в OnTerminate или OnDestroy?

    Нет, после .free.

    >штатно все объекты всюду убиваются или методом Free или с последующей установкой в Nil соотв. переменной

    Вот, после Free нужно установить все разрушенные 'переменные' на адрес $80808080. А еще лучше воспользоваться FastMM, он в режиме FullDebugMode сам всё сделает. Да и вообще его стоит оставить вместо стандартного, реально быстрее работает, и в тяжелых случаях работы с памятью гораздо меньше её фрагментирует.

    >При этом событие потока OnTerminate не возникает,

    Странно, вообще-то. У нас в одном софте поток частенько раньше падал, пока не исправили всё. OnTerminate возникал всегда, 100%. Знаю, так как мы в нём порушенный поток пересоздаём, ну и в лог пишем, что упал.
    Никогда не видел, что бы поток упал так, что даже OnTerminate не смог вызвать. Хотя, допускаю, что такое возможно.

    >И вообще, первый раз наблюдаю такое неуловимое чудо за 9 лет работы с Delphi.

    Это еще очень везет, что есть доступ до всего, и можно пощупать в реальной работе лично руками.

    Плохо - это когда ты сам - в Беларуси, а софт - в Канаде, через 7 часовых поясов. Remoute debug помогает. Медленно, правда, но в особенно тяжелых случаях - единственное решение.

    А случаи с потоками иногда бывают чудесные, да. Правда, в 100% оказывается, что сам где-то накосячил.

    >Вы как будто мысли читаете

    Угу, я хотел в конце дописать, что воспользовался телепатором ;) Впрочем, всё и так достаточно очевидно.
  • Loginov Dmitry © (11.07.08 21:27) [45]
    > Вот, после Free нужно установить все разрушенные 'переменные'
    > на адрес $80808080. А еще лучше воспользоваться FastMM,
    > он в режиме FullDebugMode сам всё сделает.


    Одно другому не мешает.
  • TStas © (12.07.08 20:38) [46]
    Мне тоже кажется, что нужно выводить всё и смотреть каждую переменную в изменениях.
    Я как-то написал программу, она работала, в ней были большие рассчёты. Но вот иногда с ошибочными результатами, хотя она не закрывалась. Лучше бы закрывалась. Ошибку я не видел в упор. Грешил на битую память компа и т. д.  Керк по аське меня убеждал: "Ищи глюк, он есть, но прячется".
    И нашёл таки, хотя глюк был не мой, а паскальСкрипта. При присвоении переменной Integer суммы двух переменных Byte, он гнустно сначала считал результат байтом, а потом уж присваивал целому числу.
    Не знаю другого гарантированного метода изобличения глюков, кроме логирования. ИМХО его и быть не может.
 
Конференция "Основная" » Тихо умирающее приложение [D7, WinXP]
Есть новые Нет новых   [134491   +13][b:0][p:0.003]