Зависание QUIK

Страницы: 1
RSS
Зависание QUIK
 
Добрый день.

QUIK 8.8.4.3. Стандартная схема оформления.
Иногда после нескольких дней работы зависает. При этом загрузка ЦП процессом 0
Сначала грешил на потокобезопасные функции. Но анализ логов показывает, что зависании происходит вне потокобезопасных функций (но это не точно ))
Можно ли по стеку определить последнюю вызванную QLua функцию?
 
Добрый день!
По скриншотам ответить на ваш вопрос, к сожалению, не сможем.

Просим выслать на почту нашей поддержки (quiksupport@arqatech.com) дамп процесса в момент зависания, архив рабочего места QUIK со скриптами, которые работали на момент появления ошибки, и подробное описание проблемы.
 
Цитата
Старатель написал:
Можно ли по стеку определить последнюю вызванную QLua функцию?
По второму можно, видимо, надо только на вкладке "модули" посмотреть базовый адрес lua53.dll, перебирать возможные варианты по скрину долго.
 
Anton, спасибо.
Скрытый текст
Что можно с этим сделать?

QUIK не совсем завис, а только основной поток находится в ожидании чего-то.
Один из скриптов в main периодически шлёт отладочную информацию через PrintDbgStr.
 
Цитата
Старатель написал:
Что можно с этим сделать?
Можно сказать, что зависло в функции lua_pushstring, но это не главное, на ее месте в принципе могла быть и другая, скрипт встал в ожидание на lua_lock (первая строка в сорце) и встал намертво. Где-то этот лок захвачен и не выпущен, возможно, в одном из (других) скриптов, возможно, косячок в самом квике. В скрипте захватить лок и не выпустить можно, например, если присутствует бесконечная рекурсия, может и другие варианты есть. Модельный пример с бесконечной рекурсией (элементарнейший, практические варианты, конечно, не так очевидны)
Код
function so()
   return so()
end

function main()
   so()
end
Тут еще важно, есть ли в рекурсивном кольце вызовы си-функций, они будут периодически отпускать лок и квик намертво не зависнет, будет течь и тормозить только. Тот же пример с вызовом си-функции квик не вешает
Код
function so()
   sleep(0)
   return so()
end

function main()
   so()
end
Тот факт, что один из скриптов в мейне все же прорывается время от времени, идею о совсем уж дедлоке ломает, однако ж, как ни крути, скрипт (и весь квик) стоит на lua_lock.

Еще замечу, что в варианте с 5.3 схему синхронизации сильно поменяли, раньше была просто критическая секция одна на весь луа, теперь там позабавнее система, вот этот кусочек на дизасме
Код
mov qword ptr [...]
...
test rax, rax
je <skip next instruction>
call rax
немного показывает, как теперь сделано. Если установлена "функция синхронизации" (т.е. она не null), то она и вызывается, иначе синхронизация пропускается и все. Как-зачем-почему не знаю, догадками мусорить не буду.
 
Старатель, дополню. Квик хочет вызвать колбек OnAllTrade и заполняет для него табличку с очередной сделкой. Все данные из ТВС уже вытащены, почти вся табличка заполнена, в последнюю очередь квик создает вложенную табличку datetime, пытается воткнуть в нее строку "day" и в этот момент виснет на lua_pushstring, как написал выше. В общем, это все мало что дает, косяк где-то в синхронизации все равно.
 
Цитата
Anton написал:
Квик хочет вызвать колбек OnAllTrade и заполняет для него табличку с очередной сделкой. Все данные из ТВС уже вытащены, почти вся табличка заполнена, в последнюю очередь квик создает вложенную табличку datetime, пытается воткнуть в нее строку "day" и в этот момент виснет на lua_pushstring, как написал выше. В общем, это все мало что дает, косяк где-то в синхронизации все равно.
Anton, спасибо.
Скрытый текст

Скрытый текст
Первые два трейда - это видимо, два скрипта из трёх запущенных. Один из которых, как я написал, выше, раз в минуту шлёт отладочную информацию. Второй просто молча работает (вроде, работает).

А третий скрипт, я также предполагал, что застрял перед вызовом одного из колбеков, потому и не отображается в логе.
Что касается бесконечной рекурсии, то их нет в скрипте. И я почти на 100% уверен, что QUIK "завис" не из-за логической ошибки в скрипте.
За эту версию говорит также тот довод, что неоднократно похожая проблема наблюдалась на скриптах из этой темы.
 
Цитата
Старатель написал:
За эту версию говорит также тот довод, что неоднократно похожая проблема наблюдалась на скриптах из этой  темы .
Там совсем другая картина, квик пытается получить колбек через lua_getglobal и падает из-за access violation. В этой теме проблема аналогичная, только на вызове не OnAllTrade, а OnParam, в остальном один в один. Думал, как оно может случиться, придумал только одно: если колбек вызывает какие-то функции квика, в процессе обработки такой функции квик может получить новое сообщение и вызвать колбек (возможно, другой) рекурсивно. Удерживаемый лок здесь не поможет, поток-то тот же самый. В некоторый момент лимит си-вызовов будет превышен, луа бросит ошибку, квик прибьет скрипт (и его стейты, естественно), рекурсия начнет раскручиваться в обратную сторону, вытесненные колбеки попытаются продолжить работу, а стейтов уже нет, вот и access violation.

Здесь же видим типичный дедлок. Возможно, базовая природа в обоих случаях та же, просто проявления разные, смотря на каком этапе находится колбек в момент ошибки. Впрочем, это надо поверять тестами все, пока только гипотезы.
 
Цитата
Anton написал:
Где-то этот лок захвачен и не выпущен, возможно, в одном из (других) скриптов, возможно, косячок в самом квике.
7500 и 5364 - потоки двух скриптов. Они большую часть времени стоят на SleepEx. Один из них, известно точно, периодически просыпается и шлёт отладочную информацию. Так что, лок захвачен явно не ими.
А вот, где поток третьего скрипта, не пойму, может прибит уже?
Скрытый текст


Цитата
Anton написал:
Там совсем другая картина, квик пытается получить колбек через lua_getglobal и падает из-за access violation.
Помимо описанных ошибок и падений в теме, несколько раз были и зависания.
Но зависает не скоро, и написать демонстрационный скрипт, гарантированно приводящий к зависанию в течение разумного времени после запуска не представляется возможным.
 
Цитата
Старатель написал:
А вот, где поток третьего скрипта, не пойму, может прибит уже?
А и правда, где мейн-то от зависшего скрипта. Если прибит, то теория с прибитием скрипта "под ковром" получает плюсик в свою пользу.

Что касается воркеров на скринах, они похожи на воркеры от gdi+ и от COM, первые ждут заданий на отрисовку, вторые маршалят вызовы через свою очередь сообщений, там своя жизнь и, думаю, арка там ничего не меняла, так что пока можно туда не смотреть.

Цитата
Старатель написал:
Но зависает не скоро, и написать демонстрационный скрипт, гарантированно приводящий к зависанию в течение разумного времени после запуска не представляется возможным.
Так вот и да. Как раз бесконечную рекурсию я и пробовал как способ захватить лок луа и не выпускать его, также пробовал насильно превысить лимит си-вызовов, но никаких падений-зависаний не словил, в этом случае квик все поймал и четко зачистил. Возможно, в более нагруженном окружении словил бы чего, дык это еще надо как-то устроить.
 
У меня тоже завис основной поток квика, последняя версия 8.8.4.3. Похоже проблема есть.
 
Александр, Добрый день!

Просим выслать архив рабочего места и работающие на момент зависания скрипты с описанием проблемы на почту нашей поддержки (quiksupport@arqatech.com)
 
Roman Azarov, У меня там скрипты на lua api написаны. Давайте я понаблюдаю еще и возможно более точно вам скажу. Но я вам выслал на почту dumps после вылета квика. Возможно это другая проблема.
 
Идея с рекурсией не подтверждается пока, потестил с перезаказами ТВС следующим скриптом
Код
local run = true
local tabid = nil
local rowid = nil
local counter = 0
local recursion_level = 0
local max_recursion_level = 0

function OnInit()
   tabid = AllocTable()
   if 0 == AddColumn(tabid, 1, 'recursion level', true, QTABLE_STRING_TYPE, 16) then
      DestroyTable(tabid)
      tabid = nil
      error("AddColumn failed")
   end
   if 0 == AddColumn(tabid, 2, 'counter', true, QTABLE_STRING_TYPE, 16) then
      DestroyTable(tabid)
      tabid = nil
      error("AddColumn failed")
   end
   if 0 == CreateWindow(tabid) then
      DestroyTable(tabid)
      tabid = nil
      error("CreateWindow failed")
   end
   rowid = InsertRow(tabid, -1)
   if -1 == rowid then
      DestroyTable(tabid)
      tabid = nil
      rowid = nil
      error("InsertRow failed")
   end
end

function OnAllTrade(v)
   recursion_level = recursion_level + 1
   counter = counter + 1
   if recursion_level > max_recursion_level then
      max_recursion_level = recursion_level
   end
   if tabid then
      SetCell(tabid, rowid, 1, tostring(max_recursion_level))
      SetCell(tabid, rowid, 2, tostring(counter))
   end
   recursion_level = recursion_level - 1
end

function OnStop()
   if tabid then
      local t = tabid
      tabid = nil
      DestroyTable(t)
      rowid = nil
   end
   run = false
end

function main()
   while run do
      sleep(1000)
   end
end
Результат - уровень рекурсии стоит 1 как влитой. Кому не лень прошу повторить упражнения, может где двоечка хотя бы выскочит, тогда будем знать, что хотя бы в принципе такое возможно. Также можно попробовать понагружать OnAllTrade перед выводом в таблицу чем-нибудь. Вариант sleep(100) попробовал, загружает квик до почти зависания, но это и логично, надо бы чем-нибудь поинтересней.
 
А почему она там должна выскочить? Если колбек выполняется, то там 1, а если висит основной поток, то, по идее, колбек не выполнится.
Либо, можно предположить, что после "отвисания" он просто вызовет все колбеки в очереди. Последовательно.
 
Цитата
Nikolay написал:
А почему она там должна выскочить?
Выше предположил, что если из колбека вызвать (не любую, естественно) функцию квика, то эта функция может (неожиданно для всех) дернуть GetMessage, получить уведомление о новых данных и тут же на радостях дернуть соответствующий колбек, в результате получим вложенное выполнение колбека внутри другого колбека. Вот и хотел такое поведение спровоцировать. Очереди колбеков, насколько понимаю, специальной нет, как сериализатор используется общая очередь виндовых сообщений основного потока, wt_de шлет туда сообщения о приехавших данных, основной поток их выбирает и тут же дергает соответствующие колбеки, т.е. да, после "отвисания" очередь будет обрабатываться в порядке прихода сообщений, тут вопрос, не может ли она "отвиснуть" внутри колбека.
 
Цитата
Anton написал:
Результат - уровень рекурсии стоит 1 как влитой.
Погонял ваш тест. Единичка осталась.
 
Цитата
Anton написал:
Идея с рекурсией не подтверждается пока, потестил с перезаказами ТВС следующим скриптом
Код
   local  run  =   true 
 local  tabid  =   nil 
 local  rowid  =   nil 
 local  counter  =   0 
 local  recursion_level  =   0 
 local  max_recursion_level  =   0 

 function   OnInit ()
   tabid  =   AllocTable ()
    if   0   =  =   AddColumn (tabid,  1 ,  'recursion level' ,  true , QTABLE_STRING_TYPE,  16 )  then 
       DestroyTable (tabid)
      tabid  =   nil 
      error( "AddColumn failed" )
    end 
    if   0   =  =   AddColumn (tabid,  2 ,  'counter' ,  true , QTABLE_STRING_TYPE,  16 )  then 
       DestroyTable (tabid)
      tabid  =   nil 
      error( "AddColumn failed" )
    end 
    if   0   =  =   CreateWindow (tabid)  then 
       DestroyTable (tabid)
      tabid  =   nil 
      error( "CreateWindow failed" )
    end 
   rowid  =   InsertRow (tabid,  -  1 )
    if   -  1   =  =  rowid  then 
       DestroyTable (tabid)
      tabid  =   nil 
      rowid  =   nil 
      error( "InsertRow failed" )
    end 
 end 

 function   OnAllTrade (v)
   recursion_level  =  recursion_level  +   1 
   counter  =  counter  +   1 
    if  recursion_level  >  max_recursion_level  then 
      max_recursion_level  =  recursion_level
    end 
    if  tabid  then 
       SetCell (tabid, rowid,  1 , tostring(max_recursion_level))
       SetCell (tabid, rowid,  2 , tostring(counter))
    end 
   recursion_level  =  recursion_level  -   1 
 end 

 function   OnStop ()
    if  tabid  then 
       local  t  =  tabid
      tabid  =   nil 
       DestroyTable (t)
      rowid  =   nil 
    end 
   run  =   false 
 end 

 function   main ()
    while  run  do 
       sleep ( 1000 )
    end 
 end 
  
Результат - уровень рекурсии стоит 1 как влитой. Кому не лень прошу повторить упражнения, может где двоечка хотя бы выскочит, тогда будем знать, что хотя бы в принципе такое возможно. Также можно попробовать понагружать OnAllTrade перед выводом в таблицу чем-нибудь. Вариант sleep(100) попробовал, загружает квик до почти зависания, но это и логично, надо бы чем-нибудь поинтересней.
зачем Вы данные всех сделок собираете в таблицу QLUA - это же тормозуха.
Собирайте в таблицу луа, либо в массив на C и будет вам счастье.
 
Цитата
nikolz написал:
зачем Вы данные всех сделок собираете в таблицу QLUA
Ничего никуда не собираю, сижу примус починяю, а тут и добрый совет подоспел. Попал тксть под раздачу.
 
Цитата
Anton написал:
Цитата
nikolz написал:
зачем Вы данные всех сделок собираете в таблицу QLUA
Ничего никуда не собираю, сижу примус починяю, а тут и добрый совет подоспел. Попал тксть под раздачу.
тогда поясните, что вы делаете в функции колбека:
-------------------------------
function   OnAllTrade (v)
  recursion_level  =  recursion_level  +   1
  counter  =  counter  +   1
   if  recursion_level  >  max_recursion_level  then
     max_recursion_level  =  recursion_level
   end
   if  tabid  then
     SetCell (tabid, rowid,  1 , tostring(max_recursion_level))
     SetCell (tabid, rowid,  2 , tostring(counter))
   end
  recursion_level  =  recursion_level  -   1
end
-------------------------
 
Цитата
nikolz написал:
тогда поясните
Как можете видеть, при входе в OnAllTrade инкрементируется recursion_level, а при выходе - декрементируется. Таким образом, он содержит глубину рекурсии по OnAllTrade в данный момент. В свою очередь, max_recursion_level содержит максимально достигнутую глубину рекурсии за время работы скрипта. Первая выделенная строчка выводит max_recursion_level в таблицу квика. Вторая выводит простой счетчик вызовов OnAllTrade, дабы было видно, что скрипт не завис. Цель всех упражнений - втыкать перед выводом в таблицу различные функции взаимодействия с квиком (чем тормознутее, тем лучше) и смотреть, не получится ли в каком-то из вариантов уровень рекурсии больше единицы.
 
Цитата
Anton написал:
смотреть, не получится ли в каком-то из вариантов уровень рекурсии больше единицы.
В QUIK существующая cхема обработки колбеков следующая:
  1.  Существует единственный служебный поток, из которого запускаются потоки скриптов пользователя (main).
  2.  При запуске пользовательских скриптов выполняется регистрация объявленных в них колбеков.
  3.  Колбеки всех запущенных пользователем скриптов обрабатываются в служебном потоке последовательно, так как поток один. Если в нескольких пользовательских скриптах объявлен некоторый колбек (с одинаковым названием), то при возникновении соответствующего ему события, для каждого скрипта в его state запускается свой (объявленный) колбек и выполняется это в служебном потоке последовательно.  Таким образом никакие существующие пользовательские колбеки не могут обрабатываться параллельно.
   С учетом выше изложенного  всегда max_recursion_level = 1 и это подтверждает предложенный вами тестовый скрипт.
Страницы: 1
Читают тему (гостей: 1)
Наверх