原文链接:http://blog.163.com/zhoumhan_0351/blog/static/399542272009826105222389/
1)基本概念
日志文件:仿真日志(DES log,Discrete Event Simulation log)和错误日志(Error log)。它的内容是在仿真过程中由进程调用OPNET 函数op_prg_log_handle_create ()op_prg_log_entry_write ()写入的。 在Help 菜单下可以打开错误日志文件。错误日志文件以文本方式保存为<home>/op_admin/err_log,除了在菜单中打开也可以在OPNET 控制台(console)窗口输入op_vuerr 命令查看。 它包含了函数调用堆栈信息,我们可以从函数阶层性的调用关系中精确定位出错位置。
在编写函数时必须使用FIN(function begin)、FOUT(function out)、FRET(function return)等界定函数范围的标识符,而且必须使它们配对。编写程序时切记使FIN 和FOUT/FRET 配对。要产生ODB 调试信息,必须将仿真核心类型设定为development,优化的仿真核心(optimized)为了加快仿真速度不产生ODB 调试信息。之后我们还需要在仿真属性中包含debug 环境变量。
ODB 为控制和管理仿真行为提供一个交互式环境。ODB 支持断点(Breakpoint)定义,跟踪并显示仿真诊断信息。ODB 功能的实现有赖于进程模型中编写相应的程序支持,作为ODB 指令激活调试状态(breakpoint、trace 和action)的依据,可以在ODB 窗口中输入help<参数:all,basic,action,event,memeory,misc,object,packet,process,scripting,stop,trace>查看感兴趣的指令。
ODB常用的指令分为basic,event,object,packet,stop,trace,process几类。Basic类
指令主要包含了一些基本的操作;Event类指令主要针对事件进行操作;Object类指令主要
针对各类对象(如节点,信道等)进行操作。Packet类指令处理所有与包相关的操作。
指令类型 | 指令名称 | 功能描述 |
Basic类 | tstop | 为与特定时间最接近的事件设置断点 |
| cont | 继续事件运行直至下一个断点 |
| next | 执行下面几个事件 |
| quit | 退出程序 |
| status | 显示用户当前所设的断点,跟踪信息。 |
| mstop | 为特定进程模块设置断点 |
| delstop | 取消断点设置 |
Event类 | evprint | 打印事件信息 |
| evstop | 在某个事件处设置断点 |
Object类 | attrget | 获取某类的属性值 |
| attrprint | 打印目标的属性信息 |
| attrset | 设置目标的属性值 |
| objassoc | 打印与目标关联的信息 |
| objid | 获取目标的id |
| objpkmap | 打印由指定目标所拥有的包的列表 |
| objprint | 打印目标的信息 |
| objmap | 打印所指定类型的目标列表 |
Packet类 | iciprint_pk | 打印与包关联的ici信息 |
| pkmap | 打印指定的包的列表 |
| pkstop | 为指定的包设置断点 |
| pttrace | 跟踪所指定的包树 |
| pktrace | 跟踪所指定的包 |
| protrace | 为目标进程调用的核心函数设置跟踪 |
Trace类 | fulltrace | 显示所有事件调用函数的情况 |
| ltrace | 激活对某个标签的跟踪 |
| mtrace | 针对某个指定模块,显示调用该模块所执行的程序 |
| deltrace | 取消对某个标签的跟踪 |
Process类 | promap | 打印进程模块当前包含的进程信息 |
| prodiag | 执行隶属于某进程诊断块中的程序 |
| proldiag | 将诊断块中的标签激活,并执行诊断块中的程序 |
| prolstop | 设置标记断点 |
Diagnostic Blocks | proldiag | 执行目标进程的诊断区程序 |
proldiag 带的参数有2 个,分别是进程ID 和标签(label)。它的效果等同于3条指令的叠加,首先ltrace 激活标签;然后prodiag 执行进程诊断块中的程序,并且打印标签被激活程序段的信息;最后,在执行完程序后deltrace 取消对标签的跟踪。
deltrace 取消对某个标签的跟踪,与激活标签不同的是,它带的参数为trace_id,而不是标签本身,但是trace_id 是系统分配的,不为我们所知,需要通过输入status 指令查看。
2)针对结构错误(Structural Error)的ODB 调试实例
我们得知是第13 个事件出错
我们让仿真停止在第13 个事件执行之前,如下所示,在事件栏中我们看到当前中断类型为流中断,另外还有两个ID 号,其中execution ID 为当前事件ID,schedule ID 标明当前事件处在仿真核心事件列表中的位置,这两个值可能不相同,因为随着事件的增加和消减,仿真核心列表是不断变化的。Source 指明了当前中断源,例如这里表明中断是由全球网top下的pksw1 子网下的node_0 节点下的src 进程模块在执行事件9 时发出来的。Data 指明了与当前中断相关的信息,这里表明封包是从1 号流端口接收来的,封包的ID 为0。Module指明当前中断的接收方,top.pksw1.node_0.proc(processor)指明了物件的阶层关系和类型,OPNET 中最高层物件永远为top,代表全球网。
接着将执行第13 个事件,为了观察间中执行的代码,我们启动完全跟踪(fulltrace),之后输入status 命令就可以查看已设定的中断和跟
踪有哪些。
接下来输入next 命令,让仿真执行下一个事件。从进程信息栏中我们可以看出当前进程(Invoking process)的ID 号为1,进程模型的名称为pksw_nd_proc。进程收到中断后将执行红色idle状态的窗口执行代码(exit executives),首先判断中断的类型为流中断,接着获取流中断索引号,其值为1。执行完之后满足条件SRC_ARRVL,从idle 状态再次转移到idle 状态,同时执行条件子程序xmt(),间中试图从0 号流索引的包流中获取封包,这时我们已经看出一些端倪,应该是从1 号流索引收包才对,果然指示包流中并没有数据包(strm.is empty),接着出现封包指针为空的错误提示,因此op_pk_nfd_set_int32 代码肯定不能正常运行。到这里我们找到了错误所在,只要把op_pk_get(0)改为op_pk_get(1)就行了。
注意到最后两行的提示Press (ENTER) to continue。这是在edit->preference 中,console_exit_pause设定为TRUE 带来的结果。
2)针对逻辑错误的ODB 调试实例
例如某个端口正常情况下应该收到数据包,吞吐量却为零。
我们通过pktrace 命令启动对Packet ID 为1 的封包的跟踪,pktrace 跟踪有关包的所有执行语句,直至包被销毁。在新版的OPNET 中支持对包设置断点,任何涉及到处理封包的事件到达时仿真都会停下来,如下图所示,要推进事件必须再次输入继续仿真的命令(cont 或者next),这样让我们清晰查看包在每个事件中的行为。
下面我们再次输入cont 命令,包被top.pksw1.node_1.src 进程模块创建,接着被
top.pksw1.node_1.proc 进程接收并将其dest_address 域设定为1,如下图方框所示,之后被传输到底层。
接着我们再连续输入cont 命令,直到包被交换机节点进程(top.pksw1.hub.hub)接收,发现包的dest_address 被修改为0,如图所示。我们仔细想一下交换机应该取出包的目的地址,根据目的地址再选择正确的路由,而不是去重新设置,这时我们已经找到了这个逻辑上的错误,只要将op_pk_nfd_set_int32 改为op_pk_nfd_get_int32 问题就解决了。
3)针对进程模块的ODB 调试
由于进程是实现整个仿真的基础,因此针对进程的调试是ODB 调试的主要内容,主要包括四个部分,分别是定位进程、控制进程、跟踪进程及显示进程状态。
(1)定位进程
(11)promap <Objid>显示指定进程模块包含的所有进程的Process ID。
(12)promap all 显示所有仿真中存在的进程Process ID,如图所示。
进程标记, 用来更直观地区别同种类型进程(父进程产生多个进程,这些进程
是由同一个进程模型派生而来的),使用这种方法必须在进程模块中通过
op_pro_tag_set(pro_handle,tag_string)来设置进程标记。
(2)控制进程
ODB 调试时输入指令prostop <proc_id>为进程调用设置断点。无论什么中断,只要调
用该进程,仿真都会暂时中断。首先在进程模型中通过op_prg_odb_bkpt(label)为指定位置设置断点,这个断点以标签(label)标识,ODB 调试过程中如果通过prolstop <proc_id> <label>激活了该断点标签,则仿真会在被设置标签的位置中断。
(3)跟踪进程
(31)protrace <proc_id>跟踪并显示调用指定进程的信息。
(32)ltrace <label>是最实用的调试指令,例如如果想找到程序中的逻辑错误,可以在可疑的程序段中加入这样一个语句。
if (op_prg_odb_ltrace_active("label")==OPC_TRUE){ printf(...)},
printf 打印需要观察的变量,在ODB 中激活标签就可以看到仿真中这些变化的情况。
(33)proltrace <proc_id> <label>显示指定进程中设置过某个标签的位置所对应的信息。
(4)显示进程状态
(41)在进程的诊断块(Diagnostic block)中编写与调试相关的程序后,ODB 调试时
就能通过prodiag <proc_id>执行诊断块对应的程序,从而显示调试信息。
(42)proldiag <proc_id> <label>指令相当于prodiag <proc_id>与ltrace <label>的叠加。
ODB>objmap proc dp
查看进程对象的信息,dp 为进程的名字。
ODB> promap 2
查看进程模块当前被激活的进程信息,其中2 为Objid。
ODB> prostop 0
为进程设置断点,其中0 为process id
ODB> protrace 0
激活进程跟踪信息显示, 其中0 为process id
ODB> status
显示指令状态。
ODB> cont,继续执行事件直至仿真运行到断点位置。
ODB> next,执行下一个事件。
子进程dp_child 中的有设置进程标记的语句:sprinf(tag_string,"DynProc Child (stream %d)", op_intrpt_strm());
根据包流的输入端口索引号制定进程标记tag_string
op_pro_tag_set(op_pro_self(),tag_string);
为进程自身设置标签。
ODB> delstop all
ODB> deltrace all
ODB> status,再次显示指令状态,发现断点已经被取消
ODB> prolstop 6 "dynproc_50"
这时激活进程6 的断点标签,
当执行到进程中的op_prg_odb_bkpt("dynproc_50")语句时程序中止运行。
ODB> prodiag 6,运行进程诊断块的代码