Oberon space

General Category => Общий раздел => Тема начата: ilovb от Декабрь 04, 2013, 09:17:18 am

Название: Горячая замена кода
Отправлено: ilovb от Декабрь 04, 2013, 09:17:18 am
В конфе в очередной раз зашел разговор за горячую замену кода в ББ. У народа оказывается с этим есть проблемы. И более того говорят что ее там нет!

Сначала было непонятно, что собственно хотят изобразить. Теперь же есть пример проблемного кода:

MODULE A;
IMPORT StdLog;
 
TYPE
T0* = EXTENSIBLE RECORD END;
PT* = POINTER TO T0;
 
VAR
r* : PT;

PROCEDURE (ob : PT) Some*(), NEW, EXTENSIBLE;
BEGIN
StdLog.Int(42);
END Some;

PROCEDURE Test*(); BEGIN
r.Some()
END Test;
 
BEGIN
END A.
 
MODULE B;
IMPORT A, StdLog;
 
TYPE
T1* = RECORD (A.T0) END;
PT* = POINTER TO T1;
VAR
r : PT;

PROCEDURE (ob : PT) Some*();
BEGIN
StdLog.Int(13)
END Some;
 
PROCEDURE Test*();
BEGIN
NEW(r);
A.r := r;
A.Test();
END Test;

END B.
 
(!) B.Test
Unload B
(!) A.Test
Trap
https://gist.github.com/valexey/7777367

Вах! Вот ведь неразрешимый трабл! Если модуль-владелец отдает свой объект в другой модуль, то ББ не отслеживает при вызове методов, что модуль-владелец может быть выгружен. То что мы в данном случае получаем штатный трап народ не впечатлило. Тот факт, что эта ошибка уровня разыменования NIL (что как бэ намекает) никого тоже не колышит:
Цитировать
[01:44:23] <vlad2> boris_ilov: это хреновый трап и принципиально не такой как в случа NIL
Хотя лично я разницы не вижу (и ББ тоже не видит, в чем легко убедиться заглянув в исходники)

Теперь по обсуждаемому коду.
Совершенно очевидно, что этот код не будет работать. Что автор хотел получить от ББ для меня загадка. ББ не подгружает модуль если его прямо или косвенно не импортировать. Неявный импорт средой не отслеживается очевидно по соображениям производительности. НО! Даже если бы ББ это делал, то это не помогло бы автору. Ибо проблема рук и лени средствами среды не лечится.
Если автор хотел горячую замену реализации в реальном времени, то очевидно, что это делается не так. И это известно любому, кто читал документацию.
Уже неоднократно говорилось о паттерне Dir (который собсна и описан в доке). Народ упрямо игнорит сию информацию.
Горячая замена с паттерном Dir делается элементарно и безопасно.

Почему-то я делал такое ни один раз и никаких AV не получал. Более того я даже не знал что ББ не отслеживает выгрузку модуля. И все потому, что мне в голову не приходило изобразить такой код. Паттерн Dir - это первое, что я вспомню если мне нужно подменить реализацию в реальном времени.

Так в чем же проблема? В ББ? Или в нежелании использовать ООП, в ООП-языке?
Название: Re: Горячая замена кода
Отправлено: Geniepro от Декабрь 04, 2013, 09:44:39 am
Горячая замена с паттерном Dir делается элементарно и безопасно.

Почему-то я делал такое ни один раз и никаких AV не получал. Более того я даже не знал что ББ не отслеживает выгрузку модуля. И все потому, что мне в голову не приходило изобразить такой код. Паттерн Dir - это первое, что я вспомню если мне нужно подменить реализацию в реальном времени.

А как будет это выглядеть с использованием паттерна Dir?
Название: Re: Горячая замена кода
Отправлено: ilovb от Декабрь 04, 2013, 09:54:13 am
Чтобы приготовить горячую замену в реальном времени нужно:
0. Уяснить, что замена реализации - это подмена объекта. (если у нас куча объектов, то это элементарно решается композицией, т.е. объекты ссылаются на один объект-реализацию)
1. Экспортная (для чтения) переменная Dir - текущая реализация
2. Экспортная (для чтения) переменная StdDir - Реализация по дефолту от модуля-владельца Dir
3. Метод установки объекта в Dir (с проверкой на NIL)

Во время работы программы можно подменять Dir хоть до посинения. И мы всегда можем вернуться к дефолтной реализации.

Все.

Если мы вдруг желаем заменять реализацию в процессе разработки (перекомпилируем и выгружаем модули), то достаточно в секции CLOSE добавить вызов метода SetDir(StdDir).
Название: Re: Горячая замена кода
Отправлено: ilovb от Декабрь 04, 2013, 10:05:27 am
На самом деле Dir - это про фабрики, но у нас тут немного другая задача.
Название: Re: Горячая замена кода
Отправлено: Wlad от Декабрь 04, 2013, 02:29:14 pm
В конфе в очередной раз зашел разговор за горячую замену кода в ББ. У народа оказывается с этим есть проблемы. И более того говорят что ее там нет!
Свои пять копеек.
Мне интересно, а как вообще можно (в "архитектурном смысле") уничтожать объект, который служит оболочкой для ссылаемой сущности/переменной???
Модуль, кроме чисто синтаксической роли объединения чего-то "в одну кучку", ещё ведь является неким "объектом"-синглтоном. Или я не прав?
Название: Re: Горячая замена кода
Отправлено: vlad от Декабрь 04, 2013, 04:13:55 pm
То что мы в данном случае получаем штатный трап народ не впечатлило. Тот факт, что эта ошибка уровня разыменования NIL (что как бэ намекает) никого тоже не колышит:
Цитировать
[01:44:23] <vlad2> boris_ilov: это хреновый трап и принципиально не такой как в случа NIL
Хотя лично я разницы не вижу (и ББ тоже не видит, в чем легко убедиться заглянув в исходники)

Поясняю: трап хреновый потому что он ровно из той же серии, что и разыменование невалидного (dangled) указателя в C/C++. Только в случае С/C++ никто не кричит, что такой трап штатный, а идут и фиксают :) Хотя, например, C++Builder из коробки такие трапы ловит не хуже ББ и ничего не падает (наследнику дельфи, чего с него взять).

Последствия/уязвимости у таких трапов, соответственно, точно такие же как и у традиционных небезопасных языков (форматирование жесткого диска и т.д. и т.п.).
Название: Re: Горячая замена кода
Отправлено: vlad от Декабрь 04, 2013, 04:18:01 pm
Мне интересно, а как вообще можно (в "архитектурном смысле") уничтожать объект, который служит оболочкой для ссылаемой сущности/переменной???
Модуль, кроме чисто синтаксической роли объединения чего-то "в одну кучку", ещё ведь является неким "объектом"-синглтоном. Или я не прав?

Все правильно. Естественно, что при насильной выгрузке/замене будут определенные конфликты. Началось все с того, что Алексей будучи под впечатлением от того как хорошо продуманы эти моменты в эрланге сказал, что в ББ оно вообще никакое. Вот и все.
Название: Re: Горячая замена кода
Отправлено: Geniepro от Декабрь 04, 2013, 04:23:03 pm
В конфе в очередной раз зашел разговор за горячую замену кода в ББ. У народа оказывается с этим есть проблемы. И более того говорят что ее там нет!

Свои пять копеек.
Мне интересно, а как вообще можно (в "архитектурном смысле") уничтожать объект, который служит оболочкой для ссылаемой сущности/переменной???
Модуль, кроме чисто синтаксической роли объединения чего-то "в одну кучку", ещё ведь является неким "объектом"-синглтоном. Или я не прав?

Ну, скажем, в Ерланге сделано примерно так:

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

Если же изменения были достаточно серьёзными и привели к нарушению целостности данных (например, из-за изменения структур данных в этом модуле), то придётся останавливать софт и выгружать старые версии модулей вручную -- как в оберонах...
Название: Re: Горячая замена кода
Отправлено: valexey_u от Декабрь 04, 2013, 04:24:57 pm
Поясню свою мысль - в ББ есть закгрузка и выгрузка модулей. Но горячей замены кода там нет.

Её там можно сделать для некоторых (своих) специально спроектированных модулей.

(аналогия - Точно также можно сделать сборку мусора для некоторых (своих) классов в С++. Но от этого в С++ сборщик мусора внезапно не появляется)

Вот и всё.
Название: Re: Горячая замена кода
Отправлено: valexey_u от Декабрь 04, 2013, 04:30:13 pm
Если же изменения были достаточно серьёзными и привели к нарушению целостности данных (например, из-за изменения структур данных в этом модуле), то придётся останавливать софт и выгружать старые версии модулей вручную -- как в оберонах...

Это не так :-) Для этого там есть OTP. Грубо говоря, при замене кода тебе прилетает событие и ты можешь сконвертировать все данные в новый формат. Вообще же в ББ просто инкапсуляция недостаточна для полноценной замены кода. Поясню - нужена не общая свалка в памяти, где объекты (и ссылки на них) гуляют как хотят и модифицируются как хотят, а нужно множество отдельных кучек, где за каждую кучку ответственен вполне конкретный кусок кода, множество процессов которые взаимодействуют через сообщения (полноценные, а не те которые в "messge bus" в ББ). Вот тогда появляется шанс сделать правильную горячую замену кода в общем случае.

Как, по моему, Зефиров говорил - в erlang'e есть искаробки какая-то горячая замена кода, в других системах её можно делать ручками (и ББ не исключение, да), в erlang'e можно сделать правильную горячую замену кода, в других системах её просто не сделать.
Название: Re: Горячая замена кода
Отправлено: vlad от Декабрь 04, 2013, 04:34:39 pm
Чтобы приготовить горячую замену в реальном времени нужно:

У тебя заход вообще не с той стороны. Проблема не в том, что в ББ нельзя сделать горячую замену кода. Конечно можно и очень даже просто (особенно по сравнению с каким-нибудь С). Проблема в том насколько хорошо это работает при ежедневной разработке, когда нет цели делать горячую замену (и думать о ней), а есть цель пилить код. Потому что ББ подразумевает при разработке постоянную загрузку/выгрузку просто потому, что он так работает, а не потому что это требуется в конечном приложении.

Так вот, с учетом проблем, демонстрируемых в примере, я легко себе представляю ситуацию, когда при разработке/тестировании достаточно большой подсистемы (ну пусть будет > 100 модулей для определенности) будут "странные" глюки только потому, что где-то "недогрузилась" новая версия модуля (в результате каких-то неочевидных зависимостей). Естественно это все неактуально, когда речь идет о чем-то маленьком и обозримом - а-ля очередной парсер или тетрис.

Также мне не очень понятно как это может хорошо работать в случае расширения непосредственно каркаса. Например, мы модифицируем модуль которые отрисовывает шрифты в окошках и выгружаем его.... Или на таймер (50 раз в секунду) повесили коллбэк, который ведет в выгружаемый модуль. Ну и тому подобное.
Название: Re: Горячая замена кода
Отправлено: valexey_u от Декабрь 04, 2013, 04:41:55 pm
Или на таймер (50 раз в секунду) повесили коллбэк, который ведет в выгружаемый модуль. Ну и тому подобное.
Ну вот у меня как раз, тот самый штатный нормальный трап при неосторожной выгрузке модуля (чтобы заменить более свежей версией) генерился со скоростью 30 раз в секунду :-) Правда весело? ;-)

Один Compile&Unload и ББ встал колом - все зарасло окошками с трапами.

Причем ничего военного я не делал - просто встраиваемую кастомную вьюшку (показывало изображение с камеры в реальном времени). Вот так работает каркас.
Название: Re: Горячая замена кода
Отправлено: valexey_u от Декабрь 04, 2013, 04:44:39 pm
Тащемто вот тема: http://oberspace.dyndns.org/index.php/topic,495.0.html
Название: Re: Горячая замена кода
Отправлено: ilovb от Декабрь 04, 2013, 04:46:23 pm
Последствия/уязвимости у таких трапов, соответственно, точно такие же как и у традиционных небезопасных языков (форматирование жесткого диска и т.д. и т.п.).

Какие еще уязвимости? O_o
Название: Re: Горячая замена кода
Отправлено: vlad от Декабрь 04, 2013, 04:47:15 pm
Вот так работает каркас.

Ну это просто обратная сторона (нерекламируемая :) подхода "программа и IDE как единое целое". Насильная выгрузка модулей тут просто добавляет чуток энтропии :)
Название: Re: Горячая замена кода
Отправлено: vlad от Декабрь 04, 2013, 04:51:29 pm
Какие еще уязвимости? O_o

http://en.wikipedia.org/wiki/Dangling_pointer#Security_holes_involving_dangling_pointers
Название: Re: Горячая замена кода
Отправлено: ilovb от Декабрь 04, 2013, 04:51:53 pm
Цитата: vlad
Проблема в том насколько хорошо это работает при ежедневной разработке, когда нет цели делать горячую замену (и думать о ней), а есть цель пилить код. Потому что ББ подразумевает при разработке постоянную загрузку/выгрузку просто потому, что он так работает, а не потому что это требуется в конечном приложении.

Цитата: ilovb
Если мы вдруг желаем заменять реализацию в процессе разработки (перекомпилируем и выгружаем модули), то достаточно в секции CLOSE добавить вызов метода SetDir(StdDir).
Название: Re: Горячая замена кода
Отправлено: vlad от Декабрь 04, 2013, 04:56:22 pm
Какие еще уязвимости? O_o

Грубо говоря - в 99.9% случаев ты получишь трап. Потому что адрес находится вне адресного пространства процесса или мусор лежащий по этому адресу будет квалифицирован как invalid processor instruction. А в оставшемся 0.1% ты получишь "странное" поведение. А если хакеры постараются - то и security hole.

Для сравнения - в случае NIL указателя ты в 100% получишь трап.
Название: Re: Горячая замена кода
Отправлено: valexey_u от Декабрь 04, 2013, 04:59:34 pm
Какие еще уязвимости? O_o

Грубо говоря - в 99.9% случаев ты получишь трап. Потому что адрес находится вне адресного пространства процесса или мусор лежащий по этому адресу будет квалифицирован как invalid processor instruction. А в оставшемся 0.1% ты получишь "странное" поведение. А если хакеры постараются - то и security hole.

Для сравнения - в случае NIL указателя ты в 100% получишь трап.
Эмм.. Совсем не факт что эта дыра в ББ есть. Нужно смотреть в детали реализации.
Название: Re: Горячая замена кода
Отправлено: ilovb от Декабрь 04, 2013, 05:00:15 pm
vlad: Ты думаешь что в ББ это именно так работает? :)
Название: Re: Горячая замена кода
Отправлено: vlad от Декабрь 04, 2013, 05:02:56 pm
Цитата: ilovb
Если мы вдруг желаем заменять реализацию в процессе разработки (перекомпилируем и выгружаем модули), то достаточно в секции CLOSE добавить вызов метода SetDir(StdDir).

Еще раз. Я не желаю ничего там заменять. Я желаю разрабатывать свою программу, которой нафиг не сдалась динамическая я загрузка, потому что она грузится один раз и летит на Марс. Но ББ заставляет меня выгружать/загружать модули как часть процесса разработки. Пример показывает, что при этом могут возникать проблемы.
Название: Re: Горячая замена кода
Отправлено: ilovb от Декабрь 04, 2013, 05:06:40 pm
Цитата: vlad
Я не желаю ничего там заменять
Так с этого и надо было начинать.

"Но ББ заставляет "
Заставляет? Вот ведь скотина! Лучше б "разрешил" завершить прогу, пересобрать и снова запустить.

А что собсна мешает работать в православном стиле с++? O_o
Название: Re: Горячая замена кода
Отправлено: vlad от Декабрь 04, 2013, 05:07:21 pm
vlad: Ты думаешь что в ББ это именно так работает? :)

Если бы это работало по другому, то трап был бы более информативным.
Название: Re: Горячая замена кода
Отправлено: valexey_u от Декабрь 04, 2013, 05:08:06 pm
Вообще, теоретически, в ББ МОЖНО сделать на уровне фреймворка горячую замену кода. Действительно можно. Нужно добавить "команду" Update (unload & load), которая будет выполняться атомарно (благо многозадачности в ББ нет, так что и с этим проблем нет). В процессе такого update выполняем:
1) выгрузку старой версии модуля.
2) поиск всех оставшихся объектов в памяти за которые ответственен данный модуль
3) загрузка новой версии модуля
4) передача всех найденных объектов специальной функции конвертации новой версии модуля (в том числе указателей на функции!) в качестве VAR-параметров. Пусть конвертирует да меняет.

Все четыре пункта должны идти непрерывно и атомарно (то есть message loop не крутится).
Название: Re: Горячая замена кода
Отправлено: ilovb от Декабрь 04, 2013, 05:10:45 pm
Пример показывает, что при этом могут возникать проблемы.

Пример не показывает абсолютно ничего кроме радиуса...
Название: Re: Горячая замена кода
Отправлено: ilovb от Декабрь 04, 2013, 05:12:48 pm
Если бы это работало по другому, то трап был бы более информативным.

Этот трап не более и не менее информативный чем любой другой трап в ББ.
Номер ошибки есть. Дамп есть. Где сработал трап показано. Что еще надо?
Название: Re: Горячая замена кода
Отправлено: vlad от Декабрь 04, 2013, 05:13:18 pm
Цитата: vlad
Я не желаю ничего там заменять
Так с этого и надо было начинать.

Сознаюсь, я отклонился от темы, но я обозначил интересующий меня аспект еще вот здесь: http://oberspace.dyndns.org/index.php/topic,598.msg20104.html#msg20104:
Цитировать
Проблема в том насколько хорошо это работает при ежедневной разработке, когда нет цели делать горячую замену


"Но ББ заставляет "
Заставляет? Вот ведь скотина! Лучше б "разрешил" завершить прогу, пересобрать и снова запустить.

Претензий к "заставляет" не было. Он так работает - пожалуйста. Причем такой подход рекламируется и имеет определенные преимущества. А мы тут как раз степень фатальности недостатков такого подхода обсуждаем :)
Название: Re: Горячая замена кода
Отправлено: ilovb от Декабрь 04, 2013, 05:16:33 pm
2) поиск всех оставшихся объектов в памяти за которые ответственен данный модуль

Если реализация устанавливается только в экспортные переменные, то сделать контроль раз плюнуть. Kernel позволяет перебрать экспортные переменные модуля.
А вот если объекты не в глобальных переменных... то это нужно что-то вроде сборщика мусора только для модулей.
Название: Re: Горячая замена кода
Отправлено: vlad от Декабрь 04, 2013, 05:18:50 pm
Этот трап не более и не менее информативный чем любой другой трап в ББ.
Номер ошибки есть. Дамп есть. Где сработал трап показано. Что еще надо?

"Метод объекта принадлежит такому-то модулю выгруженному в такое-то время"?

P.S. Номер ошибки - это вообще прошлый век. Ценен только как дополнение к нормальной диагностике, для статистики и т.п. локализаций.
Название: Re: Горячая замена кода
Отправлено: vlad от Декабрь 04, 2013, 05:21:56 pm
Дамп есть. Где сработал трап показано. Что еще надо?

Кстати, место где сработало полезно только в сравнении с вообще ничем. Потому что само место будет вполне валидным - вызов метода не-NIL объекта.
Название: Re: Горячая замена кода
Отправлено: valexey_u от Декабрь 04, 2013, 05:22:21 pm
2) поиск всех оставшихся объектов в памяти за которые ответственен данный модуль

Если реализация устанавливается только в экспортные переменные, то сделать контроль раз плюнуть. Kernel позволяет перебрать экспортные переменные модуля.
А вот если объекты не в глобальных переменных... то это нужно что-то вроде сборщика мусора только для модулей.
Да нет, с задачей в принципе справится и менеджер памяти (сборщик мусора, ага) - пройдется по всем объектам в памяти (или по крайней мере в куче - это можно устроить, если update сделать отложенным действием выполняемым поближе к message loop - чтобы гарантированно на стеке объектов не было) посмотрит их тип (метаинформация у него есть, и из кого модуля тип данного объекта родом - тоже понятно) и составит списочек. То есть в принципе, все механизмы для реализации в ББ есть, нужно только чутка в ядро залезть и подкрутить ;-)
Название: Re: Горячая замена кода
Отправлено: ilovb от Декабрь 04, 2013, 05:24:25 pm
"Метод объекта принадлежит такому-то модулю выгруженному в такое-то время"?

Тыг да. Других вариантов то и нет. (кроме NIL procedure call)
Название: Re: Горячая замена кода
Отправлено: valexey_u от Декабрь 04, 2013, 05:26:55 pm
"Метод объекта принадлежит такому-то модулю выгруженному в такое-то время"?

Тыг да. Других вариантов то и нет. (кроме NIL procedure call)
Про время там ничего нет.

Более того, если модуль выгрузить, а затем загрузить (не изменяя ничего, не перекомпилируя) - ошибка все равно вылезет. Хотя модуль загружен и никак не изменился.
Название: Re: Горячая замена кода
Отправлено: ilovb от Декабрь 04, 2013, 05:29:39 pm
Зачем время?  :o

Цитировать
Более того, если модуль выгрузить, а затем загрузить (не изменяя ничего, не перекомпилируя) - ошибка все равно вылезет. Хотя модуль загружен и никак не изменился.
А как может быть иначе если модуль очевидно по другому адресу загружается?
Название: Re: Горячая замена кода
Отправлено: valexey_u от Декабрь 04, 2013, 05:33:45 pm
Зачем время?  :o

Цитировать
Более того, если модуль выгрузить, а затем загрузить (не изменяя ничего, не перекомпилируя) - ошибка все равно вылезет. Хотя модуль загружен и никак не изменился.
А как может быть иначе если модуль очевидно по другому адресу загружается?
Ну, ты же понимаешь, что это детали реализации просачиваются? ;-)

На самом деле, в культурных средах, у каждого модуля (собранного) есть свой uuid, генерируемый автоматом при компиляции. И если uuid совпадает, то вновь загруженный модуль работает как ни в чем не бывало. То ли там адреса те же использут, то ли еще как мигрируют - это детали реализации на самом деле. Но это сделать МОЖНО. Если делать каркас на совесть ;-)

Поэтому же неплохо бы в трапе показывать еще и ВЕРСИЮ модуля (тот самый uuid, или в качестве uuid хотя бы timestamp компиляции использовать). С точки зрения реализации такая информация ничего не стоит, делается элементарно, ресурсов не жрет, а головной боли у пользователя становится меньше.
Название: Re: Горячая замена кода
Отправлено: ilovb от Декабрь 04, 2013, 05:42:00 pm
"Если делать каркас на совесть ;-)"
Не понимаю при чем тут совесть. Разрабы сделали ровно то, что хотели. Как делать горячую замену кода в доке описали. У них там своя атмосфера и философия.
А твои хотелки - это ммм... только твои хотелки.
Название: Re: Горячая замена кода
Отправлено: vlad от Декабрь 04, 2013, 05:44:00 pm
Зачем время?  :o

Большая подсистема. Переделал модуль. Пошел чего-то еще чинить. Потом еще чего-то. Потом бах - трап. Про то, что 8 часов назад перекомпиливал какой-то модуль уже и не вспомнить без подсказки.
Название: Re: Горячая замена кода
Отправлено: vlad от Декабрь 04, 2013, 05:45:44 pm
А твои хотелки - это ммм... только твои хотелки.

Да. Согласен. Народ типа работает и не жужжит. Все хорошо. Пока на промышленные масштабы никто не замахивается :)
Название: Re: Горячая замена кода
Отправлено: valexey_u от Декабрь 04, 2013, 05:48:18 pm
"Если делать каркас на совесть ;-)"
Не понимаю при чем тут совесть. Разрабы сделали ровно то, что хотели. Как делать горячую замену кода в доке описали. У них там своя атмосфера и философия.
А твои хотелки - это ммм... только твои хотелки.
Это не хотелки, это фантазии (хотелки будут когда и если я снова туда серьезно сунусь - но тогда я просто реализую то что мне нужно, ведь конечный продукт, распространяемый в виде модуля к основной сборке ББ, не планируется, так что свой локальный ББ можно корячить каким угодно несовместимым образом) :-) Понятно что у них бюджет сдулся и они дело бросили на полдороге. Еще десяток ревизий ББ не повредил бы. Имеющееся очень круто для 1995 или даже 2000 года, но сейчас уже совсем другие ожидания и требования.
Название: Re: Горячая замена кода
Отправлено: ilovb от Декабрь 04, 2013, 05:59:03 pm
Зачем время?  :o

Большая подсистема. Переделал модуль. Пошел чего-то еще чинить. Потом еще чего-то. Потом бах - трап. Про то, что 8 часов назад перекомпиливал какой-то модуль уже и не вспомнить без подсказки.

vlad, ты кого пытаешься убедить в этих сферических проблемах?  :)
В трапе вся информация есть. Повторяю второй раз.
Название: Re: Горячая замена кода
Отправлено: valexey_u от Декабрь 04, 2013, 06:05:48 pm
Зачем время?  :o

Большая подсистема. Переделал модуль. Пошел чего-то еще чинить. Потом еще чего-то. Потом бах - трап. Про то, что 8 часов назад перекомпиливал какой-то модуль уже и не вспомнить без подсказки.

vlad, ты кого пытаешься убедить в этих сферических проблемах?  :)
В трапе вся информация есть. Повторяю второй раз.
На самом деле той информации которую Влад хочет, там нет. У тебя в системе может накопиться множество объектов созданных РАЗНЫМИ версиями модуля. Отличить трап один от другого ты не сможешь.

Кроме того, довольно странно - ты точно знаешь что модуль загружен и работает, а тебя трапает с сообщением о том, что этот модуль выгружен. То есть версионность таки нужна.
Название: Re: Горячая замена кода
Отправлено: ilovb от Декабрь 04, 2013, 06:16:50 pm
У тебя в системе может накопиться множество объектов созданных РАЗНЫМИ версиями модуля.
Если программировать в стиле Си то да, проблемы будут. Но я то про другое. Нужно хоть немного включать мозг же. Если ты нагерил кучу объектов в модуле А и передал их в модуль Б, то естественно нужно сделать себе коммандер, который выгружает сначала Б, потом А.
А если ты планируешь горячую замену кода, то это вообще говнокод. Для многих объектов горячая замена делается через композицию, и проблема вообще исчезает, т.к. как объекты не вызывают методы выгруженного модуля.

ps Возможно было бы неплохо иметь возможность в самом модуле указать, что его можно выгружать только после выгрузки таких-то модулей.
Название: Re: Горячая замена кода
Отправлено: ilovb от Декабрь 04, 2013, 06:28:36 pm
Вот накидал простой шаблон:
Модуль держатель подменяемой реализации.
MODULE HotR;
TYPE
   Directory* = POINTER TO EXTENSIBLE RECORD END;
VAR
   dir-, stdDir-: Directory;

PROCEDURE (d: Directory) Foo*, NEW, EMPTY;

PROCEDURE SetDir* (d: Directory);
BEGIN
   ASSERT(d # NIL, 20);
   dir := d;
END SetDir;

BEGIN
   NEW(stdDir);
   SetDir(stdDir);
END HotR.
Модуль который дергает подменяемую реализацию:
MODULE HotB;
IMPORT
   HotR, Services;
TYPE
   Action = POINTER TO RECORD (Services.Action) END;
VAR
   action: Action;

PROCEDURE (a: Action) Do;
BEGIN
   HotR.dir.Foo;
   Services.DoLater(a, Services.now)
END Do;

PROCEDURE Start*;
BEGIN
   NEW(action);
   Services.DoLater(action, Services.now)
END Start;

PROCEDURE Stop*;
BEGIN
   Services.RemoveAction(action)
END Stop;

END HotB.

^Q HotB.Start
^Q HotB.Stop
Модуль, который подменяет реализацию в горячем режиме:
MODULE HotA;
IMPORT
   R := HotR, Log;
TYPE
   D1 = POINTER TO RECORD (R.Directory) END;
   D2 = POINTER TO RECORD (R.Directory) END;
   
PROCEDURE (d: D1) Foo;
BEGIN
   Log.String('D1');
END Foo;

PROCEDURE (d: D2) Foo;
BEGIN
   Log.String('D2');
END Foo;

PROCEDURE Set1*;
VAR d: D1;
BEGIN
   NEW(d);
   R.SetDir(d);
END Set1;

PROCEDURE Set2*;
VAR d: D2;
BEGIN
   NEW(d);
   R.SetDir(d);
END Set2;

CLOSE
   R.SetDir(R.stdDir);
END HotA.

^Q HotA.Set1
^Q HotA.Set2
Название: Re: Горячая замена кода
Отправлено: Geniepro от Декабрь 05, 2013, 11:18:18 am
Небольшая статейка об Эрланге, "объектно-ориентированном фреймворке  для создания распределенных отказоустойчивых приложений"
http://eax.me/erlang-one-year/
Название: Re: Горячая замена кода
Отправлено: X512 от Декабрь 18, 2013, 01:38:49 pm
Поясняю: трап хреновый потому что он ровно из той же серии, что и разыменование невалидного (dangled) указателя в C/C++. Только в случае С/C++ никто не кричит, что такой трап штатный, а идут и фиксают :) Хотя, например, C++Builder из коробки такие трапы ловит не хуже ББ и ничего не падает (наследнику дельфи, чего с него взять).

Последствия/уязвимости у таких трапов, соответственно, точно такие же как и у традиционных небезопасных языков (форматирование жесткого диска и т.д. и т.п.).
Вообще то Blackbox вообще не умеет выгружать модули, он лишь умеет игнорировать "выгруженный" модуль при импорте и инвалидировать кодовую секцию (см. Kernel.UnloadMod). При этом модуль-зомби продолжает находиться в памяти до завершения процесса Blackbox.

Trap "illegal execution" просто показывает, что пытаются выполнять код без разрешения на чтение и исполнение, убранные при инвалидации.  Можно сделать Trap информативнее следующими правками процедуры "DevDebug.Trap":

   PROCEDURE Trap;
      VAR a0: TextModels.Attributes; prop: Properties.StdProp; action: Action;
          msg: ARRAY 512 OF CHAR; mod: Kernel.Module;
   BEGIN
      out.ConnectTo(TextModels.CloneOf(StdLog.buf));
      a0 := out.rider.attr;
      out.rider.SetAttr(TextModels.NewWeight(a0, Fonts.bold));
      IF Kernel.err = 129 THEN out.WriteSString("invalid WITH")
      ELSIF Kernel.err = 130 THEN out.WriteSString("invalid CASE")
      ELSIF Kernel.err = 131 THEN out.WriteSString("function without RETURN")
      ELSIF Kernel.err = 132 THEN out.WriteSString("type guard")
      ELSIF Kernel.err = 133 THEN out.WriteSString("implied type guard")
      ELSIF Kernel.err = 134 THEN out.WriteSString("value out of range")
      ELSIF Kernel.err = 135 THEN out.WriteSString("index out of range")
      ELSIF Kernel.err = 136 THEN out.WriteSString("string too long")
      ELSIF Kernel.err = 137 THEN out.WriteSString("stack overflow")
      ELSIF Kernel.err = 138 THEN out.WriteSString("integer overflow")
      ELSIF Kernel.err = 139 THEN out.WriteSString("division by zero")
      ELSIF Kernel.err = 140 THEN out.WriteSString("infinite real result")
      ELSIF Kernel.err = 141 THEN out.WriteSString("real underflow")
      ELSIF Kernel.err = 142 THEN out.WriteSString("real overflow")
      ELSIF Kernel.err = 143 THEN
         out.WriteSString("undefined real result  (");
         out.WriteIntForm(Kernel.val MOD 10000H, TextMappers.hexadecimal, 4, "0", TextMappers.hideBase); out.WriteSString(", ");
         out.WriteIntForm(Kernel.val DIV 10000H, TextMappers.hexadecimal, 3, "0", TextMappers.hideBase); out.WriteChar(")")
      ELSIF Kernel.err = 144 THEN out.WriteSString("not a number")
      ELSIF Kernel.err = 200 THEN out.WriteSString("keyboard interrupt")
      ELSIF Kernel.err = 201 THEN
         out.WriteSString("NIL dereference")
      ELSIF Kernel.err = 202 THEN
         out.WriteSString("illegal instruction: ");
         out.WriteIntForm(Kernel.val, TextMappers.hexadecimal, 5, "0", TextMappers.showBase)
      ELSIF Kernel.err = 203 THEN
         IF (Kernel.val >= -4) & (Kernel.val < 65536) THEN out.WriteSString("NIL dereference (read)")
         ELSE out.WriteSString("illegal memory read (ad = "); WriteHex(Kernel.val); out.WriteChar(")")
         END
      ELSIF Kernel.err = 204 THEN
         IF (Kernel.val >= -4) & (Kernel.val < 65536) THEN out.WriteSString("NIL dereference (write)")
         ELSE out.WriteSString("illegal memory write (ad = "); WriteHex(Kernel.val); out.WriteChar(")")
         END
      ELSIF Kernel.err = 205 THEN
         IF (Kernel.val >= -4) & (Kernel.val < 65536) THEN out.WriteSString("NIL procedure call")
         ELSE
            mod := Kernel.modList;
            WHILE (mod # NIL) & ~((mod.refcnt = -1) & (Kernel.val >= mod.code) & (Kernel.val < mod.code + mod.csize)) DO mod := mod.next END;
            IF mod # NIL THEN
               out.WriteSString('Call of unloaded module "'); out.WriteSString(mod.name); out.WriteChar('"')
            ELSE
               out.WriteSString("illegal execution (ad = "); WriteHex(Kernel.val); out.WriteChar(")")
            END
         END
      ELSIF Kernel.err = 257 THEN out.WriteSString("out of memory")
      ELSIF Kernel.err = 10001H THEN out.WriteSString("bus error")
      ELSIF Kernel.err = 10002H THEN out.WriteSString("address error")
      ELSIF Kernel.err = 10007H THEN out.WriteSString("fpu error")
      ELSIF Kernel.err < 0 THEN
         out.WriteSString("Exception "); out.WriteIntForm(-Kernel.err, TextMappers.hexadecimal, 3, "0", TextMappers.showBase)
      ELSE
         out.WriteSString("TRAP "); out.WriteInt(Kernel.err);
         IF Kernel.err = 126 THEN out.WriteSString("  (not yet implemented)")
         ELSIF Kernel.err = 125 THEN out.WriteSString("  (call of obsolete procedure)")
         ELSIF Kernel.err >= 100 THEN out.WriteSString("  (invariant violated)")
         ELSIF Kernel.err >= 60 THEN out.WriteSString("  (postcondition violated)")
         ELSIF Kernel.err >= 20 THEN out.WriteSString("  (precondition violated)")
         END
      END;
      GetTrapMsg(msg);
      IF msg # "" THEN out.WriteLn; out.WriteString(msg) END;

      out.WriteLn; out.rider.SetAttr(a0);
      out.WriteLn; ShowStack;
      NEW(action); action.text := out.rider.Base();
      Services.DoLater(action, Services.now);
      out.ConnectTo(NIL)
   END Trap;
Название: Re: Горячая замена кода
Отправлено: vlad от Декабрь 18, 2013, 02:17:10 pm
Вообще то Blackbox вообще не умеет выгружать модули, он лишь умеет игнорировать "выгруженный" модуль при импорте и инвалидировать кодовую секцию (см. Kernel.UnloadMod). При этом модуль-зомби продолжает находиться в памяти до завершения процесса Blackbox.

А что тогда происходит, если миллион раз выгрузить/загрузить модуль? Память кончится?
Название: Re: Горячая замена кода
Отправлено: valexey_u от Декабрь 18, 2013, 02:26:03 pm
Вообще то Blackbox вообще не умеет выгружать модули, он лишь умеет игнорировать "выгруженный" модуль при импорте и инвалидировать кодовую секцию (см. Kernel.UnloadMod). При этом модуль-зомби продолжает находиться в памяти до завершения процесса Blackbox.

А что тогда происходит, если миллион раз выгрузить/загрузить модуль? Память кончится?
Написал тест. Тестится пока - но похоже таки да. Кончится. Память кушается и не освобождается. ББ уже скушал 64 метра ОЗУ.
Название: Re: Горячая замена кода
Отправлено: valexey_u от Декабрь 18, 2013, 02:27:50 pm
Вообще то Blackbox вообще не умеет выгружать модули, он лишь умеет игнорировать "выгруженный" модуль при импорте и инвалидировать кодовую секцию (см. Kernel.UnloadMod). При этом модуль-зомби продолжает находиться в памяти до завершения процесса Blackbox.

А что тогда происходит, если миллион раз выгрузить/загрузить модуль? Память кончится?
Написал тест. Тестится пока - но похоже таки да. Кончится. Память кушается и не освобождается. ББ уже скушал 64 метра ОЗУ.
Хотя быть может эта память отжирается StdLog'ом? Ведь туда валятся сообщения о выгрузке модуля.
Название: Re: Горячая замена кода
Отправлено: valexey_u от Декабрь 18, 2013, 02:39:59 pm
Да, это был StdLog. В тест вкралась ошибка и он был не корректным.
Название: Re: Горячая замена кода
Отправлено: X512 от Декабрь 18, 2013, 02:56:47 pm
А что тогда происходит, если миллион раз выгрузить/загрузить модуль? Память кончится?
Да, именно так. Элементарно проверяется, особенно если в выгружаемом модуле есть большие массивы (так память израсходуется быстрее).

Код проверки:
MODULE A;
   VAR
      a: ARRAY 1000 OF POINTER TO RECORD
         p: ANYPTR
      END;
END A.

MODULE B;
   IMPORT Kernel;
   
   PROCEDURE Do*;
      VAR mod: Kernel.Module;
   BEGIN
      mod := Kernel.ThisMod("A");
      WHILE mod # NIL DO
         Kernel.UnloadMod(mod);
         mod := Kernel.ThisMod("A")
      END
      (* mod = NIL, память кончилась *)
   END Do;
   
END B.

(!) B.Do
Название: Re: Горячая замена кода
Отправлено: valexey_u от Декабрь 18, 2013, 03:05:15 pm
Поправил тест. Там похоже захардкожено ограничение на объем отводимый на загруженные модули.

Через какое-то время у меня в StdLog начали валиться такие вот сообщения:
command error: not enough memory for Test1

При этом, после останова теста (руками) ББ так и не восстановился. В частности не получается открыть диалог для сохранения файла (вываливается с трапом), не удается скопировать в системный буфер обмена текст. Всего памяти ББ скушал порядка 38 Мб.

Вот текст тестовых модулей (они работают в паре):
MODULE test1;
IMPORT StdLog;

PROCEDURE Do*();
BEGIN
    StdLog.Clear;
    StdLog.Int(42);
END Do;

END Test1.

MODULE Test;

IMPORT DevDebug;

PROCEDURE Do*();
VAR
    i : INTEGER;
BEGIN
    i:=0;
    WHILE i<1000000 DO
        INC(i);
        DevDebug.Execute;
        DevDebug.Unload;
    END;
END Do;

END Test.

Запускается это так:
Test1.Do
(!)Test.Do

Выделяем текст Test1.Do, и жмем на коммандер перед Test.Do
Название: Re: Горячая замена кода
Отправлено: X512 от Декабрь 18, 2013, 03:22:55 pm
Кстати память частично освобождается при выгрузке модуля, но выгруженный участок памяти больше никогда не используется. Ограничение памяти под модули вероятно вызвано резервированием 1.5 ГБ виртуальной памяти под кучу в стандартном режиме (есть ещё режим "dllMode" в котором память выделяется через стандартную Win32 кучу). Для памяти под модули используется виртуальная память (VirtualAlloc), а не куча сборщика мусора.