Автор Тема: Горячая замена кода  (Прочитано 23400 раз)

valexey_u

  • Hero Member
  • *****
  • Сообщений: 3013
    • Просмотр профиля
Re: Горячая замена кода
« Ответ #30 : Декабрь 04, 2013, 05:22:21 pm »
2) поиск всех оставшихся объектов в памяти за которые ответственен данный модуль

Если реализация устанавливается только в экспортные переменные, то сделать контроль раз плюнуть. Kernel позволяет перебрать экспортные переменные модуля.
А вот если объекты не в глобальных переменных... то это нужно что-то вроде сборщика мусора только для модулей.
Да нет, с задачей в принципе справится и менеджер памяти (сборщик мусора, ага) - пройдется по всем объектам в памяти (или по крайней мере в куче - это можно устроить, если update сделать отложенным действием выполняемым поближе к message loop - чтобы гарантированно на стеке объектов не было) посмотрит их тип (метаинформация у него есть, и из кого модуля тип данного объекта родом - тоже понятно) и составит списочек. То есть в принципе, все механизмы для реализации в ББ есть, нужно только чутка в ядро залезть и подкрутить ;-)
Y = λf.(λx.f (x x)) (λx.f (x x))

ilovb

  • Hero Member
  • *****
  • Сообщений: 2538
  • just another nazi test
    • Просмотр профиля
    • Oberon systems
Re: Горячая замена кода
« Ответ #31 : Декабрь 04, 2013, 05:24:25 pm »
"Метод объекта принадлежит такому-то модулю выгруженному в такое-то время"?

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

valexey_u

  • Hero Member
  • *****
  • Сообщений: 3013
    • Просмотр профиля
Re: Горячая замена кода
« Ответ #32 : Декабрь 04, 2013, 05:26:55 pm »
"Метод объекта принадлежит такому-то модулю выгруженному в такое-то время"?

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

Более того, если модуль выгрузить, а затем загрузить (не изменяя ничего, не перекомпилируя) - ошибка все равно вылезет. Хотя модуль загружен и никак не изменился.
Y = λf.(λx.f (x x)) (λx.f (x x))

ilovb

  • Hero Member
  • *****
  • Сообщений: 2538
  • just another nazi test
    • Просмотр профиля
    • Oberon systems
Re: Горячая замена кода
« Ответ #33 : Декабрь 04, 2013, 05:29:39 pm »
Зачем время?  :o

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

valexey_u

  • Hero Member
  • *****
  • Сообщений: 3013
    • Просмотр профиля
Re: Горячая замена кода
« Ответ #34 : Декабрь 04, 2013, 05:33:45 pm »
Зачем время?  :o

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

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

Поэтому же неплохо бы в трапе показывать еще и ВЕРСИЮ модуля (тот самый uuid, или в качестве uuid хотя бы timestamp компиляции использовать). С точки зрения реализации такая информация ничего не стоит, делается элементарно, ресурсов не жрет, а головной боли у пользователя становится меньше.
Y = λf.(λx.f (x x)) (λx.f (x x))

ilovb

  • Hero Member
  • *****
  • Сообщений: 2538
  • just another nazi test
    • Просмотр профиля
    • Oberon systems
Re: Горячая замена кода
« Ответ #35 : Декабрь 04, 2013, 05:42:00 pm »
"Если делать каркас на совесть ;-)"
Не понимаю при чем тут совесть. Разрабы сделали ровно то, что хотели. Как делать горячую замену кода в доке описали. У них там своя атмосфера и философия.
А твои хотелки - это ммм... только твои хотелки.

vlad

  • Hero Member
  • *****
  • Сообщений: 1391
    • Просмотр профиля
Re: Горячая замена кода
« Ответ #36 : Декабрь 04, 2013, 05:44:00 pm »
Зачем время?  :o

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

vlad

  • Hero Member
  • *****
  • Сообщений: 1391
    • Просмотр профиля
Re: Горячая замена кода
« Ответ #37 : Декабрь 04, 2013, 05:45:44 pm »
А твои хотелки - это ммм... только твои хотелки.

Да. Согласен. Народ типа работает и не жужжит. Все хорошо. Пока на промышленные масштабы никто не замахивается :)

valexey_u

  • Hero Member
  • *****
  • Сообщений: 3013
    • Просмотр профиля
Re: Горячая замена кода
« Ответ #38 : Декабрь 04, 2013, 05:48:18 pm »
"Если делать каркас на совесть ;-)"
Не понимаю при чем тут совесть. Разрабы сделали ровно то, что хотели. Как делать горячую замену кода в доке описали. У них там своя атмосфера и философия.
А твои хотелки - это ммм... только твои хотелки.
Это не хотелки, это фантазии (хотелки будут когда и если я снова туда серьезно сунусь - но тогда я просто реализую то что мне нужно, ведь конечный продукт, распространяемый в виде модуля к основной сборке ББ, не планируется, так что свой локальный ББ можно корячить каким угодно несовместимым образом) :-) Понятно что у них бюджет сдулся и они дело бросили на полдороге. Еще десяток ревизий ББ не повредил бы. Имеющееся очень круто для 1995 или даже 2000 года, но сейчас уже совсем другие ожидания и требования.
Y = λf.(λx.f (x x)) (λx.f (x x))

ilovb

  • Hero Member
  • *****
  • Сообщений: 2538
  • just another nazi test
    • Просмотр профиля
    • Oberon systems
Re: Горячая замена кода
« Ответ #39 : Декабрь 04, 2013, 05:59:03 pm »
Зачем время?  :o

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

vlad, ты кого пытаешься убедить в этих сферических проблемах?  :)
В трапе вся информация есть. Повторяю второй раз.

valexey_u

  • Hero Member
  • *****
  • Сообщений: 3013
    • Просмотр профиля
Re: Горячая замена кода
« Ответ #40 : Декабрь 04, 2013, 06:05:48 pm »
Зачем время?  :o

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

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

Кроме того, довольно странно - ты точно знаешь что модуль загружен и работает, а тебя трапает с сообщением о том, что этот модуль выгружен. То есть версионность таки нужна.
Y = λf.(λx.f (x x)) (λx.f (x x))

ilovb

  • Hero Member
  • *****
  • Сообщений: 2538
  • just another nazi test
    • Просмотр профиля
    • Oberon systems
Re: Горячая замена кода
« Ответ #41 : Декабрь 04, 2013, 06:16:50 pm »
У тебя в системе может накопиться множество объектов созданных РАЗНЫМИ версиями модуля.
Если программировать в стиле Си то да, проблемы будут. Но я то про другое. Нужно хоть немного включать мозг же. Если ты нагерил кучу объектов в модуле А и передал их в модуль Б, то естественно нужно сделать себе коммандер, который выгружает сначала Б, потом А.
А если ты планируешь горячую замену кода, то это вообще говнокод. Для многих объектов горячая замена делается через композицию, и проблема вообще исчезает, т.к. как объекты не вызывают методы выгруженного модуля.

ps Возможно было бы неплохо иметь возможность в самом модуле указать, что его можно выгружать только после выгрузки таких-то модулей.

ilovb

  • Hero Member
  • *****
  • Сообщений: 2538
  • just another nazi test
    • Просмотр профиля
    • Oberon systems
Re: Горячая замена кода
« Ответ #42 : Декабрь 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

Geniepro

  • Hero Member
  • *****
  • Сообщений: 1955
  • Знайте- истина в том, что повторено трижды подряд!
    • Просмотр профиля
Re: Горячая замена кода
« Ответ #43 : Декабрь 05, 2013, 11:18:18 am »
Небольшая статейка об Эрланге, "объектно-ориентированном фреймворке  для создания распределенных отказоустойчивых приложений"
http://eax.me/erlang-one-year/
to iterate is human, to recurse, divine

Салат «рекурсия»: помидоры, огурцы, салат…

X512

  • Newbie
  • *
  • Сообщений: 45
    • Просмотр профиля
Re: Горячая замена кода
« Ответ #44 : Декабрь 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;