1513 lines
75 KiB
HTML
1513 lines
75 KiB
HTML
<?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 <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <qtedbgs.h></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(&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(&program_lib, &ProgramList->PgmLib, 20);
|
|
|
|
/* Call StartSourceDebug: giving the name and library of the */
|
|
/* stop handler. This will start ILE debug mode */
|
|
QteStartSourceDebug(&StopHandler, &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, &rtvModViewDataLength,
|
|
"VEWL0100", &program_lib,
|
|
"*PGM ", "*ALL ", Library,
|
|
&errorCode);
|
|
|
|
/* Get a buffer large enough to hold all view information */
|
|
rtvModViewDataLength = pgm_dbg_dta->BytesAvailable;
|
|
pgm_dbg_dta = (_TE_VEWL0100_T *)malloc(rtvModViewDataLength);
|
|
|
|
/* Call QteRetrieveModuleViews again, passing a big enough buffer. */
|
|
QteRetrieveModuleViews((char *)pgm_dbg_dta, &rtvModViewDataLength,
|
|
"VEWL0100", &program_lib,
|
|
"*PGM ", "*ALL ", Library,
|
|
&errorCode);
|
|
|
|
/* If number of elements is zero, program is not debuggable. */
|
|
if (pgm_dbg_dta->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 < pgm_dbg_dta->NumberElements; i++) {
|
|
QteRegisterDebugView(&iViewID, &iViewLines, Library, TimeStamp,
|
|
&program_lib, "*PGM ",
|
|
pgm_dbg_dta->Element[i].ModuleName,
|
|
&pgm_dbg_dta->Element[i].ViewNumber,
|
|
&errorCode);
|
|
|
|
/* overwrite unneeded ViewNumber with obtained view id */
|
|
pgm_dbg_dta->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<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 <something-else> */
|
|
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< pgm_dbg_dta->NumberElements; k++) {
|
|
printf("%d) %.10s:%.50s",
|
|
k,
|
|
pgm_dbg_dta->Element[k].ModuleName,
|
|
pgm_dbg_dta->Element[k].ViewDescription);
|
|
if (current_view == k) /* indicate if view is current */
|
|
printf("<---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->Element[current_view].ViewNumber;
|
|
|
|
buffer = malloc(bufferLength); /* malloc space for big text buf */
|
|
|
|
/* Call Retrieve_View_Text for the current view. */
|
|
QteRetrieveViewText((char *)buffer, &bufferLength, &viewID,
|
|
&StartLine, &NumberOfLines, &LineLength,
|
|
&errorCode);
|
|
|
|
/* Print out the text */
|
|
for (i=0,line_number=1;
|
|
line_number <= buffer->NumLines;
|
|
line_number++,i+=LineLength) {
|
|
printf("%3d) %.70s\n", line_number, buffer->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->Element[current_view].ViewNumber;
|
|
CompilerID = &pgm_dbg_dta->Element[current_view].CompilerID;
|
|
|
|
/* Give command to QteSubmitDebugCommand */
|
|
QteSubmitDebugCommand(OutputBuffer, &OutputBufferLength,
|
|
&view_ID, InputBuffer, &InputBufferLength,
|
|
*CompilerID, &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<Results->Header.EntryCount; i++) {
|
|
switch (Results->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->Data[i].V.BreakPosition.Line);
|
|
break;
|
|
case _TE_kExpressionTextR :
|
|
printf("%s",
|
|
((char *)Results) + Results->Data[i].V.
|
|
ExpressionText.oExpressionText);
|
|
break;
|
|
case _TE_kExpressionValueR :
|
|
printf(" = %s\n",
|
|
((char *)Results) + Results->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->Element[current_view].ModuleName,
|
|
sizeof(_TE_NAME_T)) != 0) { /* a different module? */
|
|
for (i=0; i<pgm_dbg_dta->NumberElements; i++) {
|
|
if (memcmp(Module,
|
|
pgm_dbg_dta->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<pgm_dbg_dta->NumberElements; i++) {
|
|
if ((memcmp(Module,
|
|
pgm_dbg_dta->Element[i].ModuleName,
|
|
sizeof(_TE_NAME_T)) == 0) &&
|
|
(memcmp("*STATEMENT",
|
|
pgm_dbg_dta->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 *)&Map_Return_Structure, &MapLength,
|
|
&pgm_dbg_dta-> Element[stmt_view].ViewNumber,
|
|
&Statements[0], &Column,
|
|
&pgm_dbg_dta->Element[current_view].ViewNumber,
|
|
&errorCode);
|
|
|
|
/* Tell the user about the program that stopped. */
|
|
for (i=0;i<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 <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <qtedbgs.h>
|
|
|
|
/* 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<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->Element[current_view].ViewNumber;
|
|
|
|
buffer = malloc(bufferLength); /* malloc space for big text buf */
|
|
|
|
/* Call Retrieve_View_Text for the current view. */
|
|
QteRetrieveViewText((char *)buffer, &bufferLength, &viewID,
|
|
&StartLine, &NumberOfLines, &LineLength,
|
|
&errorCode);
|
|
|
|
/* Print out the text */
|
|
for (i=0,line_number=1;
|
|
line_number <= buffer->NumLines;
|
|
line_number++,i+=LineLength) {
|
|
printf("%3d) %.70s\n", line_number, buffer->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< pgm_dbg_dta->NumberElements; k++) {
|
|
printf("%d) %.10s:%.50s",
|
|
k,
|
|
pgm_dbg_dta->Element[k].ModuleName,
|
|
pgm_dbg_dta->Element[k].ViewDescription);
|
|
if (current_view == k) /* indicate if view is current */
|
|
printf("<---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 <something-else> */
|
|
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->Element[current_view].ViewNumber;
|
|
CompilerID = &pgm_dbg_dta->Element[current_view].CompilerID;
|
|
|
|
/* Give command to QteSubmitDebugCommand */
|
|
QteSubmitDebugCommand(OutputBuffer, &OutputBufferLength,
|
|
&view_ID, InputBuffer, &InputBufferLength,
|
|
*CompilerID, &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<Results->Header.EntryCount; i++) {
|
|
switch (Results->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->Data[i].V.BreakPosition.Line);
|
|
break;
|
|
case _TE_kExpressionTextR :
|
|
printf("%s",
|
|
((char *)Results) + Results->Data[i].V.
|
|
ExpressionText.oExpressionText);
|
|
break;
|
|
case _TE_kExpressionValueR :
|
|
printf(" = %s\n",
|
|
((char *)Results) + Results->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(&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, &rtvModViewDataLength,
|
|
"VEWL0100", &program_lib,
|
|
"*PGM ", "*ALL ", Library,
|
|
&errorCode);
|
|
|
|
/* Get a buffer large enough to hold all view information */
|
|
rtvModViewDataLength = pgm_dbg_dta->BytesAvailable;
|
|
pgm_dbg_dta = (_TE_VEWL0100_T *)malloc(rtvModViewDataLength);
|
|
|
|
/* Call QteRetrieveModuleViews again, passing a big enough buffer. */
|
|
QteRetrieveModuleViews((char *)pgm_dbg_dta, &rtvModViewDataLength,
|
|
"VEWL0100", &program_lib,
|
|
"*PGM ", "*ALL ", Library,
|
|
&errorCode);
|
|
|
|
/* If number of elements is zero, program is not debuggable. */
|
|
if (pgm_dbg_dta->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 < pgm_dbg_dta->NumberElements; i++) {
|
|
QteRegisterDebugView(&iViewID, &iViewLines, Library, TimeStamp,
|
|
&program_lib, "*PGM ",
|
|
pgm_dbg_dta->Element[i].ModuleName,
|
|
&pgm_dbg_dta->Element[i].ViewNumber,
|
|
&errorCode);
|
|
|
|
/* overwrite unneeded ViewNumber with obtained view id */
|
|
pgm_dbg_dta->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(&program_lib, &ProgramList->PgmLib, 20);
|
|
|
|
/* Call StartSourceDebug: giving the name and library of the */
|
|
/* stop handler. This will start ILE debug mode */
|
|
QteStartSourceDebug(&StopHandler, &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->Element[current_view].ModuleName,
|
|
sizeof(_TE_NAME_T)) != 0) { /* a different module? */
|
|
for (i=0; i<pgm_dbg_dta->NumberElements; i++) {
|
|
if (memcmp(Module,
|
|
pgm_dbg_dta->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<pgm_dbg_dta->NumberElements; i++) {
|
|
if ((memcmp(Module,
|
|
pgm_dbg_dta->Element[i].ModuleName,
|
|
sizeof(_TE_NAME_T)) == 0) &&
|
|
(memcmp("*STATEMENT",
|
|
pgm_dbg_dta->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 *)&Map_Return_Structure, &MapLength,
|
|
&pgm_dbg_dta-> Element[stmt_view].ViewNumber,
|
|
&Statements[0], &Column,
|
|
&pgm_dbg_dta->Element[current_view].ViewNumber,
|
|
&errorCode);
|
|
|
|
/* Tell the user about the program that stopped. */
|
|
for (i=0;i<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> |