1 (изменено: Freeman, 29.12.2020 в 01:53)

Тема: Вывод отладочных сообщений в консоль вместо приложения BOARD

В примитивном случае всё очень просто:

program ConBoard;

uses
  KolibriOS, CRT;

const
  CON_WINDOW_CLOSED = $200;

var
  Ch: Byte;

begin
  InitConsole('CONBOARD');
  repeat
    if DebugRead(Ch) = 1 then
      Write(AnsiChar(Ch));
  until LongBool(ConsoleInterface.GetFlags and CON_WINDOW_CLOSED);
end.

Для чтения байта из системного буфера для отладочных сообщений используется функция DebugRead, если буфер не пуст и байт был прочитан, то функция возвращает значение 1.
Эта конструкция

LongBool(ConsoleInterface.GetFlags and CON_WINDOW_CLOSED

используется для определения, закрыто ли уже окно консоли, или ещё нет(обычно по нажатию кнопки с крестиком в правом углу окна).

Но нам бы хотелось также фильтровать сообщения по отправителю, записывать данные в файл, также неплохо было бы выводить время получения(или хотя бы вывода) сообщения.

Приложение BOARD фильтрует по Kernel(префикс 'K :') и User, но мы будем также учитывать и сообщения от приложения Launcher(префикс 'L: ').
Суть фильтрации в подсвечивании сообщений от разных отправителей разными цветами.

А вот исходный код более функционального приложения

program ConBoard;

uses
  KolibriOS, CRT;

const
  CommandLine = PPAnsiChar(28);
  CON_WINDOW_CLOSED = $200;

var
  LogFilePath: PAnsiChar = '/tmp0/1/BOARDLOG.TXT';

var
  Ch: Byte;
  Prefix: array[0..3] of AnsiChar = #0#0#0#0;
  PrefixIndex: LongWord = 0;
  IsStartLine: Boolean = True;
  FA: TFileAttributes;
  BytesWritten: LongWord;

begin
  if CommandLine^[0] <> #0 then
    LogFilePath := CommandLine^;
  InitConsole('CONBOARD');
  repeat
    if DebugRead(Ch) = 1 then
    begin
      if IsStartLine then
      begin
        Prefix[PrefixIndex] := AnsiChar(Ch);
        if PrefixIndex = High(Prefix) - 1 then
        begin
          // Kernel
          if (Prefix[0] = 'K') and (Prefix[1] = ' ') and (Prefix[2] = ':') then
            TextColor(Yellow)
          else
            // Launcher
            if (Prefix[0] = 'L') and (Prefix[1] = ':') and (Prefix[2] = ' ') then
              TextColor(White)
            else
              TextColor(LightGray);
          IsStartLine := False;
          PrefixIndex := Low(Prefix);
          with GetSystemTime do
            ConsoleInterface.PrintF('[%02x:%02x:%02x] ', Hours, Minutes, Seconds);
          Write(Prefix);
          Prefix := #0#0#0#0;
        end
        else
          Inc(PrefixIndex);
      end
      else
      begin
        Write(AnsiChar(Ch));
        if Ch = 10 then
        begin
          IsStartLine := True;
          TextColor(LightGray);
        end;
      end;
      if GetFileAttributes(LogFilePath, FA) = 5{not found} then
        CreateFile(LogFilePath);
      WriteFile(LogFilePath, Ch, SizeOf(Ch), FA.Size, BytesWritten);
    end;
  until LongBool(ConsoleInterface.GetFlags and CON_WINDOW_CLOSED);
end.

Вот так оно выглядитmisc.php?action=pun_attachment&amp;item=81&amp;download=0Также лог сохраняется как обычно в файл BOARDLOG.TXT на tmp-диске, который можно потом открыть, например, в Tinypad(как на скриншоте)

Недоработка при работе с доской отладки

Бывают ситуации, когда один процесс начинает писать часть сообщения на доску отладки, и тут же начинает писать второй процесс,
сообщения "перемешиваются" — ничего не поделаешь, похоже решения проблемы просто изначально не было предусмотрено, всё-таки система
многозадачная.
Пример:
User хочет написать "Hello world"
но успевает только "Hello"
а затем, например, Kernel что-то пишет, получается

"Hello" дальше на этой строке сообщение от Kernel
"world"

Прикладываю скомпилированное приложение CONBOARD.KEX

Post's attachments

Иконка вложений CONBOARD.KEX 1.25 Кб, 117 скачиваний с 2020-12-28 

Иконка вложений CONBOARD.PNG 73.95 Кб, 58 скачиваний с 2020-12-28 

2 (изменено: Leency, 05.01.2021 в 02:11)

Re: Вывод отладочных сообщений в консоль вместо приложения BOARD

Сам думал о такой программе и даже делал, где-то в теме про борду есть.

"Hello" дальше на этой строке сообщение от Kernel
"world"

И этот момент продумывал. Для этого дать возможность программам задавать метки.
APP1: Hello
APP2: world
И иметь возможность фильтровать по этим меткам. Для этого сверху например добавить тулбар с метками... и переключение по Ctrl+N. Надо подумать.

3

Re: Вывод отладочных сообщений в консоль вместо приложения BOARD

А как на доске отладки переходить на следующую строку? Делаю поддержку ошибок времени выполнения. Вывожу #10, не работает.

4

Re: Вывод отладочных сообщений в консоль вместо приложения BOARD

Freeman пишет:

А как на доске отладки переходить на следующую строку?

Ну по идее так: #13#10.

Freeman пишет:

Вывожу #10, не работает.

У меня тут(пример с выводом на доску отладки) вроде и так работало.

Freeman пишет:

Делаю поддержку ошибок времени выполнения.

Только зачем было в процедуре _RunError хардкодить ковертирование чисел, да к тому же ещё и дважды: десятичные и шестнадцатеричные по отдельности?
Ведь потом всё равно нужно будет добавлять такие функции как IntToStr, IntToHex...
На фоне этого экономия на байтах с IsConsole кажется ну совсем уж абсурдной lol
Здесь приводил уже пример более универсальной процедуры для конвертации.
В отличии от того, что есть в _RunError, эта процедура поддерживает 64-битные числа, система счисления от 2 до 36.
Но при этом объём кода почти такой же.

Также в этой процедуре используется, но не сохраняется регистр EBX, хотя всё равно это заканчивается "JMP _Halt0", но на всякий случай.

После замены

-function DebugRead(var Data: Byte): LongWord; stdcall;
+function DebugRead(var Data: KolibriChar): Boolean; stdcall

стало работать неправильно.
Раньше проверка была на равенство единице, а теперь на неравенство нулю.
Но причина похоже в ядре.
Вот из справки

----------------------------- Read byte ------------------------------
Takes away byte from the buffer.
Parameters:
  * eax = 63 - function number
  * ebx = 2 - subfunction number
Returned value:
  * eax = ebx = 0 - the buffer is empty
  * eax = byte, ebx = 1 - byte was successfully read

В нашем случае возвращается значение ebx = 2, похоже просто из-за того, что ядро оставило ebx без изменения, а до этого он был равен 2, потому что "ebx = 2 - subfunction number".
Ядро должно устанавливать ebx = 0, если буфер пуст, а этого не происходит.
Хм.. и почему же до сих пор никто не заметил баг, ведь приложением BOARD все разработчики(да и не только) наверняка пользовались и не раз?
А там просто проверка на равенство единице

Добавлено 2021-01-06 в 07:50

Вывести строку текста с символами перехода на новую строку на доску отладки можно вот так:

program DebugPrintLineTest;

procedure DebugPrintLine(Text: PAnsiChar);
asm
        PUSH   EBX
        PUSH   ESI
        MOV    ESI, EAX // Text
        MOV    EAX, 63
        MOV    EBX, 1
        XOR    ECX, ECX
@next:
        MOV    CL, [ESI]
        JECXZ  @done
        INT    64
        INC    ESI
        JMP    @next
@done:
        MOV    CL, 13
        INT    64
        MOV    CL, 10
        INT    64
        POP    ESI
        POP    EBX
end;

begin
  DebugPrintLine('        PUSH   EBX');
  DebugPrintLine('        PUSH   ESI');
  DebugPrintLine('        MOV    ESI, EAX // Text');  
  DebugPrintLine('        MOV    EAX, 63');
  DebugPrintLine('        MOV    EBX, 1');
  DebugPrintLine('        XOR    ECX, ECX');
  DebugPrintLine('@next:');
  DebugPrintLine('        MOV    CL, [ESI]');
  DebugPrintLine('        JECXZ  @done');
  DebugPrintLine('        INT    64');
  DebugPrintLine('        INC    ESI');
  DebugPrintLine('        JMP    @next');
  DebugPrintLine('@done:');
  DebugPrintLine('        MOV    CL, 13');
  DebugPrintLine('        INT    64');
  DebugPrintLine('        MOV    CL, 10');
  DebugPrintLine('        INT    64');
  DebugPrintLine('        POP    ESI');
  DebugPrintLine('        POP    EBX');
end.

5 (изменено: Freeman, 06.01.2021 в 06:45)

Re: Вывод отладочных сообщений в консоль вместо приложения BOARD

0CodErr пишет:

Только зачем было в процедуре _RunError хардкодить ковертирование чисел, да к тому же ещё и дважды: десятичные и шестнадцатеричные по отдельности?
Ведь потом всё равно нужно будет добавлять такие функции как IntToStr, IntToHex...

_RunError должна работать в условиях, когда всё упало и уже не до излишеств. С добавлением проверки ввода-вывода в стандартные процедуры она будет линковаться во все программы. Лишнего кода должен быть минимум. IntToStr и IntToHex сидят в SysUtils и работают со строками, поддержка которых уже немаленькая по коду, плюс тянет менеджер памяти. Другая весовая категория. А шестнадцатиричные числа будут шариться через публичную константу HexDigits.

0CodErr пишет:

После замены

-function DebugRead(var Data: Byte): LongWord; stdcall;
+function DebugRead(var Data: KolibriChar): Boolean; stdcall

стало работать неправильно.
Раньше проверка была на равенство единице, а теперь на неравенство нулю.

Добавил workaround.

С переводом строки был мой баг. Спать надо больше. Исправил. Простой вывод #10 работает.

6 (изменено: Freeman, 06.01.2021 в 13:52)

Re: Вывод отладочных сообщений в консоль вместо приложения BOARD

        cmp    al, 1 // kernel bug workaround
        sete   al

Тогда уж регистр BL надо сравнивать с единицей.

Лишнего кода должен быть минимум

Так и я  о том же smile
В System также присутствует процедура Str, которая представлена в том числе такими функциями _StrLong и _StrInt64.
Ну и, как ты сам написал, аналоги сидят в SysUtils — получается дублирование кода.
Дублирования не будет только если мы не будем использовать функцию Str — а она очень даже используется, ну, к примеру, вызов происходит в _WriteInt64, а Console в KolibriOS как раз не поддерживает вывод 64-битных чисел.

То есть, я хочу сказать, что конвертирование из-за _RunError  будет тянуться везде в любом случае.
Но иногда(а скорее всего часто) будет ещё и дублирование из-за процедуры Str и IntToStr.

Добавлено 2021-01-06 в 15:37

https://github.com/vapaamies/KolibriOS/ … 24975b3a96

           LongWord(Prefix) := 0; // Prefix := #0#0#0#0

Мне кажется, это не очень хорошая идея.
Да, сейчас этот хак сработает, но что если потом мы захотим поддерживать ещё какие-то более длинные префиксы?
Да и вообще, думаю, в приложениях желательно поменьше хаков — компилятор ведь и так оптимизирующий, зато код будет более понятным.

  if CmdLine^ <> #0 then
    LogFilePath := CmdLine;

Опять же предлагаю использовать стандартную ParamStr

7

Re: Вывод отладочных сообщений в консоль вместо приложения BOARD

0CodErr пишет:

Да, сейчас этот хак сработает, но что если потом мы захотим поддерживать ещё какие-то более длинные префиксы?

Компилятор заругается, и программист увидит комментарий.

0CodErr пишет:

Опять же предлагаю использовать стандартную ParamStr

Когда появится в System. Примеры не статичны, я обновляю их по мере добавления новых функций.

8

Re: Вывод отладочных сообщений в консоль вместо приложения BOARD

Насчёт текущего воркараунда

cmp    bl, 2 // kernel bug workaround
setne al

Я предлагал

Тогда уж регистр BL надо сравнивать с единицей.

Потому что, когда в ядре это пофиксят, то "с единицей" продолжит работать верно, а текущий вариант "сравнение с двойкой" работать перестанет.
Да и в документации сказано только про 1(это работает) и 0(не работает), но не про 2(и не должно работать).
И теперь возникнет необходимость объяснить другим, что сначала нужно обновиться до актуальной версии KolibriOS.

А лучше сразу это пофиксить, чего тянуть-то smile
Итак, вот это место в ядре http://websvn.kolibrios.org/filedetails … #line-4929
Продублирую тут код

.ret:
        pop     ebx eax
        ret
 
@@:
        mov     [esp+32], ecx
        mov     [esp+20], ecx
        jmp     .ret
 
.read:
        cmp     eax, 2
        jne     .ret
        test    ecx, ecx
        jz      @b
        add     esp, 8  ; returning data in ebx and eax, so no need to restore them
        mov     eax, msg_board_data+1
        mov     ebx, msg_board_data
        movzx   edx, byte [ebx]
        call    memmove
        dec     [msg_board_count]
        mov     [esp + 32], edx ;eax
        mov     [esp + 20], dword 1
        ret

В чём может быть проблема?
После установки возвращаемых значений

        mov     [esp+32], ecx
        mov     [esp+20], ecx

происходит прыжок на

.ret:
        pop     ebx eax
        ret

а "pop" двигает указатель стека

Как можно пофиксить? Например так:
вместо

@@:
        mov     [esp+32], ecx
        mov     [esp+20], ecx
        jmp     .ret

сделать так

@@:
        add     esp, 8  ; returning data in ebx and eax, so no need to restore them
        mov     [esp+32], ecx
        mov     [esp+20], ecx
        ret

Добавлено 2021-12-08 в 03:36

Я уже думаю, может нам самим это пофиксить в ядре?
А то у колибристов баги годами могут висеть.
И workaround переделать, как я предлагал

BL надо сравнивать с единицей

Добавлено 2021-12-10 в 22:15

Я заметил, что у этой темы очень много просмотров по сравнению с аналогичными темами.
Возможно, я не совсем правильно подобрал заголовок темы, и пользователи ожидали найти информацию непосредственно про отладку самого приложения с выводом сообщений не в BOARD(как зачастую это бывает), а в консоль.
По отладке, кстати, есть отдельная тема Отладка программ в KolibriOS.

Приведу тогда заодно пример и про вывод отладочных сообщений из самого приложения.
Предположим, у нас есть очень простое приложение(просто окно):

program Test;

uses
  KolibriOS;

begin
  while True do
    case WaitEvent of
      REDRAW_EVENT:
        begin
          BeginDraw;
          DrawWindow(75, 45, 300, 250, 'Test', $00FFFFFF,
            WS_SKINNED_SIZABLE + WS_CLIENT_COORDS + WS_CAPTION, CAPTION_MOVABLE);
          EndDraw;
        end;
      KEY_EVENT:
        GetKey;
      BUTTON_EVENT:
        Break;
    end;
end.

и мы хотим во время его работы выводить значения координат окна(Left и Top) в консоль.

Само положение окна можно узнать из структуры ThreadInfo, для этого необходимо вызвать функцию GetThreadInfo.

А для того, чтобы у нас была возможность использовать консоль, добавим в uses модуль CRT и инициализацию консоли в начале программы

InitConsole('Debug');

Теперь после этого мы можем использовать стандартную процедуру WriteLn для вывода сообщений в консоль.
Вот как теперь будет выглядеть программа:

program Test;

uses
  KolibriOS, CRT;

var
  ThreadInfo: TThreadInfo;  
  
begin
  InitConsole('Debug');
  while True do
    case WaitEvent of
      REDRAW_EVENT:
        begin
          BeginDraw;
          DrawWindow(75, 45, 300, 250, 'Test', $00FFFFFF,
            WS_SKINNED_SIZABLE + WS_CLIENT_COORDS + WS_CAPTION, CAPTION_MOVABLE);
          EndDraw;            
          GetThreadInfo($FFFFFFFF, ThreadInfo);
          with ThreadInfo do
            WriteLn('Left = ', Window.Left, '; Top = ', Window.Top);               
        end;
      KEY_EVENT:
        GetKey;
      BUTTON_EVENT:
        Break;
    end;
end.

Перемещайте окно, и в консоли будут выводиться его координатыmisc.php?action=pun_attachment&amp;item=104&amp;download=0Прилагаю в качестве примера это тестовое приложение DebugWithConsole.kex.

Post's attachments

Иконка вложений DebugWithConsole.kex 1.08 Кб, 65 скачиваний с 2021-12-10 

Иконка вложений DebugWithConsole.PNG 21.89 Кб, 33 скачиваний с 2021-12-10