SBCLT developer's notes

Статьи по тегам   Список тегов

Оглавление

GENERAL
DETAILS
SBCL-INFORMATION
STEPPER

Детальное оглавление

GENERAL
интернет
порядок-сборки-SBCL
как-ориентироваться-в-сборке-SBCL
в-трекер
трассируем-функции-работы-с-типами
просмотр-FLOW-графа-компилятора
обзор-отладки-SBCL
русские-синонимы-как-боросться-со-STRING=
терминология-CMU-CL
PROJECT-GOALS
TYPEP-NEVER-CHANGES-P
расширение-системы-типов
DETAILS
SBCL-INFORMATION
как-добавить-новый-вид-определения-в-SBCL
шпаргалка-по-типам-в-SBCL
краткое-описание-системы-типов-SBCL
DEFINE-TYPE-METHOD
вывод-типов-в-SBCL
структуры-данных-компилятора-SBCL
пытаемся-добавить-тип-IMMUTABLE
сборка-SBCL-быстрее
описание-процесса-сборки-SBCL
пытаемся-отключить-CONSTRAINTS
больше-шагов-в-пошаговой-отладке
пути-к-формам
ставить-брекпойнт-мышью
STEPPER
Брекпойнты
Степпер

Все статьи

GENERAL Ссылка: SBCLT-DOCS::GENERAL


интернет Ссылка: SBCLT-DOCS::интернет

Настроить EMACS


порядок-сборки-SBCL Ссылка: SBCLT-DOCS::порядок-сборки-SBCL

По материалам "Sanely Bootstrappable Common Lisp".

host-1

Создаёт кросс-компилятор (xc) - приложение на CL внутри компилятора, а также приложение sb!fasl:genesis. Также порождаются Си файлы для начального образа на целевой системе - src/runtime/genesis.h

target-1

Создаёт исполняемый файл src/runtime/sbcl (без образа, только часть на Си и ассемблере). Выполняет grovel, собирающий из Си с помощью экспериментов программу, описывающую конфигурацию target-а (output/stuff-groveled-from-headers.lisp)

host-2

sb!xc:compile-file компилирует исходник SBCL снова и создаёт *.lisp-obj - то ли объектные, то ли fasl-файлы.

genesis-2

В том же образе SBCL имитирует загрузку *.lisp-obj и сохранение образа в память.

cold-init

Запуск образа, составленного из результатов target-1 и genesis-2

target-2

В новом образе переименовываем пакеты, убирая восклицательные знаки, загружается PCL. Затем сохраняется output/sbcl.core и на этом процесс завершён.

Порядок выполнения задаётся в make.sh, порядок обработки лисповых файлов при сборке кросс-компилятора и при кросс-компиляции - в build-order.lisp-expr, а определения пакетов для целевого образа - в package-data-list.lisp-expr . В файле порядка сборки есть ключи, говорящие о том, что некоторые файлы на некоторых этапах нужно пропустить, например, :not-host, :not-target - см. .исбцл *expected-stem-flags*

Теги: SBCL-INFO

как-ориентироваться-в-сборке-SBCL Ссылка: SBCLT-DOCS::как-ориентироваться-в-сборке-SBCL

Пакеты sb!xx - для кросс-компилятора, в конечном образе они имеют вид sb-xx.

Пакет sb!xc содержит части common-lisp, к-рые должны для кросс-компилятора отличаться от обычных. Существует только в кросс-компиляторе и не попадате в конечный образ.

#!+ делает условную компиляцию по $2, заданных для цели, а не для 'хоста' кросс-компиляции.

фича :sb-xc-host, :sb-xc в хостовом лиспе говорят о том, какая фаза сборки сейчас идёт (описаны в base-target-features.lisp-expr).

def!type - версия deftype, к-рая выполняется при построении кросс-компилятора и определяет тип в хосте, а также обезпечивает создание такого типа и в целевом лиспе. То же - def!struct и def!type

См. также Теги: SBCL-INFO

в-трекер Ссылка: SBCLT-DOCS::в-трекер

Есть политика компиляции check-constant-modification .

Visual C++2013 redistributable

Проанализировать вообще применения %instance-ref. В Make-load-form выглядит уместным, а вот, к примеру, в circle-subst - не очень.

sb-kernel::%instance-cas - это дыра в нашем механизме - нужно, видимо, проверять мутабельность перед использованием.

Что есть fop? В них мы ставим %set-instance-ref

Сделать для раскладки окон 'пересчитать текущую раскладку окон так, чтобы они ложились поверх текущего положения консоли'

backend-type-predicates может быть интересным.

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

А также: Изучить недокументированную декларацию '.исбцлт explicit-check'.

А также: (defoptimizer (array-element-type derive-type) ((array)) кодирует данные типами, и получается (cons integer), к-рый у нас срезается до cons. Поэтому падает тест coerce derive-type. Вероятно, мы сможем это исправить не раньше, чем добавим иммутабельность.

А также: не работает pgup, pgdn на цифровой клавиатуре в линуксе.

Документировать explicit-check, см. .исбцл { explicit-check}

typexpand, typexpand-all - документировать.

Нужно учесть, что (and immutable integer) == integer - возможно, нужно написать соотв. методы. Также это должно поддерживаться в supertype-such-that-...

Принцип лесов: когда правим компилятор, критично, что есть интерпретатор, позволяющий исправить сломанные функции. Иначе каждая ошибка при правке компилятора требовала бы отката и пересборки. Это - некий общий принцип.


трассируем-функции-работы-с-типами Ссылка: SBCLT-DOCS::трассируем-функции-работы-с-типами

Есть несколько функций, к-рые могут вызываться клиентами, и которые сами могут друг друга вызывать. Хотим узнать, как они вызываются клиентами, но не хотим знать, как они друг друга вызывают. Один из вариантов приблизитьва - трассировать все эти функции, но только при условии, что на стеке нет трассируемой функции. Делается так:

 (defun trace-depth () (if (boundp 'sb-debug::*traced-entries*)
                       (length sb-debug::*traced-entries*)
                       0))

 (trace sb-kernel::csubtypep :condition (= (trace-depth) 0))
; и такая же трассировка для sb-kernel::types-equal-or-intersect, 
; для sb-kernel::type-intersection, sb-kernel::type-intersection2 и, возможно, др. 

sb-c::ir1-phases == 
 (trace sb-c::eliminate-dead-code :condition (progn (sb-c::describe-component (sb-debug:arg 0) *standard-output*)))


sb-c::my-ir1-optimize ==

 (defun my-ir1-optimize (fn cast) (let ((c cast)) (multiple-value-prog1 (funcall fn cast) (sb-c::describe-object cast *trace-output*))))

 (decorate-function:decorate-function 'sb-c::ir1-optimize-cast 'my-ir1-optimize)

SB-C:DEFTRANSFORM ==
 (defun my-ir1-transform (fn node transform) ; ir1-transform
             (let ((result (funcall fn node transform)))

               (print (if (eq result t) "Transform failed" `("Node after transform: " ,node)) *trace-output*)
               result))

 (decorate-function:def-function-decoration ir1-transform 'my-ir1-transform)


просмотр-FLOW-графа-компилятора Ссылка: SBCLT-DOCS::просмотр-FLOW-графа-компилятора

Из старых находок: Выяснили, что полезно трассировать (trace sb-c::ir1-phases :break t), чтобы получить вид flow графа до начала его обработки, и при этом sb-c::describe-component помогает более-менее внятно посмотреть на его содержимое.

Для проверки constraint propagation нужно ещё что-то протрассировать и посмотреть. Возможно, надо править describe, чтобы увидет больше сведений о типах.

В инспекторе происходит ошибка печати, т.к. (я предполагаю) инспектор запускается в отдельном потоке, а нужна переменная SB-C::*COMPILER-IR-OBJ-MAP*, связанная только в потоке компилятора. Вроде эту ошибку поправил.


обзор-отладки-SBCL Ссылка: SBCLT-DOCS::обзор-отладки-SBCL

Полезны следующие концепции:

 (compile-file ... :trace-file t)

См. также

русские-синонимы-как-боросться-со-STRING= Ссылка: SBCLT-DOCS::русские-синонимы-как-боросться-со-STRING=

Идея для синонимов - печатать имена символов так, чтобы в них был некий запретный character. Тогда при попытке intern синтетического имени возникнет ошибка. Но этот character должен выбрасываться при любой печати, если только не установлена спец. переменная. Ну или вообще чтобы с такими печатанными именами можно было оперировать только в особых условиях, исключающих возможность превращения в символы


терминология-CMU-CL Ссылка: SBCLT-DOCS::терминология-CMU-CL

Design of CMU CL, 17.1 - VMR Control representation, а также glossary

Теги: CONCEPT

PROJECT-GOALS Ссылка: SBCLT-DOCS::PROJECT-GOALS

Теги: CONCEPT

TYPEP-NEVER-CHANGES-P Ссылка: SBCLT-DOCS::TYPEP-NEVER-CHANGES-P

Исправление

SB-C::TYPE-NEEDS-CONSERVATION-P - уже похоже на то, что надо, но не используется должным образом.

typep-never-changes-p для типа означает, что если некий объект в данный момент времени является членом этого типа, то он является членом этого типа на протяжении всей своей жизни. Например, таков тип cons. Но типы (cons integer) и (satisfies ufo) не таковы. Причин для нестабильности членства две:

С мутабельностью всё понятно, а с переопределением труднее:

Ф-я sb-c::supertype-such-that-typep-never-changes (смысл следует из названия) для (cons integer) вернёт cons. При передаче информации о типе места в процессе вывода типов, должен быть передан не сам тип, а только возврат этой функции.

ОГО! sb-c::lvar-conservative-type похоже на то, что мы хотим!!!

Теги: CONCEPTTYPE-SYSTEM-EXTENSION

расширение-системы-типов Ссылка: SBCLT-DOCS::расширение-системы-типов

Нам нужны дополнительные атрибуты типов для выражения иммутабельности, неизменности типа во времени (и, возможно, иные). Как этого добиться?

Теги: CONCEPTTYPE-SYSTEM-EXTENSION

DETAILS Ссылка: SBCLT-DOCS::DETAILS


SBCL-INFORMATION Ссылка: SBCLT-DOCS::SBCL-INFORMATION


как-добавить-новый-вид-определения-в-SBCL Ссылка: SBCLT-DOCS::как-добавить-новый-вид-определения-в-SBCL

"c:/yar/lp/budden-tools/sbcl--find-definition-sources-by-name--patch.lisp", sb-introspect::sb-introspect--find-definition-sources-by-name-sb--decorated

Теги: SBCL-INFO

шпаргалка-по-типам-в-SBCL Ссылка: SBCLT-DOCS::шпаргалка-по-типам-в-SBCL

Внутреннее представление типа

(sb-kernel::specifier-type 'integer) ; туда
(sb-kernel::type-specifier *) ; и обратно

Проверка пересечения типов

Проверка типов проверяет на непустоту персечения, т.к. у нас вся библиотека нетипизированная!

(sb-kernel::types-equal-or-intersect (sb-kernel::specifier-type 'integer) (sb-kernel::specifier-type 'string))
;; и есть такая же для values, может пригодиться для сигнатур методов:
(sb-kernel::values-subtypep (sb-kernel::values-specifier-type '(values integer integer &optional)) (sb-kernel::values-specifier-type '(values rational integer &optional)))

;; допустим, мы создали тип, расширяющийся в существующий defstruct str
(deftype fake-type (argument)
  (declare (ignore argument))
  'str)

;; как узнать, что он есть структура? А вот как:
(sb-kernel::specifier-type '(fake-type 1))
;; ==> #<SB-KERNEL:STRUCTURE-CLASSOID STR>

Наследование структур устроено так:

тег-самого-старшего-предка
тег-более-молодого-предка
...
тег-этого-типа

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

sb-kernel::layout-of - Return the layout for an object. This is the basic operation for finding out the "type" of an object, and is used for generic function dispatch

Теги: SBCL-INFO

краткое-описание-системы-типов-SBCL Ссылка: SBCLT-DOCS::краткое-описание-системы-типов-SBCL

type-class.lisp

sb-kernel::ctype = compiler type - ссылается на type-class.

sb-kernel::type-class - разновидность типа, например, named, values, number, hairy. Экземпляры структуры type-class хранятся в sb-kernel::*type-classes*. Экземпляр type-class содержит операции над типами такого класса, например, равенство и пересечение типов.

Примитивы построения системы типов

sb-kernel::!define-type-class - определяет класс типов def-type-translator - подобен deftype

sb-kernel::named-type, sb-kernel::member-type - структуры - наследники ctype, ссылаются на экземпляры type-class с именем named, member и т.п.

sb-vm::!define-primitive-object - определяются массивы, в т.ч. с fill-pointer -ом, и некоторые другие объекты.

Теги: SBCL-INFO

DEFINE-TYPE-METHOD Ссылка: SBCLT-DOCS::DEFINE-TYPE-METHOD

служат для упрощения типов. define-type-method: если не определён, то соотв. операция не меняет тип метод для subtype может возврщать два значения, как и сам subtype simple методы служат для случая, когда оба операнда имеют одинаковый type-class complex методы служат для случая, когда один из аргументов (обычно - первый) имеет иной type-class

Горячее переопределение

Похоже, что для возможности горячего переопределения define-type-method нужно две вещи:

  1. Переопределить макрос sb-kernel::!cold-init-form
  2. Переопределить макрос sb-kernel::!define-type-method

Пересечение

Методы :simple-intersection :complex-intersection2 описаны возле

sb-kernel::%type-intersection2

Теги: SBCL-INFOTYPE-INFERENCE

вывод-типов-в-SBCL Ссылка: SBCLT-DOCS::вывод-типов-в-SBCL

Есть, как минимум,

Забавно то, что они должны работать вместе, но работают по отдельности, см. примечание у SB-C::CONSTRAINT-PROPAGATE-IN-BLOCK, а также FIXME в том же файле. Видимо, на данном этапе мы радуемся тому, что они не взаимодействуют и занимаемся только системой вывода типов.

Проговорим всё от начала до конца.

(defun foo (x y)
  (declare (type foo x))
  (the (cons integer) x) ; теперь мы 'знаем', что x имеет тип 
  (bar)
  (the baz))

Это без вывода типов означает:

(defun foo (x y)
  (the foo x)
  (the (and foo (cons integer)) x) 
  (bar)
  (the (and foo baz) x))

А с выводом типов это означает:

(defun foo (x y)
  (the (and foo cons) x)
  (the (and foo (cons integer)) x)
  (bar)
  (the (and foo cons) x))

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

От выведенного распространяется только conservative-type

Теги: SBCL-INFOTYPE-INFERENCE

структуры-данных-компилятора-SBCL Ссылка: SBCLT-DOCS::структуры-данных-компилятора-SBCL

sb-c::component - связный кусок графа потоков

sb-c::ctran изображает передачу управления в какой-то sb-c::node

sb-c::node изображает вычисление

sb-c::valued-node - вычисление, порождающее значение - ссылается на lvar

sb-c::combination (потомок node) - вызов функции

И отдельный sb-c::mv-combination для mv

sb-c::сblock (aka block) изображает линейную цепочку из sb-c::node (см. поле start в нём)

sb-c::node-derived-type ;  the bottom-up derived type for this node.

Аномалия терминологии

В lvar и ctran есть поля "use(s)", что на самом деле означает 'источник' - откуда lvar получает значение и, соотв., откуда передаётся управление.

sb-c::lvar - linear-var временное хранилище данных

;; cached type of this lvar's value. If NIL, then this must be
;; recomputed: see sb-c::LVAR-DERIVED-TYPE
sb-c::lver-%derived-type 

;; Cached type which is checked by DEST. If NIL, then this must be
;; recomputed: see sb-c::LVAR-EXTERNALLY-CHECKABLE-TYPE
sb-c::%externally-checkable-type 

sb-c::leaf - переменная, функция, константа.

  ;; the type which values of this leaf must have
  sb-c::leaf-type
  ;; the type which values of this leaf have last been defined to have
  ;; (but maybe won't have in future, in case of redefinition)
  sb-c::leaf-defined-type

sb-c::ref - наследник valued-node, содержит ссылку на leaf

derived-type по умолчанию берётся из leaf-type

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

Устройство памяти (рабочая гипотеза)

В 64 разрядах fixnum - это всё, у чего младший бит равен нулю. Поинтер - всё, у чего два младших бита равны единице. Всё остальное - это 10. Остальные два бита поинтера заняты тегом типа поинтера. Конс - это просто два слова, полностью занятые двумя объектами. В него больше ничего не впихнуть.

Указатель на конс - два бита, что это поинтер, два бита, что это конс, остальное - адрес. Как сделать немутабельным? (А) Уменьшить адресуемое пр-во, тогда в указатель (но не в сам конс) можно будет вписать признак мутабельности. (Б) Сделать отдельный внешний массив, в к-ром хранятся биты мутабельности:

502 CL-USER>(defparameter kb 1024)
KB
503 CL-USER>(defparameter mb (* kb kb))
MB
504 CL-USER>(defparameter gb (* mb kb))
GB
505 CL-USER>(/ gb 16) ; столько байт занимает 1 объект
67108864
506 CL-USER>(/ (dynamic-space-size) 16) ; объект занимает 16 байт, значит у нас всего столько объектов
67108864
509 CL-USER>(defparameter *mutability-table* (make-array 67108864 :element-type '(unsigned-byte 2)))
> (room)
16,777,328 bytes for         3 simple-array-unsigned-byte-2 objects.

Т.е. они экономично упакованы. Что тут придётся сделать - так это двигать данные в этом массиве при сборке мусора. Но не будем на это отвлекаться и сделаем пока слабую хеш-таблицу. А в будущем мы могли бы вынести тег типа в этот массив и тем самым получить бинарную совместимость с Си.

Теги: SBCL-INFO

пытаемся-добавить-тип-IMMUTABLE Ссылка: SBCLT-DOCS::пытаемся-добавить-тип-IMMUTABLE

Что нужно учесть: функция freeze на начальном этапе НЕ должна сообщать о том, что аргумент и результат совпадают, поскольку это приведёт к ложному выводу о том, что аргумент был иммутабелен и до момента вызова freeze. Далее неплохо бы сделать так, чтобы инфа о типе распространялась только в будущее, а не в прошлое.

Что возьмём за основу? а) !def-type-translator б) !define-type-class

Уже есть (!define-type-class constant :inherits values). Похоже, это про ситуацию, когда значение параметра известно во время компиляции, см., напр, sb-c::constant-function-call-value . Но можно попробовать взять за основу и менять по аналогии.

Теги: SBCL-INFO

сборка-SBCL-быстрее Ссылка: SBCLT-DOCS::сборка-SBCL-быстрее

Этим занимается slam.sh, но он не является build-файлом. Перекомпилирует только изменённые файлы, игнорируя зависимости. Также нужна фича after-xc-core

Теги: SBCL-INFO

описание-процесса-сборки-SBCL Ссылка: SBCLT-DOCS::описание-процесса-сборки-SBCL

SBCL: a Sanely-Bootstrappable Common Lisp

Контрибы, включая тесты: sh make-target-contrib.sh sb-aclrepl - один контриб, или без параметров - все контрибы.

Теги: SBCL-INFO

пытаемся-отключить-CONSTRAINTS Ссылка: SBCLT-DOCS::пытаемся-отключить-CONSTRAINTS

Идея состоит в том, чтобы снизить сложность. Но не получается:

(disassemble (compile (defun foo (x) (typecase x ((integer 0 100) (+ 1 x)) (t 538)))))

Эта функция перестаёт оптимизировать +, т.е. видимо, идея отключать constraint-ы - плохая.

Теги: SBCL-INFO

больше-шагов-в-пошаговой-отладке Ссылка: SBCLT-DOCS::больше-шагов-в-пошаговой-отладке

Недоделано в степпере (как минимум)

(flet ((f (&key (arg default)) arg))

Не входим в default - это нужно исправлять, видимо, в sb-c::ir1-convert-hairy-args, добавляя default-vals-super-forms . Для глобальных функций нам здесь ещё не хватает возможности вставать за пределы компилируемой формы, т.к. вычисление происходит в вызываемой, а не в вызывающей форме. Хотя... можно ведь сделать это и в вызываемой, а значит - делаем.

FIXME: При вызове неопределённой функции какой-то бред выводится степпером.

FIXME:

(defun f (&key (a 8)) a)
(funcall f) 

Если мы не встаём на 8, то похоже, что надо исправлять FIXME STEPPER в sb-c::convert-more-entry

Нас не устраивает, что так мало шагов в пошаговой отладке. Хотим найти, как это делается, и добавить ещё.

sb-c::ir2-convert-block
  sb-c::ir2-convert-template
    sb-vm::step-instrument-before-vop
      SB-VM::EMIT-SINGLE-STEP-TEST

Код такой:
     (when (emit-step-p call)
       (vop sb!vm::step-instrument-before-vop call block))



sb-c:multiple-call и т.п. - делаются с помощью macrolet define-full-call, к-рого нет в образе
  sb-vm::emit-single-step-test

Код такой:
               (when step-instrumenting
                 (emit-single-step-test)
                 (inst jmp :eq DONE)
                 (inst break single-step-around-trap))
               DONE

Теги: SBCL-INFO

пути-к-формам Ссылка: SBCLT-DOCS::пути-к-формам

sb-c::*source-paths* - хранит eq отображение форма->путь во время компиляции. Проблема - для формы 42 нет смысла хранить путь, т.к. может быть более одного вхождения 42 в исходном тексте. Пусть описан у sb-di::form-number-translations, но вообще есть расширенное понятие пути, см. ф-ю sb-c::source-path-tlf-number и рядом с ней.

sb-c::note-source-path - запомнить один путь

SB-C::FIND-SOURCE-PATHS - вызывается читателем для определения номеров подформ. Номера будут записаны в отладочную инфу. С ней синхронизируется sb-di:form-number-translations, к-рая вызывается отладчиком при поиске места в исходнкие, где мы стоим . Запускать: (let ((sb-c::$2 (make-hash-table))) (sb-c::find-source-paths '(defun f (x) 8) 1) sb-c::$2)

sb-c::get-source-path - взять из таблицы

sb-c::source-path-original-source - подтверждает, что второй эл-т надо пропустить.

sb-c::source-path-forms - говорит о том, что путь содержит не только original-source, но и ещё что-то перед ним.

swank/sbcl::stream-source-position возвращает смещение в форме по "пути".

Порядок отладки проблем с ненахождением точки:

```

  1. Каков номер формы? вызвать (sb-di::form-number-translations Определение 43 1) он напечатает номера.

  2. Раскомментариваем печать в swank/sbcl::stream-source-position и шагаем к этому месту. Здесь номер формы, взятый из отладочной инфы. Если его нет, то его не записали туда.

Теги: SBCL-INFO

ставить-брекпойнт-мышью Ссылка: SBCLT-DOCS::ставить-брекпойнт-мышью

sb-di::safe-readtable - ближайшее похожее. Возможный план состоит в том, чтобы наложить ограничения на readtable, чтобы любой нестандартный читатель возвращал осмысленный объект (или даже какой-то syntax object), не имея побочных эффектов, или чтобы он мог запускаться в безопасном режиме без побочных эффектов. Для символов у нас уже есть заглушка


STEPPER Ссылка: SBCLT-DOCS::STEPPER


Брекпойнты Ссылка: SBCLT-DOCS::Брекпойнты

Пример, работающий под Linux и под Windows. Но неплохо бы убедиться, что брекпойнты используются для trace, тогда будет ясно, действительно ли они работоспособны.

>(compile (defun zz (x) (print x)))
>(sb-di::activate-breakpoint (sb-di::make-breakpoint #'(lambda (&rest ignore) (break "Wouch!")) (sb-di::fun-debug-fun #'zz) :kind :fun-end))
>(compile (defun khoj () (zz 3)))
>(khoj)

Теги: SBCL-INFO

Степпер Ссылка: SBCLT-DOCS::Степпер

Разбираем stepper.

Conditions

Что делает swank для перехода в пошаговый режим?

swank/backend:activate-stepping - просто вызывает (sb-impl::enable-stepping)

Где вставляются conditions?

sb-vm::step-instrument-before-vop - вставляет before-trap

А sb-c:multiple-call-named и аналоги вставляет around-trap - это происходит, если в combination node (вызове функции) есть step-info, а step-info там есть, если sb-c::step-form-p вернул истину.

Как связываются между собой сигнализаторы

(inst break single-step-around-trap)
(inst break single-step-before-trap)

и обработчики? Видимо, через sb-di:handle-single-step-before-trap, sb-di:handle-single-step-around-trap . Они обе вызываются sb-impl::step-form, к-рая сигналит 'step-form-condition. при этом, around-trap ВМЕСТО собственно вызова функции делает так, чтобы вызвался wrapper от функции.

Неприятные вопросы

Как достаются переменные и почему они иногда кривые?

Кривые они в пошаговом режиме тут:

(defun ff (x)
  "Outer function"
  (let ((y (+ x 1))
        (z (- x 1)))
    (sb-impl::enable-stepping)
    (list
     ;; здесь y и z видна
     (gg y)
     ;; а здесь - нет. 
     (+ z y)
     )
    ))

(defun gg (y) y)

Там всё сложно, у нас нет ресурсов разбираться.

Наше решение

Вводим флаг sb-c::$2 , чтобы динамически включать-выключать наш способ инструментирования.

Чтобы переменные показывались, мы выкидываем before-trap, а вместо него делаем вызов ненужной пустой функции sb-c::stupid-step-fn-itself. В ней мы делаем step, и при этом обманываем отладчик, скрывая факт существования на стеке этой вот sb-c::stupid-step-fn-itself.

Далее нам нужно починить step/next/out, к-рый может от этого сломаться.

Теги: SBCL-INFO

Статьи по тегам

Теги