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功能。它有一个简单的界面如下:

安装

tkcon

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

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

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

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

tclsh
% package require tkcon
2.7
% 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来提供调用参数

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

set argc 5
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

#!/usr/bin/tclsh
#===============================================================================
# FILE               :  stepsource.tcl
# USAGE              :  ./stepsource.tcl <options>
# DESCRIPTION        :  ---
# REQUIREMENT        :  ---
# AUTHOR             :  Harris Zhu (harriszh), harriszh@cadence.com
# Created On         :  2018-06-30 21:16
# Last Modified      :  2018-06-30 21:16
# Update Count       :  1
# REVISION           :  ---
#===============================================================================namespace eval ::stepsource {variable VERSION "1.0"proc StepCommand {stepcommand} {switch -regexp -- $stepcommand {^\[0-9\]+$ {set ::stepsource::currentBreakPoint $stepcommandreturn}^b\ *-*[0-9?]*$ {if {$stepcommand == "b -"} {set ::stepsource::breakPoints {} ; return}set bOption [lindex $stepcommand 1]if {$bOption == "?"} {puts $::stepsource::outChannel "breakpoints: $::stepsource::breakPoints" ; return}if {![string first - $bOption]} {set eraseBreakPoint [lsearch $::stepsource::breakPoints [expr abs($bOption)]]if {$eraseBreakPoint > -1} {set ::stepsource::breakPoints [lreplace $::stepsource::breakPoints $eraseBreakPoint $eraseBreakPoint]}puts $::stepsource::outChannel "breakpoints: $::stepsource::breakPoints"return}set ::stepsource::breakPoints "$::stepsource::breakPoints $bOption"set ::stepsource::breakPoints [lsort -unique $::stepsource::breakPoints]if {$bOption == {}} {set ::stepsource::currentBreakPoint $bOption}return}^l\ *[0-9]*\ *[0-9]*$ {regexp {l ([0-9]+) *([0-9]*)} $stepcommand trash listStart listEndif ![info exists listStart] {set listStart 1set listEnd $::stepsource::lineCount} else {if {(![string is integer -strict $listEnd]) || ($listEnd < $listStart)} {set listEnd $listStart}}for {set i $listStart} {$i <= $listEnd} {incr i} {if ![info exists ::stepsource::lineArray($i)] {return}puts -nonewline $::stepsource::outChannel "$::stepsource::lineArray($i)"}return}}switch -- $stepcommand {a {foreach var [uplevel 3 info vars] {if ![uplevel 3 array exists [list $var]] {continue}puts $::stepsource::outChannel "-----------------------------------"uplevel 3 parray [list $var]}}c {foreach var [lsort [uplevel 3 info vars]] {if {$var == "errorInfo"} {continue}if ![uplevel 3 info exists [list $var]] {continue}if [uplevel 3 array exists [list $var]] {continue}set changeIcon "=="catch {if {$::stepsource::varValues($var) != [uplevel 3 set [list $var]]} {set changeIcon "->"}}if {![info exists ::stepsource::varValues($var)]} {set changeIcon "->"}if {$changeIcon == "->"} {puts $::stepsource::outChannel [format "%-30s %s %s" $var $changeIcon "[uplevel 3 set [list $var]]"]}set varrayadd $var ; lappend varrayadd [uplevel 3 set [list $var]] ; array set ::stepsource::currentValues $varrayadd}set ::stepsource::varDefault c}e {set level [expr [info level] - 1]set ::stepsource::watchLevel $levelif {$level <= $::stepsource::highestLevel} {unset ::stepsource::watchLevel}}g {foreach var [lsort [info globals]] {if [array exists ::$var] {puts $::stepsource::outChannel [format "%-27s %s" $var Array:] ; continue}set changeIcon "=="catch {if {$::stepsource::varValues($var) != [set $var]} {set changeIcon "->"}}puts $::stepsource::outChannel [format "%-30s %s %s" $var $changeIcon "[set ::$var]"]set varrayadd $var ; lappend varrayadd [set ::$var] ; array set ::stepsource::currentValues $varrayadd}}h {puts $::stepsource::outChannel {\<line#>    run until line number<return>    run next linea        list array valuesb        run until next breakpointb ?        list breakpointsb <line#>    set breakpointb -<line#>    unset breakpointb -        unset all breakpointsc        list changed variable valuese        run to end of current procedureg        list global variablesh        helpl        list all instrumented linesl <line#> [<line#>]     list line numbersv        list variable valuesx        abort execution<anything else>    execute as tcl command}}v {foreach var [lsort [uplevel 3 info vars]] {if ![uplevel 3 info exists [list $var]] {continue}if [uplevel 3 array exists [list $var]] {puts $::stepsource::outChannel [format "%-27s %s" $var Array:] ; continue}set changeIcon "=="catch {if {$::stepsource::varValues($var) != [uplevel 3 set [list $var]]} {set changeIcon "->"}}if {![info exists ::stepsource::varValues($var)]} {set changeIcon "->"}puts $::stepsource::outChannel [format "%-30s %s %s" $var $changeIcon "[uplevel 3 set [list $var]]"]set varrayadd $var ; lappend varrayadd [uplevel 3 set [list $var]] ; array set ::stepsource::currentValues $varrayadd}set ::stepsource::varDefault v}x {error "abort"}{} {set ::stepsource::currentBreakPoint 0}default {catch {uplevel 3 $stepcommand} resultputs $::stepsource::outChannel $result}}}proc StepNumber {linenumber} {set level [info level]if ![info exists ::stepsource::highestLevel] {set ::stepsource::highestLevel $level}if {$level < $::stepsource::highestLevel} {set $::stepsource::highestLevel $level}if ![info exists ::stepsource::currentBreakPoint] {set ::stepsource::currentBreakPoint 0}if {$::stepsource::currentBreakPoint > $::stepsource::lineCount} {set ::stepsource::currentBreakPoint $::stepsource::lineCount}set returnOK 1catch {if {[info level] < $::stepsource::watchLevel} {unset ::stepsource::watchLevelset returnOK 0} else {set ::stepsource::currentBreakPoint {}}}if {$::stepsource::currentBreakPoint == 0} {set returnOK 0}if {$linenumber == $::stepsource::currentBreakPoint} {unset ::stepsource::currentBreakPoint ; set returnOK 0}if {[lsearch -exact $::stepsource::breakPoints $linenumber] > -1} {set returnOK 0}if $returnOK {return}catch {set currentProcedure [lindex [info level -2] 0]if {[uplevel 2 info procs $currentProcedure] == {}} {set currentProcedure {}}}if ![info exists ::stepsource::lastProcedure] {set ::stepsource::lastProcedure {}}if ![info exists currentProcedure] {set currentProcedure {}}if {($level != $::stepsource::highestLevel) && ($::stepsource::lastProcedure != $currentProcedure)} {puts $::stepsource::outChannel "||||current procedure: $currentProcedure"}set ::stepsource::lastProcedure $currentProcedureset stepCommand $::stepsource::varDefaultStepCommand $stepCommandwhile {$stepCommand != {}} {puts $::stepsource::outChannel "\n-----------------------------------"puts $::stepsource::outChannel $::stepsource::lineArray($linenumber)\nputs -nonewline $::stepsource::outChannel >set stepCommand [gets $::stepsource::inChannel]StepCommand $stepCommandif {([string is integer -strict $stepCommand]) || ($stepCommand == "b") || ($stepCommand == {}) || ($stepCommand == "e")} {catch {array set ::stepsource::varValues [array get ::stepsource::currentValues]}catch {array unset ::stepsource::currentValues}break}}}proc StepSource {filename} {namespace eval ::stepsource {}set ::stepsource::filename $filenamenamespace eval ::stepsource {if {[info procs original_unknown] == {}} {rename ::unknown original_unknownproc ::unknown {args} {if [string is integer -strict $args] {::stepsource::StepNumber $args} else {set ::stepsource::unk_args $argsuplevel 1 ::stepsource::original_unknown $::stepsource::unk_args}}}if ![info exists inChannel] {set inChannel stdin}if ![info exists outChannel] {set outChannel stdout}if ![info exists breakPoints] {set breakPoints {}}if ![info exists varDefault] {set varDefault v}if ![info exists sourcedFiles] {set sourcedFiles {}}if {[lsearch -exact $sourcedFiles $filename] < 0} {lappend sourcedFiles $filename}if ![info exists ::stepsource::sourceProcs] {set ::stepsource::sourceProcs {}}set mtime [file mtime $filename]set oldMtime 0catch {set oldMtime $mtimes($filename)}array unset lineArrayset lineCount 1foreach sF $sourcedFiles {set $sF {}set f [open $sF r]set noNumberLine {}while {![eof $f]} {set line [gets $f]set firstWord [string trim [string range [string trim $line] 0 [expr [string wordend [string trim $line] 0] - 1]]]set secondWord [string trim [string range [string trim $line] [string length $firstWord] [string wordend [string trim $line] [expr [string length $firstWord] + 1]]]]if ![regexp {(::[^ ]+)(\ |$)} $line trash firstNameSpace] {set firstNameSpace {}}if {$firstWord == ":"} {set firstWord $firstNameSpace}if {[string index $secondWord 0] == ":"} {set secondWord $firstNameSpace}if {$firstWord == "proc"} {lappend ::stepsource::sourceProcs $secondWord}if {([info commands $firstWord] != {}) || ([lsearch -exact $::stepsource::sourceProcs $firstWord] > -1)} {set $sF "[set $sF]$noNumberLine[set lineCount]\;\t$line\n"set arrayadd $lineCount ; lappend arrayadd $noNumberLine$lineCount\;\t$line\n ; array set lineArray $arrayaddset noNumberLine {}incr lineCount} elseif {($firstWord == "\{") && (([info commands $secondWord] != {}) || ([lsearch -exact $::stepsource::sourceProcs $secondWord] > -1))} {set arrayadd $lineCount ; lappend arrayadd $noNumberLine$lineCount\;\t$line\n ; array set lineArray $arrayaddregsub {\{} $line "\{$lineCount\;" lineset $sF "[set $sF]$noNumberLine\t$line\n"set noNumberLine {}incr lineCount} else {set noNumberLine $noNumberLine\t$line\n}}close $fif {$noNumberLine != {}} {set $sF "[set $sF]$noNumberLine"}}}set ::stepsource::sourceProcs {}uplevel 1 eval \$\{::stepsource::$::stepsource::filename\}}}# end namespace eval ::stepsourceproc ::ss {args} {catch {unset ::stepsource::watchLevel}catch {unset ::stepsource::currentBreakPoint}catch {array unset ::stepsource::varValues}uplevel 1 $args}package provide stepsource $::stepsource::VERSION

使用

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

command Usage
<line#> run until line number
<return> run next line
a list array values
b run until next breakpoint
b ? list breakpoints
b <line#> set breakpoint
b -<line#> unset breakpoint
b - unset all breakpoints
c list changed variable values
e run to end of current procedure
g list global variables
h help
l list all instrumented lines
l <line#> [<line#>] list line numbers
v list variae values
x abort execion
<anything else> execute as tcl command

启动后界面发下

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


RamDebugger

安装

RamDebugger需要下面四个库:

tcllib

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

tklib

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

Img

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

tktreectrl

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

RamDebugger

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

使用

建议打开变量状态框,如上面图右侧
方案是点击下面红框的选项

基本功能和其他相似,也是run/stop, add breakpoints, step/next等功能

比较

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

总结

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

如何实现对tcl脚本的类GDB调试相关推荐

  1. 用C#实现对Oracle 存储过程/函数/包的调试(附源代码)

    Oracle用SYS.DBMS_DEBUG实现对存储过程/函数/包的调试.如果你用DESC SYS.DBMS_DEBUG命令查看它的发现其 成员函数和方法并不是特别多,但是为了实现这个功能却花了我近1 ...

  2. CV之FDFA:利用MTCNN的脚本实现对LFW数据集进行FD人脸检测和FA人脸校准

    CV之FD&FA:利用MTCNN的脚本实现对LFW数据集进行FD人脸检测和FA人脸校准 目录 运行结果 运行过程 运行(部分)代码 在裁剪好的LFW数据集进行验证 运行结果 运行过程 time ...

  3. 用Python脚本实现对Linux服务器的监控

    一.前言 二.概述 三.Python 版本说明 四./proc 文件系统 五.对CPU监测 六.对系统负载监测 七.对内存信息的获取 八.对网络接口的监测 九.监控apache服务器进程的Python ...

  4. 用 Python 脚本实现对 Linux 服务器的监控

    版本说明 Linux 服务器是 Ubuntu 12.10 Python 版本 是 2.7 工作原理:基于/proc 文件系统 Linux 系统为管理员提供了非常好的方法,使其可以在系统运行时更改内核, ...

  5. Qt实战案例(28)——利用QSQL相关类实现对MySQL数据库的基本操作及相关设置详解

    目录 一.项目介绍 二.项目基本配置 2.1 安装MySQL 2.2 创建Qt项目 2.3 移动libmysql.dll文件 三.UI界面设计 四.主程序实现 4.1 pro文件 4.2 main.c ...

  6. migrate-mongo实现对mongo数据库执行脚本版本控制

    描述: migrate-mongo是一个js模块,通过migrate-mongo可以实现远程操作mongodb数据库.其中执行脚本的版本控制是通过配置文件进行远程MongoDB数据库连接配置,执行时在 ...

  7. Note:HTTP服务器是如何实现对PHP支持的

    目前各种服务器HTTP Server对PHP的支持一共有三种: 通过HTTP Server内置的模块来实现,例如Apache的mod_php5,类似的Apache内置的mod_perl可以对perl支 ...

  8. python执行adb命令_Python实现对adb命令封装

    我就废话不多说了,大家还是直接看代码吧! #!/usr/bin/evn python # -*- coding:utf-8 -*- # FileName adbtools.py # Author: H ...

  9. GDB调试及其调试脚本的使用

    一.GDB调试 1.1. GDB 概述 GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具.或许,各位比较喜欢那种图形界面方式的,像VC.BCB等IDE的调试,但如果你是在UNIX/Lin ...

最新文章

  1. java mvc view_对Springmvc view层的理解
  2. 坑系列 —— 缓存+哈希=高并发?
  3. 存储引擎——概述|| 各种存储引擎的特性(InnoDB||MyISAM||MEMORY||MERGE) ||存储引擎的选择
  4. [原]ubuntu14.04 网卡逻辑修改没有文件/etc/udev/rules.d/70-persistent-net.rules
  5. [PAT乙级]1010 一元多项式求导
  6. 使用可自定义的定期计划自动执行数据库备份
  7. 小程序navigator跳转路径url写法
  8. 一个共享XP、2003上文件与打印机简单易行的方法
  9. 强悍修改WIN7的文件夹背景(修改DLL)
  10. Tensorflow手写数字识别
  11. 帝国cms二次开发应用
  12. 操作员或系统管理员_什么是系统管理员?
  13. BZOJ 2037: [Sdoi2008]Sue的小球(DP)
  14. 六年级计算机信息技术教案反思,小学六年级信息技术教学反思.doc
  15. 网页视频播放器-插件
  16. 关于node-pyg的路径问题导致build失败
  17. 如何设置vs窗口的属性管理器和解决方案管理器的位置
  18. 【数论】欧几里得算法
  19. QT5 OpenGL (四, 绘制立体图形)
  20. 孤独后厂村,IT人百态:30万互联网人跳不出的中国硅谷

热门文章

  1. 【最短路】 ZOJ 1544 Currency Exchange 推断负圈
  2. 知名财经大V称:京东金融遇到了点“问题”!
  3. ConcurrentDictionary线程不安全么,你难道没疑惑,你难道弄懂了么?
  4. 我和我的Android
  5. linux深度定制,Linux Deepin 12.06 beta1 发布
  6. 仅用几行Python代码就能帮小姐姐复制U盘文件,实用干货
  7. win7怎么配置程序服务器错误日志文件,win7怎么配置程序服务器
  8. android开发重要控件,Android界面编程——Android基本控件
  9. Vb自动读取本地HTML,VB获取网页源代码的五种方法
  10. 华为交换机ssh思科交换机_思科交换机交换机中ip、mac地址绑定