Technical Blog Post
Abstract
浅析IBM i上C/C++应用程序编译调试方法
Body
软件调试对于编程人员来说有着非常重要的意义,应用程序功能的开发和完善是在不断调试中完成的。本文围绕IBM i上C/C++语言的编译调试问题进行分析和介绍。主要介绍两方面的内容:一是system i上C/C++源程序编译方法,二是C/C++程序在system i上的调试方法。IBM i上编辑应用程序代码可以选择绿屏或者其他平台编辑后上传的方式,这里采用windows 端自选编辑器编辑示例程序test.c,然后通过FTP将源文件传输到system i上编译调试的方式。假设已通过此方式将C的源文件bubbleSort.c、bubbleFun.c、bubbleMain.c上传到目录/myApp下。
一 C/C++源程序编译方法
System i上编译C/C++应用程序采用ILE的程序模型,源文件首先被编译成MODULE对象,Module对象可以连接生成PGM对象和SRVPGM对象。为便于理解,可以将Windows平台的obi、exe、dll类型对照到IBM i的MODULE、PGM、SRVPGM对象。这里需要注意,一个PGM可以链接一个或者多个MODULE和SRVPGM,一个SRVPGM同样可以链接多个MODULE和不同的SRVPGM。下面首先介绍MODULE的创建方法,然后根据生成的MODULE介绍如何创建PGM和SRVPGM。在介绍创建MODULE、PGM、SRVPGM详细步骤中,仅对本文涉及到的参数项进行解释说明,其他选项的详细信息可参阅IBM info center。
创建MODULE
a)绿屏命令行下输入“CRTCMOD”,按F4提示功能键。准备调用ILE C编译器。
MODULE:待编译源文件生成的目标MODULE名。
Library:生成的MODULE放置的目标库名。
Source stream file:源代码所在的IFS路径。填写源文件的位置时可以采用IFS文件路径或QSYS.LIB MBR文件的方式(须填写Source file、library和Source member),本例采用文件IFS路径。
Text 'description': 该MODULE的描述信息(非必填信息)。
本例中上述选项所填的信息如下图所示,其中APP为系统中之前创建的library。
一 C/C++源程序编译方法
System i上编译C/C++应用程序采用ILE的程序模型,源文件首先被编译成MODULE对象,Module对象可以连接生成PGM对象和SRVPGM对象。为便于理解,可以将Windows平台的obi、exe、dll类型对照到IBM i的MODULE、PGM、SRVPGM对象。这里需要注意,一个PGM可以链接一个或者多个MODULE和SRVPGM,一个SRVPGM同样可以链接多个MODULE和不同的SRVPGM。下面首先介绍MODULE的创建方法,然后根据生成的MODULE介绍如何创建PGM和SRVPGM。在介绍创建MODULE、PGM、SRVPGM详细步骤中,仅对本文涉及到的参数项进行解释说明,其他选项的详细信息可参阅IBM info center。
创建MODULE
a)绿屏命令行下输入“CRTCMOD”,按F4提示功能键。准备调用ILE C编译器。
MODULE:待编译源文件生成的目标MODULE名。
Library:生成的MODULE放置的目标库名。
Source stream file:源代码所在的IFS路径。填写源文件的位置时可以采用IFS文件路径或QSYS.LIB MBR文件的方式(须填写Source file、library和Source member),本例采用文件IFS路径。
Text 'description': 该MODULE的描述信息(非必填信息)。
本例中上述选项所填的信息如下图所示,其中APP为系统中之前创建的library。
b) 按F10 → PageDown功能键。
Output options: 表示是否产生相应的编译listing结果信息,该信息可显示编译的选项设置、过程和结果用以更正程序语法错误。选择*PRINT代表在SPOOL FILE中生成编译信息,通过WRKSPLF命令查看该文件。
Title: 生成的listing文件的主标题,最大80字符。用于标识生成的文件。
Subtitle: 生成的listing文件的副标题,最大80字符。
本例中上述选项所填信息如下图所示:
Output options: 表示是否产生相应的编译listing结果信息,该信息可显示编译的选项设置、过程和结果用以更正程序语法错误。选择*PRINT代表在SPOOL FILE中生成编译信息,通过WRKSPLF命令查看该文件。
Title: 生成的listing文件的主标题,最大80字符。用于标识生成的文件。
Subtitle: 生成的listing文件的副标题,最大80字符。
本例中上述选项所填信息如下图所示:
c)按PageDown功能键。
Debugging view:控制如何编译带有调试信息的程序,*SOURCE表示编译生成的调试信息中带有源代码的路径,调试时可以自动找到并显示源代码,但源代码的路径在编译后不可以移到其他地方。*STMT表示生成的调试信息中不包含源代码的位置信息,但包含源程序语句序号的信息。用户可以根据过程名和语句编号来设置断点。*LIST表示编译生成的调试信息中包含完整的源代码清单,编译完成后,该源代码清单与源程序文件没有直接关系,属于不同的副本,故源代码的路径可以更改。其编译出的程序源代码尺寸较前两种大。此处我们填写为*ALL,同时生成前面三种调试信息。
本例中Debugging view 填写*ALL, 未涉及的选项标签在本例中为默认设置。
d)正确填写以上信息后按回车键。
若源代码书写正确编译成功,将在所填写的库位置产生相应的带有debug信息的TESTMODULE *MODULE对象。若编译出错,可到spool File中查找BUBBLE文件根据错误的提示修改源码,重复上述编译步骤。
创建PGM
应用上面生成的MODULE BUBBLE对象创建PGM方法如下:
a) 绿屏命令行下输入CRTPGM,按F4提示功能键。
Program: 待生成的PGM名。
Library: 生成的程序所放置的目标位置库名。
Module: 生成该PGM所需要的MODULE,可以填写一个或多个MODULE。
Library : 所添MODULE的位置库名。
Text 'description':待生成的PGM的描述信息。
本例中上述选项所填信息如下图所示:
Debugging view:控制如何编译带有调试信息的程序,*SOURCE表示编译生成的调试信息中带有源代码的路径,调试时可以自动找到并显示源代码,但源代码的路径在编译后不可以移到其他地方。*STMT表示生成的调试信息中不包含源代码的位置信息,但包含源程序语句序号的信息。用户可以根据过程名和语句编号来设置断点。*LIST表示编译生成的调试信息中包含完整的源代码清单,编译完成后,该源代码清单与源程序文件没有直接关系,属于不同的副本,故源代码的路径可以更改。其编译出的程序源代码尺寸较前两种大。此处我们填写为*ALL,同时生成前面三种调试信息。
本例中Debugging view 填写*ALL, 未涉及的选项标签在本例中为默认设置。
d)正确填写以上信息后按回车键。
若源代码书写正确编译成功,将在所填写的库位置产生相应的带有debug信息的TESTMODULE *MODULE对象。若编译出错,可到spool File中查找BUBBLE文件根据错误的提示修改源码,重复上述编译步骤。
创建PGM
应用上面生成的MODULE BUBBLE对象创建PGM方法如下:
a) 绿屏命令行下输入CRTPGM,按F4提示功能键。
Program: 待生成的PGM名。
Library: 生成的程序所放置的目标位置库名。
Module: 生成该PGM所需要的MODULE,可以填写一个或多个MODULE。
Library : 所添MODULE的位置库名。
Text 'description':待生成的PGM的描述信息。
本例中上述选项所填信息如下图所示:
b)按F10 → PageDown功能键。
此处介绍下Bind service program相关选项,当绑定 SRVPGM时需要填写下面信息。
Service program:绑定的SRVPGM名字。
Library:绑定的SRVPGM所在的库。
Activation:激活服务程序的方式。*IMMED代表调用应用程序时立即激活服
务程序,*DEFER表示当应用程序调用到服务程序导出的过程时再加载。我们生成的第一个PGM中不包含SRVPGM,上述信息不填,采用默认值。后文调试SRVPGM时,需要自行创建绑定有待调试SRVPGM的PGM。c++源文件生成MODULE的命令是CRTCPPMOD,生成PGM命令仍为CRTPGM。
创建SRVPGM
System i上ILE SRVPGM不能独立运行,但可以被其他SRVPGM和PGM调用。一般创建SRVPGM所需的MODULE对象的源文件不包含main()函数。创建SRVPGM前首先创建MODULE,命名为BUBBLESRV,方法与前文介绍的相同,注意开启DEBUG参数。接下来在绿屏下输入CRTSRVPGM,按F4提示功能键。
Service program:待生成的目标SRVPGM名称。
Library: 待生成的SRVPGM所在的库名字。
Module:生成该SRVPGM所链接的MODULE名字。一个SRVPGM可以链接一个或多个MODULE。
Library:被链接的MODULE所在的库名字。
Export: 标识此SRVPGM导出的函数或者数据。可以通过源文件标识特定的导出函数或者数据,此时需要填写Export source file,Library,Export source member。也可以填写*ALL,表示MODULE中导出的的所有函数和数据在生成的SRVPGM中也将全部导出。
本例中上述选项所填信息如下图所示:
此处介绍下Bind service program相关选项,当绑定 SRVPGM时需要填写下面信息。
Service program:绑定的SRVPGM名字。
Library:绑定的SRVPGM所在的库。
Activation:激活服务程序的方式。*IMMED代表调用应用程序时立即激活服
务程序,*DEFER表示当应用程序调用到服务程序导出的过程时再加载。我们生成的第一个PGM中不包含SRVPGM,上述信息不填,采用默认值。后文调试SRVPGM时,需要自行创建绑定有待调试SRVPGM的PGM。c++源文件生成MODULE的命令是CRTCPPMOD,生成PGM命令仍为CRTPGM。
创建SRVPGM
System i上ILE SRVPGM不能独立运行,但可以被其他SRVPGM和PGM调用。一般创建SRVPGM所需的MODULE对象的源文件不包含main()函数。创建SRVPGM前首先创建MODULE,命名为BUBBLESRV,方法与前文介绍的相同,注意开启DEBUG参数。接下来在绿屏下输入CRTSRVPGM,按F4提示功能键。
Service program:待生成的目标SRVPGM名称。
Library: 待生成的SRVPGM所在的库名字。
Module:生成该SRVPGM所链接的MODULE名字。一个SRVPGM可以链接一个或多个MODULE。
Library:被链接的MODULE所在的库名字。
Export: 标识此SRVPGM导出的函数或者数据。可以通过源文件标识特定的导出函数或者数据,此时需要填写Export source file,Library,Export source member。也可以填写*ALL,表示MODULE中导出的的所有函数和数据在生成的SRVPGM中也将全部导出。
本例中上述选项所填信息如下图所示:
二 C/C++绿屏调试方法
下面介绍IBM I 上C/C++应用程序绿屏环境下的调试方法。调试方法可以按照调试过程中JOB的个数简单分为两种类型,单job调试和两个job之间联合调试。所谓单job调试是指被调试的PGM对象在当前的JOB中运行和调试。所谓两个job之间联调是指被调试的PGM对象在当前job中调试,但在另外一个job中运行。通过启动特定的服务job监控调用被调试PGM的job,服务job负责绑定并触发绿屏调试界面。下面分别进行阐述。
IBM i绿屏下单job调试
在一个job中调试PGM是C/C++应用程序调试的最简单情况,它的基本原理可以概括为:启动待调试的PGM源码 → 在源码上设置断点 → 退出源码界面 → 触发待调试的PGM → 调试 → 关闭。详细步骤如下:
a) 启动待调试PGM。
绿屏命令行下执行“STRDBG APP/BUBBLE”启动待调试的PGM源码(BUBBLE为上文生成
的PGM对象),源代码调试界面如图所示:
下面介绍IBM I 上C/C++应用程序绿屏环境下的调试方法。调试方法可以按照调试过程中JOB的个数简单分为两种类型,单job调试和两个job之间联合调试。所谓单job调试是指被调试的PGM对象在当前的JOB中运行和调试。所谓两个job之间联调是指被调试的PGM对象在当前job中调试,但在另外一个job中运行。通过启动特定的服务job监控调用被调试PGM的job,服务job负责绑定并触发绿屏调试界面。下面分别进行阐述。
IBM i绿屏下单job调试
在一个job中调试PGM是C/C++应用程序调试的最简单情况,它的基本原理可以概括为:启动待调试的PGM源码 → 在源码上设置断点 → 退出源码界面 → 触发待调试的PGM → 调试 → 关闭。详细步骤如下:
a) 启动待调试PGM。
绿屏命令行下执行“STRDBG APP/BUBBLE”启动待调试的PGM源码(BUBBLE为上文生成
的PGM对象),源代码调试界面如图所示:
b)设置断点/条件断点。
可以通过命令行或功能键设置断点。如br 25将在25行设置断点;br 25 when value=1 将设置条件断点;按功能键F6将在鼠标当前所在行设置断点,断点行以白色显示。断点设置完成后按F12返回。此处我们在行Test.c的第11行设置断点命令br 11。
c) 触发待调试程序。
如何触发已设断点的待调试应用程序是调试的关键步骤,本例相对比较容易,直接执行CL command:“CALL APP/BUBBLE”,待调试程序将被触发并停止在之前所设置的11行断点处,当前运行以白色显示。将如图所示。
可以通过命令行或功能键设置断点。如br 25将在25行设置断点;br 25 when value=1 将设置条件断点;按功能键F6将在鼠标当前所在行设置断点,断点行以白色显示。断点设置完成后按F12返回。此处我们在行Test.c的第11行设置断点命令br 11。
c) 触发待调试程序。
如何触发已设断点的待调试应用程序是调试的关键步骤,本例相对比较容易,直接执行CL command:“CALL APP/BUBBLE”,待调试程序将被触发并停止在之前所设置的11行断点处,当前运行以白色显示。将如图所示。
d) 调试程序。此时仍然有命令行和功能键两种方式控制debug程序的执行。如F10单步执行, F11显示光标所在处变量的值。其调试方法和技巧与其他平台类似。可以在绿屏命令行下执行”HELP“命令查询具体的命令的使用方法。此处我们单步执行,按F10程序执行一行如图所示:
快捷键对应的命令可以参照下表:
功能 | 快捷键 | 命令 |
显示所有支持的Debug命令 | F1 | HELP |
光标当前行设置为断点 | F6 | BREAK |
单步执行,跳过函数。 | F10 | STEP |
显示光标所在处变量的值 | F11 | EVAL |
运行到下一个断点 | F12 | NEXT |
查看当前模块的所有断点 | F13 | NO |
查看module列表 | F14 | NO |
切换视图模式 | F15 | NO |
查看调用栈 | F21 | NO |
单步执行,进入函数内部 | F22 | STEP INTO |
查看输出窗口 | F23 | WATCH |
调试程序可以反复修改并调用call命令触发调试,也可以通过F14功能键添加更多的调试程序。需要注意的是STRDBG命令只能成功执行一次,即针对一个调试程序有效,对程序成功调试之后需调用ENDDBG命令结束程序调试,否则无法调试下一个不同的程序。
IBM i绿屏下两个Job联合调试
上面的调试属于在一个job内完成调试工作,但在实际应用中很多待调试程序以服务程序对象SRVPGM方式存在所以如何调用SRVPGM、如何触发DEBUG调试器是调试SRVPGM的关键。IBM i上利用服务JOB来调试ILE程序或者ILE服务程序,调试器和被调试程序将位于不同的JOB中,即两个job间联合调试。其基本原理是:作为服务job B连接到job A,服务job B负责设置断点和调试程序。而调试程序的触发则是在job A中完成的。详细的调试步骤为:
a)编译带有调试信息的ILE服务程序。
创建MODULE:CRTCMOD MODULE(APP/SRVMOD)
SRCSTMF('/myApp/bubbleFun.c') OUTPUT(*PRINT) DBGVIEW(*ALL)
创建SRVPGM:CRTSRVPGM SRVPGM(APP/SRV)
MODULE(APP/SRVMOD) EXPORT(*ALL)
b) 编译带有调试信息的调用ILE服务程序的PGM。
创建MODULE:CRTCMOD MODULE(APP/SRVP) SRCSTMF('/myApp/bubbleMain.c') OUTPUT(*PRINT) DBGVIEW(*ALL)
创建PGM:CRTPGM PGM(APP/BUBBLEMAIN) MODULE(APP/SRVP) BNDSRVPGM((APP/SRV *IMMED))
c) 启动绿屏终端A,A中将会触发调试的服务程序。
d) 启动另一个绿屏终端B,在终端B中通过STRSRVJOB命令来捕获终端A中触发的服务程序。该命令有Job name、User、Number三个参数需要填写,代表需要被监控的终端A上的作业。终端A上shift+ESC回车,选择3,即可显示A上终端作业,如图:
IBM i绿屏下两个Job联合调试
上面的调试属于在一个job内完成调试工作,但在实际应用中很多待调试程序以服务程序对象SRVPGM方式存在所以如何调用SRVPGM、如何触发DEBUG调试器是调试SRVPGM的关键。IBM i上利用服务JOB来调试ILE程序或者ILE服务程序,调试器和被调试程序将位于不同的JOB中,即两个job间联合调试。其基本原理是:作为服务job B连接到job A,服务job B负责设置断点和调试程序。而调试程序的触发则是在job A中完成的。详细的调试步骤为:
a)编译带有调试信息的ILE服务程序。
创建MODULE:CRTCMOD MODULE(APP/SRVMOD)
SRCSTMF('/myApp/bubbleFun.c') OUTPUT(*PRINT) DBGVIEW(*ALL)
创建SRVPGM:CRTSRVPGM SRVPGM(APP/SRV)
MODULE(APP/SRVMOD) EXPORT(*ALL)
b) 编译带有调试信息的调用ILE服务程序的PGM。
创建MODULE:CRTCMOD MODULE(APP/SRVP) SRCSTMF('/myApp/bubbleMain.c') OUTPUT(*PRINT) DBGVIEW(*ALL)
创建PGM:CRTPGM PGM(APP/BUBBLEMAIN) MODULE(APP/SRVP) BNDSRVPGM((APP/SRV *IMMED))
c) 启动绿屏终端A,A中将会触发调试的服务程序。
d) 启动另一个绿屏终端B,在终端B中通过STRSRVJOB命令来捕获终端A中触发的服务程序。该命令有Job name、User、Number三个参数需要填写,代表需要被监控的终端A上的作业。终端A上shift+ESC回车,选择3,即可显示A上终端作业,如图:
f) 在终端A中启动一个作业,在该作业中调用被调试的ILE服务程序,此过程是单Job调试方式中的debug触发,执行CALL LCF/ TESTMAIN。
g) 当终端A中的作业调用到ILE服务程序提供的某个过程时,终端B将自动显示调试界面,并且程序将停止在上文在A终端设置的断点处。如图所示:
h) 在终端B中,利用上文介绍的PGM对应的调试命令进行调试。
i) 调试结束后,终端B中按下F3或者F12功能键退出调试器。在终端B中,命令行下执行“ENDDBG”退出调试模式。在终端B中,命令行下执行“ENDSRVJOB”,结束终端B对终端A中作业的调试。
以上介绍了System i上C/C++应用程序绿屏编译调试MODULE、PGM、SRVPGM的基本方法,在实际应用中,应用程序的编译还需考虑很多因素。由于篇幅所限,很多详细的编译选项未全部阐述,读者可以根据上述编译命令查阅相关文档自行尝试。绿屏调试是system i调试的一种方式,GUI方式调试具有更好的人机交互界面,有兴趣的读者可以尝试。
i) 调试结束后,终端B中按下F3或者F12功能键退出调试器。在终端B中,命令行下执行“ENDDBG”退出调试模式。在终端B中,命令行下执行“ENDSRVJOB”,结束终端B对终端A中作业的调试。
以上介绍了System i上C/C++应用程序绿屏编译调试MODULE、PGM、SRVPGM的基本方法,在实际应用中,应用程序的编译还需考虑很多因素。由于篇幅所限,很多详细的编译选项未全部阐述,读者可以根据上述编译命令查阅相关文档自行尝试。绿屏调试是system i调试的一种方式,GUI方式调试具有更好的人机交互界面,有兴趣的读者可以尝试。
作者:李长锋,王大炜,王定国
[{"Business Unit":{"code":"BU058","label":"IBM Infrastructure w\/TPS"},"Product":{"code":"SWG60","label":"IBM i"},"Component":"","Platform":[{"code":"PF025","label":"Platform Independent"}],"Version":"","Edition":"","Line of Business":{"code":"LOB57","label":"Power"}}]
UID
ibm11145428