OberonCore

Библиотека  Wiki  Форум  BlackBox  Компоненты  Проекты
Текущее время: Пятница, 29 Март, 2024 14:06

Часовой пояс: UTC + 3 часа




Начать новую тему Ответить на тему  [ Сообщений: 27 ]  На страницу 1, 2  След.
Автор Сообщение
СообщениеДобавлено: Четверг, 15 Март, 2007 00:22 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
rv82 писал(а):
Я уже неоднократно читал, что в системе нет пошагового отладчика. Это очень огорчает вот по какой причине. По долгу службы мне приходится иногда писать программы, в которых присутствует множество переменных, и на каждом шаге цикла
они меняются. Цикл содержит большое количество шагов (от нескольких тысяч, иногда о
нескольких миллионов). Удержать в голове все эти переменные практически невозможно из-за большого их количества. Так вот, не подскажет ли кто, может уже предпринимались попытки создать отладчик самостоятельно? Или может есть приемы для остановки цикла, контроля переменных, а затем для перехода на последующие шаги цикла?

Я тут пораскинул мозгами и состряпал тулзовину, которая поможет в этой ситуации.

Прошу любить и жаловать - модуль DevLocalWatch.
Пользоваться очень просто, в интересующей нас процедуре в начале пишем DevLocalWath.Begin (передавая либо пустую строку, если нас интересуют все локальные переменные, либо список интересующих нас переменных через пробел),
затем в тех местах процедуры, где нас интересуют "срезы" локальных переменных - DevLocalWatch.Snap, а в конце - DevLocalWatch.End.
В итоге, после вызова End откроется окошко, содержащее табулированную информации об истории изменения переменных.

Вот пример использования:

Код:
   PROCEDURE Test ;
      VAR x, y: INTEGER;
            z: REAL;
   BEGIN
      x := 10; y := 0; z := 1;
      DevLocalWatch.Begin("x y z");
      WHILE x > 0 DO
         DevLocalWatch.Snap;
         z := z * (x + y);
         DEC(x); INC(y)      
      END;
      DevLocalWatch.End
   END Test;


А вот сам код модуля DevLocalWatch:
Код:
MODULE DevLocalWatch;
(* (C) 2007 Ilya Ermakov
   http://oberoncore.ru *)

   IMPORT Mem, S := SYSTEM, Kernel, Strings, TextModels, TextMappers, TextViews, Views;

   CONST
      (* atomic types signatures *)
      boolean = 1X;
      shortchar = 2X;
      char = 3X;
      byte = 4X;
      shortint = 5X;
      integer = 6X;
      shortreal = 7X;
      real = 8X;
      set = 9X;
      longint = 0AX;
      
      regBP = 5;
      
   TYPE
      Variable = RECORD
         name: ARRAY 256 OF CHAR;
         type: SHORTCHAR;
         offset: INTEGER
      END;
      
      Value = RECORD [union]
         boolean: BOOLEAN;
         shortchar: SHORTCHAR;
         char: CHAR;
         byte: BYTE;
         shortint: SHORTINT;
         integer: INTEGER;
         shortreal: SHORTREAL;
         real: REAL
      END;
      PtrValue = POINTER TO Value;
      
      ValueBlock = POINTER TO RECORD
         next: ValueBlock;
         mem: ARRAY 1024 * SIZE(Value) OF BYTE;
         used: INTEGER
      END;
         
      Snapshot = POINTER TO RECORD
         next: Snapshot;
         values: POINTER TO ARRAY OF PtrValue
      END;
      
   VAR
      stack: INTEGER;                                 (* Stack frame pointer *)
      varMap: POINTER TO ARRAY OF Variable;   (* Map of local variables *)
      valHeap: ValueBlock;                           (* Heap for value elem allocation *)
      history, hisLast: Snapshot;                     (* Loop history *)

   PROCEDURE AddVarToMap (IN name: ARRAY OF CHAR; type: SHORTCHAR; offset: INTEGER);
      VAR n: INTEGER;
   BEGIN
      n := LEN(varMap);
      Mem.SetLength(varMap, n+1);
      varMap[n].name := name$;
      varMap[n].type := type;
      varMap[n].offset := offset
   END AddVarToMap;

   PROCEDURE Begin* (IN varToWatch: ARRAY OF CHAR);
      VAR retAdr: INTEGER;
            mod: Kernel.Module;
            ref, end: INTEGER;
            name: Kernel.Name;
            mode, form: SHORTCHAR;
            desc: Kernel.Type;
            offs, spos: INTEGER;
   BEGIN
      S.GETREG(regBP, stack); (* get frame of current proc *)
      S.GET(stack+4, retAdr);
      S.GET(stack, stack);            (* get frame of target proc *)
      NEW(varMap, 1);
      Mem.SetLength(varMap, 0);
      mod := Kernel.modList;
      WHILE (mod # NIL) & ((retAdr < mod.code) OR (retAdr >= mod.code + mod.csize)) DO
         mod := mod.next
      END;
      ASSERT(mod # NIL, 100);
      DEC(retAdr, mod.code);
      ref := mod.refs;
      REPEAT Kernel.GetRefProc(ref, end, name) UNTIL (end = 0) OR (retAdr < end);
      ASSERT(retAdr < end, 101);
      Kernel.GetRefVar(ref, mode, form, desc, offs, name);
      WHILE mode # 0X DO
         IF (mode = 1X) & (offs < 0) & (form >= boolean) & (form <= longint) THEN (* variable, non parameter, atomic *)
            IF varToWatch # "" THEN
               Strings.Find(varToWatch, name$, 0, spos)
            END;
            IF (varToWatch = "") OR (spos # -1) & ( (spos = 0) OR (varToWatch[spos-1] = " ") )
               & ((varToWatch[spos+LEN(name$)] = " ") OR (varToWatch[spos+LEN(name$)] = 0X)) THEN
               AddVarToMap(name$, form, offs)
            END
         END;
         Kernel.GetRefVar(ref, mode, form, desc, offs, name)
      END;
      NEW(valHeap)      
   END Begin;
   
   PROCEDURE NewVal (): PtrValue;
      VAR blk: ValueBlock;
   BEGIN
      IF valHeap.used = LEN(valHeap.mem) THEN
         NEW(blk);
         blk.next := valHeap;
         valHeap := blk
      END;
      INC(valHeap.used, SIZE(Value));
      RETURN S.VAL(PtrValue, S.ADR(valHeap.mem[valHeap.used - SIZE(Value)]))
   END NewVal;
   
   PROCEDURE Snap*;
      VAR s: Snapshot;
            fp, i: INTEGER;
            val: PtrValue;
   BEGIN
      ASSERT(varMap # NIL, 20); (* Begin has been called before *)
      S.GETREG(regBP, fp); S.GET(fp, fp);
      ASSERT(stack = fp, 21);    (* Begin & Loop called from same procedure *)
      NEW(s);
      NEW(s.values, LEN(varMap));
      FOR i := 0 TO LEN(varMap)-1 DO
         val := NewVal();
         CASE varMap[i].type OF
         | boolean: S.GET(fp + varMap[i].offset, val.boolean)
         | shortchar: S.GET(fp + varMap[i].offset, val.shortchar)
         | char: S.GET(fp + varMap[i].offset, val.char)
         | byte: S.GET(fp + varMap[i].offset, val.byte)
         | shortint: S.GET(fp + varMap[i].offset, val.shortint)
         | integer: S.GET(fp + varMap[i].offset, val.integer)
         | shortreal: S.GET(fp + varMap[i].offset, val.shortreal)
         | real: S.GET(fp + varMap[i].offset, val.real)
         END;
         s.values[i] := val
      END;
      IF hisLast # NIL THEN
         hisLast.next := s;
         hisLast := s
      ELSE
         history := s;
         hisLast := s
      END
   END Snap;
   
   PROCEDURE ^ WriteVal (type: SHORTCHAR; IN val: Value; VAR f: TextMappers.Formatter);
      
   PROCEDURE End*;
      VAR tm: TextModels.Model;
            f: TextMappers.Formatter;
            i: INTEGER;
            s: Snapshot;
            tv: TextViews.View;
   BEGIN
      tm := TextModels.dir.New();
      f.ConnectTo(tm);
      FOR i := 0 TO LEN(varMap) - 1 DO
         f.WriteTab;
         f.WriteString(varMap[i].name)
      END;
      f.WriteLn;
      s := history;
      WHILE s # NIL DO
         FOR i := 0 TO LEN(varMap) - 1 DO
            f.WriteTab;
            WriteVal(varMap[i].type, s.values[i], f)
         END;
         f.WriteLn;
         s := s.next
      END;
      tv := TextViews.dir.New(tm);
      Views.OpenView(tv);
      varMap := NIL;
      valHeap := NIL;
      history := NIL; hisLast := NIL;
      Kernel.Collect
   END End;
   
   PROCEDURE WriteVal (type: SHORTCHAR; IN val: Value; VAR f: TextMappers.Formatter);
   BEGIN
      CASE type OF
      | boolean: f.WriteBool(val.boolean)
      | shortchar: f.WriteChar(val.shortchar)
      | char: f.WriteChar(val.char)
      | byte: f.WriteInt(val.byte)
      | shortint: f.WriteInt(val.shortint)
      | integer: f.WriteInt(val.integer)
      | shortreal: f.WriteReal(val.shortreal)
      | real: f.WriteReal(val.real)
      END
   END WriteVal;

END DevLocalWatch.


Последний раз редактировалось Илья Ермаков Четверг, 15 Март, 2007 13:05, всего редактировалось 1 раз.

Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Четверг, 15 Март, 2007 06:35 

Зарегистрирован: Вторник, 13 Март, 2007 06:15
Сообщения: 93
Небольшая проблемка возникла. При компиляции сообщается, что модуль Mem не найден.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Четверг, 15 Март, 2007 12:18 

Зарегистрирован: Суббота, 26 Ноябрь, 2005 18:38
Сообщения: 1857
Илья Ермаков писал(а):
Я тут пораскинул мозгами и состряпал тулзовину, которая поможет в этой ситуации.


Чем данный подход отличается от обычного логирования?


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Четверг, 15 Март, 2007 13:02 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
При "обычном логировании" нужно ручками что-то куда-то выводить. А тут - все верно, тоже логирование, но автоматическое. В итоге - лично я непредставляю - зачем еще нужно что-то по шагам гонять... Вся картинка перед глазами...


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Четверг, 15 Март, 2007 13:06 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
Исправил небольшую ошибку в коде выше.

Модуль Mem введен в Service Pack 4 - скачивайте в разделе Дистрибутивы.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Четверг, 15 Март, 2007 13:14 

Зарегистрирован: Суббота, 26 Ноябрь, 2005 10:37
Сообщения: 875
Откуда: Россия, Владивосток
Я думаю таким же манером несложно написать процедуру DEBUG (cond : BOOLEAN), которая бадет выводить окно наподобие трапа и ждать нажатия кнопки "продолжить"


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Четверг, 15 Март, 2007 13:39 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
В принципе, можно. Только окно потребуется использовать отдельное. Либо системный MessageBox, либо создавать средствами WinApi в отдельном потоке.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Четверг, 15 Март, 2007 13:51 
Администратор

Зарегистрирован: Вторник, 15 Ноябрь, 2005 01:14
Сообщения: 4695
Откуда: Россия, Орёл
Что-то вы, товарищи, начинаете отклоняться от линии партии... :):):)


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Четверг, 15 Март, 2007 13:56 

Зарегистрирован: Суббота, 26 Ноябрь, 2005 10:37
Сообщения: 875
Откуда: Россия, Владивосток
Илья Ермаков писал(а):
В принципе, можно. Только окно потребуется использовать отдельное. Либо системный MessageBox, либо создавать средствами WinApi в отдельном потоке.

На самом деле нужна штатная возможность создавать модальные диалоги. Например для настроек сонвертера при открытии документа.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Четверг, 15 Март, 2007 14:26 
Аватара пользователя

Зарегистрирован: Вторник, 19 Сентябрь, 2006 21:54
Сообщения: 2449
Откуда: Россия, Томск
Илья Ермаков писал(а):
В принципе, можно. Только окно потребуется использовать отдельное. Либо системный MessageBox, либо создавать средствами WinApi в отдельном потоке.

Тогда уж лучше сделать отдельное приложение - отладчик, а к основному присоединяться через TCP/IP. Там вызов DEBUG и ожидание продолжения - здесь дамп памяти и кнопка "Продолжить".
Ivor писал(а):
На самом деле нужна штатная возможность создавать модальные диалоги. Например для настроек конвертера при открытии документа.

А я считаю, что стандартные диалоги открытия, сохранения файла и печати нужно сделать немодальными. А еще - разрешить через стандартный Open-диалог открывать несколько документов сразу.
Для настроек конвертера модальность не нужна. Что мешает конвертеру отобразить окно настроек и отложить открытие файла до нажатия пользователем кнопки "Продолжить"?


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Четверг, 15 Март, 2007 14:48 

Зарегистрирован: Суббота, 26 Ноябрь, 2005 10:37
Сообщения: 875
Откуда: Россия, Владивосток
Александр Ильин писал(а):
А я считаю, что стандартные диалоги открытия, сохранения файла и печати нужно сделать немодальными. А еще - разрешить через стандартный Open-диалог открывать несколько документов сразу.

Да, это было бы неплохо.
Цитата:
Для настроек конвертера модальность не нужна. Что мешает конвертеру отобразить окно настроек и отложить открытие файла до нажатия пользователем кнопки "Продолжить"?

Устройство каркаса ББ :) Если посмотреть на HostDialog.GetIntSpec можно заметить, что в случае открфтия файла с маской *.* модальность диалога выбора конвертера эмулируется - иначе не получается. Насколько я помню, проблема в кнопке "отмена" - отменить то мы отменили, а процесс то уже идёт


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Четверг, 15 Март, 2007 14:51 

Зарегистрирован: Суббота, 26 Ноябрь, 2005 02:12
Сообщения: 473
Откуда: KZ
Зачем всё это нужно, когда есть HALT/ASSERT ?


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Четверг, 15 Март, 2007 15:02 
Администратор

Зарегистрирован: Вторник, 15 Ноябрь, 2005 01:14
Сообщения: 4695
Откуда: Россия, Орёл
Жестоко.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Четверг, 15 Март, 2007 15:06 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
Alexander Shiryaev писал(а):
Зачем всё это нужно, когда есть HALT/ASSERT ?

HALT/ASSERT прерывает выполнение. А тут, как видите, цель - проследить все витки цикла...

Зачем? Ну, у меня необходимости никогда не возникало. Но вот человеку для специфических задач потребовалось :-)


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Четверг, 15 Март, 2007 15:49 

Зарегистрирован: Суббота, 26 Ноябрь, 2005 18:38
Сообщения: 1857
Илья Ермаков писал(а):
При "обычном логировании" нужно ручками что-то куда-то выводить.


Тут тоже ручками...

Илья Ермаков писал(а):
В итоге - лично я непредставляю - зачем еще нужно что-то по шагам гонять... Вся картинка перед глазами...


Чтобы не искать нужное место в мегабайтах логов.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Четверг, 15 Март, 2007 16:44 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
Если цикл настолько сложен, что требует отладки, может, стоит инвариант записать и формально вывести? :-)


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Пятница, 16 Март, 2007 05:27 

Зарегистрирован: Вторник, 13 Март, 2007 06:15
Сообщения: 93
Илья Ермаков писал(а):
Исправил небольшую ошибку в коде выше.

Модуль Mem введен в Service Pack 4 - скачивайте в разделе Дистрибутивы.

Урррааааа! Зработалааааа!
Хорошая вещь! Значительно упрощает процесс поиска ошибок.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Пятница, 16 Март, 2007 10:30 
Аватара пользователя

Зарегистрирован: Пятница, 25 Ноябрь, 2005 12:02
Сообщения: 8500
Откуда: Троицк, Москва
Илья Ермаков писал(а):
Если цикл настолько сложен, что требует отладки, может, стоит инвариант записать и формально вывести? :-)


Однозначно стоит. Ясно, что что-то там недодумано...


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Пятница, 16 Март, 2007 10:46 

Зарегистрирован: Вторник, 13 Март, 2007 06:15
Сообщения: 93
info21 писал(а):
Илья Ермаков писал(а):
Если цикл настолько сложен, что требует отладки, может, стоит инвариант записать и формально вывести? :-)


Однозначно стоит. Ясно, что что-то там недодумано...

Если честно, с трудом себе представляю, о чём идёт речь...
Ух очень я привык к Смолтоку. А с КП слишком мало "общался", так что некоторые вещи в нём пока непонятны.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Пятница, 16 Март, 2007 11:18 
Аватара пользователя

Зарегистрирован: Вторник, 19 Сентябрь, 2006 21:54
Сообщения: 2449
Откуда: Россия, Томск
rv82 писал(а):
Урррааааа! Зработалааааа!
Хорошая вещь! Значительно упрощает процесс поиска ошибок.

Надо теперь попросить Илью написать модуль DevLocalGuard, который бы "значительно усложнил процесс создания ошибок".
:D :D


Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 27 ]  На страницу 1, 2  След.

Часовой пояс: UTC + 3 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 2


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Вся информация, размещаемая участниками на конференции (тексты сообщений, вложения и пр.) © 2005-2024, участники конференции «OberonCore», если специально не оговорено иное.
Администрация не несет ответственности за мнения, стиль и достоверность высказываний участников, равно как и за безопасность материалов, предоставляемых участниками во вложениях.
Без разрешения участников и ссылки на конференцию «OberonCore» любое воспроизведение и/или копирование высказываний полностью и/или по частям запрещено.
Powered by phpBB® Forum Software © phpBB Group
Русская поддержка phpBB