Мой дядя самых честных правил программ исходники за так ...

25 авг. 2009 г.

Пролог: Изучение с нуля

Добрался таки до пролога. Чтобы не потерять, все источники собираю в одном месте.

Реализации:

Редакторы кода:

Источники первичной информации:

       Большая статья из которой можно почерпнуть практически всю доступную информацию о языке Prolog.

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

  • Prolog ISO/IEC 13211-1:1995 - стандарт языка,

       В 1995 году был принят стандарт языка, но к сожалению в свободном доступе его обнаружить не удалось, есть только черновик в PostScript формате.

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

Туториалы:

Книги:

Задачи:

Другое:

Очень краткое введение

Пролог – это декларативный логический язык программирования. Основными его конструкциями являются логические предложения (например: human('Socrat') :- true.), на основании которых пролог строит некоторые логические выводы. Предложения объединяются в правила с помощью операции and, которая обозначается знаком ','. Предложение при проверке может создавать побочные эффекты, что используется для ввода/вывода.

Краткий путеводитель по синтаксису

% Это строчный комментарий
/* А это блочный комментарий */

goal(X, Y) :-   % goal - это цель, для которой мы собираем правила,
                % имя цели всегда начинается с маленькой буквы.
                % результат у цели - это всегда true или false.
                % X, Y - параметры цели.

    X > Y       % Первое утверждение, которое утверждает что X больше Y.

    ,           % Разделитель между утверждениями, означает логическое И.

    X + 1 == Y  % Второе утверждение, которое может быть верным,
                % только если Y = X + 1.

    .           % Точка означает конец описания цели.

/*
 * Суммирую:
 * Имя цели всегда с маленькой буквы,
 * Параметры цели, и все другие переменные с большой буквы,
 * Утверждения разделяются запятой,
 * Описание цели всегда заканчивается точкой.
 */

prolog('logic language').
% Так записываются некоторые факты,
% и по сути являются сокращениями для цели,
% которая всегда возвращает true.
% в данном случае эквивалент выглядел бы так:
% prolog('logic language') :- true.

% Цели могут быть рекурсивными.
% Посчитаем сумму от 0 до X.
sum(0, 0).   % факт используется для остановки рекурсии.
sum(X, Result) :-
    Y is X - 1,             % is - это по сути присвоение значения.
    sum(Y, SubResult),      % Рекурсивный вызов.
    Result is X + SubResult.


% Цель, которая будет использоваться при запуске программы,
% имя 'main' выбрано мною произвольно.
main :-
    % запрашиваем значение факта
    prolog(Which),
    % выводим значение на экран
    format('prolog is a ~a.', [Which]),

    % newline
    nl,

    % задаём значение X
    X is 100,
    % считаем сумму от 0 до 100
    sum(X, Result),
    % выводим результат с помощью printf-like функции
    format('sum(0, ~a) = ~a.', [X, Result]),

    % newline.
    nl.

/*
 * Вывод программы при запуске:
 * prolog is a logic language.
 * sum(0, 100) = 5050.
 */

Типы данных

Атом: атом или 'атом', начинается с маленькой буквы или обрамляется одинарными кавычками.

Строка: "Строка", обрамляется двойными кавычками, по факту представляет список из чисел – кодов ASCII символов строки.

Число: 10, целые (-1,0,1), плавающая запятая (0.123, 123.456).

Список: [0, 1, 2], набор элементов перечисленных через запятую и обрамлённых квадратными скобками. Элементы могут быть разнородными (разных типов). Списки можно деструктурировать через синтаксис [Head | Tail].

 

Первые результаты

Теперь, с помощью новоприобретённых знаний, можно написать несколько известных примеров:

HelloWorld.pro:

main :- writeln('Hello, Prolog World!'). 

Factorial.pro:

% factorial implementation
factorial(1, 1).
factorial(X, Result) :-
    X > 0,
    X1 is X - 1,
    factorial(X1, Result1),
    Result is X * Result1.
 
main :-
    factorial(7, X),
    writeln(X). 

Fibonachi.pro:

% Fibbonachi sequence implementation
fib(0, 1).
fib(1, 1).
fib(X, Result) :-
    X1 is X - 1,
    X2 is X - 2,
    fib(X1, Result1),
    fib(X2, Result2),
    Result is Result1 + Result2.
 
main :-
    fib(7, X),
    writeln(X). 

QuickSort.pro:

qsort([], []).
qsort([X], [X]).
qsort([Head | Tail], Result) :-
    partition(Head, Tail, Left, Right),
    qsort(Left, LeftSorted),
    qsort(Right, RightSorted),
    append(LeftSorted, [Head | RightSorted], Result).
 
partition(_, [], [], []).
partition(Pivot, [Head | Tail], [Head | Left], Right) :-
    Head =< Pivot,
    partition(Pivot, Tail, Left, Right).
partition(Pivot, [Head | Tail], Left, [Head | Right]) :-
    Head > Pivot,
    partition(Pivot, Tail, Left, Right).
 
append([], Right, Right).
append([Head | Left], Right, [Head | Result]) :-
    append(Left, Right, Result).
 
main :-
    qsort([1, 5, 4, 6, 3, 2], X),
    writeln(X). 

 

Какие ещё есть логические языки программирования?

Путём нехитрых поисков в источниках близким к прологу собрал такой список:

        Расширение языка Haskell для реализации логического программирования.

       Mercury is a new logic/functional programming language, which combines the clarity and expressiveness of declarative programming with advanced static analysis and error detection features.

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

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

14 авг. 2009 г.

Расширение отладчика SOS для Visual Studio 2008

Много раз во время отладки приложений, мне хотелось получать гораздо больше информации от отладчика, о том как расположены мои объекты в памяти, сколько занимают места, как давно созданы и сколько их существует. Обычно, такое нужно, когда хочется составить более полное представление о том, как работает программа.

Так вот, оказывается существует специальное расширение для отладчиков Visual Studio и WinDbg, которое поставляется вместе с .Net Framework. Это расширение и называется SOS и находится в %SystemRoot%\Microsoft.Net\v2.0.50727\sos.dll для CLR 2.0.

Примечание: SOS расшифровывается как Son Of Strike.

Продемонстрирую работу с этим расширением и заодно покажу в действии набор команд, которые в нём существуют.

Итак, первое. Реализуем небольшой пример:

using System;
 
namespace Home.Andir.Examples
{
    class A
    {
        public uint Value { get; set; }
        public override string ToString()
        {
            return string.Format("A {{ Value = 0x{0:x} }}", Value);
        }
    }
 
    class Program
    {
        static void Main(string[] args)
        {
            TestSOSMethod(
                "Hello debugging world!");
        }
 
        private static void TestSOSMethod(string ignoreThis)
        {
            var a = new A() { Value = 0xDEADBEEF };
            var b = new A() { Value = 0xBEEFDEAD };
 
            Console.WriteLine("a = {0}, b = {1}", a, b);
 
            Console.ReadKey(); // breakpoint is here
        }
    }
}

В примере, вызывается метод с одним параметром и создаются два объекта типа Home.Andir.Examples.A с магическими числами, которые потом будет хорошо видно в отладчике.

Теперь, чтобы можно было воспользоваться возможностями SOS, нужно включить в свойствах проекта неуправляемую отладку.

Скриншот: VS2008 включение отладки в неуправляемом коде

Теперь, устанавливаем точку остановки (Breakpoint) в статическом методе TestSOSMethod на строке Console.ReadKey(). И запускаем в режиме отладки (F5).

В консоли будет выведено:

a = A { Value = 0xdeadbeef }, b = A { Value = 0xbeefdead }

Впрочем, как и ожидалось. Теперь загрузим sos.dll, для этого нужно открыть окно Immediate (Debug –> Windows –> Immediate) и написать первой командой .load sos.dll:

Скриншот: окно Immediate и загрузка sos.dll 

В окне выведено:

.load sos.dll
extension C:\Windows\Microsoft.NET\Framework\v2.0.50727\sos.dll loaded

Отлично! SOS успешно загрузился, и теперь можно поэкспериментировать с его командами.

Начнём, как обычно с help.

!help
-------------------------------------------------------------------------------
SOS is a debugger extension DLL designed to aid in the debugging of managed
programs. Functions are listed by category, then roughly in order of
importance. Shortcut names for popular functions are listed in parenthesis.
Type "!help <functionname>" for detailed info on that function. 

Object Inspection                  Examining code and stacks
-----------------------------      -----------------------------
DumpObj (do)                       Threads
DumpArray (da)                     CLRStack
DumpStackObjects (dso)             IP2MD
DumpHeap                           U
DumpVC                             DumpStack
GCRoot                             EEStack
ObjSize                            GCInfo
FinalizeQueue                      EHInfo
PrintException (pe)                COMState
TraverseHeap                       BPMD 

Examining CLR data structures      Diagnostic Utilities
-----------------------------      -----------------------------
DumpDomain                         VerifyHeap
EEHeap                             DumpLog
Name2EE                            FindAppDomain
SyncBlk                            SaveModule
DumpMT                             GCHandles
DumpClass                          GCHandleLeaks
DumpMD                             VMMap
Token2EE                           VMStat
EEVersion                          ProcInfo 
DumpModule                         StopOnException (soe)
ThreadPool                         MinidumpMode 
DumpAssembly                       
DumpMethodSig                      Other
DumpRuntimeTypes                   -----------------------------
DumpSig                            FAQ
RCWCleanupList
DumpIL

Как видим, функций довольно много, и названия у них вполне себе говорящие. Подробнее о каждой функции можно узнать, если набрать !help <имя команды>.

Примечание: В дальнейшем, вывод команды !help <имя команды> будет показываться в урезанном виде, чтобы исключить неважные в данном случае детали. Для подробного описания команды используйте MSDN или самостоятельно вызывайте эту команду.

Продолжим с нашим примером. Изучим команду clrstack, которая как очевидно из названия может инспектировать содержимое стека текущего потока.

!help clrstack
-------------------------------------------------------------------------------
!CLRStack [-a] [-l] [-p]

CLRStack attempts to provide a true stack trace for managed code only. It is
handy for clean, simple traces when debugging straightforward managed 
programs. The -p parameter will show arguments to the managed function. The 
-l parameter can be used to show information on local variables in a frame.
SOS can't retrieve local names at this time, so the output for locals is in
the format <local address> = <value>. The -a (all) parameter is a short-cut
for -l and -p combined.

Сейчас мы находимся внутри метода TestSOSMethod и можно посмотреть, что находится в данный момент в стеке текущего потока.

!clrstack -a
OS Thread Id: 0x1984 (6532)
ESP       EIP     
0012ec2c 013b0178 Home.Andir.Examples.Program.TestSOSMethod(System.String)
    PARAMETERS:
        ignoreThis = 0x01a09290
    LOCALS:
        0x0012ec4c = 0x01a09304
        0x0012ec48 = 0x01a09310
        0x0012ec44 = 0x01a09304
        0x0012ec40 = 0x01a09310

0012ec94 013b00a0 Home.Andir.Examples.Program.Main(System.String[])
    PARAMETERS:
        args = 0x01a09280

0012eef4 67971b4c [GCFrame: 0012eef4] 

Видим, что в стеке находится два метода и у текущего метода, есть один параметр с именем ignoreThis и четыре записи в локальных переменных с неизвестными именами (при этом объекта всего два, что видно по повторяющимся адресам). Теперь посмотрим значения этих параметров и переменных. Для начала, рассмотрим параметр ignoreThis.

Чтобы посмотреть объекты в памяти существует команда dumpobj (сокращение do) которая принимает в параметре адрес объекта.

!help dumpobj
-------------------------------------------------------------------------------
!DumpObj [-nofields] <object address>

This command allows you to examine the fields of an object, as well as learn 
important properties of the object such as the EEClass, the MethodTable, and 
the size.

The arguments in detail:
-nofields:     do not print fields of the object, useful for objects like 
                  String

Итак, вызываем dumpobj для объект ignoreThis (используем параметр –nofields чтобы не выводить ненужные нам поля объекта string):

!dumpobj -nofields 0x01a09290
Name: System.String
MethodTable: 670d88a4
EEClass: 66e9a498
Size: 62(0x3e) bytes
 (C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String: Hello debugging world!

Итак, в параметре находится та самая строка, ради которой этот параметр и затевался :-) Занимает она 62 байта, что с учётом длины строки в 22 символа, и по 2 байта на символ в кодировке Unicode – получаем 18 байт оверхеда на хранение такой строки в типе System.String.

Продолжаем исследовать объекты в стеке, теперь посмотрим на локальные переменные (сразу все по очереди):

!do 0x01a09304
Name: Home.Andir.Examples.A
MethodTable: 0024338c
EEClass: 00241830
Size: 12(0xc) bytes
 (D:\development\projects\andir-notes\examples\SOSDebugExtension\SOSDebugExtension\bin\Debug\SOSDebugExtension.exe)
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
670b9cc8  4000001        4        System.UInt32  1 instance 3735928559 <Value>k__BackingField

!do 0x01a09310
Name: Home.Andir.Examples.A
MethodTable: 0024338c
EEClass: 00241830
Size: 12(0xc) bytes
 (D:\development\projects\andir-notes\examples\SOSDebugExtension\SOSDebugExtension\bin\Debug\SOSDebugExtension.exe)
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
670b9cc8  4000001        4        System.UInt32  1 instance 3203391149 <Value>k__BackingField

Видим два объекта в памяти типа Home.Andir.Examples.A, которые занимают по 12 байт и содержат ровно одно поле со странным именем <Value>k__BackingField и типом System.UInt32, как очевидно, которое является автоматически сгененированным полем для автосвойства Value.

Каждый объект занимает 12 байт, а так как внутри находится только 1 поле длиной 4 байта, то получаем что оверхед равняется 8 байтам.

Ещё одна полезная команда, которая позволит убедиться, что у нас в памяти находится ровно два объекта. Это dumpheap, у которой есть параметр –type для фильтрации вывода по имени типа (всё же в хипе находится довольно много служебной информации).

!dumpheap -type Home.Andir.Examples.A
 Address       MT     Size
01a09304 0024338c       12     
01a09310 0024338c       12     
total 2 objects
Statistics:
      MT    Count    TotalSize Class Name
0024338c        2           24 Home.Andir.Examples.A
Total 2 objects

О том, сколько всякого мусора находится в GC Heap можно узнать с помощью этой же команды и параметра –stat.

!dumpheap -stat
total 7737 objects
Statistics:
      MT    Count    TotalSize Class Name
670e18f0        1           12 System.Collections.Generic.GenericEqualityComparer`1[[System.String, mscorlib]]
670e0ba0        1           12 System.Security.Permissions.ReflectionPermission
670df8a8        1           12 System.Resources.FastResourceComparer
670dcd44        1           12 System.__Filters
....

Теперь понятно, зачем нужна фильтрация по имени типа. На самом деле, команда dumpheap – очень мощная команда и позволяет исследовать многие параметры GC Heap (см. !help dumpheap, где есть в том числе и примеры её полноценного использования).

Далее

Для подробного изучения порекомендую посетить следующие ресурсы:

Также, рекомендую прочитать Success Story об удачном использовании SoS для обнаружения проблем в приложении ASP.Net: Dan McKinley: App Server Autopsy.

11 авг. 2009 г.

Visual Studio 2010 – Historical Debugger

Случайно посмотрел PodCast про новую фенечку в Visual Studio 2010. Это так называемый Historical Debugger ("Исторический отладчик").

На мой первый взгляд – весьма примечательная и полезная вещь.

MSDN 9 Channel: An Introduction to the Historical Debugger

Get Microsoft Silverlight

Замечание: Ну и быстро говорит этот товарищ!

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

А для продвинутых пользователей (тестеров и самих разработчиков) появится возможность приложить "Лог исполнения" (Execution Log) к описанию ошибки в багтрекере. Затем разработчик ответственный за исправление ошибки сможет использовать этот лог для быстрой диагностики ошибки и её исправления.

Настройки в VS:

Скриншот: VS2010 Options - Historical Debugging

Новая вкладка:

Скриншот: Вкладка "Debug History" в действии 

Подробнее о новом отладчике можно прочитать: