theLink 10.0
Loading...
Searching...
No Matches
perfclient

The client part of the performance test tool.

usage
usage: perfclient [OPTION]... [ARGUMENT]...

  This tool is the client part of the performance test toolkit and expect
    the 'perfserver' as argument.

  The following tests are defined:
    --all                Do all the following tests.
    --all-performance    Do all tests relevant for performance testing.
    --all-no-parent      Do all the following tests but without parent.
    --send-nothing       Send empty package just to test the service callback.
    --send               The data is send from the client to the server using ...
        1. a 1 byte char 
        2. a 2 byte short 
        3. a 4 byte integer 
        4. a 8 byte double 
        5. a binary of size between 1 and 1000 bytes
    --send-string        Same as '--send' but use 'string-data' only
    --send-and-wait      Same as '--send' but use 'MqSendEND_AND_WAIT' -> DEFAULT
    --send-and-callback  Same as '--send' but use 'MqSendEND_AND_CALLBACK'
    --send-persistent    Use a persistent-transaction together with 'MqSendEND_AND_WAIT'
      --storage  STRING  database file, #memdb# or #tmpdb#  (default: file)
    --parent             Create and Delete a PARENT-context in a loop
      --spawn|--thread|--fork    choose starter (default: lng-specific)
      --wrk NUMBER               number of parallel workers (default: 1)
    --child              Create and Delete a CHILD-context in a loop
    --bus or --bfl       Create and Delete a MkBufferStreamS or MkBufferListS in a loop
    --bin/str            send ad wait for binary/string data

  perfclient [ARGUMENT]... syntax:
    perfclient [OPTION]... @ server [OPTION]... [ARGUMENT]

  msgque [OPTION]:
    --help-msgque    print msgque specific help

  perfclient [OPTION]:
    --num NUMBER             number of test-cycles (default: -1)
    --sec SECONDS            seconds per test (default: 2)
    --timeout-event SECONDS  timeout for background wait (default: 3)
    -h, --help               print this help

code
/**
 *   @file         NHI1/example/c/perfclient.c
 *   @brief        tag: nhi1-release-250425
 *   @copyright    (C) NHI - #1 - Project - Group
 *                 This software has NO permission to copy,
 *                 please contact AUTHOR for additional information
 */

/* LABEL-START */

#define META_FILE_NAME "client.c"

/* LABEL-END */

/** \ingroup validation
 *  \defgroup client client
 *  \{
 *  \brief client \client_desc
 *
 *  \verbinclude client.help
 */

#include <limits.h>
#include <float.h>
#include <unistd.h>

#include "common.h"
#include "stat.h"

#define META_CONTEXT_S mqctx

/*****************************************************************************/
/*                                                                           */
/*                                     main                                  */
/*                                                                           */
/*****************************************************************************/

static int NUM_DEFAULT=-1, SEC_DEFAULT=2, WRK_DEFAULT=1;
static void ClientHelp ( const char * ) __attribute__ ((noreturn));
static char STORAGE_DEFAULT[128];
static int TIMEOUT_EVENT_DEFAULT=3;

static MK_I32 callnum = 0;

static MK_STRN int2str (MK_I32 i) {
  static MkThreadLocal char buffer[30];
  snprintf(buffer,20,"%i", i);
  return buffer;
}

static int parent_count = 0;
static enum MkErrorE count_callback ( MQ_SERVICE_CALL_ARGS ) {
  parent_count += MqReadI32_e(mqctx);
  return MK_OK;
error:
  return MkErrorStack_1X(mqctx);
}

static enum MkErrorE RET_ECUL ( MQ_SERVICE_CALL_ARGS ) {
  MK_I8 valY;
  MK_I16 valS;
  MK_I32 valI;
  MK_DBL valD;
  MK_BUF buf;

  callnum++;

  MqReadI8_E  (mqctx, &valY);
  MqReadI16_E (mqctx, &valS);
  MqReadI32_E (mqctx, &valI);
  MqReadDBL_E (mqctx, &valD);
  MqReadBUF_E (mqctx, &buf);

  return MK_OK;
error:
  return MkErrorStack_1X(mqctx);
}

static enum MkErrorE RET_SDTR ( MQ_SERVICE_CALL_ARGS ) {
  MK_I32 id;
  MK_I8 valY;
  MK_I16 valS;
  MK_I32 valI;
  MK_DBL valD;
  MK_BUF buf;

  callnum++;

  MqReadT_START_E (mqctx);
  MqReadI32_E (mqctx, &id);
  MqReadT_END_E (mqctx);

  MqReadI8_E  (mqctx, &valY);
  MqReadI16_E (mqctx, &valS);
  MqReadI32_E (mqctx, &valI);
  MqReadDBL_E (mqctx, &valD);
  MqReadBUF_E (mqctx, &buf);

  return MK_OK;
error:
  return MkErrorStack_1X(mqctx);
}

/// \brief display help using \b -h or \b --help command-line option
/// \param base the executable usually: <tt>basename(argv[0])</tt>
static void
ClientHelp ( const char * base  )
{
  MkRtSetup_NULL;
  MkBufferCreateLOCAL_T(MkBuffer1024C,buf,0);
  MkBufferAppendV(buf,
    "usage: %s [OPTION]... [ARGUMENT]...\n"
    "\n"
    "  This tool is the client part of the performance test toolkit and expect\n"
    "    the 'perfserver' as argument.\n"
    "\n"
    "  The following tests are defined:\n"
    "    --all                Do all the following tests.\n"
    "    --all-performance    Do all tests relevant for performance testing.\n"
    "    --all-no-parent      Do all the following tests but without parent.\n"
    "    --send-nothing       Send empty package just to test the service callback.\n"
    "    --send               The data is send from the client to the server using ...\n"
    "        1. a 1 byte char \n"
    "        2. a 2 byte short \n"
    "        3. a 4 byte integer \n"
    "        4. a 8 byte double \n"
    "        5. a binary of size between 1 and 1000 bytes\n"
    "    --send-string        Same as '--send' but use 'string-data' only\n"
    "    --send-and-wait      Same as '--send' but use 'MqSendEND_AND_WAIT' -> DEFAULT\n"
    "    --send-and-callback  Same as '--send' but use 'MqSendEND_AND_CALLBACK'\n"
    "    --send-persistent    Use a persistent-transaction together with 'MqSendEND_AND_WAIT'\n"
    "      --storage  STRING  database file, #memdb# or #tmpdb#  (default: file)\n"
    "    --parent             Create and Delete a PARENT-context in a loop\n"
    "      --spawn|--thread|--fork    choose starter (default: lng-specific)\n"
    "      --wrk NUMBER               number of parallel workers (default: %i)\n"
    "    --child              Create and Delete a CHILD-context in a loop\n"
    "    --bus or --bfl       Create and Delete a MkBufferStreamS or MkBufferListS in a loop\n"
    "    --bin/str            send ad wait for binary/string data\n"
    "\n"
    "  %s [ARGUMENT]... syntax:\n"
    "    %s [OPTION]... %c server [OPTION]... [ARGUMENT]\n"
    "\n"
    "%s"
    "\n"
    "  %s [OPTION]:\n"
    "    --num NUMBER             number of test-cycles (default: %i)\n"
    "    --sec SECONDS            seconds per test (default: %i)\n"
    "    --timeout-event SECONDS  timeout for background wait (default: %i)\n"
    "    -h, --help               print this help\n"
    "\n", base, WRK_DEFAULT, base, base, MK_ALFA, MqHelp (NULL), base, NUM_DEFAULT, SEC_DEFAULT, TIMEOUT_EVENT_DEFAULT
  );

  fputs(bufR.super.buf.storage.first.C,stderr);

  exit(EXIT_SUCCESS);
}

// ===================================================================================
// === MARK_W
// ===

// "global" because PerfWorker_STR0 and PerfWorker_END0 require access
static MkThreadLocal  bool doNext = true;     // --sec require this
static MkThreadLocal  int worker_count = 0;

// [perfworker-example]
// asynchronous call
static enum MkErrorE PerfWorker_STR0( MQ_SERVICE_CALL_ARGS ) {
  MK_BFL largv = NULL;
  MK_BFL pargv = NULL;
  MK_I32 num   = MqReadI32_e(mqctx);
         pargv = MkBufferListDup(MqReadBFL_e(mqctx));
  enum MkErrorE ret = MK_OK;

  if (num == -1) num = 999999;  // endless

  // start test until "doNext" is "false" or "num" exceeds
  for (int i=0; doNext && i<num; i++) {
    // CREATE the context
    MQ_CTX ctx = MqContextCreate(NULL, mqctx);
    largv = MkBufferListDup(pargv);
    MqLinkCreate_C (ctx, largv) {
      MqContextErrorMove(mqctx,ctx);
      MqContextDelete(ctx);
      goto error;
    }

//MqSend(ctx,"E","MARK:C","start");

    MkBufferListDelete(largv);

    // DELETE the context
    MqContextDelete(ctx);

    // count the loops
    worker_count++;

    // give PerfWorker_END0 time to update "doNext"
    MqProcessEvent(mqctx, MQ_WAIT_NO, MK_TIMEOUT_DEFAULT);
  }

end:
//printV("ret=%i, Delete=%p\n", ret, largv)
  MkBufferListDelete(largv);
  MkBufferListDelete(pargv);

  if (doNext == true) {
    // finish the test WITHOUT "END0" -> test on --num, answer required.
    MqSend_E(mqctx, "R", "I", worker_count);
  }
  return ret;

error:
  if (doNext == true) {
    ret = MkErrorStack_1X(mqctx);
  } else {
    ret = MkErrorReset_1X(mqctx);
  }
  goto end;
}
// [perfworker-example]

static enum MkErrorE PerfWorker_END0( MQ_SERVICE_CALL_ARGS ) {
  doNext = false;
  return MqSend(mqctx, "R", "I", worker_count);
}

static enum MkErrorE PerfWorker_GET0( MQ_SERVICE_CALL_ARGS ) {
printXI(mqctx, worker_count);
  return MqSend(mqctx, "R", "I", worker_count);
}

enum MkErrorE
PerfWorkerSetup ( MQ_SERVICE_CALL_ARGS ) 
{
  MqServiceCreate_E (mqctx, "STR0", PerfWorker_STR0, NULL, NULL, NULL);
  MqServiceCreate_E (mqctx, "END0", PerfWorker_END0, NULL, NULL, NULL);
  MqServiceCreate_E (mqctx, "GET0", PerfWorker_GET0, NULL, NULL, NULL);
  return MK_OK;
error:
  return MkErrorStack_1X(mqctx);
}

enum MkErrorE
PerfWorker ( MQ_CALLBACK_FACTORY_CTOR_ARGS )
{
  MQ_CTX mqctx = MqContextCreate(NULL,tmpl);
  MqConfigSetServerSetup(mqctx, PerfWorkerSetup, NULL, NULL, NULL);
  *contextP = mqctx;
  return MK_OK;
}


// ===================================================================================
// ===
// ===

// help doxygen to build external references to the "main" proc of a tool
//#define ClientMain main

// BEGIN-C-CLIENT-1
/// \brief main entry-point for the tool
/// \param argc the number of command-line arguments
/// \param argv the command-line arguments as an array of strings
/// \return the exit number

enum MkErrorE
PerfClient ( MQ_CALLBACK_FACTORY_CTOR_ARGS )
{
  MQ_CTX mqctx = MqContextCreate(NULL,tmpl);
  MqContextC_T->fHelp = ClientHelp;
  *contextP = mqctx;
  return MK_OK;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

#define SIZE 1000
MkBinaryR bin;

enum MkErrorE
PerfSendPackageStart_RT (MK_RT_ARGS MQ_CTX mqctx, int count)
{
  MqSendSTART_E (mqctx);
  MqSendI8_E  (mqctx, (MK_I8)  (count % SCHAR_MAX));
  MqSendI16_E (mqctx, (MK_I16) (count % SHRT_MAX));
  MqSendI32_E (mqctx, (MK_I32) (count % INT_MAX));
  MqSendDBL_E (mqctx, (MK_DBL) (count % INT_MAX));
  MqSendBIN_E (mqctx, MkBinaryCreateSlice(bin,0,(count%SIZE)+1));

error:
  return MkErrorGetCode_0E();
}

#define PerfSendPackageStart(...) PerfSendPackageStart_RT(MK_RT_CALL __VA_ARGS__)

enum MkErrorE
PerfClientExec (MK_RT_ARGS MQ_CTX mqctx, MK_BFL largv)
{
  MK_BUF buf;		      // storage for return values
  MK_BFL parentArgv = NULL;
  // the commandline-arguments (before and after the first MK_ALFA)

  // what should be tested ?
  bool  sendT              =  false  ; // test MqSendEND time?
  bool  sendStringT        =  false  ; // test MqSendEND time? using string only arguments
  bool  sendNothingT       =  false  ; // test MqSendEND time? but send NO data, just call the service
  bool  sendAndWaitT       =  false  ; // test MqSendEND_AND_WAIT round-trip time?
  bool  sendAndCallT       =  false  ; // test MqSendEND_AND_CALLBACK round-trip time?
  bool  sendPersistentT    =  false  ; // test MqSendEND_AND_WAIT together with persistent transactions round-trip time?
  bool  parentT            =  false  ; // test parent-context creation time?
  bool  childT             =  false  ; // test childT-context creation time?
  bool  allT               =  false  ; // test all
  bool  allWithoutParentT  =  false  ; // test all ... without parent
  bool  allPerformanceT    =  false  ; // test all ... but only tests relevant for performance testing
  bool  busT               =  false  ; // test MkBufferStreamS
  bool  bflT               =  false  ; // test MkBufferListS
  bool  binT               =  false  ; // test binary data
  bool  strT               =  false  ; // test string data

  MK_I32 num = NUM_DEFAULT;
  MK_I32 sec = SEC_DEFAULT;
  MK_I32 wrk = WRK_DEFAULT;
  MK_I32 timeoutEvent = TIMEOUT_EVENT_DEFAULT;
  MK_STRN storage  = "unknown";
  MK_BIN binD = MkSysMalloc (MK_ERROR_PANIC, (SIZE));
  memset (binD, 'A', SIZE);
  bin = MkBinaryCreate(SIZE,binD);
  MK_STR strD = MkSysMalloc (MK_ERROR_PANIC, (SIZE+1));
  memset ((void*)strD, 'S', SIZE);
  strD[SIZE] = '\0';
  //MkStringR str = MkStringCreate(SIZE,strD);

  MkBufferListCreateSTATIC_T(wrkargs, 0);

  // read application specific arguments
  MkBufferListCheckOptionBOL_E  (largv,  "--send",               false, true, &sendT)              ;
  MkBufferListCheckOptionBOL_E  (largv,  "--send-nothing",       false, true, &sendNothingT)       ;
  MkBufferListCheckOptionBOL_E  (largv,  "--send-string",        false, true, &sendStringT)        ;
  MkBufferListCheckOptionBOL_E  (largv,  "--send-and-wait",      false, true, &sendAndWaitT)       ;
  MkBufferListCheckOptionBOL_E  (largv,  "--send-and-callback",  false, true, &sendAndCallT)       ;
  MkBufferListCheckOptionBOL_E  (largv,  "--send-persistent",    false, true, &sendPersistentT)    ;
  MkBufferListCheckOptionBOL_E  (largv,  "--parent",             false, true, &parentT)            ;
  MkBufferListCheckOptionBOL_E  (largv,  "--child",              false, true, &childT)             ;
  MkBufferListCheckOptionBOL_E  (largv,  "--all",                false, true, &allT)               ;
  MkBufferListCheckOptionBOL_E  (largv,  "--all-no-parent",      false, true, &allWithoutParentT)  ;
  MkBufferListCheckOptionBOL_E  (largv,  "--all-performance",    false, true, &allPerformanceT)    ;
  MkBufferListCheckOptionBOL_E  (largv,  "--bus",                false, true, &busT)               ;
  MkBufferListCheckOptionBOL_E  (largv,  "--bfl",                false, true, &bflT)               ;
  MkBufferListCheckOptionBOL_E  (largv,  "--bin",                false, true, &binT)               ;
  MkBufferListCheckOptionBOL_E  (largv,  "--str",                false, true, &strT)               ;
  if (allT) {
    sendT = sendAndWaitT = sendAndCallT = sendPersistentT = parentT = childT = sendStringT = sendNothingT = busT = bflT = binT = strT = true;
  } else if (allWithoutParentT) {
    sendT = sendAndWaitT = sendAndCallT = sendPersistentT =                    sendStringT = sendNothingT = busT = bflT = binT = strT = true;
                                                            parentT = childT                                                          = false;
  } else if (allPerformanceT) {
    sendT = sendAndWaitT = sendAndCallT                   = parentT = childT               = sendNothingT = busT = bflT               = true;
                                          sendPersistentT =                    sendStringT                              = binT = strT = false;
  } else if (
      sendT                ==  false 
      &&  sendAndWaitT     ==  false
      &&  sendAndCallT     ==  false
      &&  sendPersistentT  ==  false
      &&  parentT          ==  false
      &&  childT           ==  false
      &&  sendStringT      ==  false
      &&  sendNothingT     ==  false
      &&  busT             ==  false
      &&  bflT             ==  false
      &&  binT             ==  false
      &&  strT             ==  false
  ) {
    sendNothingT = true;
  }
  if (sendPersistentT) {
    snprintf(STORAGE_DEFAULT,128,"test.%u.dat", (unsigned int) getpid());
  }

  // the user can supply --num to change the number of iterations
  MkBufferListCheckOptionI32_E (largv,"--sec", SEC_DEFAULT, true, &sec);
  MkBufferListCheckOptionI32_E (largv,"--num", NUM_DEFAULT, true, &num);
  MkBufferListCheckOptionI32_E (largv,"--wrk", WRK_DEFAULT, true, &wrk);
  MkBufferListCheckOptionSTR_E (largv,"--storage",  STORAGE_DEFAULT,  true, &storage);
  MkBufferListCheckOptionI32_E (largv,"--timeout-event",  TIMEOUT_EVENT_DEFAULT, true, &timeoutEvent);

  // save parent args for later use
  parentArgv = MkBufferListDup(largv);

  if (sendT||sendStringT||sendNothingT||sendAndWaitT||sendAndCallT||sendPersistentT||
        childT||busT||bflT||binT||strT) {
    // NEW --parent use worker
    // OLD this seems outdated - keep for docu
    // -> Even "--parent" require this initial server because the worker require context->link.alfa 

    // start the server
    MqLinkCreate_E (mqctx, largv);

    // check for wrong arguments
    MqCheckForLeftOverArguments_E (mqctx, largv);

    // initialize memory, just run one test-case to initialize dynamic data
    MqSendSTART_E (mqctx);
    MqSendBIN_E (mqctx, bin);
    MqSendEND_AND_WAIT_E (mqctx, "ECOU", MK_TIMEOUT_NORMAL);
    MqReadBUF_E (mqctx, &buf);
  }

  // start the MqSendEND_AND_WAIT transaction-performance test
  MkDLogV (mqctx, 0, "%-30s : %10s [ %8s / %-10s ]\n", "start ------------------------", "result", "count", "sec" );

  // ---------------------------------------------------------------------------------------

  if (sendNothingT) {
    StatTimerSP itemT = StatCreate (mqctx,sec,num);

    {
      StatCtxSP stat = StatCtxCreate (mqctx, "--send-nothing");

      // init
      MqSendSTART_E (mqctx);
      MqSendEND_E (mqctx, "NTHT", 0);
      MqSendSYNC_E (mqctx);

      // test
      StatInit (itemT);
        while (StatCheck(itemT)) {
	  MqSendSTART_E (mqctx);
	  MqSendEND_E (mqctx, "NTHT", 0);
	};
	// just sync with the server and wait until all "NTHT" are processed
	MqSendSYNC_E (mqctx);
      StatCtxCalc (stat, itemT, -1);
      StatCtxPrint (stat);
      StatCtxDelete (&stat);
    }

    // cleanup
    StatDelete (&itemT);

  } // finish the MqSendEND test

  // ---------------------------------------------------------------------------------------

  if (sendT) {

    // [Example-MqSendSYNC]
    StatTimerSP itemT = StatCreate (mqctx,sec,num);
    {
      StatCtxSP stat = StatCtxCreate (mqctx, "--send");

      // init
      PerfSendPackageStart(mqctx,9999);
      MqSendEND_E (mqctx, "RDUL", 0);
      MqSendSYNC_E (mqctx);

      // test 
      StatInit (itemT);
        // TEST: start as many as possible asyncron-service-calles in "itemT" intervall.
        while (StatCheck(itemT)) {
          PerfSendPackageStart(mqctx, StatCount(itemT));
	  MqSendEND_E (mqctx, "RDUL", 0);
	}
        // just sync with the server
        MqSendSYNC_E (mqctx);
      StatCtxCalc (stat, itemT, -1);
      StatCtxPrint (stat);
      StatCtxDelete (&stat);
    }
    // [Example-MqSendSYNC]

    // cleanup
    StatDelete (&itemT);

  } /* finish the MqSendEND test */

  // ---------------------------------------------------------------------------------------

  if (sendStringT) {

    // test
    StatTimerSP itemT = StatCreate (mqctx,sec,num);
    {
      StatCtxSP stat = StatCtxCreate (mqctx, "--send-string");

      // init
      MqSendSTART_E (mqctx);
      MqSendSTR_E (mqctx, "Hallo");
      MqSendSTR_E (mqctx, "shahdahdhe73uz38djdjfkfod93");
      MqSendSTR_E (mqctx, "huch");
      MqSendSTR_E (mqctx, "keybord?");
      MqSendSTR_E (mqctx, "human!!");
      MqSendEND_E (mqctx, "RDUC", 0);
      MqSendSYNC_E (mqctx);

      // test
      StatInit (itemT);
        while (StatCheck(itemT)) {
	  MqSendSTART_E (mqctx);
	  MqSendSTR_E (mqctx, "Hallo");
	  MqSendSTR_E (mqctx, "shahdahdhe73uz38djdjfkfod93");
	  MqSendSTR_E (mqctx, "huch");
	  MqSendSTR_E (mqctx, "keybord?");
	  MqSendSTR_E (mqctx, "human!!");
	  MqSendEND_E (mqctx, "RDUC", 0);
	};
	// just sync with the server and wait until all "RDUC" are processed
	MqSendSYNC_E (mqctx);
      StatCtxCalc (stat, itemT, -1);
      StatCtxPrint (stat);
      StatCtxDelete (&stat);
    }

    // cleanup
    StatDelete (&itemT);

  } // finish the MqSendEND test

  // ---------------------------------------------------------------------------------------

  if (sendAndCallT) {

    StatTimerSP itemT = StatCreate (mqctx,sec,num);
    {
      StatCtxSP stat = StatCtxCreate (mqctx, "--send-and-callback");

      // init
      PerfSendPackageStart(mqctx,9999);
      MqSendEND_AND_CALLBACK_E (mqctx, "ECUL", RET_ECUL, NULL, NULL, 0);
      MqProcessEvent_E (mqctx, MQ_WAIT_ONCE, timeoutEvent);
      MqSendSYNC_E (mqctx);

      // test
      callnum = 0;
      StatInit (itemT);
        while (StatCheck(itemT)) {
          int count = StatCount(itemT);
          PerfSendPackageStart(mqctx, count);
	  MqSendEND_AND_CALLBACK_E (mqctx, "ECUL", RET_ECUL, NULL, NULL, 0);
	  // don't flood the socket buffer with unread messages
	  if ((count % 7) == 0) {
	    while (MqProcessEvent (mqctx, MQ_WAIT_NO, timeoutEvent) == MK_OK);
	  }
	  MkErrorCheck_0E();
	};

      // wait until all callbacks are processed
      while (callnum != StatCount(itemT)) {
	MqProcessEvent_E (mqctx, MQ_WAIT_ONCE, timeoutEvent);
      }

      StatCtxCalc (stat, itemT, -1);
      StatCtxPrint (stat);
      StatCtxDelete (&stat);
    }

    // cleanup
    StatDelete (&itemT);

    //MkSysSleep_E (MK_ERROR_IGNORE, 2);

  } // finish the MqSendEND_AND_CALLBACK test

  // ---------------------------------------------------------------------------------------

  if (sendAndWaitT) {

    enum MkErrorE doSendAndWaitT_RT(MK_RT_ARGS MQ_CTX mqctx, int count) {
      MK_I8 valY;
      MK_I16 valS;
      MK_I32 valI;
      MK_DBL valD;
      MK_BUF buf;
      PerfSendPackageStart(mqctx,count);
      MqSendEND_AND_WAIT_E (mqctx, "ECUL", MK_TIMEOUT_NORMAL);
      MqReadI8_E (mqctx, &valY);
      MqReadI16_E (mqctx, &valS);
      MqReadI32_E (mqctx, &valI);
      MqReadDBL_E (mqctx, &valD);
      MqReadBUF_E (mqctx, &buf);
      return MK_OK;
      error:
      return MkErrorStack_1X(mqctx);
    }
    #define doSendAndWaitT(x,c) MkErrorCheck(doSendAndWaitT_RT(MK_RT_CALL x,c))

    // test
    StatTimerSP itemT = StatCreate (mqctx,sec,num);
    {
      StatCtxSP stat = StatCtxCreate (mqctx, "--send-and-wait");

      // init
      doSendAndWaitT(mqctx,9999);

      // test 
      StatInit (itemT);
        while (StatCheck(itemT)) {
          doSendAndWaitT(mqctx,StatCount(itemT));
	};

      StatCtxCalc (stat, itemT, -1);
      StatCtxPrint (stat);
      StatCtxDelete (&stat);
    }

    // cleanup
    StatDelete (&itemT);

  } // finish the MqSendEND_AND_WAIT performance test

  // ---------------------------------------------------------------------------------------

  if (sendPersistentT) {
    char systemC[200] = "";

    StatTimerSP itemT = StatCreate (mqctx,sec,num);
    {
      StatCtxSP stat = StatCtxCreate (mqctx, "--send-persistent");
      // setup the callback
      MqServiceCreate_E (mqctx, "SDTR", RET_SDTR, NULL ,NULL, NULL);
      if (storage[0] != '#') {
        snprintf(systemC,200,"rm %s-* 2>/dev/null", storage);
        system(systemC);
      }
      // set transaction-database name
      MqSend (mqctx,"W", "STDB:C", storage);

      // init - prepare sql queries
      int count = 9999;
      MqSendSTART_E (mqctx);
      MqSendT_START_E (mqctx);
      MqSendI32_E (mqctx, 999);
      MqSendT_END_E (mqctx);
      MqSendI8_E (mqctx, (MK_I8) (count % SCHAR_MAX));
      MqSendI16_E (mqctx, (MK_I16) (count % SHRT_MAX));
      MqSendI32_E (mqctx, (MK_I32) (count % INT_MAX));
      MqSendDBL_E (mqctx, (MK_DBL) (count % INT_MAX));
      MqSendBIN_E (mqctx, MkBinaryCreateSlice(bin,0,10));
      MqSendEND_AND_TRANSACTION_E (mqctx, "ECUL", "SDTR", MK_TIMEOUT_NORMAL);
      MqProcessEvent_E (mqctx, MQ_WAIT_ONCE, timeoutEvent);
      MqSendSYNC_E (mqctx);

      // test
      callnum = 0;
      StatInit (itemT);
        while (StatCheck(itemT)) {
          count = StatCount(itemT);
	  MqSendSTART_E (mqctx);
	  MqSendT_START_E (mqctx);
	  MqSendI32_E (mqctx, 999);
	  MqSendT_END_E (mqctx);
          MqSendI8_E (mqctx, (MK_I8) (count % SCHAR_MAX));
          MqSendI16_E (mqctx, (MK_I16) (count % SHRT_MAX));
          MqSendI32_E (mqctx, (MK_I32) (count % INT_MAX));
          MqSendDBL_E (mqctx, (MK_DBL) (count % INT_MAX));
	  MqSendBIN_E (mqctx, MkBinaryCreateSlice(bin,0,(count%SIZE)+1));
	  MqSendEND_AND_TRANSACTION_E (mqctx, "ECUL", "SDTR", MK_TIMEOUT_NORMAL);
	};

      // wait until all callbacks are processed
      while (callnum != StatCount(itemT)) {
	MqProcessEvent_E (mqctx, MQ_WAIT_ONCE, timeoutEvent);
      }

      StatCtxCalc (stat, itemT, -1);
      StatCtxPrint (stat);
      StatCtxDelete (&stat);
      MqServiceDelete_E (mqctx, "SDTR");
    }

    // cleanup
    StatDelete (&itemT);
    MkSysSleep_E (MkOBJ(mqctx),1);
    MqSend_E(mqctx,"W","STDC");
    if (storage[0] != '#') {
      system(systemC);
    }

  } // finish the MqSendTRANSACTION performance test

  // ---------------------------------------------------------------------------------------
  // MARK_P

  // start the parent-context creation test
  if (parentT) {
    bool onError = false;
    // init
    StatTimerSP itemT = StatCreate (mqctx,sec,num);
    {
      enum MqStartE start = 0;
      MQ_CTX *slaves = MkSysCalloc(MK_ERROR_PANIC,sizeof(*slaves),(size_t)wrk);

      if (MkBufferListCheckOptionBOL_e (largv,"--spawn"  ,false,true)) start = MQ_START_SPAWN;
      if (MkBufferListCheckOptionBOL_e (largv,"--thread" ,false,true)) start = MQ_START_THREAD;
      if (MkBufferListCheckOptionBOL_e (largv,"--fork"   ,false,true)) start = MQ_START_FORK;

      // create the workers
      for (int i=0; i<wrk; i++) {
        MkBufferListReset(wrkargs);
        switch (start) {
          case MQ_START_DEFAULT :  break;
          case MQ_START_SPAWN   :  MkBufferListAppendSTR(wrkargs, "--spawn");  break;
          case MQ_START_THREAD  :  MkBufferListAppendSTR(wrkargs, "--thread"); break;
          case MQ_START_FORK    :  MkBufferListAppendSTR(wrkargs, "--fork");   break;
        }
        MkBufferListAppendVA(wrkargs,  
          "--prefix", "wk-cl-", "--postfix", int2str(i), "@", "--prefix", "wk-sv-", NULL
        );

        MqSlaveWorker_E(mqctx, i+MQ_SLAVE_USER, "PerfWorker", wrkargs);
      }

      //system("tree-print.bash");

      // loop to create the parent context
      StatCtxSP stat = StatCtxCreate (mqctx, "--parent");
      StatInit (itemT);
      // start the test
      {
        if (num == NUM_DEFAULT) {
          // test: --num not set, test on --sec
          // start test on all --wrk
          for (int i=0; i<wrk; i++) {
            MkBufferListReset(wrkargs);
            MkBufferListAppendLA(wrkargs, parentArgv);
            slaves[i] = MqSlaveGet_e(mqctx,i+MQ_SLAVE_USER);
            // start test
            MqSend_E(slaves[i], "E", "STR0:IL", num , wrkargs);
          }
          // wait '--sec' seconds
          MkSysSleep_E(MkOBJ(mqctx), sec);
          // stop the test and get "count" from worker
          for (int i=0; i<wrk; i++) {
            MqSend_E(slaves[i], "C", count_callback, "END0");
          }
        } else {
//MqSend(mqctx,"W","MARK:C","start");

          // test: --num
          // start test on all --wrk
          for (int i=0; i<wrk; i++) {
//printV("i=%d, num=%d", i, num)
            MkBufferListReset(wrkargs);
            MkBufferListAppendLA(wrkargs, parentArgv);
            slaves[i] = MqSlaveGet_e(mqctx,i+MQ_SLAVE_USER);
            // start test
            MqSend_E(slaves[i], "C", count_callback, "STR0:IL", num , wrkargs);
          }
        }

        // wait for --wrk answers from STR0 (--num) or END0 (--sec)
        for (int i=0; i<wrk; i++) {
          check_MkErrorE(MqProcessEvent(mqctx, MQ_WAIT_ONCE, timeoutEvent)) goto error2;
        }

        goto end2;
        // this should not happen !!
        error2:
          if (MkErrorIsTIMEOUT_0E()) {
            MkErrorSetC_2M(mqctx,
              "TIMEOUT in wait for parent respons, forget to start 'perfserver' with: "
                "--spawn, --thread or --fork ?");
            MkErrorStack_0E();
          }
          onError = true;
          // continue to "cleanup" the test
          goto skip_statistics;
      }
      end2:

      // delete the master an ALL slaves
//printXI(mqctx, count);
      StatCtxCalc (stat, itemT, parent_count);
      StatCtxPrint (stat);
      StatCtxDelete (&stat);

      // delete the workers
      skip_statistics:
      for (int i=0; i<wrk; i++) {
        MqSlaveDelete_E(mqctx, i+MQ_SLAVE_USER);
        slaves[i]=NULL;
      }
      MkSysFree(slaves);
    }

    // cleanup
    StatDelete (&itemT);

    if (onError) goto error;
  }

  // ---------------------------------------------------------------------------------------

  // start the childT-context creation test
  if (childT) {
    StatTimerSP itemT = StatCreate (mqctx,sec,num);

    // fill template configuration
    MQ_CTX template = MqContextCreate(NULL, mqctx);
    MqConfigSetName (template, "childT");
    {
      // loop to create the parent context
      StatCtxSP stat = StatCtxCreate (mqctx, "--child");
      StatInit (itemT);
      while (StatCheck(itemT)) {
        MQ_CTX ctx = MqContextCreate(NULL,template);
	MqLinkCreateChild_C (ctx, mqctx, NULL) {
          MqContextErrorMove(mqctx,ctx);
          MqContextDelete(ctx);
          goto error;
        }
        MqContextDelete(ctx);
      }
      StatCtxCalc (stat, itemT, -1);
      StatCtxPrint (stat);
      StatCtxDelete (&stat);
    }
    MqContextDelete(template);

    // cleanup
    StatDelete (&itemT);
  }

  // ---------------------------------------------------------------------------------------
  // MARK_S

  // test: MqBufferStreamS
  if (busT) {
    StatTimerSP itemT = StatCreate (mqctx,sec,num);

    {
      StatCtxSP stat = StatCtxCreate (mqctx, "--bus");
      MK_I8 valY;
      MK_I16 valS;
      MK_I32 valI;
      MK_DBL valD;
      MK_STRN valC;

      // init storage…
      int count = 9999;
      {
        MqSendSTART_E (mqctx);
        MqSendI8_E (mqctx, (MK_I8) (count % SCHAR_MAX));
        MqSendI16_E (mqctx, (MK_I16) (count % SHRT_MAX));
        MqSendI32_E (mqctx, (MK_I32) (count % INT_MAX));
        MqSendDBL_E (mqctx, (MK_DBL) (count % INT_MAX));
        MqSendSTR_E (mqctx, "Hallo World!");
        MqSendEND_AND_WAIT_E (mqctx, "BUST", MK_TIMEOUT_NORMAL);
        MqReadI8_E (mqctx, &valY);
        MqReadI16_E (mqctx, &valS);
        MqReadI32_E (mqctx, &valI);
        MqReadDBL_E (mqctx, &valD);
        MqReadSTR_E (mqctx, &valC);
      }

      // test BUST
      StatInit (itemT);
      while (StatCheck(itemT)) {
        count = StatCount(itemT);
        MqSendSTART_E (mqctx);
        MqSendI8_E (mqctx, (MK_I8) (count % SCHAR_MAX));
        MqSendI16_E (mqctx, (MK_I16) (count % SHRT_MAX));
        MqSendI32_E (mqctx, (MK_I32) (count % INT_MAX));
        MqSendDBL_E (mqctx, (MK_DBL) (count % INT_MAX));
        MqSendSTR_E (mqctx, "Hallo World!");
        MqSendEND_AND_WAIT_E (mqctx, "BUST", MK_TIMEOUT_NORMAL);
        MqReadI8_E (mqctx, &valY);
        MqReadI16_E (mqctx, &valS);
        MqReadI32_E (mqctx, &valI);
        MqReadDBL_E (mqctx, &valD);
        MqReadSTR_E (mqctx, &valC);
      }
      StatCtxCalc (stat, itemT, -1);
      StatCtxPrint (stat);
      StatCtxDelete (&stat);
    }

    // cleanup
    StatDelete (&itemT);
  }

  // ---------------------------------------------------------------------------------------
  // MARK_F

  // test: MqBufferListS
  if (bflT) {
    StatTimerSP itemT = StatCreate (mqctx,sec,num);

    {
      StatCtxSP stat = StatCtxCreate (mqctx, "--bfl");
      MK_I8 valY;
      MK_I16 valS;
      MK_I32 valI;
      MK_DBL valD;
      MK_STRN valC;

      // init storage…
      int count = 9999;
      {
        MqSendSTART_E (mqctx);
        MqSendI8_E  (mqctx, (MK_I8)  (count % SCHAR_MAX));
        MqSendI16_E (mqctx, (MK_I16) (count % SHRT_MAX));
        MqSendI32_E (mqctx, (MK_I32) (count % INT_MAX));
        MqSendDBL_E (mqctx, (MK_DBL) (count % INT_MAX));
        MqSendSTR_E (mqctx, "Hallo World!");
        MqSendEND_AND_WAIT_E (mqctx, "BFLT", MK_TIMEOUT_NORMAL);
        MqReadI8_E (mqctx, &valY);
        MqReadI16_E (mqctx, &valS);
        MqReadI32_E (mqctx, &valI);
        MqReadDBL_E (mqctx, &valD);
        MqReadSTR_E (mqctx, &valC);
      }

      // test BFLT
      StatInit (itemT);
      while (StatCheck(itemT)) {
        int count = StatCount(itemT);
        MqSendSTART_E (mqctx);
        MqSendI8_E  (mqctx, (MK_I8)  (count % SCHAR_MAX));
        MqSendI16_E (mqctx, (MK_I16) (count % SHRT_MAX));
        MqSendI32_E (mqctx, (MK_I32) (count % INT_MAX));
        MqSendDBL_E (mqctx, (MK_DBL) (count % INT_MAX));
        MqSendSTR_E (mqctx, "Hallo World!");
        MqSendEND_AND_WAIT_E (mqctx, "BFLT", MK_TIMEOUT_NORMAL);
        MqReadI8_E (mqctx, &valY);
        MqReadI16_E (mqctx, &valS);
        MqReadI32_E (mqctx, &valI);
        MqReadDBL_E (mqctx, &valD);
        MqReadSTR_E (mqctx, &valC);
      }
      StatCtxCalc (stat, itemT, -1);
      StatCtxPrint (stat);
      StatCtxDelete (&stat);
    }

    // cleanup
    StatDelete (&itemT);
  }

  // ---------------------------------------------------------------------------------------
  // MARK_B

  // test: binary
  if (binT) {
    StatTimerSP itemT = StatCreate (mqctx,sec,num);

    // fill template configuration
    {
      StatCtxSP stat = StatCtxCreate (mqctx, "--bin");

      // init
      int idx = (9999%SIZE)+1;
      MqSendSTART_E (mqctx);
      MqSendBIN_E (mqctx, MkBinaryCreateSlice(bin,0,idx));
      MqSendEND_E (mqctx, "BINT", 0);
      MqSendSYNC_E (mqctx);

      // test BINT
      StatInit (itemT);
        while (StatCheck(itemT)) {
          idx = (StatCount(itemT)%SIZE)+1;
          MqSendSTART_E (mqctx);
          MqSendBIN_E (mqctx, MkBinaryCreateSlice(bin,0,idx));
          MqSendEND_E (mqctx, "BINT", 0);
        }
	// just sync with the server
	MqSendSYNC_E (mqctx);
      StatCtxCalc (stat, itemT, -1);
      StatCtxPrint (stat);
      StatCtxDelete (&stat);
    }

    // cleanup
    StatDelete (&itemT);
  }

  // ---------------------------------------------------------------------------------------
  // MARK_S

  // test: string
  if (strT) {
    StatTimerSP itemT = StatCreate (mqctx,sec,num);

    // fill template configuration
    {
      StatCtxSP stat = StatCtxCreate (mqctx, "--str");

      // init
      int idx = (9999%SIZE)+1;
      MqSendSTART_E (mqctx);
      char tmp = strD[idx];
      strD[idx] = '\0';
      enum MkErrorE ret = MqSendSTR (mqctx, strD);
      strD[idx] = tmp;
      MkErrorCheck(ret);
      MqSendEND_E (mqctx, "STRT", 0);
      MqSendSYNC_E (mqctx);

      // test STRT
      StatInit (itemT);
        while (StatCheck(itemT)) {
          idx = (StatCount(itemT)%SIZE)+1;
          MqSendSTART_E (mqctx);
          char tmp = strD[idx];
          strD[idx] = '\0';
          enum MkErrorE ret = MqSendSTR (mqctx, strD);
          strD[idx] = tmp;
          MkErrorCheck(ret);
          MqSendEND_E (mqctx, "STRT", 0);
        }
	// just sync with the server
	MqSendSYNC_E (mqctx);
      StatCtxCalc (stat, itemT, -1);
      StatCtxPrint (stat);
      StatCtxDelete (&stat);
    }

    // cleanup
    StatDelete (&itemT);
  }


// cleanup
  MkDLogC (mqctx, 0, "end: ----------------------------------------\n");

  goto exit;

error:
  MkErrorAppendC_2M(mqctx, "use '-h' or '--help' for usage");

exit:
  MkSysFree(binD);
  MkSysFree(strD);
  MkBufferListFree(wrkargs);
  MkBufferListDelete(parentArgv);
  return MkErrorStack_0E_Check();
}

// package-main
int main (int argc, MK_STRN argv[]) 
{
  AllRtSetup_NULL;

/*
printI(MK_RT_REF.error_mk.text.super.buf.var.cursize);
printI(MK_RT_REF.error_mk.text.super.buf.storage.size);
return 0;
*/

  // setup commandline arguments for later use

  MK_BFL largv = MkBufferListCreateVC(argc, argv);
  MQ_CTX mqctx = NULL;

  // create "PerfClient" factory… and make it to the default.
  MqFactoryDefault( MqFactoryAdd_1(PerfClient) );
  MqFactoryAdd_1(PerfWorker);

  // inspect commandline-argument for the "factory" to choose… and create a object
  MqFactoryNew_E (MqFactoryGetCalledL(largv), NULL, &mqctx);

  if (MqConfigGetIsServer(mqctx)) {
    // this is the worker for "--spawn"
    MqLinkCreate_E (mqctx, largv);
    MqCheckForLeftOverArguments_E (mqctx, largv);
    MkBufferListDelete(largv);
    MqProcessEvent_E (mqctx,MQ_WAIT_FOREVER,MK_TIMEOUT_DEFAULT);

  } else {
    // this is the frontend
    MkErrorCheck(PerfClientExec(MK_RT_CALL mqctx, largv));
  }
error:
  MkBufferListDelete(largv);
  MqExit_1(mqctx);
  return 0;
}

/** \} client */

// vim: tabstop=8

See also
perfserver , README_PERFORMANCE