XMLSERVICE/Toolkit debugging and service

Goto Main Page

Who is this page for?

Instructions designed for IBM i developer learning PHP and XMLSERVICE …

Debug technique: It’s as easy as 1-2-3-4-5-6-7-8-9 :)

  1. Run the test script that contains control “*debug” and script will “hang” while it waits on #2

$ctl .= " *debug";

  1. A MSGW inquiry message in DSPMSG QSYSOPR will be generated by the toolkit. Note the job information (number, name, user) provided in the MSGW.
  2. STRSRVJOB using that job information as parameters.
  3. STRDBG with the program and library you wish to debug.
  4. Answer the MSGW. Any answer will do–“G” is fine.
  5. The RPG program source will appear in debug mode in your terminal, ready to step through, allowing you to inspect variables, etc.
  6. When done inspecting and stepping, let the RPG program complete (using function keys indicated on screen).
  7. ENDDBG
  1. ENDSRVJOB

Other debug options:

          Job1 (threaded)   Job 2                        Job 3 (DB2 userid/password)    Job 4 (optional XTOOLKIT job)
                            (ctl=*debugcgi)              (ctl=*debugproc)                (ctl=*debug)
browser -> Apache          ->XMLCGI (Apache CGI child) -> QSQSRVR (XMLSERVICE *here)
                                                      -> QSQSRVR (XMLSERVICE client) -> XTOOLKIT (XMLSERVICE ipc=/tmp/flinstone)

$ctl .= " *debugcgi";  // Job 2 - debug XMLCGI to see REST/HTTP data passed by client (when using REST only)
$ctl .= " *debugproc"; // Job 3 - debug XMLSERVICE "client" to see DB2 passed data (DB2 interface)
$ctl .= " *debug";     // Job 4 - debug XMLSERVICE "server" to see XMLSERVICE calls (DB2 interface)
                      // Note:   when ctl='*here', both XMLSERVICE "client"/"server"
                      //         are in QSQSRVSR job (NO XTOOLKIT job)
                      // remote: Attaching with LUW drivers changes QSQSRVR ...
                      //   CLIENT (Client Access drivers) <==> QZDAxxxx
                      //   CLIENT (DB2 Connect drivers)   <==> QRWxxxx

Working with service provider?

Here are a few common things that can provide useful information if working with outside support people.

  1. easy way 1.7.1 … just send provider XMLSERVLOG/LOG and XMLSERVLOG/DUMP in SAVF

assuming you have a trace of the issue (see xmlservice logging instructions)

  1. what do you see in error logs for simple tests ???
call qp2term
> tail /usr/local/zendsvr/var/log/php.log
> tail /myasp2/www/zend2/logs/error_log.Q112061800
> tail /myasp2/www/zend2/logs/access_log.Q112061800
  1. Summarize configuration (below)???

My Example

SYSASP -- ZENDSVR library ... everything as installed
SYSASP -- /usr/local/zendsvr ... everything as installed
SYSASP -- /tmp ... many Zend "enterprise" components use /tmp
MYASP2 -- /myasp2/www/zend2 ... ALL "user data" moved from /www/zendsvr (/conf, /logs, /htdocs)
More ... /myasp2/www/zend2
-- NO symbolic links between SYSAPS and MYASP2 for true "independent"ASP.
-- config files /myasp2/www/zend2/conf
attach: fastcgi.conf
attach: httpd.conf
  1. Around time of simple test failure … do you see job logs?

wrkoutq

If jobs are still active (php-cgi, etc.) ...
wrkactjob
5      ZEND2        QTMHHTTP    BCI      .0  PGM-QZSRHTTP     SIGW
        ZEND2        QTMHHTTP    BCI      .0  PGM-php-cgi.bi   THDW
5      ZEND2        QTMHHTTP    BCI      .0  PGM-php-cgi.bi   TIMW
10. Display job log, if active, on job queue, or pending
F10 -- full job log
  1. Around time of simple test failure … do you see VLOGS???
STRSST
1. Start a service tool
5. Licensed Internal Code log
1. Select entries from the Licensed Internal Code (LIC) log
Specify Licensed Internal Code Log Selection Values
-- leave as is enter ---
      0100A890  i5/OS PASE               4700  0013  06/15/12  09:03:43      7        <--- PASE
      0100A891  LIC log interface        0401  0100  06/15/12  09:04:08      1
      0100A892  Signals management       4600  0001  06/15/12  09:04:51    255
      0100A893  Process management       1300  0001  06/18/12  21:02:06      1

Maybe look for PASE, storage management, ASP, so on around “failure time”

Note: You can also dump logs to spool …

When you have no idea (dumping many processes)?

Some times you just have no idea what is going on, here is a handy macro to dump a lot of stacks.

STRSST/STRDST
1. Start a service tool
4. Display/Alter/Dump
1. Display/Alter storage
... or option for dump to printer ...
2. Licensed Internal Code (LIC) data
14. Advanced analysis
Option    Command
    1      processinfo

In this case dumping all process dealing with keyword "ZEND" appearing in job ...

                      Specify Advanced Analysis Options
Output device  . . . . . . :   Display
Type options, press Enter.
  Command . . . . :   PROCESSINFO
  Options . . . . .   -NAMES ZEND

Note: Information dumped printer/display is same as paseps macro.

Check active XMLSERVICE job

If you are using private connections (InternalKey or $ipc=’/tmp/packers’), the XMLSERVICE job is probably available for examination with wrkactjob.

                            Work with Active Jobs                     LP0264D
                                                            05/17/12  11:35:12
CPU %:      .0     Elapsed time:   00:00:00     Active jobs:   313

Type options, press Enter.
  2=Change   3=Hold   4=End   5=Work with   6=Release   7=Display message
  8=Work with spooled files   13=Disconnect ...
                    Current
Opt  Subsystem/Job  User        Type  CPU %  Function        Status
5      XTOOLKIT     DB2         BCH      .0  PGM-XMLSERVICE   SEMW
  1. Use option 5=work -> 10. Display job log -> F10=Display detailed messages to examine joblog on errors …
                              Display All Messages
                                                            System:   LP0264D
Job . . :   XTOOLKIT      User . . :   DB2           Number . . . :   435915

  >> CALL PGM(XMLSERVICE/XMLSERVICE) PARM('/tmp/packers')
    Pointer not set for location referenced.
    Application error.  MCH3601 unmonitored by ZZSRV at statement 0000000448,
      instruction X'0000'.
  1. Use option 5=work -> 11. Display call stack -> F5=Refresh to examine stack during stress tests …
                              Display Call Stack
                                                            System:   LP0264D
Job:   XTOOLKIT       User:   DB2            Number:   437582
Thread:   0000000C


Type  Program                  Statement         Procedure
    1  QCMD       QSYS                     /01C8
      XMLSERVICE XMLSERVICE                      _QRNP_PEP_XMLSERVICE
      XMLSERVICE XMLSERVICE    1133              XMLSERVICE
      XMLSERVICE XMLSERVICE    4607              RUNSERVER
      XMLSERVICE XMLSERVICE    2983              SIGSETTIMEOUT
      XMLSERVICE XMLSERVICE    2876              SIGTIMEROFF
      QP0SSRV1   QSYS          19                setitimer
      QP0SSRV2   QSYS          159               qp0sitimer__F12qp0sitimer_t >

Check the logs

Check PHP log for messages

On my IBM i machine:

EDTF STMF('/usr/local/zendsvr/var/log/php.log')
-- or --
call qp2term (or ssh myibmi)
> tail /usr/local/zendsvr/var/log/php.log
... stuff
... in /MYASP2/www/zend2/htdocs/hello.php on line 1

On my Linux machine:

$ tail /usr/local/zend/var/log/php.log
[16-May-2012 16:30:12] PHP Warning:  db2_close() expects parameter 1 to be resource ...

Check Apache logs for messages

error logs for date in question:

EDTF STMF('/myasp2/www/zend2/logs/error_log.Q112051500')
-- or --
call qp2term (or ssh myibmi)
> tail /myasp2/www/zend2/logs/error_log.Q112051500
[Tue May 15 17:10:11 2012] [error] [client 9.5.158.38] CGI PROGRAM /QSYS.LIB/XMLSERVICE.LIB/XMLCGI.PGM RETURNED EXCEPTION ID CEE9901
[Tue May 15 17:10:11 2012] [error] [client 9.5.158.38] SEE JOBLOG FOR JOB 428979/QTMHHTTP  /ZEND2

access logs for date in question:

EDTF STMF('/myasp2/www/zend2/logs/access_log.Q112051500')
-- or --
call qp2term (or ssh myibmi)
> tail /myasp2/www/zend2/logs/access_log.Q112051500
9.5.158.38 - - [15/May/2012:17:47:41 -0500] "GET /cgi-bin/xmlcgi.pgm?db2=LP0264D

Check PHP Toolkit logs for messages

toolkit.ini logfile

EDTF STMF('/usr/local/zendsvr/share/ToolkitApi/toolkit.log')
-- or --
call qp2term (or ssh myibmi)
> tail /usr/local/zendsvr/share/ToolkitAPI/toolkit.log
15 May 2012 22:53:35.752099 Running stateless; no IPC needed. Service library: ZENDSVR
15 May 2012 22:53:36.588466 i5Error: num=14 cat=9 msg="No more entries." desc="No more entries."

location set in toolkit.ini ...
EDTF STMF('/usr/local/zendsvr/share/ToolkitApi/toolkit.ini')
[log]
; warnings and errors will be written to the logfile.
logfile = "/usr/local/zendsvr/share/ToolkitApi/toolkit.log"

toolkit.ini debugLogFile

EDTF STMF('/usr/local/zendsvr/share/ToolkitApi/debug.log')
-- or --
call qp2term (or ssh myibmi)
> tail /usr/local/zendsvr/share/ToolkitAPI/debug.log
<data type='1A' var='ds1' comment='DSCHARA'><![CDATA[E]]></data>
<data type='1A' var='ds2' comment='DSCHARB'><![CDATA[F]]></data>

location set in toolkit.ini ...
EDTF STMF('/usr/local/zendsvr/share/ToolkitApi/toolkit.ini')
; debug turns PHP toolkit's debug mode on or off (true/false). Default log file: /usr/local/zendsvr/share/ToolkitApi/debug.log
; This log will grow large, so leave this false when you do not need to log everything.
debug = true
debugLogFile = "/usr/local/zendsvr/share/ToolkitApi/debug.log"

PHP and XMLSERVICE bad XML

EDTF STMF('/tmp/bad.xml')
-- or --
> tail /tmp/bad.xml
start
<?xml version="1.0" encoding="ISO-8859-1" ?><script><cmd><success><&#1836;CDATA&#65533;+++ success QSYS/DLTDTAARA DTAARA(XMLSERVICE/BETTYBOOP)||></success></cmd>

Check one level at a time

Troubles on your PHP site or installation???

Often times if you take a deep breath, slow down and look at each level of web site components you can find your issue without reaching for the bat phone and calling Zend or IBM. The following set of tests walks up the PHP levels of components to give you confidence you are looking at the correct layer of your issue. Of course after you complete smaller PHP scripts (hello.php, etc.), you can likely use the same step up next level techniques on your sophisticated applications (WorldPeace.php).

Level 0 – PHP Working

If you are running on the IBM i machine it is always best to make sure your Apache/PHP setup can do anything.

Note: Your machine may have document root at /www/zendsvr vs. /MYASP2/www/zend2 (out-of-box installed Zend Server).

Test number #0

Install the following simple test in your Document root and see if “Hello World” appears.

/MYASP2/www/zend2/htdocs/hello.php
<?php
echo "Hello world";
?>

Run the test:

call qp2term (or ssh myibmi)
> export PATH=/usr/local/zendsvr/bin:$PATH
> export LIBPATH=/usr/local/zendsvr/lib
> cd /MYASP2/www/zend2/htdocs
> php hello.php
Hello world>

Level 1 – Apache/PHP working

If you are running on the IBM i machine it is always best to make sure your Apache/PHP setup can do anything.

Note: Your machine may have document root at /www/zendsvr vs. /MYASP2/www/zend2 (out-of-box installed Zend Server).

Test number #1

Install the following simple test in your Document root and see if “Hello World” appears in your browser.

/MYASP2/www/zend2/htdocs/hello.php
<?php
echo "Hello world";
?>

Ok, next run stress test …

call qp2term
> cd /usr/local/Zend/apache2/bin
> ab -t 25 -c 10 http://myibmi/hello.php
-t 25 -- 25 seconds
-c 10 -- 10 concurrent browsers (simulates ten browsers)
  • this test is designed to drive CPU to 100% (a good thing), so don’t panic about CPU

wrkactjob refresh (F10) - if you do not see multiple php-cgi jobs getting CPU re-run tests with more load

call qp2term
> cd /usr/local/Zend/apache2/bin
> ab -t 25 -c 10 http://myibmi/hello.php &; ab -t 25 -c 10 http://myibmi/hello.php &; ab -t 25 -c 10 http://myibmi/hello.php &;
-t 25 -- 25 seconds
-c 10 -- 10 concurrent browsers (simulates ten browsers)
Multiply by 3 Apache ab jobs running background -- 30 concurrent browsers (simulates thirty browsers)
  • above using “&;” to fork/exec into background/batch many jobs(s) Apache ab to really hammer your web site
  • ab tool is not perfect, so if you start too many of these forked jobs (’&;’) the tool may core dump (die) – i usually can get 3 - 6 jobs working (30-60 browsers)
  • if you still do not see a split of CPU between php-cgi jobs you may be missing HTTP PTFS

Level 2 – db2 connection working

At this level we want to check our DB2 connections.

Note:

  • Your machine may have document root at /www/zendsvr vs. /MYASP2/www/zend2 (out-of-box installed Zend Server).
  • This same test can be run 2-tier from Linux/Windows to IBM i using DB2 Connect

Test number #2

Install the following simple test in your Document root and see if “success” appears in your browser.

/MYASP2/www/zend2/htdocs/connection2.inc
<?php
$database            = "*LOCAL"; // *LOCAL on IBM i ... LP0264D on Linux
$cwdatabase          = "localhost";
$user                = "DB2";
$password            = "XXXXXXXX";
$libxmlservice       = "ZENDSVR"; // ZZCALL (Zend Server)
$i5persistentconnect = false;
?>

/MYASP2/www/zend2/htdocs/xxtoolkit_connect.php
<?php
require_once('connection2.inc');
// flip between persistent and non-persistent connections
for ($i=0;$i<500;$i++) {
  for ($i5persistentconnect=1;$i5persistentconnect>-1;$i5persistentconnect--) {
    if ($i5persistentconnect) $conn = db2_pconnect($database,$user,$password);
    else $conn = db2_connect($database,$user,$password);
    if (!$conn) echo "<br>Bad connect: $conn,$database,$user,perm=$i5persistentconnect";
    else echo "<br>Good connect:  $conn,$database,$user,perm=$i5persistentconnect";
    if ($i5persistentconnect) $ok = true;
    else $ok = db2_close($conn);
    echo ",ok=$ok\n";
  }
}

run the test Apache …

Point your browser to php program ...
http://myibmi/xxtoolkit_connect.php
Good connect: Resource id #2,*LOCAL,DB2,perm=1,ok=1
Good connect: Resource id #3,*LOCAL,DB2,perm=0,ok=1
Good connect: Resource id #4,*LOCAL,DB2,perm=1,ok=1
:

run the test 2-tier … I choose to run from command line on my Linux machine, but Linux Apache would also work.

$ which php
/usr/local/zend/bin/php
$ php xxtoolkit_connect.php
<br>Good connect:  Resource id #5,LP0264D,DB2,perm=1,ok=1
<br>Good connect:  Resource id #6,LP0264D,DB2,perm=0,ok=1
<br>Good connect:  Resource id #7,LP0264D,DB2,perm=1,ok=1
:

Level 3 - XMLSERVICE XML interface Working

At this level we want to check our PHP raw XML Toolkit built on top of ibm_db2 connections (Level 2).

Note:

  • Your machine may have document root at /www/zendsvr vs. /MYASP2/www/zend2 (out-of-box installed Zend Server).
  • This same test can be run 2-tier from Linux/Windows to IBM i using DB2 Connect.
  • RPG test *PGM ZENDSVR/ZZCALL is included with Zend Server installation, so test will run out-of-box

Test number #3

Install the following simple test in your Document root and see if “success” appears in your browser.

/MYASP2/www/zend2/htdocs/connection2.inc
<?php
$database            = "*LOCAL"; // *LOCAL on IBM i ... LP0264D on Linux
$cwdatabase          = "localhost";
$user                = "DB2";
$password            = "XXXXXXXX";
$libxmlservice       = "ZENDSVR"; // ZZCALL (Zend Server)
$i5persistentconnect = false;
?>

/MYASP2/www/zend2/htdocs/xxtoolkit_raw.php
<?php
require_once('connection2.inc');
if ($i5persistentconnect) $conn = db2_pconnect($database,$user,$password);
else $conn = db2_connect($database,$user,$password);
if (!$conn) echo "Bad connect: $conn,$database,$user,perm=$i5persistentconnect";

$stmt = db2_prepare($conn, "call XMLSERVICE.iPLUG4K(?,?,?,?)");
$ctl  = "*sbmjob"; // *here for no additional private job
$ipc='/tmp/packers';
// $ipc  = "";     // *here no need ipc
$clobIn = "<?xml version='1.0'?>
<pgm name='ZZCALL' lib='$libxmlservice'>
<parm  io='both'>
  <data type='1A'>a</data>
</parm>
<parm  io='both'>
  <data type='1A'>b</data>
</parm>
<parm  io='both'>
  <data type='7p4'>11.1111</data>
</parm>
<parm  io='both'>
  <data type='12p2'>222.22</data>
</parm>
<parm  io='both'>
  <ds>
  <data type='1A'>x</data>
  <data type='1A'>y</data>
  <data type='7p4'>66.6666</data>
  <data type='12p2'>77777.77</data>
  </ds>
</parm>
<return>
  <data type='10i0'>0</data>
</return>
</pgm>";
$clobOut = "";
$ret=db2_bind_param($stmt, 1, "ipc", DB2_PARAM_IN);
$ret=db2_bind_param($stmt, 2, "ctl", DB2_PARAM_IN);
$ret=db2_bind_param($stmt, 3, "clobIn", DB2_PARAM_IN);
$ret=db2_bind_param($stmt, 4, "clobOut", DB2_PARAM_OUT);
$ret=db2_execute($stmt);
// var_dump($clobOut);
if (strpos($clobOut,"4444444444.44")>0) echo "success";
else echo "fail";
?>

run the test Apache …

Point your browser to php program ...
http://myibmi/xxtoolkit_raw.php
success

run the test 2-tier … I choose to run from command line on my Linux machine, but Linux Apache would also work.

$ which php
/usr/local/zend/bin/php
$ php xxtoolkit_raw.php
success

Ok, next run stress test …

call qp2term
> cd /usr/local/Zend/apache2/bin
> ab -t 25 -c 10 http://lp0264d/xxtoolkit_raw.php
-t 25 -- 25 seconds
-c 10 -- 10 concurrent browsers (simulates ten browsers)
  • this test is designed to drive CPU to 100% (a good thing), so don’t panic about CPU

Level 4 – PHP New Toolkit working

At this level we want to check our PHP CW Toolkit built on top of raw XML Toolkit (Level 3).

Note:

  • Your machine may have document root at /www/zendsvr vs. /MYASP2/www/zend2 (out-of-box installed Zend Server).
  • This same test can be run 2-tier from Linux/Windows to IBM i using DB2 Connect.
  • RPG test *PGM ZENDSVR/ZZCALL is included with Zend Server installation, so test will run out-of-box

Test number #4

Install the following simple test in your Document root and see if “success” appears in your browser.

/MYASP2/www/zend2/htdocs/connection2.inc
<?php
$database            = "*LOCAL"; // *LOCAL on IBM i ... LP0264D on Linux
$cwdatabase          = "localhost";
$user                = "DB2";
$password            = "XXXXXXXX";
$libxmlservice       = "ZENDSVR"; // ZZCALL (Zend Server)
$i5persistentconnect = false;
?>

/MYASP2/www/zend2/htdocs/xxtoolkit_new.php
<?php
require_once('connection2.inc');
require_once("ToolkitService.php");
if ($i5persistentconnect) $conn = db2_pconnect($database,$user,$password);
else $conn = db2_connect($database,$user,$password);
if (!$conn) echo "Bad connect: $conn,$database,$user,perm=$i5persistentconnect";

try { $ToolkitServiceObj = ToolkitService::getInstance($conn); }
catch (Exception $e) { die($e->getMessage()); }
$param[] = $ToolkitServiceObj->AddParameterChar   ('both',  1,  'INCHARA', 'var1', 'Y');
$param[] = $ToolkitServiceObj->AddParameterChar   ('both',  1,  'INCHARB', 'var2', 'Z');
$param[] = $ToolkitServiceObj->AddParameterPackDec('both',  7,4,'INDEC1',  'var3', '001.0001');
$param[] = $ToolkitServiceObj->AddParameterPackDec('both', 12,2,'INDEC2',  'var4', '0000000003.04');
  $ds[] = $ToolkitServiceObj->AddParameterChar   ('both',  1,  'DSCHARA', 'ds1',  'A');
  $ds[] = $ToolkitServiceObj->AddParameterChar   ('both',  1,  'DSCHARB', 'ds2',  'B');
  $ds[] = $ToolkitServiceObj->AddParameterPackDec('both',  7,4,'DSDEC1',  'ds3',  '005.0007');
  $ds[] = $ToolkitServiceObj->AddParameterPackDec('both', 12,2,'DSDEC1',  'ds4',  '0000000006.08');
$param[] = $ToolkitServiceObj->AddDataStruct($ds);
$clobOut  = $ToolkitServiceObj->PgmCall('ZZCALL', $libxmlservice, $param, null, null);
// var_dump($clobOut);
$value = "what is ...".$clobOut["io_param"]["ds4"];
if (strpos($value,"4444444444.44")>-1) echo "success";
else echo "fail";

run the test Apache …

Point your browser to php program ...
http://myibmi/xxtoolkit_new.php
success

run the test 2-tier … I choose to run from command line on my Linux machine, but Linux Apache would also work.

$ which php
/usr/local/zend/bin/php
$ php xxtoolkit_new.php
success

Ok, next run stress test …

call qp2term
> cd /usr/local/Zend/apache2/bin
> ab -t 25 -c 10 http://lp0264d/xxtoolkit_new.php
-t 25 -- 25 seconds
-c 10 -- 10 concurrent browsers (simulates ten browsers)
  • this test is designed to drive CPU to 100% (a good thing), so don’t panic about CPU

Level 5 – PHP CW Toolkit working (optional)

At this level we want to check our PHP CW Toolkit built on top of new Toolkit (Level 4).

Note:

  • Your machine may have document root at /www/zendsvr vs. /MYASP2/www/zend2 (out-of-box installed Zend Server).
  • This same test can be run 2-tier from Linux/Windows to IBM i using DB2 Connect.
  • RPG test *PGM ZENDSVR/ZZCALL is included with Zend Server installation, so test will run out-of-box

Test number #5

Install the following simple test in your Document root and see if “success” appears in your browser.

/MYASP2/www/zend2/htdocs/connection2.inc
<?php
$database            = "*LOCAL"; // *LOCAL on IBM i ... LP0264D on Linux
$cwdatabase          = "localhost";
$user                = "DB2";
$password            = "XXXXXXXX";
$libxmlservice       = "ZENDSVR"; // ZZCALL (Zend Server)
$i5persistentconnect = false;
?>

/MYASP2/www/zend2/htdocs/xxtoolkit_cw.php
<?php
require_once('connection2.inc');
require_once('CW/cw.php'); // new toolkit compatibility (Alan)

/* connect */
if ($i5persistentconnect) $conn = i5_pconnect($cwdatabase,$user,$password);
else $conn = i5_connect($cwdatabase,$user,$password);
if (!$conn) echo "Bad connect: $conn,$cwdatabase,$user,perm=$i5persistentconnect";

if (!$conn)
{ $tab = i5_error();
  die("fail Connect: ".$tab[2]." "."$tab[3], $tab[0]");
}

/* prepare */
$description =
array
(
  // single parms
  array
  ( "Name"=>"INCHARA","IO"=>I5_IN|I5_OUT,"Type"=>I5_TYPE_CHAR,"Length"=>"1"),
  array
  ( "Name"=>"INCHARB","IO"=>I5_IN|I5_OUT,"Type"=>I5_TYPE_CHAR,"Length"=>"1"),
  array
  ( "Name"=>"INDEC1","IO"=>I5_IN|I5_OUT,"Type"=>I5_TYPE_PACKED,"Length"=>"7.4"),
  array
  ( "Name"=>"INDEC2","IO"=>I5_IN|I5_OUT,"Type"=>I5_TYPE_PACKED,"Length"=>"12.2"),
  // structure parm
  array
  ( "DSName"=>"INDS1",
    "Count"=>1,
    "DSParm"=>
    array
    (
    array
    ( "Name"=>"DSCHARA","IO"=>I5_IN|I5_OUT,"Type"=>I5_TYPE_CHAR,"Length"=>"1"),
    array
    ( "Name"=>"DSCHARB","IO"=>I5_IN|I5_OUT,"Type"=>I5_TYPE_CHAR,"Length"=>"1"),
    array
    ( "Name"=>"DSDEC1","IO"=>I5_IN|I5_OUT,"Type"=>I5_TYPE_PACKED,"Length"=>"7.4"),
    array
    ( "Name"=>"DSDEC2","IO"=>I5_IN|I5_OUT,"Type"=>I5_TYPE_PACKED,"Length"=>"12.2"),
    )
  )
);
$pgm = i5_program_prepare("$libxmlservice/ZZCALL", $description);
if (!$pgm)
{ $tab = i5_error();
  die("fail Prepare: ".$tab[2]." "."$tab[3], $tab[0]");
}

// *** parameter list allocation
$list=
array
(
  "DSCHARA"=>"x",
  "DSCHARB"=>"y",
  "DSDEC1"=>66.6666,
  "DSDEC2"=>77777.77,
);
// *** parameter values passed to procedure
$in =
array
(
  "INCHARA"=>"a",
  "INCHARB"=>"b",
  "INDEC1"=>11.1111,
  "INDEC2"=>222.22,
  "INDS1"=>$list,
);
// *** name of variables created for out parameters
$out =
array
(
  "INCHARA"=>"INCHARA",
  "INCHARB"=>"INCHARB",
  "INDEC1"=>"INDEC1",
  "INDEC2"=>"INDEC2",
  "INDS1"=>"INDS1",
);
$rc=i5_program_call($pgm, $in, $out);
if ($rc != false)
{
  if ($INCHARA != 'C') die("fail C == $INCHARA\n");
  if ($INCHARB != 'D') die("fail D == $INCHARB\n");
  if ($INDEC1 != 321.1234) die("fail 321.1234 == $INDEC1\n");
  if ($INDEC2 != 1234567890.12) die("fail 1234567890.12 = $INDEC2\n");
  if ($INDS1["DSCHARA"] != 'E'
  ||  $INDS1["DSCHARB"] != 'F'
  ||  $INDS1["DSDEC1"] != 333.333
  ||  $INDS1["DSDEC2"] != 4444444444.44)
  {
    var_dump($INDS1);
    die("fail DS not correct\n");
  }

}
else
{ $tab = i5_error();
  die("fail Call: ".$tab[2]." "."$tab[3], $tab[0]");
}


// good
echo "success";
?>

run the test Apache …

Point your browser to php program ...
http://myibmi/xxtoolkit_cw.php
success

run the test 2-tier … I choose to run from command line on my Linux machine, but Linux Apache would also work.

$ which php
/usr/local/zend/bin/php
$ php xxtoolkit_cw.php
success

Ok, next run stress test …

call qp2term
> cd /usr/local/Zend/apache2/bin
> ab -t 25 -c 10 http://lp0264d/xxtoolkit_cw.php
-t 25 -- 25 seconds
-c 10 -- 10 concurrent browsers (simulates ten browsers)
  • this test is designed to drive CPU to 100% (a good thing), so don’t panic about CPU

Apache ab sample running

Apache ab web site stress tests are performed from the 5250 command line (call qp2term) or ssh myibmi using PASE.

  • You can run stress tests from 2-tier Linux/Windows using Apache ab tool, but i am using Apchae ab from PASE.
  • Apache ab tool is not perfect, but if you use relatively “sane” number of browsers like -c 10 it will work.
  • Apache ab test is designed to drive CPU to 100% (a good thing), so don’t panic about CPU

Example run my machine …

> ab -t 25 -c 10 http://lp0264d/hello.php
This is ApacheBench, Version 2.0.40-dev <$Revision: 1.146 $> apache-2.0
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Copyright 2006 The Apache Software Foundation, http://www.apache.org/

Benchmarking lp0264d (be patient)
Completed 5000 requests
Completed 10000 requests
Completed 15000 requests
Completed 20000 requests
Finished 20325 requests


Server Software:        Apache
Server Hostname:        lp0264d
Server Port:            80

Document Path:          /hello.php
Document Length:        11 bytes

Concurrency Level:      10
Time taken for tests:   25.5119 seconds
Complete requests:      20325
Failed requests:        0
Write errors:           0
Total transferred:      3394275 bytes
HTML transferred:       223575 bytes
Requests per second:    812.83 [#/sec] (mean)    <---     800 hits/second
Time per request:       12.303 [ms] (mean)
Time per request:       1.230 [ms] (mean, across all concurrent requests)
Transfer rate:          132.53 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   1.1      0      30
Processing:     2   11   7.2     10     322
Waiting:        2   10   7.2     10     322
Total:          2   11   7.3     11     322

Percentage of the requests served within a certain time (ms)
  50%     11
  66%     12
  75%     14
  80%     15
  90%     17
  95%     19
  98%     21
  99%     23
100%    322 (longest request)
>

wrkactjob – during Apache ab test you can use refresh on wrkactjob scree to see the php-cgi jobs working

  • you should expect to see multiple php-cgi jobs getting some CPU time as you refresh with F10
                            Work with Active Jobs                     LP0264D
                                                            05/16/12  13:37:25
CPU %:   100.0     Elapsed time:   00:00:00     Active jobs:   295

Type options, press Enter.
  2=Change   3=Hold   4=End   5=Work with   6=Release   7=Display message
  8=Work with spooled files   13=Disconnect ...
                    Current
Opt  Subsystem/Job  User        Type  CPU %  Function        Status
        ZEND2        QTMHHTTP    BCI      .0  PGM-zfcgi        SELW
        ZEND2        QTMHHTTP    BCI      .0  PGM-php-cgi.bi   THDW
        ZEND2        QTMHHTTP    BCI      .0  PGM-php-cgi.bi   THDW
        ZEND2        QTMHHTTP    BCI     2.8  PGM-php-cgi.bi   TIMA
        ZEND2        QTMHHTTP    BCI     1.8  PGM-php-cgi.bi   TIMA
        ZEND2        QTMHHTTP    BCI     2.3  PGM-php-cgi.bi   TIMA
        ZEND2        QTMHHTTP    BCI     2.8  PGM-php-cgi.bi   TIMA
        ZEND2        QTMHHTTP    BCI     1.8  PGM-php-cgi.bi   RUN
        ZEND2        QTMHHTTP    BCI     1.4  PGM-php-cgi.bi   TIMA

Stop XMLSERVICE for debugger attach

WRKACTJOB find XMLSERVICE private mode only

IF you are running a private connection (ipc=’/tmp/packers’), you can simply use WRKACTJOB and locate the XMLSERVICE job and attach debugger. However the next qsysopr message option is also available.

XMLSERVICE QSYSOPR message stop anytime

At times it is useful to stop the XMLSERVICE job to connect a debugger to examine an issue, especially if you are running stateless in the QSQSRVR job.

I often find the simple trick below to be very useful.

The trick …

At this time the PHP wrappers have not implemented a stop for debugger interface (cough … Alan), but XMLSERVICE has control *debug ability already available. In the following example adding $ctl  .= " *debug"; will stop XMLSERVICE job with a inquire message on qsysopr, simply attach your debugger to job # in msg, set a breakpoint in your module (or in XMLSERVICE module), and answer qsysopr message with any character to let XMLSERVICE continue to your breakpoint. That is it …

Hint: PHP wrappers are sending XML just like below, so if you have toolkit.ini debug turned on you can often simply cut/paste your XML in debug log into the variable $clobIn below.

Test #3 as example …

/MYASP2/www/zend2/htdocs/connection2.inc
<?php
$database            = "*LOCAL"; // *LOCAL on IBM i ... LP0264D on Linux
$cwdatabase          = "localhost";
$user                = "DB2";
$password            = "XXXXXXXX";
$libxmlservice       = "ZENDSVR"; // ZZCALL (Zend Server)
$i5persistentconnect = false;
?>

/MYASP2/www/zend2/htdocs/xxtoolkit_raw.php
<?php
require_once('connection2.inc');
if ($i5persistentconnect) $conn = db2_pconnect($database,$user,$password);
else $conn = db2_connect($database,$user,$password);
if (!$conn) echo "Bad connect: $conn,$database,$user,perm=$i5persistentconnect";

$stmt = db2_prepare($conn, "call XMLSERVICE.iPLUG4K(?,?,?,?)");
$ctl  = "*sbmjob";         // *here for no additional private job
$ctl  .= " *debug";        // THIS WILL STOP XMLSERVICE JOB MSG TO QSYSOPR (server side)
// $ctl  .= " *debugproc"; // THIS WILL STOP XMLSERVICE/QSQSRVR JOB MSG TO QSYSOPR (client side)
                          // if running *here either *debug/*debugproc will work
$ipc='/tmp/packers';
// $ipc  = "";     // *here no need ipc
$clobIn = "<?xml version='1.0'?>
<pgm name='ZZCALL' lib='$libxmlservice'>
<parm  io='both'>
  <data type='1A'>a</data>
</parm>
<parm  io='both'>
  <data type='1A'>b</data>
</parm>
<parm  io='both'>
  <data type='7p4'>11.1111</data>
</parm>
<parm  io='both'>
  <data type='12p2'>222.22</data>
</parm>
<parm  io='both'>
  <ds>
  <data type='1A'>x</data>
  <data type='1A'>y</data>
  <data type='7p4'>66.6666</data>
  <data type='12p2'>77777.77</data>
  </ds>
</parm>
<return>
  <data type='10i0'>0</data>
</return>
</pgm>";
$clobOut = "";
$ret=db2_bind_param($stmt, 1, "ipc", DB2_PARAM_IN);
$ret=db2_bind_param($stmt, 2, "ctl", DB2_PARAM_IN);
$ret=db2_bind_param($stmt, 3, "clobIn", DB2_PARAM_IN);
$ret=db2_bind_param($stmt, 4, "clobOut", DB2_PARAM_OUT);
$ret=db2_execute($stmt);
// var_dump($clobOut);
if (strpos($clobOut,"4444444444.44")>0) echo "success";
else echo "fail";
?>

Hint: If $clobIn PHP XML single quote / double quote issues are driving you nuts try the following technique.

BTW – For the enterprising do-it-yourself PHP builder you can see how very easy it would be to make a custom call *PGM API with parameter substitution using str_replace() function similar to test_lib_replace(), something like function ZZCALL($INCHARA,$INCHARB,$INDEC1,$INDEC2,$INDS1)

$xml = <<<ENDPROC
<?xml version='1.0'?>
<script>
<pgm name='ZZCALL' lib='xyzlibxmlservicexyz'>
<parm  io='both'>
  <data type='1A' var='INCHARA'>a</data>
</parm>
<parm  io='both'>
  <data type='1A' var='INCHARB'>b</data>
</parm>
<parm  io='both'>
  <data type='7p4' var='INDEC1'>11.1111</data>
</parm>
<parm  io='both'>
  <data type='12p2' var='INDEC2'>222.22</data>
</parm>
<parm  io='both'>
  <ds>
  <data type='1A' var='INDS1.DSCHARA'>x</data>
  <data type='1A' var='INDS1.DSCHARB'>y</data>
  <data type='7p4' var='INDS1.DSDEC1'>66.6666</data>
  <data type='12p2' var='INDS1.DSDEC2'>77777.77</data>
  </ds>
</parm>
<return>
  <data type='10i0'>0</data>
</return>
</pgm>
</script>
ENDPROC;
$clobIn = test_lib_replace($xml);

// xml common text replacement
function test_lib_replace($xml) {
  global $libxmlservice, $iOPM;
  if (!$iOPM) {
    $was = array("xyzlibxmlservicexyz");
    $now = array("$libxmlservice");
  }
  else {
    $was = array("xyzlibxmlservicexyz","<pgm");
    $now = array("$libxmlservice","<pgm mode='opm'");
  }
  $out = str_replace($was,$now,$xml);
  return $out;
}

XMLSERVICE can be stopped …

Debug technique:

It’s as easy as 1-2-3-4-5-6-7-8-9-10 :)

  1. Add the following line to your PHP script before the program call to be debugged. $toolkitConn should be your toolkit connection object.
``$toolkitConn->setOptions(array('customControl'=>'*debug')).``

Run your script.
The script will "hang" while it waits on #2 below...
(move to green screen 5250 for steps 2-10)
  1. A MSGW inquiry message in DSPMSG QSYSOPR will be generated by the toolkit.
  2. Note the job information (number, name, user) provided in the MSGW.
  3. STRSRVJOB using that job information as parameters.
  4. STRDBG with the program and library you wish to debug.
  5. Answer the MSGW. Any answer will do–“G” is fine.
  6. The RPG program source will appear in debug mode in your terminal, ready to step through, allowing you to inspect variables, etc.
  7. When done inspecting and stepping, let the RPG program complete (using function keys indicated on screen).
  8. ENDDBG
  9. ENDSRVJOB