ibm-information-center/dist/eclipse/plugins/i5OS.ic.apiref_5.4.0.1/apiexusdeb.htm

1513 lines
75 KiB
HTML
Raw Permalink Normal View History

2024-04-02 14:02:31 +00:00
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en-us" xml:lang="en-us">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="security" content="public" />
<meta name="Robots" content="index,follow" />
<meta http-equiv="PICS-Label" content='(PICS-1.1 "http://www.icra.org/ratingsv02.html" l gen true r (cz 1 lz 1 nz 1 oz 1 vz 1) "http://www.rsac.org/ratingsv01.html" l gen true r (n 0 s 0 v 0 l 0) "http://www.classify.org/safesurf/" l gen true r (SS~~000 1))' />
<meta name="DC.Type" content="reference" />
<meta name="DC.Title" content="Example: Using source debugger APIs" />
<meta name="abstract" content="The ILE source debugger APIs allow an application developer to write a debugger for ILE programs." />
<meta name="description" content="The ILE source debugger APIs allow an application developer to write a debugger for ILE programs." />
<meta name="DC.Relation" scheme="URI" content="apiexmp.htm" />
<meta name="copyright" content="(C) Copyright IBM Corporation 1998, 2006" />
<meta name="DC.Rights.Owner" content="(C) Copyright IBM Corporation 1998, 2006" />
<meta name="DC.Format" content="XHTML" />
<meta name="DC.Identifier" content="apiexusdeb" />
<meta name="DC.Language" content="en-us" />
<!-- All rights reserved. Licensed Materials Property of IBM -->
<!-- US Government Users Restricted Rights -->
<!-- Use, duplication or disclosure restricted by -->
<!-- GSA ADP Schedule Contract with IBM Corp. -->
<link rel="stylesheet" type="text/css" href="./ibmdita.css" />
<link rel="stylesheet" type="text/css" href="./ic.css" />
<title>Example: Using source debugger APIs</title>
</head>
<body id="apiexusdeb"><a name="apiexusdeb"><!-- --></a>
<!-- Java sync-link --><script language="Javascript" src="../rzahg/synch.js" type="text/javascript"></script>
<h1 class="topictitle1">Example: Using source debugger APIs</h1>
<div><p>The ILE source debugger APIs allow an application developer to
write a debugger for ILE programs.</p>
<div class="section"><p>One might ask why this would ever be done when an ILE debugger
is provided with <span class="keyword">i5/OS™</span>.
There are several reasons why an application developer might want to use these
APIs to write a different ILE debugger:</p>
<ul><li>A debugger running on a workstation could be built that would debug ILE
programs running on the iSeries™ server. This would allow a debugger to be
written that would take advantage of Windows<sup>®</sup> and other ease-of-use interfaces
available on the workstation. The workstation debugger would communicate with
code running on the iSeries server. The code running on the iSeries server
would use the debugger APIs.</li>
<li>The writer of an ILE compiler on the iSeries server might want to write a
debugger to take advantages of the features of the language. The <span class="keyword">i5/OS</span> debugger is a more general-purpose
debugger made for all ILE languages.</li>
<li>A debugger could be written with functions not available on the <span class="keyword">i5/OS</span> ILE debugger.</li>
</ul>
<div class="note"><span class="notetitle">Note:</span> Read the <a href="codedisclaimer.htm">Code license and disclaimer information</a> for important
legal information.</div>
</div>
<div class="section"><h4 class="sectiontitle">Source debugger APIs overview</h4><p>The ILE source debugger
APIs can be divided into several groups. These include APIs that:</p>
<ul><li>Start and end the debug session </li>
<li>Add programs and modules to debug </li>
<li>Manipulate text views in a program </li>
<li>Add and remove breakpoints, steps, and so on</li>
</ul>
<p>Besides APIs, there are two user exits that get called:</p>
<ul><li>The Source Debug program gets called when the Start Debug (STRDBG), Display
Module Source (DSPMODSRC), and End Debug (ENDDBG) CL commands are entered.</li>
<li>The Program Stop Handler gets called when an ILE program being debugged
hits a breakpoint, step, and so on.</li>
</ul>
<p>To demonstrate how these APIs are used, this topic presents an example
debugger with complete code examples and an explanation of what the APIs do.</p>
<p>The
ILE debugger that comes with <span class="keyword">i5/OS</span> uses
the debugger APIs just as a user-written debugger would. There is nothing
special about the <span class="keyword">i5/OS</span> debugger.
Its functions could be done by an application developer using the debugger
APIs and other <span class="keyword">i5/OS</span> APIs.</p>
</div>
<div class="section"><h4 class="sectiontitle">Scenario: A simple debugger</h4><p>Consider a simple scenario
in which the user wishes to debug an ILE program.</p>
<ol><li>From the command entry screen, the user enters the Start Debug (STRDBG)
command, passing it the name of an ILE program to debug. <pre>STRDBG P1</pre>
</li>
<li>The ILE debugger screen is displayed, showing the source of a module in
the ILE program being debugged. From this screen, the user adds a breakpoint
and then exits.</li>
<li>Back at the command entry screen, the user runs the ILE program that is
being debugged. <pre>CALL P1</pre>
</li>
<li>The ILE program hits the breakpoint previously set. The ILE debugger screen
is displayed, highlighting in the source where the program has stopped at
the breakpoint.</li>
<li>The user displays a variable in the program being debugged.</li>
<li>The user exits the ILE debugger, allowing the ILE program to run to completion.
The program ends.</li>
<li>Back at the command entry screen, the user ends the debug session. <pre>ENDDBG</pre>
</li>
</ol>
<p>This is the simplest of debug scenarios, but it illustrates how <span class="keyword">i5/OS</span>, the debugger user exits,
and the debugger APIs interact.</p>
<p>The following figure shows the various
interactions.</p>
<br /><img src="Rv3f176.gif" alt="debug scenarios" /><br /><p>A detailed explanation of the scenario follows:</p>
<ol><li>The Start Debug (STRDBG) CL command is used to start the debug session.
By default, if an ILE program is specified on the command, the <span class="keyword">i5/OS</span> ILE
debugger user exit is called. A different user exit (called the Source Debug
program) can be specified on the Start Debug command by specifying a program
name on the SRCDBGPGM parameter. <p>When the Source Debug program is called,
it is passed a reason field, which indicates why it was called. The *START
reason is passed to it by the Start Debug command, indicating that the ILE
debugger is to start itself and do any necessary initialization. When the
*START reason is indicated, the names of any ILE programs on the Start Debug
command are also passed to the Source Debug program.</p>
</li>
<li>In this scenario, the system Source Debug program initializes itself.
It calls the QteStartSourceDebug API, which tells the system that ILE debugging
is to be done. The name of a program stop handler program is passed to this
API. The stop handler is a program that the system calls when an ILE program
hits a breakpoint, step, or other condition where the system stops the program
for the debugger. <p>The Source Debug program must indicate to the system
that the ILE programs specified on the Start Debug command are to be debugged.
To do this, the QteRetrieveModuleViews API is called, once for each ILE program
specified on the Start Debug command. In this scenario, the API is called,
passing it the name of program P1. The purpose of the API is to return information
about the ILE program, including the modules and views of the program. A view
is the source text that is displayed by the debugger for a particular module.</p>
<p>Once
information about the ILE program is obtained, one or more views of the program
must be registered. Once a view is registered, the system can perform various
functions on that view in behalf of the debugger application. For performance
reasons, only the views the user is interested in displaying should be registered.</p>
<p>The
Source Debug program is now done performing the function for the *START reason.
It exits, returning control to the Start Debug command.</p>
</li>
<li>By default, if an ILE program is specified on the Start Debug command,
the ILE debug screen is displayed. To indicate to the ILE debugger that a
screen is to be put up, the Source Debug program is called by the command
again, this time with a reason of *DISPLAY. <p>Because this is the first time
any views for P1 are to be displayed, the ILE debugger must retrieve the text
to display. The first view of the first module of the program is selected
as the default view to display.</p>
<p>The Source Debug program calls the QteRetrieveViewText
API to retrieve the text associated with the default view. Next, in case this
program is already on the stack and stopped, the QteRetrieveStoppedPosition
API is called to check. If the program were on the stack, the source would
be positioned to the statement where the program was stopped, and that line
would be highlighted. In this scenario, the program is not yet on the stack,
so the first line of the source will appear at the top of the screen, and
no line will be highlighted.</p>
<p>The Source Debug program next calls User
Interface Manager (UIM) APIs to display the source on the screen.</p>
</li>
<li>At this point, the source screen is displayed showing the text of the
first view in the first module of the first ILE program specified on the Start
Debug command. From this screen, the user can enter debug commands or do other
options provided by the debugger application. <p>In this scenario, the user
adds a breakpoint to a line in the ILE program P1 being debugged. When a command
is entered, the UIM APIs call a program which is part of the ILE debugger
to process the command.</p>
<p>To process the breakpoint, the QteAddBreakpoint
is called. It is passed a view number which indicates the view being displayed,
and a line number in that view. A breakpoint is added to the program by the
API.</p>
</li>
<li>Back to the UIM screen, the user exits the ILE debugger. Once at the command
entry screen, the user then runs the program P1 which has the breakpoint.</li>
<li>When P1 hits the breakpoint, the system calls the program stop handler
defined by the QteStartSourceDebug API. The Program Stop Handler calls UIM
to put up the source for the module where the program has stopped because
of the breakpoint. The line is highlighted to show the user exactly where
the program has stopped.</li>
<li>From the source debugger screen, the user displays a variable in program
P1 which is stopped at the breakpoint. UIM calls the debugger to process the
command. The debugger calls the QteSubmitDebugCommand API, which retrieves
the value of the variable to be displayed. The debugger then displays this
value on the screen.</li>
<li>The user now exits from the source debugger screen. This allows P1, which
was stopped at a breakpoint, to continue running. When P1 ends, the user is
back at the command entry screen.</li>
<li>The user ends the debug session by entering the End Debug (ENDDBG) CL
command. The system calls the Source Debug program, passing it a reason of
*STOP. The Source Debug program calls the QteEndSourceDebug API to indicate
to the system that ILE debugging has ended. It then tears down its own environment
(closes files, frees space, and so on) and then ends. The End Debug command
completes, and the user is back to the command entry, the debug session having
ended.</li>
</ol>
</div>
<div class="section"><h4 class="sectiontitle">Example: Source debugger</h4><p>This section discusses
an example ILE debugger that demonstrates the use of some of the ILE debugger
APIs. Each function in the C program is discussed along with the APIs that
they call. Although the entire program listing is printed later (see <a href="#apiexusdeb__HDRDBSAMP">Debugger code sample</a>), each function or piece of code is printed
with the section where it is discussed to make reading the code easier.</p>
<p>The
example debugger does not use all ILE debugger APIs. Its function is limited.
After the discussion of the code, the APIs and some functions not covered
are discussed.</p>
<p id="apiexusdeb__Header_74"><a name="apiexusdeb__Header_74"><!-- --></a><strong>Compiling the debugger</strong></p>
<p>The
Create C Module (CRTCMOD) command compiles the source code of the debugger.
It is compiled into module DEBUG.</p>
<p>The Create Program (CRTPGM) command
creates program DEBUG from module DEBUG. It is necessary to bind to service
program QTEDBGS so that the calls to the debugger APIs are resolved. It is
also important to use activation group QTEDBGAG. This is an activation group
that cannot be destroyed while the job is in debug mode. Thus, all static
variables in program DEBUG remain intact throughout the debugging of the ILE
program. Only when ENDDBG is entered can the activation group be destroyed,
even if the Reclaim Resources (RCLRSC) CL command is entered.</p>
<p id="apiexusdeb__Header_75"><a name="apiexusdeb__Header_75"><!-- --></a><strong>Starting
the debugger</strong></p>
<p>The example debugger consists of a single program called
DEBUG. The program is used as the Source Debug program as well as the Program
Stop Handler. The program determines how many parameters it is being called
with, and with this information it does the function of one or the other of
the user exits.</p>
<p>The debugger can debug only one ILE program. This program
is specified on the Start Debug CL command. The program cannot be removed
from debug until ENDDBG is done. No new programs can be added.</p>
<p>To debug
an ILE program P1 with this sample debugger, the following CL command could
be entered:</p>
<pre>STRDBG P1 SRCDBGPGM(DEBUG)</pre>
<p>Note that
DEBUG must be in the library list when STRDBG is done.</p>
<p>If the command
is done, P1 is called twice, once as a Source Debug program given a reason
of *START, and again as a Source Debug program given a reason of *DISPLAY.</p>
<p>Other
variations of the Start Debug command can be given with different results.
For example, the following CL command causes DEBUG to be called only once
with a reason of *START:</p>
<pre>STRDBG P1 SRCDBGPGM(DEBUG) DSPMODSRC(*NO)</pre>
<p>This
is because STRDBG has been told not to display the debug screen, so the *DISPLAY
reason is not given until the user does the Display Module Source (DSPMODSRC)
CL command.</p>
<p>The following example does not even call DEBUGGER:</p>
<pre>STRDBG SRCDBGPGM(DEBUG)</pre>
<p>This
is because no ILE program is specified. If an ILE program receives an unmonitored
message and the ILE debugger needs to be called, DEBUG is first called with
*START as a Source Debug program. Also, if Display Module Source is entered,
the *START and then the *DISPLAY reason is passed to DEBUG.</p>
<p id="apiexusdeb__Header_76"><a name="apiexusdeb__Header_76"><!-- --></a><strong>Using
the debugger</strong></p>
<p>When the debugger is started, it allows simple debugging
commands to be entered. The C session manager is put up, which scrolls the
users commands and the debugger output. To see a list of the allowable commands,
enter <tt class="sysout">HELP</tt>.</p>
<p>The "list views" command shows
all of the views available in the program being debugged. The text description
of the view is listed, with a sequential number. This number is used by the
"switch" command to switch to that view.</p>
<p>The "list text" command prints
out the text of the current view. Text has a line number next to it. The line
number is used when setting breakpoints or other debug commands.</p>
<p>The
switch command switches the current view. The current view is the view used
when setting breakpoints, displaying variables, viewing text, and so on.</p>
<p>The
"quit" command exits the debugger.</p>
<p>Other commands are interpreted by
the QteSubmitDebugCommand API. This API will be discussed later. An example
command that can be entered is "break n", where n is the line number in the
current view. These commands are similar to the ones allowed in the ILE debugger
shipped with <span class="keyword">i5/OS</span>.</p>
<p id="apiexusdeb__Header_77"><a name="apiexusdeb__Header_77"><!-- --></a><strong>Header files used in debugger</strong></p>
<pre>#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;qtedbgs.h&gt;</pre>
<p>Besides the normal C library header files, an API header file,
qtedbgs.h is included. This file defines the functions exported from service
program QTEDBGS. This service program contains the ILE debugger APIs.</p>
<p id="apiexusdeb__Header_78"><a name="apiexusdeb__Header_78"><!-- --></a><strong>Global variables</strong></p>
<pre>static _TE_VEWL0100_T *pgm_dbg_dta = NULL;
static long current_view = 0; /* current view - defaults to 1st*/
static _TE_OBJLIB_T program_lib; /* name and lib of pgm debugged */</pre>
<p>These are global variables that hold information about the
program being debugged. These variables do not go away when program DEBUG
exits, because they are stored in the activation group which is not destroyed
until the debug session has completed.</p>
<p>The name and library of the program
are stored, as is the current view being debugged. Also, a pointer to a structure
returned by the QteRetrieveModuleViews is saved, as this information is needed
when debugging the various views of the program.</p>
<p id="apiexusdeb__Header_79"><a name="apiexusdeb__Header_79"><!-- --></a><strong>PgmList_t</strong></p>
<pre>typedef struct {
_TE_OBJLIB_T PgmLib; /* Name and Library of program */
_TE_NAME_T PgmType; /* program type, *PGM or *SRVPGM */
} PgmList_t;</pre>
<p>This is the structure of the name, library, and type of the
program being debugged.</p>
<p id="apiexusdeb__Header_80"><a name="apiexusdeb__Header_80"><!-- --></a><strong>main()</strong></p>
<pre>main (int argc, char *argv[]) {
if (argc == 4) /* called as source debug program*/
HandleSession(argv[1], (PgmList_t *)argv[2], *(int
*)argv[3]);
else if (argc == 8) /* called as program stop handler*/
HandleStop((_TE_OBJLIB_T *)argv[1], argv[2],
argv[3], argv[4],
(long *)argv[5], *(int *)argv[6],
argv[7]);
}</pre>
<p>Program DEBUG can be called in two ways. When it is called
by the STRDBG, DSPMODSRC, and ENDDBG CL commands, it is called as the Source
Debug program user exit. It is passed three parameters.</p>
<p>DEBUG can also
be called when a program being debug hits a breakpoint or step. In this case,
it is passed seven parameters.</p>
<p>DEBUG therefore can determine why it
was called by counting the number of parameters it was passed. Remember that
argc includes the program name as the first argument passed.</p>
<p>If argc
is 4 (three parameters passed to DEBUG), function HandleSession is called,
and the three parameters passed to DEBUG are passed to it, typecasted as needed.</p>
<p>If
argc is 8 (seven parameters passed to DEBUG), function HandleStop is called,
and the seven parameters passed to DEBUG are passed to it, typecasted as needed.</p>
<p>If
any other number of parameters are passed to DEBUG, it cannot have been called
from the <span class="keyword">i5/OS</span> debug support,
so DEBUG will just exit.</p>
<p id="apiexusdeb__Header_81"><a name="apiexusdeb__Header_81"><!-- --></a><strong>HandleSession()</strong></p>
<pre>void HandleSession(char reason[10],
PgmList_t ProgramList[],
int ProgramListCount) {
if (memcmp(reason,"*START ",10) == 0) /* reason is *START */
StartUpDebugger(ProgramList, ProgramListCount);
else if ( memcmp(reason,"*STOP ",10) == 0) /* reason is *STOP */
TearDownDebugger();
else if ( memcmp(reason,"*DISPLAY ",10) == 0) /* reason *DISPLAY */
ProcessCommands();
}</pre>
<p>When DEBUG is called as a session handler, it is passed three
parameters. The first parameter is a 10-character array containing a reason
field. This contains the reason why the session handler is called.</p>
<p>When
DEBUG is first called, it is passed a reason of *START, indicating that the
debugger is to initialize for an ILE debug session. When this reason is given,
the second parameter contains a list of ILE programs specified on the STRDBG
command, and the third parameter contains the number of programs specified
on parameter two. From 0 to 10 ILE programs can be specified.</p>
<p>When the
user wishes to see the ILE debugger screen, either from STRDBG or DSPMODSRC,
a reason of *DISPLAY is passed. When the user enters ENDDBG, the *STOP reason
is passed, indicating that the ILE debug session is ending. The second and
third parameters are not used when the reason is *DISPLAY or *STOP.</p>
<p>The
code tests for a reason and calls the appropriate function. There is one function
for each reason that can be passed.</p>
<p id="apiexusdeb__Header_82"><a name="apiexusdeb__Header_82"><!-- --></a><strong>TearDownDebugger()</strong></p>
<pre>void TearDownDebugger(void) {
_TE_ERROR_CODE_T errorCode = {8}; /* errors will be ignored */
/* Call EndSourceDebug to get out of ILE debug mode */
QteEndSourceDebug(&amp;errorCode);
exit(0); /* destroy activation group */
}</pre>
<p>This function is called when the user enters ENDDBG. The debugger
calls the QteEndSourceDebug API which ends ILE debugging. Since an 8 is passed
as the number of bytes provided, the message ID and error data from an error
are not returned to the caller. Thus, any errors from this API (there should
not be any) are ignored.</p>
<p>The exit() function is called, which destroys
the activation group. Thus, all global data defined in the program's variables
are lost. This is ok, since the debug session is ending at this point.</p>
<p id="apiexusdeb__Header_83"><a name="apiexusdeb__Header_83"><!-- --></a><strong>StartUpDebugger()</strong></p>
<pre>void StartUpDebugger(PgmList_t ProgramList[],
int ProgramListCount) {
_TE_ERROR_CODE_T errorCode = {0}; /* exceptions are generated */
_TE_OBJLIB_T StopHandler = {"DEBUG ", "*LIBL "};
int i;
if (ProgramListCount!=1) { /* is only 1 pgm passed on STRDBG*/
printf("Exactly ONE program must be specified on STRDBG.\n");
TearDownDebugger(); /* end debugger */
}
/* Copy program name to global variables */
memcpy(&amp;program_lib, &amp;ProgramList-&gt;PgmLib, 20);
/* Call StartSourceDebug: giving the name and library of the */
/* stop handler. This will start ILE debug mode */
QteStartSourceDebug(&amp;StopHandler, &amp;errorCode);
AddProgram(); /* add program to debug */
}</pre>
<p>This function is passed the second and third parameters which
were passed from the system when it called DEBUG with a reason of *START.
These parameters are the list of programs to be added to debug and the number
of programs in the list. This simple example debugger can only debug one program,
so if any other number of programs were specified on STRDBG, the debugger
just exits.</p>
<p>StartUpDebugger first stores the program/library element
passed to it in a global variable available to all functions. This is the
name and library of the program being debugged. It then calls the QteStartSourceDebug
API to tell the system that an ILE debug session is to begin. The name and
library of program DEBUG are passed to this API as the Program Stop Handler.
Thus, whenever the program being debugged is stopped by the debugger, program
DEBUG will be called.</p>
<p>Finally, the function calls AddProgram to add
the single program to debug.</p>
<p id="apiexusdeb__Header_84"><a name="apiexusdeb__Header_84"><!-- --></a><strong>AddProgram()</strong></p>
<pre>void AddProgram(void) {
_TE_ERROR_CODE_T errorCode = {0}; /* Signal exceptions on error */
_TE_NAME_T Library; /* Lib returned */
_TE_TIMESTAMP_T TimeStamp; /* TimeStamp returned */
int viewIndex;
long int iViewID;
long int iViewLines;
long rtvModViewDataLength = 8; /* size of receiver buffer */
char tempBuffer[8]; /* enough room for header only*/
int i, tempModuleCount;
/* Call QteRetrieveModuleViews to determine the number of bytes */
/* the receiver variable needs to be to hold all of the views for */
/* the program. */
pgm_dbg_dta = (_TE_VEWL0100_T *)tempBuffer;
QteRetrieveModuleViews((char *)pgm_dbg_dta, &amp;rtvModViewDataLength,
"VEWL0100", &amp;program_lib,
"*PGM ", "*ALL ", Library,
&amp;errorCode);
/* Get a buffer large enough to hold all view information */
rtvModViewDataLength = pgm_dbg_dta-&gt;BytesAvailable;
pgm_dbg_dta = (_TE_VEWL0100_T *)malloc(rtvModViewDataLength);
/* Call QteRetrieveModuleViews again, passing a big enough buffer. */
QteRetrieveModuleViews((char *)pgm_dbg_dta, &amp;rtvModViewDataLength,
"VEWL0100", &amp;program_lib,
"*PGM ", "*ALL ", Library,
&amp;errorCode);
/* If number of elements is zero, program is not debuggable. */
if (pgm_dbg_dta-&gt;NumberElements == 0) {
printf("Program %.10s in Library %.10s cannot be debugged.",
program_lib.obj, program_lib.lib);
TearDownDebugger();
}
/* Put the library returned by Retrieve Module Views in PgmLib */
memcpy(program_lib.lib, Library, sizeof(_TE_NAME_T));
/* Register all views in the program */
for (i=0; i &lt; pgm_dbg_dta-&gt;NumberElements; i++) {
QteRegisterDebugView(&amp;iViewID, &amp;iViewLines, Library, TimeStamp,
&amp;program_lib, "*PGM ",
pgm_dbg_dta-&gt;Element[i].ModuleName,
&amp;pgm_dbg_dta-&gt;Element[i].ViewNumber,
&amp;errorCode);
/* overwrite unneeded ViewNumber with obtained view id */
pgm_dbg_dta-&gt;Element[i].ViewNumber = iViewID;
}
}</pre>
<p>The heart of this function is the two calls to the QteRetrieveModuleViews
API and the call to QteRegisterDebugView API.</p>
<p>The QteRetrieveModuleViews
API returns information about an ILE program. It returns this information
in a structure of type _TE_VEWL0100_T. This is a fairly complex structure
that has the following fields:</p>
<pre>typedef _Packed struct { /* format VEWL0100 */
long int BytesReturned; /* number of bytes returned */
long int BytesAvailable; /* number of bytes available */
long int NumberElements; /* number of elements returned */
_Packed struct { /* one element */
_TE_NAME_T ModuleName; /* name of module in program */
_TE_NAME_T ViewType; /* type of view: */
_TE_COMPILER_ID_T CompilerID; /* compiler ID */
_TE_NAME_T MainIndicator; /* main indicator */
_TE_TIMESTAMP_T TimeStamp; /* time view was created */
_TE_TEXTDESC_T ViewDescription; /* view description */
char Reserved[3];
long int ViewNumber; /* view number within module */
long int NumViews; /* number of views in this module*/
} Element[1]; /* one element */
} _TE_VEWL0100_T;</pre>
<p>This structure has a header portion which holds the number
of bytes returned by the API (BytesReturned), the number of bytes that can
be returned by the API, used when there is not enough room for the API to
return all of its data (BytesAvailable), and the number of elements (views)
returned by the API (NumberElements).</p>
<p>Since there is no way to know
in advance how many views a program has, the QteRetrieveModuleViews API should
be called once with only enough storage to return the number of bytes that
the API needs to return all of its information. Thus, the first call to the
API provides only 8 bytes of storage for the API to return its data. This
allows the API to fill in the BytesAvailable field.</p>
<p>QteRetrieveModuleViews
is passed a buffer to hold the receiver variable and the length of that buffer
(in this case, 8 bytes). It is also passed a format name which identifies
the structure of the receiver variable. The only allowable format name at
this time is VEWL0100. A structure containing the program name and library
name of the ILE program is passed. Also, the program type is passed. In this
example debugger, only *PGM objects can be debugged, but it is possible to
debug *SRVPGM objects using the ILE debugger APIs.</p>
<p>The name of the module
is provided, in which case information about that module is returned. *ALL
indicates that information about all modules in the program is to be returned.
A return library variable is passed. This is so that when *LIBL is passed
as a library name, the real library name can be obtained, making subsequent
API calls faster because the library list won't have to be searched again.</p>
<p>Finally
an error code structure is passed to the API. This structure is initialized
with a zero, indicating that the API is not to fill in any error code data.
Instead, the API issues an exception if an error occurs. No errors are expected,
so this should not matter.</p>
<p>Before QteRetrieveModuleViews is called again,
a buffer large enough to hold all of the information is created. The API is
called again with the same parameters, but this time the entire information
will be stored by the API in the allocated buffer.</p>
<p>If the API does not
return any elements, this means that none of the modules has debug data. In
this case, the program cannot be debugged, so the debug session is ended.</p>
<p>Now
that a list of views has been retrieved, it is time to register all of the
views to the system, making it possible to do debug operations against them.
In a real debugger, only the views requested to be seen by the user would
be registered to save processing time, but in this example, all views will
be registered at once.</p>
<p>Not all of the fields in the VEWL0100 structure
are needed by this debugger. However, they will be described here. The API
returns one element for each view in the program. Each module in the program
may have several views. All views for a particular module are contiguous in
the list.</p>
<div class="tablenoborder"><table cellpadding="4" cellspacing="0" summary="" frame="border" border="1" rules="all"><thead align="left"><tr valign="bottom"><th valign="bottom" id="d0e349">View</th>
<th valign="bottom" id="d0e351">Description</th>
</tr>
</thead>
<tbody><tr><td valign="top" headers="d0e349 "><em>ModuleName</em></td>
<td valign="top" headers="d0e351 ">This is the name of the module in the program which this particular
view is for.</td>
</tr>
<tr><td valign="top" headers="d0e349 "><em>ViewType</em></td>
<td valign="top" headers="d0e351 ">This indicates the type of view. A *TEXT view contains text retrieved
from source files residing on the iSeries server. The text contains sequence
information from these files that the debugger may not want to display. A
*LISTING view contains text that is stored with the program object itself.
A *STATEMENT view contains information about HLL statements in the module,
and this information is not generally displayed to the user but is used by
the debugger. In the case of this debugger, all views are displayed exactly
as the text for the views are retrieved.</td>
</tr>
<tr><td valign="top" headers="d0e349 "><em>CompilerID</em></td>
<td valign="top" headers="d0e351 ">This indicates the language that the particular module is written in.
This is not used by the example debugger.</td>
</tr>
<tr><td valign="top" headers="d0e349 "><em>MainIndicator</em></td>
<td valign="top" headers="d0e351 ">Only one module in a program is the module with the program entry procedure
(main() in the case of ILE C programs). If a particular view in the list comes
from this module, then this field indicates that the module contains this
procedure. This field is not used by the example debugger.</td>
</tr>
<tr><td valign="top" headers="d0e349 "><em>TimeStamp</em></td>
<td valign="top" headers="d0e351 ">This indicates when the view was created. This is useful in allowing
the debugger to detect if a program has been recompiled and the debugger has
down-level view information. This field is not used by the example debugger.</td>
</tr>
<tr><td valign="top" headers="d0e349 "><em>ViewDescription</em></td>
<td valign="top" headers="d0e351 ">This is text given to the view by the compiler creating the view. It
is a description of the view which can be displayed by the debugger.</td>
</tr>
<tr><td valign="top" headers="d0e349 "><em>ViewNumber</em></td>
<td valign="top" headers="d0e351 ">This is a sequence number of the view in a particular module. When
registering a view, the program name, module name, and view number must be
provided.</td>
</tr>
<tr><td valign="top" headers="d0e349 "><em>NumViews</em></td>
<td valign="top" headers="d0e351 ">This is how many views are in the module. All elements for views in
a given module have the same value for this field. This field is not used
by the example debugger.</td>
</tr>
</tbody>
</table>
</div>
<p>A loop through all the views returned by QteRetrieveModuleViews
is done, registering the view using the QteRegisterDebugView API. The program
name, program type, module name, and view number of the module are passed
as inputs to the API. The API returns the library of the program (in case
*LIBL) is passed in as the program library), the timestamp of the view (in
case the program has been recompiled between the time the view information
was retrieved and the time the view was registered), the number of lines of
text in the view, and a view ID. The view ID is a handle, and it is used in
identifying the registered view to various APIs. For example, when retrieving
text for a particular view, the view must be registered, and the view ID returned
when registering the view is passed to the QteRetrieveViewText API.</p>
<p>The
structure that held the views retrieved by QteRetrieveModuleViews is also
used by the debugger. The view number is no longer needed, since it is just
a sequence number passed to QteRegisterDebugView. Thus, this number is overwritten
and will hold the view ID, which is needed by other debugger APIs.</p>
<p id="apiexusdeb__Header_85"><a name="apiexusdeb__Header_85"><!-- --></a><strong>ProcessCommands()</strong></p>
<pre>void ProcessCommands(void) {
char InputBuffer[80];
char *token;
int i;
int step=0; /* do an exit for step when 1 */
if (pgm_dbg_dta == NULL) { /* if no debug data */
printf("Debug session has ended.\n");
exit(0); /* end the debugger */
}
while(!step) { /* read until step or quit cmd */
ReadLine(InputBuffer,sizeof(InputBuffer));
token = strtok(InputBuffer," ");
if (token==NULL) continue; /* ignore blank lines */
else if (strcmp(token,"quit") == 0) /* the quit command? */
return; /* exit debugger */
else if (strcmp(token,"list") == 0) /* the list command? */
ProcessListCommand(); /* process command */
else if (strcmp(token,"switch") == 0) { /* switch command? */
token = strtok(NULL," "); /* get view number token */
if (token == NULL)
printf("'switch' must be followed by a view number.\n");
else
current_view = atoi(token); /* switch current view */
}
else if (strcmp(token,"help") == 0) {
printf("The following are the allowed debugger commands:\n");
printf(" list views - lists all views in the program\n");
printf(" list text - lists the text of the current view\n");
printf(" switch n - changes current view to view n\n");
printf(" help - displays this help text\n");
printf(" quit - ends the debug session\n");
printf("Other commands are interpreted by the debug support.\n");
}
else { /* pass command to API */
/* Undo modifications that strtok did */
InputBuffer[strlen(InputBuffer)] = ' ';
step = ProcessDbgCommand(InputBuffer);
}
}
}</pre>
<p>This function reads an input line from the user and processes
it. If it is a command recognized by the debugger, it process it. If not,
it calls ProcessDebugCommand which lets QteSubmitDebugCommand process the
command.</p>
<p>The first test is to make sure that the pointer to the debug
data is not null. This is here for safety reasons. If program DEBUG is compiled
with the wrong activation group name or no name at all, its global variables
can be destroyed when the program exits, causing problems when the program
is called again. This test prevents debug commands from being entered if the
activation group has been destroyed, wiping out the global view data.</p>
<p>The
function loops until the quit command is entered or until a step is done.
It calls the appropriate function based on the command entered, or displays
an error message if a syntax error is detected. If the command is unknown,
it is processed by ProcessDbgCommand.</p>
<p>The switch command is processed
directly by the function. It changes the current view to a number provided.
There is no error checking in this sample debugger.</p>
<p id="apiexusdeb__Header_86"><a name="apiexusdeb__Header_86"><!-- --></a><strong>ReadLine()</strong></p>
<pre>void ReadLine(char *Buffer, int length) {
int i; /* loop counter */
printf("Enter a debugger command or 'help'.\n");
fgets(Buffer,length,stdin); /* read line of text */
/* Blank out line from \n to the end of the string. */
for (i=0; i&lt;length; i++) { /* loop, searching for newline */
if (Buffer[i] == '\n') { /* if newline character found
*/
break; /* end loop searching for newline*/
}
}
memset(Buffer+i,' ',length-i); /* blank remainder of line */
}</pre>
<p>This function reads a line of text from the user and fills
the input buffer with trailing blanks.</p>
<p id="apiexusdeb__Header_87"><a name="apiexusdeb__Header_87"><!-- --></a><strong>ProcessListCommand()</strong></p>
<pre>void ProcessListCommand(void) {
char *token; /* pointer to next token of input*/
token = strtok(NULL," "); /* get next token in input buffer*/
if (token==NULL) /* list not followed by anything */
printf("'list' must be followed by 'views' or 'text'.\n");
else if (strcmp(token,"views") == 0)/* if list views */
PrintViews();
else if (strcmp(token,"text") == 0) /* if list text */
PrintText();
else /* list &lt;something-else&gt; */
printf("'list' must be followed by 'views' or 'text'.\n");
}</pre>
<p>This routine process the list command. There are two versions
of the list command, list views and list text. The appropriate function is
called depending on the type of list command entered, or a syntax error message
is issued.</p>
<p id="apiexusdeb__Header_88"><a name="apiexusdeb__Header_88"><!-- --></a><strong>PrintViews</strong></p>
<pre>void PrintViews(void) {
int k;
/* loop through views printing view#, module, and view desc. text */
for (k=0; k&lt; pgm_dbg_dta-&gt;NumberElements; k++) {
printf("%d) %.10s:%.50s",
k,
pgm_dbg_dta-&gt;Element[k].ModuleName,
pgm_dbg_dta-&gt;Element[k].ViewDescription);
if (current_view == k) /* indicate if view is current */
printf("&lt;---Current\n");
else
printf("\n");
}
}</pre>
<p>This routine lists all of the views available in the program
being debugged. The information about the views is stored in the buffer that
was passed to QteRetrieveModuleViews.</p>
<p>The module name and view descriptive
text is printed for each view. If the current view being printed is also the
current view, this is noted by printing this fact next to the view information.</p>
<p>A
view number is printed next to each view. This is not the view ID returned
by the QteRegisterDebugView. It is a number allowing the user to change the
current view to one of the views in the list.</p>
<p id="apiexusdeb__Header_89"><a name="apiexusdeb__Header_89"><!-- --></a><strong>PrintText()</strong></p>
<pre>void PrintText(void) {
long LineLength = 92; /* length of lines of text */
long NumberOfLines = 0; /* lines to retrieve - 0 = all */
long StartLine=1; /* retrieve from line 1 (first) */
long bufferLength = 100000; /* size of retrieved text buffer */
long viewID; /* view ID of text to retrieve */
_TE_TEXT_BUFFER_T *buffer; /* text retrieved by API */
_TE_ERROR_CODE_T errorCode = {0}; /* Exceptions will be signaled */
int i; /* points to start of each line */
int line_number; /* line number counter for loop */
/* Get View ID of current view */
viewID = pgm_dbg_dta-&gt;Element[current_view].ViewNumber;
buffer = malloc(bufferLength); /* malloc space for big text buf */
/* Call Retrieve_View_Text for the current view. */
QteRetrieveViewText((char *)buffer, &amp;bufferLength, &amp;viewID,
&amp;StartLine, &amp;NumberOfLines, &amp;LineLength,
&amp;errorCode);
/* Print out the text */
for (i=0,line_number=1;
line_number &lt;= buffer-&gt;NumLines;
line_number++,i+=LineLength) {
printf("%3d) %.70s\n", line_number, buffer-&gt;Text+i);
}
free(buffer); /* free memory for buffer */
}</pre>
<p>This function retrieves the text associated with the current
view and prints it. This text is the source of the program and is the heart
of a source debugger screen.</p>
<p>The text of the current view is retrieved,
so the view ID of that view is determined. It is this view that is passed
to QteRetrieveViewText.</p>
<p>In the sample debugger, a large buffer is allocated,
and as much text as will fit in this buffer is retrieved. The QteRetrieveViewText
API returns the text and the number of lines that fit in the buffer.</p>
<p>Once
the text is retrieved, it is printed out along with the line number. The line
number is needed when setting breakpoints based on the view.</p>
<p id="apiexusdeb__Header_90"><a name="apiexusdeb__Header_90"><!-- --></a><strong>ProcessDbgCommand()</strong></p>
<pre>int ProcessDbgCommand(char InputBuffer[80]) {
_TE_ERROR_CODE_T errorCode = {64}; /* fill in bytes provided */
char OutputBuffer[4096];
struct _TE_RESULT_BUFFER_T *Results;
long InputBufferLength = 80;
long OutputBufferLength = sizeof(OutputBuffer);
long view_ID;
_TE_COMPILER_ID_T *CompilerID;
int i;
int return_value = 0;
view_ID = pgm_dbg_dta-&gt;Element[current_view].ViewNumber;
CompilerID = &amp;pgm_dbg_dta-&gt;Element[current_view].CompilerID;
/* Give command to QteSubmitDebugCommand */
QteSubmitDebugCommand(OutputBuffer, &amp;OutputBufferLength,
&amp;view_ID, InputBuffer, &amp;InputBufferLength,
*CompilerID, &amp;errorCode);
if (errorCode.BytesAvailable != 0) {
printf("Error = %.7s\n",errorCode.ExceptionID);
return return_value;
}
/* Process results from QteSubmitDebugCommand */
Results = (_TE_RESULT_BUFFER_T *) OutputBuffer;
/* Loop through Results array */
for (i=0; i&lt;Results-&gt;Header.EntryCount; i++) {
switch (Results-&gt;Data[i].ResultKind)
{
case _TE_kStepR :
printf("Step set\n");
return_value=1; /* indicate step is to be done */
break;
case _TE_kBreakR :
printf("Breakpoint set");
break;
case _TE_kBreakPositionR :
printf(" at line %d\n",
Results-&gt;Data[i].V.BreakPosition.Line);
break;
case _TE_kExpressionTextR :
printf("%s",
((char *)Results) + Results-&gt;Data[i].V.
ExpressionText.oExpressionText);
break;
case _TE_kExpressionValueR :
printf(" = %s\n",
((char *)Results) + Results-&gt;Data[i].V.
ExpressionValue.oExpressionValue);
break;
case _TE_kQualifyR :
printf("Qual set\n");
break;
case _TE_kClearBreakpointR :
printf("Breakpoint cleared\n");
break;
case _TE_kClearPgmR :
printf("All breakpoints cleared\n");
break;
default: /* ignore all other record types */
break;
} /* switch */
} /* loop through results array */
return return_value;
}</pre>
<p>This function is called to process all commands not known by
the debugger. It calls the QteSubmitDebugCommand API which is passed a view
ID, compiler ID, and a command. The API needs the compiler ID because each
programming language used in compiling a particular module has different debug
commands or command syntax, and the API needs to know which language was used
when compiling the module.</p>
<p>The API returns back a series of result records
which indicate what was done by the API. Most of this function reads the results
of the records returned and prints an appropriate response message.</p>
<p>Some
results records indicate that a particular function has been performed. These
include:</p>
<div class="tablenoborder"><table cellpadding="4" cellspacing="0" summary="" frame="border" border="1" rules="all"><thead align="left"><tr valign="bottom"><th valign="bottom" id="d0e477">Result record</th>
<th valign="bottom" id="d0e479">Description</th>
</tr>
</thead>
<tbody><tr><td valign="top" headers="d0e477 "><em>_TE_kStepR</em></td>
<td valign="top" headers="d0e479 ">The step command was successfully done.</td>
</tr>
<tr><td valign="top" headers="d0e477 "><em>_TE_kBreakR</em></td>
<td valign="top" headers="d0e479 ">The break command was successfully done.</td>
</tr>
<tr><td valign="top" headers="d0e477 "><em>_TE_kQualifyR</em></td>
<td valign="top" headers="d0e479 ">The qual command was successfully done.</td>
</tr>
<tr><td valign="top" headers="d0e477 "><em>_TE_kClearBreakpointR</em></td>
<td valign="top" headers="d0e479 ">The clear breakpoint command was successfully done.</td>
</tr>
<tr><td valign="top" headers="d0e477 "><em>_TE_kClearPgmR</em></td>
<td valign="top" headers="d0e479 ">The clear pgm command was successfully done.</td>
</tr>
</tbody>
</table>
</div>
<p>Other results records contain numeric data useful by the debugger.</p>
<div class="tablenoborder"><table cellpadding="4" cellspacing="0" summary="" frame="border" border="1" rules="all"><thead align="left"><tr valign="bottom"><th valign="bottom" id="d0e520">Result record</th>
<th valign="bottom" id="d0e522">Description</th>
</tr>
</thead>
<tbody><tr><td valign="top" headers="d0e520 "><em>_TE_kBreakPositionR</em></td>
<td valign="top" headers="d0e522 ">Contains the line number where a breakpoint was set. It is possible
that a breakpoint set on two different lines will correspond to the same HLL
statement. In this case, only one breakpoint is really set. To determine if
this is the case, it is necessary to map the position in the view where the
breakpoint is set to a position in the statement view.</td>
</tr>
</tbody>
</table>
</div>
<p>Still other results records contain string data. In this case,
the record contains an offset into the string space returned by the API as
well as a string length.</p>
<div class="tablenoborder"><table cellpadding="4" cellspacing="0" summary="" frame="border" border="1" rules="all"><thead align="left"><tr valign="bottom"><th valign="bottom" id="d0e539">Result record</th>
<th valign="bottom" id="d0e541">Description</th>
</tr>
</thead>
<tbody><tr><td valign="top" headers="d0e539 "><em>_TE_kExpressionTextR</em></td>
<td valign="top" headers="d0e541 ">This points to the expression entered in the eval command.</td>
</tr>
<tr><td valign="top" headers="d0e539 "><em>_TE_kExpressionValueR</em></td>
<td valign="top" headers="d0e541 ">This points to the value of the evaluated expression.</td>
</tr>
</tbody>
</table>
</div>
<p>There are other kinds of results records than processed by the
sample debugger. The QteSubmitDebugCommand API discusses in detail each result
record and the data it contains.</p>
<p>The API description also discusses
the syntax of the debug command that must be passed to it. The commands and
their syntax will not be discussed in depth here, but a few example commands
will be shown:</p>
<ul><li>break 5 when x == 3 <p>This is a conditional breakpoint. The debugger
will stop the program indicated by the view ID passed to the API when it reaches
line 5 of the view and when the expression "x == 3" is true. The "when" part
of the break statement is optional, in which case an unconditional breakpoint
is set.</p>
</li>
<li>step 1 into <p>The step command instructs the debug support to stop the
a program when it has executed one or more statements. In this example, the
program is stopped after 1 statement has been executed. The "into" means that
statements in procedures are counted when stepping. "over" means that statements
in called procedures are skipped over and not counted. The default step type
is "into", and the default step count is 1.</p>
</li>
<li>qual 13 <p>The qual command is necessary when there are blocks of code
with the same variable name. In this case, the user indicates where the variable
is searched for in the program. Normally, this command is not used.</p>
</li>
<li>clear 8 <p>A conditional or unconditional breakpoint is removed from line
8 of the view indicated by the view ID parameter.</p>
</li>
</ul>
<p id="apiexusdeb__Header_91"><a name="apiexusdeb__Header_91"><!-- --></a><strong>HandleStop()</strong></p>
<pre>void HandleStop(_TE_OBJLIB_T *ProgramLib,
_TE_NAME_T ProgramType,
_TE_NAME_T Module,
char reason[10],
long Statements[],
int StatementsCount,
char *message) {
int i;
_TE_MAPP0100_T Map_Return_Structure;
long Column = 1;
long MapLength = sizeof(Map_Return_Structure);
_TE_ERROR_CODE_T errorCode = {64};
long stmt_view;
/* If current view is for a different module than the one that is */
/* stopped, change current view to first view in the stopped module*/
if (memcmp(Module,
pgm_dbg_dta-&gt;Element[current_view].ModuleName,
sizeof(_TE_NAME_T)) != 0) { /* a different module? */
for (i=0; i&lt;pgm_dbg_dta-&gt;NumberElements; i++) {
if (memcmp(Module,
pgm_dbg_dta-&gt;Element[i].ModuleName,
sizeof(_TE_NAME_T)) == 0) { /* found module */
current_view = i; /* change current view to module */
printf("Current view changed to %d.\n",current_view);
break; /* exit search loop */
} /* module found */
} /* loop through views */
} /* current view to be changed */
/* Get number of statement view for module stopped */
for (i=0; i&lt;pgm_dbg_dta-&gt;NumberElements; i++) {
if ((memcmp(Module,
pgm_dbg_dta-&gt;Element[i].ModuleName,
sizeof(_TE_NAME_T)) == 0) &amp;&amp;
(memcmp("*STATEMENT",
pgm_dbg_dta-&gt;Element[i].ViewType,
sizeof(_TE_NAME_T)) == 0))
stmt_view = i;
}
/* Call QteMapViewPosition to map the stopped location (which */
/* is in terms of the *STATEMENT view) to the current view of */
/* the module */
QteMapViewPosition((char *)&amp;Map_Return_Structure, &amp;MapLength,
&amp;pgm_dbg_dta-&gt; Element[stmt_view].ViewNumber,
&amp;Statements[0], &amp;Column,
&amp;pgm_dbg_dta-&gt;Element[current_view].ViewNumber,
&amp;errorCode);
/* Tell the user about the program that stopped. */
for (i=0;i&lt;4;i++) { /* See why program stopped */
if (reason[i] == '1') {
switch(i) {
case 0: printf("Unmonitored exception");
break;
case 1: printf("Breakpoint");
break;
case 2: printf("Step completed");
break;
case 3: printf("Breakpoint condition error");
break;
}
}
}
printf(" in module %.10s at line %d.\n",
Module,
Map_Return_Structure.MapElem[0].LineNumber);
ProcessCommands(); /* put user into debugger */
}</pre>
<p>This function is called when program DEBUG is called as a Program
Stop Handler. It is passed the name, library, and type of the program stopped,
the line number in the statement view where it has stopped, a count of line
numbers stopped in, if the system cannot determine exactly where the program
has stopped (this is the case for optimized code), and an array of character
flags indicating why the program was stopped.</p>
<p>The first thing the function
does is determine if the current view is set to the module where the program
stopped. If not, then it needs to be reset to the first view in the module
where the program has stopped.</p>
<p>Next, the statement view ID for the module
stopped needs to be determined. This is necessary because the stopped position
is given in terms of the statement view, and this position needs to be converted
to a position in the current view.</p>
<p>The QteMapViewPosition API maps a
position in the statement view to a statement in another view in that module.
This allows the debugger to determine the source line of the current view
where the program has stopped, even though the program is only told the line
number in the statement view.</p>
<p>Finally, the character flags are checked
to see why the program was stopped. Note that the program can be stopped for
more than one reason, so every flag is checked, and if it is on, a message
for that flag is printed.</p>
<p>Finally, the ProcessCommands function is called,
allowing the user to enter debug commands.</p>
</div>
<div class="section"><h4 class="sectiontitle">Other APIs</h4><p>This section discusses other APIs not
covered in this example debugger. Some or all of these APIs could be used
in a real ILE source-level debugger. All of them are used in the debugger
shipped with <span class="keyword">i5/OS</span>.</p>
<p id="apiexusdeb__Header_93"><a name="apiexusdeb__Header_93"><!-- --></a><strong>QteRetrieveDebugAttributes</strong></p>
<p>This API allows a debugger
to retrieve information about the debug session. This includes the value of
the Update Production Files, set on the Start Debug command, as well as an
indication of whether the job where the debugger is running is servicing and
debugging another job.</p>
<p id="apiexusdeb__Header_94"><a name="apiexusdeb__Header_94"><!-- --></a><strong>QteSetDebugAttributes</strong></p>
<p>The
only attribute that can be set is the value of the Update Production Files.
This can also be accomplished using the Change Debug (CHGDBG) CL command.</p>
<p id="apiexusdeb__Header_95"><a name="apiexusdeb__Header_95"><!-- --></a><strong> QteRemoveDebugView</strong></p>
<p>Views that are registered
can be removed from debug. This is desirable if a program is to be removed
from debug so that it can be recompiled and added again. It is not necessary
to remove views from debug when ending the debug session, as QteEndSourceDebug
will do this automatically.</p>
<p id="apiexusdeb__Header_96"><a name="apiexusdeb__Header_96"><!-- --></a><strong>QteRetrieveStoppedPosition</strong></p>
<p>This
indicates if a program is currently stopped and on the stack, and whether
this stopped position is anywhere in a given view. This is useful whenever
a source debugger is about to put up a source screen. If the program is stopped
somewhere within the source to be displayed, this can be indicated to the
user.</p>
<p>This is necessary because a program can be stopped by other means
than the debugger. For example, an ILE program could have put up a command
entry screen, and the debugger could be displayed from there. In this case,
it is nice to indicate to the user that the program being debugged is stopped.</p>
<p id="apiexusdeb__Header_97"><a name="apiexusdeb__Header_97"><!-- --></a><strong>QteAddBreakpoint</strong></p>
<p>This and the following APIs are
not really needed, as their function can be done with the QteSubmitDebugCommand.
However, this API is much faster, since a debug language command does not
need to be parsed and interpreted. In cases where the debugger knows the information
without needing to specify a debug command to the API, these "shortcut" APIs
should be used.</p>
<p>This API performs the same function as the <tt class="sysout">break
n</tt> debug language command.</p>
<p id="apiexusdeb__Header_98"><a name="apiexusdeb__Header_98"><!-- --></a><strong>QteRemoveBreakpoint</strong></p>
<p>This
API performs the same function as the <tt class="sysout">clear n</tt> debug
language command.</p>
<p id="apiexusdeb__Header_99"><a name="apiexusdeb__Header_99"><!-- --></a><strong>QteRemoveAllBreakpoints</strong></p>
<p>This
API performs the same function as the <tt class="sysout">clear pgm</tt> debug
language command.</p>
<p id="apiexusdeb__Header_100"><a name="apiexusdeb__Header_100"><!-- --></a><strong>QteStep</strong></p>
<p>This API performs
the same function as the <tt class="sysout">step n into</tt> and <tt class="sysout">step
n over</tt> debug language commands.</p>
</div>
<div class="section" id="apiexusdeb__HDRDBSAMP"><a name="apiexusdeb__HDRDBSAMP"><!-- --></a><h4 class="sectiontitle">Debugger code sample</h4><p>Following is
the entire program listing for the ILE C program containing the example debugger
discussed in the preceding sections.</p>
<pre>/*******************************************************************/
/*******************************************************************/
/* */
/* FUNCTION: The entire program listing for the program */
/* containing the example debugger discussed in the */
/* preceding sections. */
/* */
/* LANGUAGE: ILE C */
/* */
/* APIs USED: QteRetrieveViewText, QteSubmitDebugCommand, */
/* QteEndSourceDebug, QteRetrieveModuleViews, */
/* QteRegisterDebugView, QteStartSourceDebug, */
/* QteMapViewPosition */
/* */
/*******************************************************************/
/*******************************************************************/
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;qtedbgs.h&gt;
/* Global variables holding information about a program in debug mode*/
static _TE_VEWL0100_T *pgm_dbg_dta = NULL;
static long current_view = 0; /* current view - defaults to 1st*/
static _TE_OBJLIB_T program_lib; /* name and lib of pgm debugged */
/* ReadLine: Reads a line of input and stores it in a string. */
void ReadLine(char *Buffer, int length) {
int i; /* loop counter */
printf("Enter a debugger command or 'help'.\n");
fgets(Buffer,length,stdin); /* read line of text */
/* Blank out line from \n to the end of the string. */
for (i=0; i&lt;length; i++) { /* loop, searching for newline */
if (Buffer[i] == '\n') { /* if newline character found */
break; /* end loop searching for newline*/
}
}
memset(Buffer+i,' ',length-i); /* blank remainder of line */
}
/* PrintText: This function will print the text for the current view */
void PrintText(void) {
long LineLength = 92; /* length of lines of text */
long NumberOfLines = 0; /* lines to retrieve - 0 = all */
long StartLine=1; /* retrieve from line 1 (first) */
long bufferLength = 100000; /* size of retrieved text buffer */
long viewID; /* view ID of text to retrieve */
_TE_TEXT_BUFFER_T *buffer; /* text retrieved by API */
_TE_ERROR_CODE_T errorCode = {0}; /* Exceptions will be signaled */
int i; /* points to start of each line */
int line_number; /* line number counter for loop */
/* Get View ID of current view */
viewID = pgm_dbg_dta-&gt;Element[current_view].ViewNumber;
buffer = malloc(bufferLength); /* malloc space for big text buf */
/* Call Retrieve_View_Text for the current view. */
QteRetrieveViewText((char *)buffer, &amp;bufferLength, &amp;viewID,
&amp;StartLine, &amp;NumberOfLines, &amp;LineLength,
&amp;errorCode);
/* Print out the text */
for (i=0,line_number=1;
line_number &lt;= buffer-&gt;NumLines;
line_number++,i+=LineLength) {
printf("%3d) %.70s\n", line_number, buffer-&gt;Text+i);
}
free(buffer); /* free memory for buffer */
}
/* PrintViews: Prints all the views of the program being debugged. */
void PrintViews(void) {
int k;
/* loop through views printing view#, module, and view desc. text */
for (k=0; k&lt; pgm_dbg_dta-&gt;NumberElements; k++) {
printf("%d) %.10s:%.50s",
k,
pgm_dbg_dta-&gt;Element[k].ModuleName,
pgm_dbg_dta-&gt;Element[k].ViewDescription);
if (current_view == k) /* indicate if view is current */
printf("&lt;---Current\n");
else
printf("\n");
}
}
/* ProcessListCommand: Process list command to list views or text */
void ProcessListCommand(void) {
char *token; /* pointer to next token of input*/
token = strtok(NULL," "); /* get next token in input buffer*/
if (token==NULL) /* list not followed by anything */
printf("'list' must be followed by 'views' or 'text'.\n");
else if (strcmp(token,"views") == 0)/* if list views */
PrintViews();
else if (strcmp(token,"text") == 0) /* if list text */
PrintText();
else /* list &lt;something-else&gt; */
printf("'list' must be followed by 'views' or 'text'.\n");
}
/* ProcessDbgCommand: This function will process commands sent to */
/* the QteSubmitDebugCommand API. */
int ProcessDbgCommand(char InputBuffer[80]) {
_TE_ERROR_CODE_T errorCode = {64}; /* fill in bytes provided */
char OutputBuffer[4096];
struct _TE_RESULT_BUFFER_T *Results;
long InputBufferLength = 80;
long OutputBufferLength = sizeof(OutputBuffer);
long view_ID;
_TE_COMPILER_ID_T *CompilerID;
int i;
int return_value = 0;
view_ID = pgm_dbg_dta-&gt;Element[current_view].ViewNumber;
CompilerID = &amp;pgm_dbg_dta-&gt;Element[current_view].CompilerID;
/* Give command to QteSubmitDebugCommand */
QteSubmitDebugCommand(OutputBuffer, &amp;OutputBufferLength,
&amp;view_ID, InputBuffer, &amp;InputBufferLength,
*CompilerID, &amp;errorCode);
if (errorCode.BytesAvailable != 0) {
printf("Error = %.7s\n",errorCode.ExceptionID);
return return_value;
}
/* Process results from QteSubmitDebugCommand */
Results = (_TE_RESULT_BUFFER_T *) OutputBuffer;
/* Loop through Results array */
for (i=0; i&lt;Results-&gt;Header.EntryCount; i++) {
switch (Results-&gt;Data[i].ResultKind)
{
case _TE_kStepR :
printf("Step set\n");
return_value=1; /* indicate step is to be done */
break;
case _TE_kBreakR :
printf("Breakpoint set");
break;
case _TE_kBreakPositionR :
printf(" at line %d\n",
Results-&gt;Data[i].V.BreakPosition.Line);
break;
case _TE_kExpressionTextR :
printf("%s",
((char *)Results) + Results-&gt;Data[i].V.
ExpressionText.oExpressionText);
break;
case _TE_kExpressionValueR :
printf(" = %s\n",
((char *)Results) + Results-&gt;Data[i].V.
ExpressionValue.oExpressionValue);
break;
case _TE_kQualifyR :
printf("Qual set\n");
break;
case _TE_kClearBreakpointR :
printf("Breakpoint cleared\n");
break;
case _TE_kClearPgmR :
printf("All breakpoints cleared\n");
break;
default: /* ignore all other record types */
break;
} /* switch */
} /* loop through results array */
return return_value;
}
/* ProcessCommands: Read input from user and process commands. */
void ProcessCommands(void) {
char InputBuffer[80];
char *token;
int i;
int step=0; /* do an exit for step when 1 */
if (pgm_dbg_dta == NULL) { /* if no debug data */
printf("Debug session has ended.\n");
exit(0); /* end the debugger */
}
while(!step) { /* read until step or quit cmd */
ReadLine(InputBuffer,sizeof(InputBuffer));
token = strtok(InputBuffer," ");
if (token==NULL) continue; /* ignore blank lines */
else if (strcmp(token,"quit") == 0) /* the quit command? */
return; /* exit debugger */
else if (strcmp(token,"list") == 0) /* the list command? */
ProcessListCommand(); /* process command */
else if (strcmp(token,"switch") == 0) { /* switch command? */
token = strtok(NULL," "); /* get view number token */
if (token == NULL)
printf("'switch' must be followed by a view number.\n");
else
current_view = atoi(token); /* switch current view */
}
else if (strcmp(token,"help") == 0) {
printf("The following are the allowed debugger commands:\n");
printf(" list views - lists all views in the program\n");
printf(" list text - lists the text of the current view\n");
printf(" switch n - changes current view to view n\n");
printf(" help - displays this help text\n");
printf(" quit - ends the debug session\n");
printf("Other commands are interpreted by the debug support.\n");
}
else { /* pass command to API */
/* Undo modifications that strtok did */
InputBuffer[strlen(InputBuffer)] = ' ';
step = ProcessDbgCommand(InputBuffer);
}
}
}
/* TearDownDebugger: End the debugger. */
void TearDownDebugger(void) {
_TE_ERROR_CODE_T errorCode = {8}; /* errors will be ignored */
/* Call EndSourceDebug to get out of ILE debug mode */
QteEndSourceDebug(&amp;errorCode);
exit(0); /* destroy activation group */
}
/* AddProgram: Add a program to debug mode. */
void AddProgram(void) {
_TE_ERROR_CODE_T errorCode = {0}; /* Signal exceptions on error */
_TE_NAME_T Library; /* Lib returned */
_TE_TIMESTAMP_T TimeStamp; /* TimeStamp returned */
int viewIndex;
long int iViewID;
long int iViewLines;
long rtvModViewDataLength = 8; /* size of receiver buffer */
char tempBuffer[8]; /* Temp storage */
int i, tempModuleCount;
/* Call QteRetrieveModuleViews to determine the number of bytes */
/* the receiver variable needs to be to hold all of the views for */
/* the program. */
pgm_dbg_dta = (_TE_VEWL0100_T *)tempBuffer;
QteRetrieveModuleViews((char *)pgm_dbg_dta, &amp;rtvModViewDataLength,
"VEWL0100", &amp;program_lib,
"*PGM ", "*ALL ", Library,
&amp;errorCode);
/* Get a buffer large enough to hold all view information */
rtvModViewDataLength = pgm_dbg_dta-&gt;BytesAvailable;
pgm_dbg_dta = (_TE_VEWL0100_T *)malloc(rtvModViewDataLength);
/* Call QteRetrieveModuleViews again, passing a big enough buffer. */
QteRetrieveModuleViews((char *)pgm_dbg_dta, &amp;rtvModViewDataLength,
"VEWL0100", &amp;program_lib,
"*PGM ", "*ALL ", Library,
&amp;errorCode);
/* If number of elements is zero, program is not debuggable. */
if (pgm_dbg_dta-&gt;NumberElements == 0) {
printf("Program %.10s in Library %.10s cannot be debugged.",
program_lib.obj, program_lib.lib);
TearDownDebugger();
}
/* Put the library returned by Retrieve Module Views in PgmLib */
memcpy(program_lib.lib, Library, sizeof(_TE_NAME_T));
/* Register all views in the program */
for (i=0; i &lt; pgm_dbg_dta-&gt;NumberElements; i++) {
QteRegisterDebugView(&amp;iViewID, &amp;iViewLines, Library, TimeStamp,
&amp;program_lib, "*PGM ",
pgm_dbg_dta-&gt;Element[i].ModuleName,
&amp;pgm_dbg_dta-&gt;Element[i].ViewNumber,
&amp;errorCode);
/* overwrite unneeded ViewNumber with obtained view id */
pgm_dbg_dta-&gt;Element[i].ViewNumber = iViewID;
}
}
/* Typedef for program list passed to this program at STRDBG time */
typedef struct {
_TE_OBJLIB_T PgmLib; /* Name and Library of program */
_TE_NAME_T PgmType; /* program type, *PGM or *SRVPGM */
} PgmList_t;
/* StartUpDebugger: Initialize the debugger. */
void StartUpDebugger(PgmList_t ProgramList[],
int ProgramListCount) {
_TE_ERROR_CODE_T errorCode = {0}; /* exceptions are generated */
_TE_OBJLIB_T StopHandler = {"DEBUG ", "*LIBL "};
int i;
if (ProgramListCount!=1) { /* is only 1 pgm passed on STRDBG*/
printf("Exactly ONE program must be specified on STRDBG.\n");
TearDownDebugger(); /* end debugger
*/
}
/* Copy program name to global variables */
memcpy(&amp;program_lib, &amp;ProgramList-&gt;PgmLib, 20);
/* Call StartSourceDebug: giving the name and library of the */
/* stop handler. This will start ILE debug mode */
QteStartSourceDebug(&amp;StopHandler, &amp;errorCode);
AddProgram(); /* add program to debug */
}
/* HandleSession: This function is called to handle the session */
/* events STRDBG, DSPMODSRC and ENDDBG. */
void HandleSession(char reason[10],
PgmList_t ProgramList[],
int ProgramListCount) {
if (memcmp(reason,"*START ",10) == 0) /* reason is *START */
StartUpDebugger(ProgramList, ProgramListCount);
else if ( memcmp(reason,"*STOP ",10) == 0) /* reason is *STOP */
TearDownDebugger();
else if ( memcmp(reason,"*DISPLAY ",10) == 0) /* reason *DISPLAY */
ProcessCommands();
}
/* HandleStop: This function is called to handle stop events like */
/* breakpoint, step, unmonitored exception, etc. */
void HandleStop(_TE_OBJLIB_T *ProgramLib,
_TE_NAME_T ProgramType,
_TE_NAME_T Module,
char reason[10],
long Statements[],
int StatementsCount,
char *message) {
int i;
_TE_MAPP0100_T Map_Return_Structure;
long Column = 1;
long MapLength = sizeof(Map_Return_Structure);
_TE_ERROR_CODE_T errorCode = {64};
long stmt_view;
/* If current view is for a different module than the one that is */
/* stopped, change current view to first view in the stopped module*/
if (memcmp(Module,
pgm_dbg_dta-&gt;Element[current_view].ModuleName,
sizeof(_TE_NAME_T)) != 0) { /* a different module? */
for (i=0; i&lt;pgm_dbg_dta-&gt;NumberElements; i++) {
if (memcmp(Module,
pgm_dbg_dta-&gt;Element[i].ModuleName,
sizeof(_TE_NAME_T)) == 0) { /* found module */
current_view = i; /* change current view to module */
printf("Current view changed to %d.\n",current_view);
break; /* exit search loop */
} /* module found */
} /* loop through views */
} /* current view to be changed */
/* Get number of statement view for module stopped */
for (i=0; i&lt;pgm_dbg_dta-&gt;NumberElements; i++) {
if ((memcmp(Module,
pgm_dbg_dta-&gt;Element[i].ModuleName,
sizeof(_TE_NAME_T)) == 0) &amp;&amp;
(memcmp("*STATEMENT",
pgm_dbg_dta-&gt;Element[i].ViewType,
sizeof(_TE_NAME_T)) == 0))
stmt_view = i;
}
/* Call QteMapViewPosition to map the stopped location (which */
/* is in terms of the *STATEMENT view) to the current view of */
/* the module */
QteMapViewPosition((char *)&amp;Map_Return_Structure, &amp;MapLength,
&amp;pgm_dbg_dta-&gt; Element[stmt_view].ViewNumber,
&amp;Statements[0], &amp;Column,
&amp;pgm_dbg_dta-&gt;Element[current_view].ViewNumber,
&amp;errorCode);
/* Tell the user about the program that stopped. */
for (i=0;i&lt;4;i++) { /* See why program stopped */
if (reason[i] == '1') {
switch(i) {
case 0: printf("Unmonitored exception");
break;
case 1: printf("Breakpoint");
break;
case 2: printf("Step completed");
break;
case 3: printf("Breakpoint condition error");
break;
}
}
}
printf(" in module %.10s at line %d.\n",
Module,
Map_Return_Structure.MapElem[0].LineNumber);
ProcessCommands(); /* put user into debugger */
}
/* main: Entry point for the debugger (session or stop handler) */
main (int argc, char *argv[]) {
if (argc == 4) /* called as source debug program*/
HandleSession(argv[1], (PgmList_t *)argv[2], *(int
*)argv[3]);
else if (argc == 8) /* called as program stop handler */
HandleStop((_TE_OBJLIB_T *)argv[1], argv[2],
argv[3], argv[4],
(long *)argv[5], *(int *)argv[6],
argv[7]);
}</pre>
</div>
</div>
<div>
<div class="familylinks">
<div class="parentlink"><strong>Parent topic:</strong> <a href="apiexmp.htm" title="Contains example programs that use APIs and exit programs.">Examples: APIs</a></div>
</div>
</div>
</body>
</html>