Дабы не загаживать прочие темы своими наблюдениями, создам себе отдельную. Цели получить ответ от сотрудников арки не ставлю, но если лишний раз глянут в свой код и убедятся, что заданный вопрос всего лишь ошибка наблюдений, или, паче чаяния, поправят что-нибудь, никому хуже не станет. Если в какой-то момент арка решит, что вопросы здесь излишне интимные, фил фри, как говорится, прибить всю ветку без размышлений, никто не обидится.
Для начала наблюдение номер один. Где-то в глубине qlua.dll готовится вызов OnAllTrade. Как положено, вызывается lua_checkstack. Но погодите, вызывается lua_checkstack(state, 1) и тут же на стек кладутся ДВА элемента, сама функция и ее аргумент. Кроме того, в процессе заполнения таблицы еще и временные элементы кладутся. Все ли верно, нигде ли стек не испортится?
Аналогичное наблюдение можно сделать при вызове (видимо) любого колбека с аргументом. OnConnected, OnDepoLimit, OnDepoLimitDelete, OnFirm, ...
В паре веток понаписал свое фи по поводу обработки исключений в qlua.dll, как она мне представилась через окошко отладчика. Напомню вкратце, вокруг lua_pcallk, через которую qlua вызывает пользовательские колбеки, построен try-catch блок и перед вызовом lua_pcallk устанавливается SEH-транслятор (превращающий SEH-исключения в плюсовые). Проблема в том, что исключение (плюсовое или транслированное), пролетая мимо lua_pcallk, пропускает всю его обработку после ошибки и, таким образом, стейт после этого уже нежизнеспособен. В арке последнее понимают и в случае ошибки, пролетевшей мимо lua_pcallk, тут же прибивают скрипт.
Сказавши А, хочу добавить и Б, а именно, как можно все это покрасивее организовать. Очевидно, ловить и транслировать исключения надо не в qlua.dll (там уже поздно), а в lua53.dll. Это не значит, что надо переделывать луа, его надо просто кастомизировать имеющимися в нем средствами, а именно через luaconf.h. Хочу показать, как я это сделал у себя и предложить арке посмотреть, не лучше ли будет так.
Скрытый текст
Идея простая, мы кастомизируем LUAI_TRY, что предусмотрено разработчиками луа, добавляя туда SEH-транслятор и ловушку исключений известного нам типа, а луа-ошибки (тоже исключения типа, известного только луа) оставляем как есть. Пойманные "свои" ошибки мы превращаем в луа-ошибки, так что lua_pcallk их поймает как родных и вся обработка на стороне qlua.dll сведется к получению обычной луа-ошибки с луа-стека и выводу сообщения (ну а в мейне еще и прибитию скрипта, раз уж так принято). Реализация достаточно простая и не требует вмешательства в код луа, кроме предназначенного как раз для этого luaconf.h.
В конце luaconf.h имеется секция для наших хотелок, вот так она выглядит у меня (не относящееся к делу убрал):
Код
/* =================================================================== */
/*
** Local configuration. You can use this space to add your redefinitions
** without modifying the main part of the file.
*/
/* =================================================================== */
/* SEH and C++ exceptions should be converted to lua errors
/* =================================================================== */
struct luasehtrans
{
luasehtrans() throw();
~luasehtrans();
static void onex(void * s) throw();
private:
// disallow copy/move
luasehtrans(const luasehtrans &);
luasehtrans(luasehtrans &&);
luasehtrans & operator=(const luasehtrans &);
luasehtrans & operator=(luasehtrans &&);
void * _bkp;
};
#define LUAI_THROW(L,c) throw (c)
#define LUAI_TRY(L,c,a) \
try { ::luasehtrans st; a } \
catch(...) { if ((c)->status == 0) { ::luasehtrans::onex(L); (c)->status = -1; } }
#define luai_jmpbuf int
Также добавлен файл с реализацией класса luasehtrans:
Код
#include "luaconf.h"
#include "lua.h"
#include "lobject.h"
#include <string>
#include <stdexcept>
#include <eh.h>
#include <windows.h>
struct sehex
{
sehex(struct _EXCEPTION_POINTERS * pep) throw();
const std::string & str(void) const throw();
private:
std::string _str;
};
static const char * ecstr(DWORD ec) throw()
{
switch(ec)
{
case EXCEPTION_ACCESS_VIOLATION: return "ACCESS VIOLATION";
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return "ARRAY BOUNDS EXCEEDED";
case EXCEPTION_BREAKPOINT: return "DEBUG BREAKPOINT";
case EXCEPTION_DATATYPE_MISALIGNMENT: return "MISALIGNMENT";
case EXCEPTION_FLT_DENORMAL_OPERAND: return "FPU DENORMAL OPERAND";
case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "FPU DIVISION BY ZERO";
case EXCEPTION_FLT_INEXACT_RESULT: return "FPU INEXACT RESULT";
case EXCEPTION_FLT_INVALID_OPERATION: return "FPU GENERIC EXCEPTION";
case EXCEPTION_FLT_OVERFLOW: return "FPU OVERFLOW";
case EXCEPTION_FLT_STACK_CHECK: return "FPU STACK OVERFLOW";
case EXCEPTION_FLT_UNDERFLOW: return "FPU UNDERFLOW";
case EXCEPTION_ILLEGAL_INSTRUCTION: return "ILLEGAL INSTRUCTION";
case EXCEPTION_IN_PAGE_ERROR: return "PAGE ERROR";
case EXCEPTION_INT_DIVIDE_BY_ZERO: return "DIVISION BY ZERO";
case EXCEPTION_INT_OVERFLOW: return "INTEGRAL OVERFLOW";
case EXCEPTION_INVALID_DISPOSITION: return "INVALID DISPOSITION";
case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "NONCONTINUABLE EXCEPTION";
case EXCEPTION_PRIV_INSTRUCTION: return "PRIVILEGED INSTRUCTION";
case EXCEPTION_SINGLE_STEP: return "DEBUG STEP";
case EXCEPTION_STACK_OVERFLOW: return "STACK OVERFLOW";
default: return "UNKNOWN EXCEPTION";
}
}
sehex::sehex(struct _EXCEPTION_POINTERS * pep) throw()
{
static const char fmt[] = "%s at address %p";
try
{
::PEXCEPTION_RECORD per = pep->ExceptionRecord;
if(per)
{
const char * pen = ecstr(per->ExceptionCode);
int len = _scprintf(fmt, pen, per->ExceptionAddress);
if(len++ > 0)
{
char * pbuf = static_cast<char *>(_alloca(len));
if(pbuf)
{
sprintf_s(pbuf, len, fmt, pen, per->ExceptionAddress);
_str.assign(pbuf, len - 1);
}
}
}
}
catch(...)
{
// we shouldn't show to the user a garbage string
_str.clear();
}
}
const std::string & sehex::str(void) const throw()
{
return _str;
}
static void translator(unsigned int, struct _EXCEPTION_POINTERS * pep)
{
throw sehex(pep);
}
luasehtrans::luasehtrans() throw()
: _bkp(_set_se_translator(::translator))
{
}
luasehtrans::~luasehtrans()
{
_set_se_translator(static_cast<_se_translator_function>(_bkp));
}
void luasehtrans::onex(void * vs) throw()
{
try
{
lua_State * s = static_cast<lua_State *>(vs);
std::exception_ptr pe(std::current_exception());
if(!(nullptr == pe))
{
try
{
std::rethrow_exception(pe);
}
catch(const sehex & e)
{
luaO_pushfstring(s, "%s", e.str().c_str());
}
catch(const std::exception & e)
{
luaO_pushfstring(s, "%s", e.what());
}
catch(...)
{
luaO_pushfstring(s, "unrecognized exception");
}
}
}
catch(...)
{
}
}
Тксть вот и все, а ты боялась. Теперь lua_pcallk ловит и плюсовые, и SEH, и свои луа ошибки и выдает их на-гора единым принятым в луа способом, со строкой-описанием на вершине луа-стека.
_sk_ написал: Интересно, разработчики прислушаются или нет?
У них могут быть какие-то свои дополнительные соображения, о которых мы не знаем. Посмотрим. Если вышеупомянутый lua_checkstack(1) это явно недосмотр (вообще-то, уверен, они наступили на граблю, подложенную разработчиками луа в виде неработающего EXTRA_STACK), то здесь могут быть нюансы.
#1 - по данному вопросу - Ваше сообщение получено, проблема изучается. Постараемся в ближайшее время дать ответ. #2 - Зарегистрировали пожелание на поддержку последнего обновления стандарта LUA5.3 - LUA5.3.6. Мы постараемся рассмотреть его и сообщить Вам результаты анализа. Впоследствии, по результатам анализа, будет приниматься решение о реализации пожелания в будущих версиях ПО. #3 - по данному вопросу - Ваше сообщение получено, проблема изучается. Постараемся в ближайшее время дать ответ.
Anton написал: У них могут быть какие-то свои дополнительные соображения
Может быть следующее возражение. Многие пишущие скрипты не являются профессиональными программистами и если в их скриптах будут перехвачены, но не обработаны фатальные исключения (сигнализирующие о разрушении контекста исполнения процесса QUIK), то последующее поведение QUIK будет неопределенным и это может (потенциально) привести, например, к выдачи искаженной заявки, порождающей клиенту убытки. Однако, если реализовать в QUIK дополнительно специальный режим запуска скриптов, в котором используется, предложенная вами схема обработки исключений, то это было бы хорошее решение, обеспечивающее больший контроль в разрабатываемых скриптах для тех, кто понимает как обрабатывать фатальные ошибки (например, перезапуская QUIK). При этом в сервисные функции стоило бы добавить функцию перезапуска QUIK.
TGB написал: сигнализирующие о разрушении контекста исполнения
Да, строго говоря, в луа как таковом (непойманные) исключения возникать не должны, если они возникают, то либо а) в самом квике (вне луа), либо б) в загруженной пользователем длл. По-хорошему их вообще ловить не нужно, нужно падать, с дампом по возможности. Но луа из коробки ловит плюсовые исключения все же. Можно и в эту сторону поправить, пробрасывать не-луа исключения дальше и убрать ловушку из qlua.dll, пусть рушится, глядишь и дампы какие-то более содержательные будут получаться.
Anton написал: пусть рушится, глядишь и дампы какие-то более содержательные будут получаться.
И все-таки, ваше предложение дать возможность перехватывать все исключения в скрипте, мне представляется хорошим решением. Оно может быть реализовано, например в следующем виде. В интерфейсные функции QUIK добавляется функция ppcall - полный аналог pcall, но перехватывающий абсолютно все исключения. Туда же добавляется функция перезапуска QUIK с параметром, определяющим подробность распечатки содержательного дампа перед запуском, а так же необходимость самого перезапуска. Перехват всех исключений обеспечит: 1) удобную локализацию места, возможно порождающего исключение; 2) возможность анализа состояния скрипта (вывод своего локального дампа) сразу после возникновения исключения. Такая реализация вашего предложения не затрагивает уже существующие скрипты, но предоставляет дополнительные возможности тем, кто захочет воспользоваться этими функциями. Эти функции, например, могут быть использованы в скриптах для реализации контрольных точек, обеспечивающих продолжение работы, с прерванного перезапуском места.
Сам Lua ИДЕОЛОГИЧЕСКИ предназначен именно НЕ для программистов, а Вы собираетесь "чайников" ещё и исключениями кормить! Именно они и есть место потенциальных и труднонаходимых ошибок - таких, что не всегда бывают по зубам даже высококвалифицированным программистам. Тем более, что ЛЮБЫЕ действия (как скрипта, так и юзера) могут (потенциально) принести клиенту убытки - на тго и биржа! Лично я согласен с Антоном: "По-хорошему их вообще ловить не нужно, нужно падать, с дампом по возможности". Ещё 20 лет назад я писал:
Теперь у меня ни малейших сомнений: exceptions надо давить в зародыше! Мужики! Мне просто страшно читать о проблемах, какие при этом возникают! Да на кой нужно такое счастье? Стек - это просто LIFO. Заказал ты под него динамическую память или она из тела программы - вторично. Хип - та же память, только в профиль. Надо 5-10 стеков - пожалуйста! Надо отмотать - пожалуйста. Надо диагностику по аварийному выходу из хрен знает откуда - пожалуйста. Работает, как часы. Просто до неприличия - с вашей квалификацией практически любой за пару часов напишет. Ну, "восстановит" она какие-то объекты в стеке, из которого мы уже умотали - был один мусор, будет другой. Но наши-то ТОЖЕ ВОССТАНОВИТ! Ужас! Завязывайте вы с этими исключениями! У меня стек объектов никакого отношения к SP не имеет. А некоторые классы (скажем, окна) могут иметь персональные стеки, которые никакого отношения к SP тоже не имеют. Поэтому при ошибках проблем и не возникает: мы просто ныряем ещё глубже в стек (программный), восстанавливаем объекты из стека (стеков) объектов (возможно, в область программного стека, который над нами, если они были локальными объектами или параметрами для какой-то из верхних функций), выдаем предсмертный дамп и уматываем на exit. А вот если я должен отматывать для получения информации тот стек, который по SP, да ещё при ошибке - это в морг! Я вообще не понимаю, как в этом случае получать доступ к данным.
Владимир написал: Сам Lua ИДЕОЛОГИЧЕСКИ предназначен именно НЕ для программистов
Вы же пищите на нем? И, наверное, часть пишущих скрипты на QLua являются профессиональными программистами.
2.
Цитата
TGB написал: Такая реализация вашего предложения не затрагивает уже существующие скрипты
И пусть те, кто не собирается заниматься перезапусками QUIK продолжают жить так, как жили до сих пор. Те кто не в теме, скорее всего, не используют даже pcall.
3.
Цитата
Владимир написал: Именно они и есть место потенциальных и труднонаходимых ошибок
С этим я согласен.
4.
Цитата
Владимир написал: вашей квалификацией практически любой за пару часов напишет
Как решать свои проблемы, я во многих случаях знаю, но в данном случае обсуждается не лично моя проблема.
5.
Цитата
Владимир написал: Я вообще не понимаю, как в этом случае получать доступ к данным.
TGB, Да, я пишу на нём, и другие программисты пишут - а что делать? В идеале, я бы хотел иметь в виде интерпретатора аналог моего любимого С (или хотя бы JS), но "имеем то, что имеем".
Как решать СВОИ проблемы, Вы знаете, но исключения не знают о Ваших проблемах, а Вы не знаете об их проблемах. Лично я свои проблемы намерен решать сам, не перекладывая их на исключения, а проблемы исключений решать не собираюсь вообще - буду "падать, с дампом по возможности".
новичок,Да-да, С++ для меня как красная тряпка. Я когда-то пару лет вёл ветку "C vs C++" на одном программистском форуме, где нагло утверждал, что всё, что в принципе можно реализовать на С++, я напишу на чистом С, а вот повторить на "плюсах" то, что я напишу на С, не сможет никто".
Владимир написал: exceptions надо давить в зародыше
и желательно вместе с с++ :)
С плюсами как раз проблем нет, луа его исключения ловит, стек разматывает и показывает ошибку типа "Unknown error. Possible unhandled exception". Кстати говоря, ветка для плюсовых исключений в qlua.dll не сработает никогда именно поэтому. А вот SEH исключения луа не ловит и, что важно, при этом по дефолту и стек не разматывается. А арке надо, чтобы разматывался, у них там деструкторы ждут с локами например. Чтобы разматывался, надо компилировать с /EHa и ставить транслятор, как арка и сделала. Я всего лишь предлагаю перетащить эту размотку в lua53.dll, на место преступления тксть, тогда и стейт будет оставаться живым, и qlua.dll можно без /EHa собирать, да и обработка ошибок упростится очень существенно. Либо дать уже (любому) исключению долететь до верха и пусть падает. А так как есть получается вроде изобразили размотку, на самом деле ничего толком не размотали и оставили выполняться в этом непонятном виде.
Anton, Да "к чёрту подробности"! ЗАЧЕМ "луа его исключения ловит, стек разматывает и показывает ошибку"? Кому они нужны, эти исключения? Зачем вообще нужно знать о существовании qlua.dll? Квик как-то получает данные, которые лично я намерен считать априори достоверными и не контролировать это дело вообще никак. Луа получает эти данные от Квика. Разные глюки (типа обрывов связи) элементарно ловятся по "остановке" поступающих значений из таблицы текущих торгов - ну что ещё надо? Обращаться за данными чаще, чем раз в 5-10 секунд я вообще не вижу смысла (у меня сейчас стоит 15 секунд), то есть скрипт 99% времени просто спит! И в гробу видел все исключения, вместе взятые!
Владимир написал: я бы хотел иметь в виде интерпретатора аналог моего любимого С (или хотя бы JS)
Я, наверное, Вас обрадую. В QLua (Lua) существует C-API и у Вас есть возможность, практически полностью вести свои разработки на C. Хорошее описание C-API представлено в книге одного из отцов-основоположников языка Lua http://texno.info/wp-content/uploads/2019/09/lua.pdf (можно посмотреть также https://lua.org.ru/contents_ru.html#4.7). Вы можете создать dll-пакет на C и подключить в своем скрипте. Далее вызываете в скрипте свою функцию пакета, в которой делаете все что Вам нужно (например, запускаете потоки для обработки данных и так далее). Из такого пакета доступен весь интерфейс с QUIK. Есть хороший ресурс https://quikluacsharp.ru/, на котором представлены некоторые шаблоны решений для QLua, в том числе, шаблон создания dll-пакета для QLua.
Владимир написал: я бы хотел иметь в виде интерпретатора аналог моего любимого С (или хотя бы JS)
Я, наверное, Вас обрадую. В QLua (Lua) существует C-API и у Вас есть возможность, практически полностью вести свои разработки на C. Хорошее описание C-API представлено в книге одного из отцов-основоположников языка Lua http://texno.info/wp-content/uploads/2019/09/lua.pdf (можно посмотреть также https://lua.org.ru/contents_ru.html#4.7 ). Вы можете создать dll-пакет на C и подключить в своем скрипте. Далее вызываете в скрипте свою функцию пакета, в которой делаете все что Вам нужно (например, запускаете потоки для обработки данных и так далее). Из такого пакета доступен весь интерфейс с QUIK. Есть хороший ресурс https://quikluacsharp.ru/ , на котором представлены некоторые шаблоны решений для QLua, в том числе, шаблон создания dll-пакета для QLua.
Ну наконец-то, настало время читать учебники. ------------------------------- Замечу, что QLUA - это библиотека (dll) и написана она на С-API. Поэтому можете писать свои библиотеки аналогично. ---------------- Что же касается перехвата всех исключений - то это скорее мечта дилетанта, чем реальность профи. Как нельзя найти все ошибки, так и невозможно перехватить все исключения. Это аксиома.
TGB, Нет, не обрадуете. Профессионалу практически всё равно, на чём писать - была бы обеспечена требуема функциональность. А требования к функциональности софта для торгов на бирже практически нулевые, и Lua вполне их обеспечивает, хоть и коряво. МНЕ - достаточно!
Anton написал: Потому что перехватить их все вообще не вопрос
В дополнение к комментарию, написанного Anton. Обработать фатальные исключения так, чтобы можно было продолжить работу скрипта в текущем процессе, в общем то, практически невозможно (на то они и фатальные). Однако существует хорошо известная схема обработки таких исключений: 1) выдача кода EXCEPTION_CONTINUE_SEARCH (для SEH - исключений) о последующем продолжении обработки перехваченного исключения, а также сообщения о возникновении такого исключения с выдачей локального дампа скрипта (распечатка состояния информационных структур скрипта после перехвата этого исключения для анализа возможных причин возникшего исключения): 2) перезапуск процесса, в котором возникло обсуждаемое фатальное исключение. Описанная выше схема, обычно, реализуется с использованием контрольных точек (состояний вычисления: значений внутренних переменных скрипта), зафиксированных, обычно, на энергонезависимых накопителях. Контрольные точки должны обеспечивать (после их считывания) продолжение вычислений в скрипте с последнего зафиксированного, перед перезапуском его состояния.
Если всегда его выдавать, есть решение проще - просто не ставить ловушку, результат абсолютно тот же. Большинство SEH исключений фатальными не являются, многие даже позволяют EXCEPTION_CONTINUE_EXECUTION (но с использованием транслятора так не сделать). Реально смертельных там штуки три всего, вот их точно надо выше пробрасывать без обработки.
Anton написал: В паре веток понаписал свое фи по поводу обработки исключений в qlua.dll, как она мне представилась через окошко отладчика. Напомню вкратце, вокруг lua_pcallk, через которую qlua вызывает пользовательские колбеки, построен try-catch блок и перед вызовом lua_pcallk устанавливается SEH-транслятор (превращающий SEH-исключения в плюсовые). Проблема в том, что исключение (плюсовое или транслированное), пролетая мимо lua_pcallk, пропускает всю его обработку после ошибки и, таким образом, стейт после этого уже нежизнеспособен. В арке последнее понимают и в случае ошибки, пролетевшей мимо lua_pcallk, тут же прибивают скрипт.
Сказавши А, хочу добавить и Б, а именно, как можно все это покрасивее организовать. Очевидно, ловить и транслировать исключения надо не в qlua.dll (там уже поздно), а в lua53.dll. Это не значит, что надо переделывать луа, его надо просто кастомизировать имеющимися в нем средствами, а именно через luaconf.h. Хочу показать, как я это сделал у себя и предложить арке посмотреть, не лучше ли будет так.
Скрытый текст Идея простая, мы кастомизируем LUAI_TRY, что предусмотрено разработчиками луа, добавляя туда SEH-транслятор и ловушку исключений известного нам типа, а луа-ошибки (тоже исключения типа, известного только луа) оставляем как есть. Пойманные "свои" ошибки мы превращаем в луа-ошибки, так что lua_pcallk их поймает как родных и вся обработка на стороне qlua.dll сведется к получению обычной луа-ошибки с луа-стека и выводу сообщения (ну а в мейне еще и прибитию скрипта, раз уж так принято). Реализация достаточно простая и не требует вмешательства в код луа, кроме предназначенного как раз для этого luaconf.h.
В конце luaconf.h имеется секция для наших хотелок, вот так она выглядит у меня (не относящееся к делу убрал):
Также добавлен файл с реализацией класса luasehtrans:
Код
# include "luaconf.h"
# include "lua.h"
# include "lobject.h"
# include < string >
# include < stdexcept >
# include < eh.h >
# include < windows.h >
struct sehex
{
sehex(struct _EXCEPTION_POINTERS * pep) throw();
const std::string & str(void) const throw();
private:
std::string _str;
};
static const char * ecstr(DWORD ec) throw()
{
switch(ec)
{
case EXCEPTION_ACCESS_VIOLATION: return "ACCESS VIOLATION" ;
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return "ARRAY BOUNDS EXCEEDED" ;
case EXCEPTION_BREAKPOINT: return "DEBUG BREAKPOINT" ;
case EXCEPTION_DATATYPE_MISALIGNMENT: return "MISALIGNMENT" ;
case EXCEPTION_FLT_DENORMAL_OPERAND: return "FPU DENORMAL OPERAND" ;
case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "FPU DIVISION BY ZERO" ;
case EXCEPTION_FLT_INEXACT_RESULT: return "FPU INEXACT RESULT" ;
case EXCEPTION_FLT_INVALID_OPERATION: return "FPU GENERIC EXCEPTION" ;
case EXCEPTION_FLT_OVERFLOW: return "FPU OVERFLOW" ;
case EXCEPTION_FLT_STACK_CHECK: return "FPU STACK OVERFLOW" ;
case EXCEPTION_FLT_UNDERFLOW: return "FPU UNDERFLOW" ;
case EXCEPTION_ILLEGAL_INSTRUCTION: return "ILLEGAL INSTRUCTION" ;
case EXCEPTION_IN_PAGE_ERROR: return "PAGE ERROR" ;
case EXCEPTION_INT_DIVIDE_BY_ZERO: return "DIVISION BY ZERO" ;
case EXCEPTION_INT_OVERFLOW: return "INTEGRAL OVERFLOW" ;
case EXCEPTION_INVALID_DISPOSITION: return "INVALID DISPOSITION" ;
case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "NONCONTINUABLE EXCEPTION" ;
case EXCEPTION_PRIV_INSTRUCTION: return "PRIVILEGED INSTRUCTION" ;
case EXCEPTION_SINGLE_STEP: return "DEBUG STEP" ;
case EXCEPTION_STACK_OVERFLOW: return "STACK OVERFLOW" ;
default: return "UNKNOWN EXCEPTION" ;
}
}
sehex::sehex(struct _EXCEPTION_POINTERS * pep) throw()
{
static const char fmt[] = "%s at address %p" ;
try
{
::PEXCEPTION_RECORD per = pep - > ExceptionRecord;
if (per)
{
const char * pen = ecstr(per - > ExceptionCode);
int len = _scprintf(fmt, pen, per - > ExceptionAddress);
if (len + + > 0 )
{
char * pbuf = static_cast < char * > (_alloca(len));
if (pbuf)
{
sprintf_s(pbuf, len, fmt, pen, per - > ExceptionAddress);
_str.assign (pbuf, len - 1 );
}
}
}
}
catch( .. .)
{
// we shouldn't show to the user a garbage string
_str.clear ();
}
}
const std::string & sehex::str(void) const throw()
{
return _str;
}
static void translator(unsigned int, struct _EXCEPTION_POINTERS * pep)
{
throw sehex(pep);
}
luasehtrans::luasehtrans() throw()
: _bkp(_set_se_translator(::translator))
{
}
luasehtrans::~luasehtrans()
{
_set_se_translator(static_cast < _se_translator_function > (_bkp));
}
void luasehtrans::onex(void * vs) throw()
{
try
{
lua_State * s = static_cast < lua_State * > (vs);
std::exception_ptr pe(std::current_exception());
if ( ! (nullptr = = pe))
{
try
{
std::rethrow_exception(pe);
}
catch(const sehex & e)
{
luaO_pushfstring(s, "%s" , e.str ().c_str());
}
catch(const std::exception & e)
{
luaO_pushfstring(s, "%s" , e.what ());
}
catch( .. .)
{
luaO_pushfstring(s, "unrecognized exception" );
}
}
}
catch( .. .)
{
}
}
Тксть вот и все, а ты боялась. Теперь lua_pcallk ловит и плюсовые, и SEH, и свои луа ошибки и выдает их на-гора единым принятым в луа способом, со строкой-описанием на вершине луа-стека.
Добрый день.
Действительно, сейчас обработка SEH исключений устроена некорректно. Мы переделаем её в ближайшем обновлении ПО.
C такими способностями, как у вас, удивляюсь, почему вы до сих пор не раскрутили этот qlua.dll и не написали свой плагин qapi.dll c api для обработки событий и получения данных, без lua-посредника...
Latrop написал: почему вы до сих пор не раскрутили этот qlua.dll и не написали свой плагин qapi.dll
Потому что это нарушение eula? Тем более что и смысла нет. Все почему-то думают, что "у квике усе есть, это злая qlua.dll целенаправленно гадит". Ничего там радикально другого нет, без луа будут ровно те же колбеки дергаться ровно в том же порядке.
Latrop написал: почему вы до сих пор не раскрутили этот qlua.dll и не написали свой плагин qapi.dll
Потому что это нарушение eula? Тем более что и смысла нет. Все почему-то думают, что "у квике усе есть, это злая qlua.dll целенаправленно гадит". Ничего там радикально другого нет, без луа будут ровно те же колбеки дергаться ровно в том же порядке.
eula - это ж для цивилизованных рынков, квику эта участь похоже не грозит. В России это называется - адаптация. Что явно разрешено даже по закону, если это не продавать. Понятно, что функционал там тот же, только без проблем с потоками и преобразованиями данных. Ягоды, как говорится, всегда ж проще и вкуснее складывать сразу в рот, а не выкладывать их сначала по одной в ряд на жердочку, которая того и гляди опрокинется :)
Там никаких потоков и нет, поток для мейна любезно qlua.dll нам создает. То есть будет один поток квика, что достигается и в луа, если в мейне ничего не делать, окромя периодической проверки флага выхода. Немножко синхронизации и заполнения (да, иногда ненужных) луа-табличек погоды не делают, там счет на сотни наносекунд на круг. По одной ягодки сам квик вытаскивает из очереди своей и тут же незамедлительно колбек и дергает, если он определен, и, как тысячу раз нам говорили, никогда, НИКОГДА воробьянинов не протягивал руки он не будет ничего откладывать и пересортировывать. Что действительно очень правильно и хорошо.
Latrop написал: квику эта участь похоже не грозит. В России это называется - адаптация. Что явно разрешено даже по закону, если
Всегда обожал слушать адептов "цивилизованного рынка", которые уже в во втором предложении пишут "но воровать можно, если не поймают".
Не ну зачем так? Обожать слушать это может и хорошо, но еще лучше и читать, читать законы. Кто говорит про воровать? Наше законодательство в этой части, кстати, довольно таки либеральное. Хоть в eula квика (и любой другой программы) может быть и прописан запрет на декомпиляцию, но, вы удивитесь, закон явно разрешает проводить декомпиляцию в целях изучения и реализации взаимодействия с другой программой пользователя. Поэтому на эту часть eula можно смело смотреть (и не только смотреть) в высокой колокольни. А то, что квик по своим убеждениям не делает доступной информацию по плагинам, так это также дает явное право на декомпиляцию в силу закона.
А воруете вы свое время, занимаясь каким-то, простите за сугубо личное мнение, колхозом с этими callback/main/sleep и т.п.
Anton написал: Там никаких потоков и нет, поток для мейна любезно qlua.dll нам создает. То есть будет один поток квика, что достигается и в луа, если в мейне ничего не делать, окромя периодической проверки флага выхода. Немножко синхронизации и заполнения (да, иногда ненужных) луа-табличек погоды не делают, там счет на сотни наносекунд на круг. По одной ягодки сам квик вытаскивает из очереди своей и тут же незамедлительно колбек и дергает, если он определен, и, как тысячу раз нам говорили, никогда, НИКОГДА воробьянинов не протягивал руки он не будет ничего откладывать и пересортировывать. Что действительно очень правильно и хорошо.
Это понятно. У меня итак есть QLuaNet.dll , которая просто ловит данные и раздает их в своих потоках, и квик не тормозит и мейн там не нужен, месяцами работает не выключаясь. Хоть итак все шустро и стабильно, но просто профилирование показало, что относительно много тратится квиком на перекладывание примитивов с стек луа. Моя система по TCP тики и прочие данные передает быстрее, чем квик с луа стеком взаимодействует. А когда несколько брокеров и на каждом брокере по несколько квиков, то возникает мысль может это дело еще можно ускорить.
Latrop написал: относительно много тратится квиком на перекладывание примитивов с стек луа.
Думаю, причина в синхронизации, каждый вызов lua_push*, lua_set* захватывает-выпускает лок, на большой таблице сотня туда-сюда вполне может произойти. Есть куда улучшить. Сколько-то будет выиграно, когда выпилят рудименты 5.1, сколько-то можно выиграть, захватывая явно лок перед заполнением таблицы (но выпуская перед вызовом колбека). Либо можно было бы создать один стейт без синхронизации на весь квик, заполнять табличку в нем и потом lua_xmove ее в стейт скрипта. В моменте, очевидно, более животрепещущие есть задачи, может когда-нибудь и до этого доберутся.
Anton, По моему разумению, неплохо бы сделать так:
1. Юзер формирует таблицу текущих торгов, включая туда те инструменты, по которым ему интересны обновления (это даже не дело скрипта - это настройка Квика). Если юзер намерен и сам торговать (как я, например), эта таблица должна быть видимой (у меня она "едина в трёх лицах": а) акции, торгующиеся на рубли, б) на доллары и в) на евро), в противном случае может лежать в свёрнутом виде на какой-нибудь ненужной вкладке - тогда она выполняет роль инструкции Квику обновлять данные по этому набору инструментов, чтобы скрипт всегда имел по ним актуальные данные.
2. Юзеру вообще не дозволяется иметь дело ни с чем, кроме терминала Квика - не его это собачье дело заниматься обменом с сервером. Тогда вся математика резко упрощается, и огромный пласт потенциальных ошибок просто исчезает как класс.
3. Квик сам обрабатывает все прерывания, а юзеровские "коллбеки" обслуживает в фоновом режиме как если бы они находились в потоке мейна. Таковых (у меня) всего лишь 4 штуки: OnStop, OnTimer, OnTrade, да OnOrder, и я (пока) не вижу смысла в остальных (ну, кроме обработки событий от юзера в моих таблицах).
Сложность - минимальная, скорость - максимальная, надёжность - максимальная. Ну сказка же, господа!
P.S. У меня два Квика для разных брокеров, у одного версия 8.7.1.3. у другого 8.10.1.1, на обоих мой скрипт прекрасно работает. А вот вчера зашёл к другу моему - не работает (у него версия 8.6 и ещё что-то там). Начал разбираться - оказывается, он ругается на goto и на метки (я всё-таки пристроил этот кастрированный goto для эмуляции украденного из языка continue). Ну это совсем уж свинство, господа! Язык, конечно, дерьмо, но соответствовать описанию он всё же должен! Версии плодятся, как тараканы - так почему бы не сделать одну, но нормальную?
swerg, Я не помню, какие там две последние цифры, но ругается и на goto и на метки - проверено вчера. Сам Квик был скачан с сайта ВТБ тоже буквально на днях - я скачивал намного раньше, но раза два или три потом обновлял.
Anton, Не знаю, но Ваше мнение для меня не менее интересно, чем мнение сотрудников арки.
Anton написал: В паре веток понаписал свое фи по поводу обработки исключений в qlua.dll, как она мне представилась через окошко отладчика. Напомню вкратце, вокруг lua_pcallk, через которую qlua вызывает пользовательские колбеки, построен try-catch блок и перед вызовом lua_pcallk устанавливается SEH-транслятор (превращающий SEH-исключения в плюсовые). Проблема в том, что исключение (плюсовое или транслированное), пролетая мимо lua_pcallk, пропускает всю его обработку после ошибки и, таким образом, стейт после этого уже нежизнеспособен. В арке последнее понимают и в случае ошибки, пролетевшей мимо lua_pcallk, тут же прибивают скрипт.
Сказавши А, хочу добавить и Б, а именно, как можно все это покрасивее организовать. Очевидно, ловить и транслировать исключения надо не в qlua.dll (там уже поздно), а в lua53.dll. Это не значит, что надо переделывать луа, его надо просто кастомизировать имеющимися в нем средствами, а именно через luaconf.h. Хочу показать, как я это сделал у себя и предложить арке посмотреть, не лучше ли будет так.
Скрытый текст Идея простая, мы кастомизируем LUAI_TRY, что предусмотрено разработчиками луа, добавляя туда SEH-транслятор и ловушку исключений известного нам типа, а луа-ошибки (тоже исключения типа, известного только луа) оставляем как есть. Пойманные "свои" ошибки мы превращаем в луа-ошибки, так что lua_pcallk их поймает как родных и вся обработка на стороне qlua.dll сведется к получению обычной луа-ошибки с луа-стека и выводу сообщения (ну а в мейне еще и прибитию скрипта, раз уж так принято). Реализация достаточно простая и не требует вмешательства в код луа, кроме предназначенного как раз для этого luaconf.h.
В конце luaconf.h имеется секция для наших хотелок, вот так она выглядит у меня (не относящееся к делу убрал):
Также добавлен файл с реализацией класса luasehtrans:
Код
# include "luaconf.h"
# include "lua.h"
# include "lobject.h"
# include < string >
# include < stdexcept >
# include < eh.h >
# include < windows.h >
struct sehex
{
sehex(struct _EXCEPTION_POINTERS * pep) throw();
const std::string & str(void) const throw();
private:
std::string _str;
};
static const char * ecstr(DWORD ec) throw()
{
switch(ec)
{
case EXCEPTION_ACCESS_VIOLATION: return "ACCESS VIOLATION" ;
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return "ARRAY BOUNDS EXCEEDED" ;
case EXCEPTION_BREAKPOINT: return "DEBUG BREAKPOINT" ;
case EXCEPTION_DATATYPE_MISALIGNMENT: return "MISALIGNMENT" ;
case EXCEPTION_FLT_DENORMAL_OPERAND: return "FPU DENORMAL OPERAND" ;
case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "FPU DIVISION BY ZERO" ;
case EXCEPTION_FLT_INEXACT_RESULT: return "FPU INEXACT RESULT" ;
case EXCEPTION_FLT_INVALID_OPERATION: return "FPU GENERIC EXCEPTION" ;
case EXCEPTION_FLT_OVERFLOW: return "FPU OVERFLOW" ;
case EXCEPTION_FLT_STACK_CHECK: return "FPU STACK OVERFLOW" ;
case EXCEPTION_FLT_UNDERFLOW: return "FPU UNDERFLOW" ;
case EXCEPTION_ILLEGAL_INSTRUCTION: return "ILLEGAL INSTRUCTION" ;
case EXCEPTION_IN_PAGE_ERROR: return "PAGE ERROR" ;
case EXCEPTION_INT_DIVIDE_BY_ZERO: return "DIVISION BY ZERO" ;
case EXCEPTION_INT_OVERFLOW: return "INTEGRAL OVERFLOW" ;
case EXCEPTION_INVALID_DISPOSITION: return "INVALID DISPOSITION" ;
case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "NONCONTINUABLE EXCEPTION" ;
case EXCEPTION_PRIV_INSTRUCTION: return "PRIVILEGED INSTRUCTION" ;
case EXCEPTION_SINGLE_STEP: return "DEBUG STEP" ;
case EXCEPTION_STACK_OVERFLOW: return "STACK OVERFLOW" ;
default: return "UNKNOWN EXCEPTION" ;
}
}
sehex::sehex(struct _EXCEPTION_POINTERS * pep) throw()
{
static const char fmt[] = "%s at address %p" ;
try
{
::PEXCEPTION_RECORD per = pep - > ExceptionRecord;
if (per)
{
const char * pen = ecstr(per - > ExceptionCode);
int len = _scprintf(fmt, pen, per - > ExceptionAddress);
if (len + + > 0 )
{
char * pbuf = static_cast < char * > (_alloca(len));
if (pbuf)
{
sprintf_s(pbuf, len, fmt, pen, per - > ExceptionAddress);
_str.assign (pbuf, len - 1 );
}
}
}
}
catch( .. .)
{
// we shouldn't show to the user a garbage string
_str.clear ();
}
}
const std::string & sehex::str(void) const throw()
{
return _str;
}
static void translator(unsigned int, struct _EXCEPTION_POINTERS * pep)
{
throw sehex(pep);
}
luasehtrans::luasehtrans() throw()
: _bkp(_set_se_translator(::translator))
{
}
luasehtrans::~luasehtrans()
{
_set_se_translator(static_cast < _se_translator_function > (_bkp));
}
void luasehtrans::onex(void * vs) throw()
{
try
{
lua_State * s = static_cast < lua_State * > (vs);
std::exception_ptr pe(std::current_exception());
if ( ! (nullptr = = pe))
{
try
{
std::rethrow_exception(pe);
}
catch(const sehex & e)
{
luaO_pushfstring(s, "%s" , e.str ().c_str());
}
catch(const std::exception & e)
{
luaO_pushfstring(s, "%s" , e.what ());
}
catch( .. .)
{
luaO_pushfstring(s, "unrecognized exception" );
}
}
}
catch( .. .)
{
}
}
Тксть вот и все, а ты боялась. Теперь lua_pcallk ловит и плюсовые, и SEH, и свои луа ошибки и выдает их на-гора единым принятым в луа способом, со строкой-описанием на вершине луа-стека.
Добрый день,
Описанная в данном инциденте ошибка была исправлена в версии 8.11.0 терминала QUIK. Рекомендуем вам обновить версию программы.
Уже обновил и даже попробовал. Код с транслятором в lua53.dll увидел (и в lua54.dll тоже), а вот работающим его не увидел. Предполагаю потому, что lua53.dll собрали без флага /EHa, других объяснений не могу придумать. Транслятор перед вызовом колбека ставится, но при исключении в колбеке оно улетает по-прежнему в qlua.dll и ловится уже там, то есть по-прежнему весь хвост в luaD_rawrunprotected и выше не выполняется. Чтобы это увидеть, достаточно поставить брейкпойнт в luaD_rawrunprotected сразу после блока LUAI_TRY и бросить из колбека access violation или что-то подобное. Если исключение поймано, квик встанет на брейкпойнте, но этого не происходит, ближайшее место, где удалось его остановить, это транслятор в qlua.dll, то есть где и раньше.
Уточните пожалуйста, в чем именно заключается проблема? При ее наличии, предоставьте, пожалуйста, скрипт, на котором она воспроизводится или же другие данные, наглядно ее демонстрирующие (прим. снимки экрана).
Roman Azarov написал: в чем именно заключается проблема?
У меня - ни в чем, я как бы предложил некое улучшение, его как бы реализовали, мои тесты показали, что оно никогда не сработает в текущем виде почему-то. Как брейкпойнт поставить я думаю в арке все знают, куда поставить написано выше. Тут вопрос надо ли оно кому-то. Ежли нет, так мне меньше всех.
Уже выкладывал скрипт + длл для выбрасывания исключений в колбеках, может облегчат работу. Удобнее всего в OnStop бросать, при запущенном скрипте ставим брейкпойнты перед и после LUAI_TRY, жмем остановить, убеждаемся, что на брейкпойнте ДО остановились, жмем продолжить и по идее должны остановиться на брейкпойнте ПОСЛЕ, а по факту улетаем в qlua.dll. У меня, к сожалению, сорцев и символов нет, наглядно на скринах показать не получится.
Roman Azarov написал: предоставьте, пожалуйста, скрипт, на котором она воспроизводится
Сейчас я могу предоставить кое-что получше. Вот здесь архив с тем же скриптом и - новое - (весьма приблизительной) копией вашей lua53.dll, собранной из обычного луа 5.3.5 и палок. Распаковать все в папку рабочего места с заменой оригинальной lua53.dll, запустить скрипт. Через секунду скрипт выбросит ACCESS VIOLATION - смотрите, как исключение будет обработано, - точно так же, как любая другая ошибка луа, без повреждения состояния квика, без деактивации кнопки "запустить" и прочих прелестей. Сравните с поведением оригинальной lua53.dll. Вот что ожидалось увидеть после добавления приведенного выше кода, но что-то пошло не так.
Roman Azarov написал: Anton , Благодарим за дополнительную информацию.
Мне, кажется, что Anton, действительно, сделал большую и качественную работу по улучшению обработки исключений в QLua. Наверное, можно бы было как то использовать и его исходники по обработке исключений, с каким-то реальным вознаграждением. Почему бы нет?