Как узнать, какой оператор в какой строке изменяет значение переменной в TCL

#debugging #tcl #watchpoint

#отладка #tcl #точка наблюдения

Вопрос:

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

Ответ №1:

Чтобы установить контрольную точку для переменной, вы используете trace команду. Вы можете получить расширенную информацию о контексте, в котором была назначена переменная, с помощью info команды, в частности подкоманд level и frame . (Последнее доступно только начиная с Tcl 8.5.)

Объединение подобных вещей должно дать нужную информацию:

 trace add variable x write peekLocation
proc peekLocation args {
    puts "WRITTEN FROM >[info level -1]< CALL"
    puts "DETAILS: [info frame -1]"
}
# Demonstration...
proc foobar {} {
    global x  
    set x "jibber jabber"
}
foobar
  

Однако это не совсем работает; вы можете легко найти, какая процедура выполнялась во время обновления переменной, но где в этой процедуре произошло обновление, остается вне досягаемости. (Вместо этого вы видите вызов самого обратного вызова трассировки или, на одном уровне выше по стеку, вызов процедуры, выполняющей манипуляцию, ни то, ни другое не является настолько полезным …)


[РЕДАКТИРОВАТЬ]: Другой подход заключается в предположении, какая команда выполняет обновление (например, set ), и немного повозиться, чтобы info level (единственная команда, которая может указать номер строки) могла сделать правильную вещь:

 rename set __orig_set;proc set args {doTrace;uplevel 1 [list __orig_set {*}$args]}
# Separate the probe from the instrumentation
proc doTrace {} {
    puts [info frame -2]
}
  

Это работает. Также довольно легко распространить его на другие команды, которые выполняют настройку переменных (например, [incr], [lappend], [lset]). Более замысловатый тест — это:

 proc doTrace {} {
    __orig_set f [info frame -2]
    dict with f {
        switch $type {
            source {
                puts "Write happened on line $line of file $file"
            }
            proc {
                puts "Write happened on line $line of procedure $proc"
            }
            default {
                puts "Write happened on line $line (command was >$cmd<)"
            }
        }
    }
}
  

Не стесняйтесь экспериментировать!

Комментарии:

1. Более того, извлечение номеров строк для кода, которые не относятся к процедуре, является глубоко черным искусством. Отладчики Tcl делают это, гораздо более глубоко инструментируя код, но мне не нравится такой подход.