当前位置:   article > 正文

如何实现对tcl脚本的类GDB调试

tclsh debug

debug TCL script with free tools

面临的问题

tcl脚本被广泛使用于EDA工具中,像Cadence, Synopys和mentor的工具脚本都是tcl脚本,可以在里面嵌入tcl脚本以实现比较复杂的设计流程和自动化工作。
目前tcl的调试主要依靠插入打印信息,这样需要叠代的次数比较,代码里会充满了打印语句也不太美观。

可选方案

在网上尝试了几种方案,最好的是activestate的Komodo, 它是一个IDE, 可以直接GUI下的各种调试手段,无奈它是一个收费软件,可以免费尝试21天。所以作者把目光投向了一些其他免费的方案。
GUI下的调试手段还是比较友好的,所以找到了DDT, 这个工具虽然支持的功能很简单,但都非常实用,唯一不足就是速度太慢了,所以又尝试了类似工具RamDebugger.
另外一个工具是一个简单的脚本stepsource.tcl, 它也可以实现类似的功能,只是没有GUI,但速度飞快,作者本人更喜欢使用它。
下面就简单介绍一下他们的安装和使用

DDT

介绍

可以支持Tcl8.5或更高版本的动态调试。它主要提供了step, breakpoint, variable display功能。它有一个简单的界面如下:
ddt_gui

安装

tkcon

它依赖于tkcon, 所以需要先安装tkcon.
下载地址: 如下

BTW:也可以用git clone https://github.com/wjoye/tkcon.git来下载补丁版本。

下载后可以在自己home目录下新建一个.tcllib目录,然后把下载的tkcon解压缩到里面。
tcllib

然后设置环境变量export TCLLIBPATH="/home/harriszh/.tcllib"
下面是测试是否安装好的方法:

  1. tclsh
  2. % package require tkcon
  3. 2.7
  4. % tkcon show

会调出tkcon的窗口

ddt

使用git下载ddt源码:

git clone https://github.com/Drolla/ddt.git

可以将它放到某个地方,比如作者放到了/home/harriszh/bin/
然后在bash下设置如下别名, 就可以调起ddt。

alias ddt="curdir=$(pwd); cd /home/harriszh/bin/ddt/ddt_debugger && ./ddt_debugger.tcl && cd ${curdir}"

如果是csh, 则需要

alias ddt="curdir=`pwd`; cd /home/harriszh/bin/ddt/ddt_debugger && ./ddt_debugger.tcl && cd ${curdir}"

使用

通过File->load来加载要调试的tcl文件

如果这个脚本依赖输入参数,那么可以通过config->Define initilization variables and script来提供调用参数

或者可以直接在脚本的最上面写上

  1. set argc 5
  2. set argv {1010 -- out 10 100}

所有的功能都在Debug菜单下, 和一般调试器相似, F5是run/continue, F8是设置breakpoints, shift-F5是stop, F7F7是增加watch

在运行时,右半边窗口会列出所有变量

缺点

  • 运行速度非常慢
  • 当脚本有问题时,容易hang, refresh都没用

总结

非常友好的GUI和使用方法。但因为速度问题使得作者寻找其他解决方案,如stepsource


stepsource

介绍

提供按行执行tcl脚本的功能

安装

把下面文件保存为stepsource.tcl

  1. #!/usr/bin/tclsh
  2. #===============================================================================
  3. # FILE : stepsource.tcl
  4. # USAGE : ./stepsource.tcl <options>
  5. # DESCRIPTION : ---
  6. # REQUIREMENT : ---
  7. # AUTHOR : Harris Zhu (harriszh), harriszh@cadence.com
  8. # Created On : 2018-06-30 21:16
  9. # Last Modified : 2018-06-30 21:16
  10. # Update Count : 1
  11. # REVISION : ---
  12. #===============================================================================
  13. namespace eval ::stepsource {
  14. variable VERSION "1.0"
  15. proc StepCommand {stepcommand} {
  16. switch -regexp -- $stepcommand {
  17. ^\[0-9\]+$ {
  18. set ::stepsource::currentBreakPoint $stepcommand
  19. return
  20. }
  21. ^b\ *-*[0-9?]*$ {
  22. if {$stepcommand == "b -"} {set ::stepsource::breakPoints {} ; return}
  23. set bOption [lindex $stepcommand 1]
  24. if {$bOption == "?"} {puts $::stepsource::outChannel "breakpoints: $::stepsource::breakPoints" ; return}
  25. if {![string first - $bOption]} {
  26. set eraseBreakPoint [lsearch $::stepsource::breakPoints [expr abs($bOption)]]
  27. if {$eraseBreakPoint > -1} {
  28. set ::stepsource::breakPoints [lreplace $::stepsource::breakPoints $eraseBreakPoint $eraseBreakPoint]
  29. }
  30. puts $::stepsource::outChannel "breakpoints: $::stepsource::breakPoints"
  31. return
  32. }
  33. set ::stepsource::breakPoints "$::stepsource::breakPoints $bOption"
  34. set ::stepsource::breakPoints [lsort -unique $::stepsource::breakPoints]
  35. if {$bOption == {}} {set ::stepsource::currentBreakPoint $bOption}
  36. return
  37. }
  38. ^l\ *[0-9]*\ *[0-9]*$ {
  39. regexp {l ([0-9]+) *([0-9]*)} $stepcommand trash listStart listEnd
  40. if ![info exists listStart] {
  41. set listStart 1
  42. set listEnd $::stepsource::lineCount
  43. } else {
  44. if {(![string is integer -strict $listEnd]) || ($listEnd < $listStart)} {set listEnd $listStart}
  45. }
  46. for {set i $listStart} {$i <= $listEnd} {incr i} {
  47. if ![info exists ::stepsource::lineArray($i)] {return}
  48. puts -nonewline $::stepsource::outChannel "$::stepsource::lineArray($i)"
  49. }
  50. return
  51. }
  52. }
  53. switch -- $stepcommand {
  54. a {
  55. foreach var [uplevel 3 info vars] {
  56. if ![uplevel 3 array exists [list $var]] {continue}
  57. puts $::stepsource::outChannel "-----------------------------------"
  58. uplevel 3 parray [list $var]
  59. }
  60. }
  61. c {
  62. foreach var [lsort [uplevel 3 info vars]] {
  63. if {$var == "errorInfo"} {continue}
  64. if ![uplevel 3 info exists [list $var]] {continue}
  65. if [uplevel 3 array exists [list $var]] {continue}
  66. set changeIcon "=="
  67. catch {if {$::stepsource::varValues($var) != [uplevel 3 set [list $var]]} {set changeIcon "->"}}
  68. if {![info exists ::stepsource::varValues($var)]} {set changeIcon "->"}
  69. if {$changeIcon == "->"} {puts $::stepsource::outChannel [format "%-30s %s %s" $var $changeIcon "[uplevel 3 set [list $var]]"]}
  70. set varrayadd $var ; lappend varrayadd [uplevel 3 set [list $var]] ; array set ::stepsource::currentValues $varrayadd
  71. }
  72. set ::stepsource::varDefault c
  73. }
  74. e {
  75. set level [expr [info level] - 1]
  76. set ::stepsource::watchLevel $level
  77. if {$level <= $::stepsource::highestLevel} {unset ::stepsource::watchLevel}
  78. }
  79. g {
  80. foreach var [lsort [info globals]] {
  81. if [array exists ::$var] {puts $::stepsource::outChannel [format "%-27s %s" $var Array:] ; continue}
  82. set changeIcon "=="
  83. catch {if {$::stepsource::varValues($var) != [set $var]} {set changeIcon "->"}}
  84. puts $::stepsource::outChannel [format "%-30s %s %s" $var $changeIcon "[set ::$var]"]
  85. set varrayadd $var ; lappend varrayadd [set ::$var] ; array set ::stepsource::currentValues $varrayadd
  86. }
  87. }
  88. h {
  89. puts $::stepsource::outChannel {\
  90. <line#> run until line number
  91. <return> run next line
  92. a list array values
  93. b run until next breakpoint
  94. b ? list breakpoints
  95. b <line#> set breakpoint
  96. b -<line#> unset breakpoint
  97. b - unset all breakpoints
  98. c list changed variable values
  99. e run to end of current procedure
  100. g list global variables
  101. h help
  102. l list all instrumented lines
  103. l <line#> [<line#>] list line numbers
  104. v list variable values
  105. x abort execution
  106. <anything else> execute as tcl command
  107. }
  108. }
  109. v {
  110. foreach var [lsort [uplevel 3 info vars]] {
  111. if ![uplevel 3 info exists [list $var]] {continue}
  112. if [uplevel 3 array exists [list $var]] {puts $::stepsource::outChannel [format "%-27s %s" $var Array:] ; continue}
  113. set changeIcon "=="
  114. catch {if {$::stepsource::varValues($var) != [uplevel 3 set [list $var]]} {set changeIcon "->"}}
  115. if {![info exists ::stepsource::varValues($var)]} {set changeIcon "->"}
  116. puts $::stepsource::outChannel [format "%-30s %s %s" $var $changeIcon "[uplevel 3 set [list $var]]"]
  117. set varrayadd $var ; lappend varrayadd [uplevel 3 set [list $var]] ; array set ::stepsource::currentValues $varrayadd
  118. }
  119. set ::stepsource::varDefault v
  120. }
  121. x {
  122. error "abort"
  123. }
  124. {} {
  125. set ::stepsource::currentBreakPoint 0
  126. }
  127. default {
  128. catch {uplevel 3 $stepcommand} result
  129. puts $::stepsource::outChannel $result
  130. }
  131. }
  132. }
  133. proc StepNumber {linenumber} {
  134. set level [info level]
  135. if ![info exists ::stepsource::highestLevel] {set ::stepsource::highestLevel $level}
  136. if {$level < $::stepsource::highestLevel} {set $::stepsource::highestLevel $level}
  137. if ![info exists ::stepsource::currentBreakPoint] {set ::stepsource::currentBreakPoint 0}
  138. if {$::stepsource::currentBreakPoint > $::stepsource::lineCount} {set ::stepsource::currentBreakPoint $::stepsource::lineCount}
  139. set returnOK 1
  140. catch {
  141. if {[info level] < $::stepsource::watchLevel} {
  142. unset ::stepsource::watchLevel
  143. set returnOK 0
  144. } else {
  145. set ::stepsource::currentBreakPoint {}
  146. }
  147. }
  148. if {$::stepsource::currentBreakPoint == 0} {set returnOK 0}
  149. if {$linenumber == $::stepsource::currentBreakPoint} {unset ::stepsource::currentBreakPoint ; set returnOK 0}
  150. if {[lsearch -exact $::stepsource::breakPoints $linenumber] > -1} {set returnOK 0}
  151. if $returnOK {return}
  152. catch {
  153. set currentProcedure [lindex [info level -2] 0]
  154. if {[uplevel 2 info procs $currentProcedure] == {}} {set currentProcedure {}}
  155. }
  156. if ![info exists ::stepsource::lastProcedure] {set ::stepsource::lastProcedure {}}
  157. if ![info exists currentProcedure] {set currentProcedure {}}
  158. if {($level != $::stepsource::highestLevel) && ($::stepsource::lastProcedure != $currentProcedure)} {puts $::stepsource::outChannel "||||current procedure: $currentProcedure"}
  159. set ::stepsource::lastProcedure $currentProcedure
  160. set stepCommand $::stepsource::varDefault
  161. StepCommand $stepCommand
  162. while {$stepCommand != {}} {
  163. puts $::stepsource::outChannel "\n-----------------------------------"
  164. puts $::stepsource::outChannel $::stepsource::lineArray($linenumber)\n
  165. puts -nonewline $::stepsource::outChannel >
  166. set stepCommand [gets $::stepsource::inChannel]
  167. StepCommand $stepCommand
  168. if {([string is integer -strict $stepCommand]) || ($stepCommand == "b") || ($stepCommand == {}) || ($stepCommand == "e")} {
  169. catch {array set ::stepsource::varValues [array get ::stepsource::currentValues]}
  170. catch {array unset ::stepsource::currentValues}
  171. break
  172. }
  173. }
  174. }
  175. proc StepSource {filename} {
  176. namespace eval ::stepsource {}
  177. set ::stepsource::filename $filename
  178. namespace eval ::stepsource {
  179. if {[info procs original_unknown] == {}} {
  180. rename ::unknown original_unknown
  181. proc ::unknown {args} {
  182. if [string is integer -strict $args] {
  183. ::stepsource::StepNumber $args
  184. } else {
  185. set ::stepsource::unk_args $args
  186. uplevel 1 ::stepsource::original_unknown $::stepsource::unk_args
  187. }
  188. }
  189. }
  190. if ![info exists inChannel] {set inChannel stdin}
  191. if ![info exists outChannel] {set outChannel stdout}
  192. if ![info exists breakPoints] {set breakPoints {}}
  193. if ![info exists varDefault] {set varDefault v}
  194. if ![info exists sourcedFiles] {set sourcedFiles {}}
  195. if {[lsearch -exact $sourcedFiles $filename] < 0} {lappend sourcedFiles $filename}
  196. if ![info exists ::stepsource::sourceProcs] {set ::stepsource::sourceProcs {}}
  197. set mtime [file mtime $filename]
  198. set oldMtime 0
  199. catch {set oldMtime $mtimes($filename)}
  200. array unset lineArray
  201. set lineCount 1
  202. foreach sF $sourcedFiles {
  203. set $sF {}
  204. set f [open $sF r]
  205. set noNumberLine {}
  206. while {![eof $f]} {
  207. set line [gets $f]
  208. set firstWord [string trim [string range [string trim $line] 0 [expr [string wordend [string trim $line] 0] - 1]]]
  209. set secondWord [string trim [string range [string trim $line] [string length $firstWord] [string wordend [string trim $line] [expr [string length $firstWord] + 1]]]]
  210. if ![regexp {(::[^ ]+)(\ |$)} $line trash firstNameSpace] {set firstNameSpace {}}
  211. if {$firstWord == ":"} {set firstWord $firstNameSpace}
  212. if {[string index $secondWord 0] == ":"} {set secondWord $firstNameSpace}
  213. if {$firstWord == "proc"} {lappend ::stepsource::sourceProcs $secondWord}
  214. if {([info commands $firstWord] != {}) || ([lsearch -exact $::stepsource::sourceProcs $firstWord] > -1)} {
  215. set $sF "[set $sF]$noNumberLine[set lineCount]\;\t$line\n"
  216. set arrayadd $lineCount ; lappend arrayadd $noNumberLine$lineCount\;\t$line\n ; array set lineArray $arrayadd
  217. set noNumberLine {}
  218. incr lineCount
  219. } elseif {($firstWord == "\{") && (([info commands $secondWord] != {}) || ([lsearch -exact $::stepsource::sourceProcs $secondWord] > -1))} {
  220. set arrayadd $lineCount ; lappend arrayadd $noNumberLine$lineCount\;\t$line\n ; array set lineArray $arrayadd
  221. regsub {\{} $line "\{$lineCount\;" line
  222. set $sF "[set $sF]$noNumberLine\t$line\n"
  223. set noNumberLine {}
  224. incr lineCount
  225. } else {
  226. set noNumberLine $noNumberLine\t$line\n
  227. }
  228. }
  229. close $f
  230. if {$noNumberLine != {}} {set $sF "[set $sF]$noNumberLine"}
  231. }
  232. }
  233. set ::stepsource::sourceProcs {}
  234. uplevel 1 eval \$\{::stepsource::$::stepsource::filename\}
  235. }
  236. }
  237. # end namespace eval ::stepsource
  238. proc ::ss {args} {
  239. catch {unset ::stepsource::watchLevel}
  240. catch {unset ::stepsource::currentBreakPoint}
  241. catch {array unset ::stepsource::varValues}
  242. uplevel 1 $args
  243. }
  244. package provide stepsource $::stepsource::VERSION

使用

在tclsh下先source stepsource.tcl, 然后::stepsource::StepSource <your_tcl_script>就可以进行调试模式
它的命令如下:

commandUsage
<line#>run until line number
<return>run next line
alist array values
brun until next breakpoint
b ?list breakpoints
b <line#>set breakpoint
b -<line#>unset breakpoint
b -unset all breakpoints
clist changed variable values
erun to end of current procedure
glist global variables
hhelp
llist all instrumented lines
l <line#> [<line#>]list line numbers
vlist variae values
xabort execion
<anything else>execute as tcl command

启动后界面发下
step_source_run
直接按回车键就可以执行下一行
可以输入puts $argv等任意tcl命令
输入b 53在第53行加入breakpoint
取消用b -53


RamDebugger

安装

RamDebugger需要下面四个库:

tcllib

tcllib下载地址:这里
tclsh ./installer.tcl来启动安装界面
tcllib_install

tklib

tklib下载地址: 这里
直接解压缩到$TCLLIBPATH里就好
tklib_install

Img

Img下载地址: 这里
同tklib, 直接解压缩到$TCLLIBPATH
img_install

tktreectrl

tktreectrl下载地址: 这里
解压缩后, 在相应目录里执行./configure --prefix=/home/harriszh/.tcllib && make
在$TCLLIBPATH下新建目录treectrl2.4.1,
然后把library里的两个tcl文件, libtreectrl2.4.so 和pkgIndex.tcl拷到treectrl2.4.11就可以了
tktreectl_install

RamDebugger

下载文件源文件地址后解压缩
在解压缩后的目录里执行tclsh ./RamDebugger.tcl就可以看到下面界面
RamDebugger

使用

建议打开变量状态框,如上面图右侧
方案是点击下面红框的选项
var_pane
基本功能和其他相似,也是run/stop, add breakpoints, step/next等功能
ramdebugger_debug

比较

功能和DDT相似,但速度快了很多,一样没法停止正在执行的程序。
工具本身依赖较多库,所以安装比较麻烦
如果要使用GUI来debug tcl,这个还是最优的免费解决方案

总结

作者比较使用了三个调试器后,最满意的还是stepsource, 安装,速度和功能都能满足需要, 不依赖其他库,功能简洁又满足需求。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/188008
推荐阅读
相关标签
  

闽ICP备14008679号