OberonCore https://forum.oberoncore.ru/ |
|
Пошаговый отладчик? - Сделаем пошаговый просмотровщик... https://forum.oberoncore.ru/viewtopic.php?f=1&t=404 |
Страница 1 из 2 |
Автор: | Илья Ермаков [ Четверг, 15 Март, 2007 00:22 ] |
Заголовок сообщения: | Пошаговый отладчик? - Сделаем пошаговый просмотровщик... |
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. |
Автор: | rv82 [ Четверг, 15 Март, 2007 06:35 ] |
Заголовок сообщения: | |
Небольшая проблемка возникла. При компиляции сообщается, что модуль Mem не найден. |
Автор: | Vlad [ Четверг, 15 Март, 2007 12:18 ] |
Заголовок сообщения: | Re: Пошаговый отладчик? - Сделаем пошаговый просмотровщик... |
Илья Ермаков писал(а): Я тут пораскинул мозгами и состряпал тулзовину, которая поможет в этой ситуации.
Чем данный подход отличается от обычного логирования? |
Автор: | Илья Ермаков [ Четверг, 15 Март, 2007 13:02 ] |
Заголовок сообщения: | |
При "обычном логировании" нужно ручками что-то куда-то выводить. А тут - все верно, тоже логирование, но автоматическое. В итоге - лично я непредставляю - зачем еще нужно что-то по шагам гонять... Вся картинка перед глазами... |
Автор: | Илья Ермаков [ Четверг, 15 Март, 2007 13:06 ] |
Заголовок сообщения: | |
Исправил небольшую ошибку в коде выше. Модуль Mem введен в Service Pack 4 - скачивайте в разделе Дистрибутивы. |
Автор: | Иван Горячев [ Четверг, 15 Март, 2007 13:14 ] |
Заголовок сообщения: | |
Я думаю таким же манером несложно написать процедуру DEBUG (cond : BOOLEAN), которая бадет выводить окно наподобие трапа и ждать нажатия кнопки "продолжить" |
Автор: | Илья Ермаков [ Четверг, 15 Март, 2007 13:39 ] |
Заголовок сообщения: | |
В принципе, можно. Только окно потребуется использовать отдельное. Либо системный MessageBox, либо создавать средствами WinApi в отдельном потоке. |
Автор: | Борис Рюмшин [ Четверг, 15 Март, 2007 13:51 ] |
Заголовок сообщения: | |
Что-то вы, товарищи, начинаете отклоняться от линии партии... :):) |
Автор: | Иван Горячев [ Четверг, 15 Март, 2007 13:56 ] |
Заголовок сообщения: | |
Илья Ермаков писал(а): В принципе, можно. Только окно потребуется использовать отдельное. Либо системный MessageBox, либо создавать средствами WinApi в отдельном потоке.
На самом деле нужна штатная возможность создавать модальные диалоги. Например для настроек сонвертера при открытии документа. |
Автор: | Александр Ильин [ Четверг, 15 Март, 2007 14:26 ] |
Заголовок сообщения: | |
Илья Ермаков писал(а): В принципе, можно. Только окно потребуется использовать отдельное. Либо системный MessageBox, либо создавать средствами WinApi в отдельном потоке. Тогда уж лучше сделать отдельное приложение - отладчик, а к основному присоединяться через TCP/IP. Там вызов DEBUG и ожидание продолжения - здесь дамп памяти и кнопка "Продолжить". Ivor писал(а): На самом деле нужна штатная возможность создавать модальные диалоги. Например для настроек конвертера при открытии документа.
А я считаю, что стандартные диалоги открытия, сохранения файла и печати нужно сделать немодальными. А еще - разрешить через стандартный Open-диалог открывать несколько документов сразу. Для настроек конвертера модальность не нужна. Что мешает конвертеру отобразить окно настроек и отложить открытие файла до нажатия пользователем кнопки "Продолжить"? |
Автор: | Иван Горячев [ Четверг, 15 Март, 2007 14:48 ] |
Заголовок сообщения: | |
Александр Ильин писал(а): А я считаю, что стандартные диалоги открытия, сохранения файла и печати нужно сделать немодальными. А еще - разрешить через стандартный Open-диалог открывать несколько документов сразу. Да, это было бы неплохо. Цитата: Для настроек конвертера модальность не нужна. Что мешает конвертеру отобразить окно настроек и отложить открытие файла до нажатия пользователем кнопки "Продолжить"?
Устройство каркаса ББ Если посмотреть на HostDialog.GetIntSpec можно заметить, что в случае открфтия файла с маской *.* модальность диалога выбора конвертера эмулируется - иначе не получается. Насколько я помню, проблема в кнопке "отмена" - отменить то мы отменили, а процесс то уже идёт |
Автор: | Alexander Shiryaev [ Четверг, 15 Март, 2007 14:51 ] |
Заголовок сообщения: | |
Зачем всё это нужно, когда есть HALT/ASSERT ? |
Автор: | Борис Рюмшин [ Четверг, 15 Март, 2007 15:02 ] |
Заголовок сообщения: | |
Жестоко. |
Автор: | Илья Ермаков [ Четверг, 15 Март, 2007 15:06 ] |
Заголовок сообщения: | |
Alexander Shiryaev писал(а): Зачем всё это нужно, когда есть HALT/ASSERT ?
HALT/ASSERT прерывает выполнение. А тут, как видите, цель - проследить все витки цикла... Зачем? Ну, у меня необходимости никогда не возникало. Но вот человеку для специфических задач потребовалось |
Автор: | Vlad [ Четверг, 15 Март, 2007 15:49 ] |
Заголовок сообщения: | |
Илья Ермаков писал(а): При "обычном логировании" нужно ручками что-то куда-то выводить. Тут тоже ручками... Илья Ермаков писал(а): В итоге - лично я непредставляю - зачем еще нужно что-то по шагам гонять... Вся картинка перед глазами...
Чтобы не искать нужное место в мегабайтах логов. |
Автор: | Илья Ермаков [ Четверг, 15 Март, 2007 16:44 ] |
Заголовок сообщения: | |
Если цикл настолько сложен, что требует отладки, может, стоит инвариант записать и формально вывести? |
Автор: | rv82 [ Пятница, 16 Март, 2007 05:27 ] |
Заголовок сообщения: | |
Илья Ермаков писал(а): Исправил небольшую ошибку в коде выше.
Модуль Mem введен в Service Pack 4 - скачивайте в разделе Дистрибутивы. Урррааааа! Зработалааааа! Хорошая вещь! Значительно упрощает процесс поиска ошибок. |
Автор: | Info21 [ Пятница, 16 Март, 2007 10:30 ] |
Заголовок сообщения: | |
Илья Ермаков писал(а): Если цикл настолько сложен, что требует отладки, может, стоит инвариант записать и формально вывести?
Однозначно стоит. Ясно, что что-то там недодумано... |
Автор: | rv82 [ Пятница, 16 Март, 2007 10:46 ] |
Заголовок сообщения: | |
info21 писал(а): Илья Ермаков писал(а): Если цикл настолько сложен, что требует отладки, может, стоит инвариант записать и формально вывести? Однозначно стоит. Ясно, что что-то там недодумано... Если честно, с трудом себе представляю, о чём идёт речь... Ух очень я привык к Смолтоку. А с КП слишком мало "общался", так что некоторые вещи в нём пока непонятны. |
Автор: | Александр Ильин [ Пятница, 16 Март, 2007 11:18 ] |
Заголовок сообщения: | |
rv82 писал(а): Урррааааа! Зработалааааа!
Хорошая вещь! Значительно упрощает процесс поиска ошибок. Надо теперь попросить Илью написать модуль DevLocalGuard, который бы "значительно усложнил процесс создания ошибок". |
Страница 1 из 2 | Часовой пояс: UTC + 3 часа |
Powered by phpBB® Forum Software © phpBB Group https://www.phpbb.com/ |