"Fossies" - the Fresh Open Source Software Archive

Member "sfk-1.9.6/mtk/mtktrace.cpp" (22 Feb 2020, 30793 Bytes) of package /linux/misc/sfk-1.9.6.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "mtktrace.cpp" see the Fossies "Dox" file reference documentation.

    1 /*
    2    Micro Tracing Kernel 0.7.2 by stahlworks technologies.
    3    Unlimited Open Source License, free for use in any project.
    4 
    5    the following line protects this file against instrumentation:
    6    // [instrumented]
    7 
    8    NOTE: Win32 GUI apps should never use "term:" tracing, but only "file:".
    9 
   10    Changes:
   11    0.7.2
   12    -  add: time stamp in front of ever log record
   13    -  add: optional tracing of block exits,
   14            define MTK_TRACE_BLOCK_EXITS to use.
   15    0.7.1
   16    -  add: some marker characters {} around block entries.
   17    -  chg: improved error message if filename contains quotes.
   18    -  fix: dumpLastSteps produced no output (missing prefix check).
   19    -  fix: after dumpStackTrace, dumpLastSteps the log file was closed
   20            and therefore no further tracing to file was possible.
   21    -  fix: compile warnings (signed/unsigned comparison).
   22 
   23    0.7.0
   24    -  chg: mtkdump: now also dumps an ascii representation of the data.
   25    -  add: mtkb: if provided function name is empty, replace by line number.
   26    -  fix: syntax of instrumented tag, mtktrace.cpp was not excluded.
   27    -  add: missing includes for standalone compile of mtktrace.cpp.
   28    -  add: text "error: " and "warning:" on corresponding record types.
   29    -  chg: by default, short form "export MTK_TRACE=filename:test.log"
   30            now traces twex statements into file.
   31            this is different from "export MTK_TRACE=file:,filename:test.log"
   32            which defines an output file for mtkDump calls, but does NOT
   33            log normal trace messages into file.
   34    -  add: optimized cr/lf removal on tracing statements.
   35    -  mtkHexDump now expects type void*
   36    -  fflush on flog writing.
   37    -  full tracing support via file:
   38          export MTK_TRACE=file:twex,filename:test.log
   39       without the need to have any terminal output.
   40    -  no longer writing default logname, user must explicitely
   41       specify filename: to create a tracefile.
   42 */
   43 
   44 #include <stdlib.h>
   45 #include <stdio.h>
   46 #include <stdarg.h>
   47 #include <string.h>
   48 
   49 #ifdef _WIN32
   50  #include <windows.h>
   51  #define pthread_self GetCurrentThreadId
   52  #define vsnprintf _vsnprintf
   53 #else
   54  #include <pthread.h>
   55 #endif
   56 
   57 #define MTKMAXTHREADS 300  // by default, threadid's are listed as 2-byte hex
   58 #define MTKMAXLEVEL   200  // max. block nesting level supported for tracing
   59 #define ulong unsigned long
   60 #define uchar unsigned char
   61 
   62 #define MTKMAXMSG    1000  // 1000 (0.1 MB) to 10000 (1.2 MB)
   63 #define MTKMSGSIZE    118
   64 
   65 // trace mode 'b' lists only block entries by default.
   66 // to list also block exits, set this define:
   67 // #define MTK_TRACE_BLOCK_EXITS
   68 
   69 // some projects provide their own printf implementation
   70 #ifdef PROJECT_PRINTF
   71 extern "C" int PROJECT_PRINTF(const char *format, ...);
   72 #else
   73  #define PROJECT_PRINTF printf
   74 #endif
   75 
   76 // =============================
   77 // ===== MTK internal core =====
   78 // =============================
   79 
   80 #define MTKLBUFSIZE 1024  // output line buffer
   81 
   82 class MTKMain
   83 {
   84 public:
   85    MTKMain ();
   86 
   87    void traceMethodEntry(void *pRefInst, const char *pszFile, int nLine, const char *pszfn);
   88    void traceMethodExit (void *pRefInst, const char *pszFile, int nLine, const char *pszfn);
   89    void traceMessageRaw (const char *pszFile, int nLine, char cPrefix, char *pszRaw);
   90    uchar tracePre       (const char *pszFile, int nLine, char cPrefix);
   91    // returns non-zero (OK) if there is an interest in this kind of message
   92 
   93    void dumpStackTrace  (bool bOnlyOfCurrentThread);
   94    void dumpLastSteps   (bool bOnlyOfCurrentThread);
   95    void setRingTrace    (char *pszMask);
   96    void setTermTrace    (char *pszMask);
   97    void setFileTrace    (char *pszMask);
   98 
   99    ulong nthreads;
  100    ulong asysid[MTKMAXTHREADS+2];
  101    ulong alevel[MTKMAXTHREADS+2];
  102    const char *apprefile[MTKMAXTHREADS+2];
  103    ulong anpreline[MTKMAXTHREADS+2];
  104    char  acpreprefix[MTKMAXTHREADS+2];
  105    const char *apfile[MTKMAXTHREADS+2][MTKMAXLEVEL+2];
  106    const char *apfunc[MTKMAXTHREADS+2][MTKMAXLEVEL+2];
  107    ulong anline[MTKMAXTHREADS+2][MTKMAXLEVEL+2];
  108    ulong iclmsg;  // counts endless, modulo past read
  109    char  amsg[MTKMAXMSG+2][MTKMSGSIZE+2];
  110    // record structure per amsg:
  111    // - three bytes prefix (thread,level,prefix)
  112    // - then the message text (mtklog content)
  113    char  alinebuf[MTKLBUFSIZE+10];
  114    char  alinebuf2[MTKLBUFSIZE+10];
  115    ulong ncurthrsid;
  116    ulong ncurthridx;
  117    uchar nlockmode;
  118    ulong nringx; // trace extended interface input into ringbuffer
  119    ulong ntermx;  // dump  extended interface input onto terminal
  120    ulong nfilex;  // dump  extended interface input info file
  121    const char *pszFilename;
  122    FILE *flog;
  123    uchar bflushlog;
  124 };
  125 
  126 static const char *glblPszBlank =
  127    "                                                  "
  128    "                                                  "
  129    "                                                  "
  130    "                                                  ";
  131 
  132 static uchar glblMTKAlive = 0;
  133 static MTKMain glblMTKInst;
  134 
  135 MTKMain::MTKMain()
  136 {
  137    nthreads = 0;
  138    memset(asysid, 0, sizeof(asysid));
  139    memset(alevel, 0, sizeof(alevel));
  140    memset(apprefile, 0, sizeof(apprefile));
  141    memset(anpreline, 0, sizeof(anpreline));
  142    memset(acpreprefix, 0, sizeof(acpreprefix));
  143    memset(apfile, 0, sizeof(apfile));
  144    memset(apfunc, 0, sizeof(apfunc));
  145    memset(anline, 0, sizeof(anline));
  146    iclmsg = 0;
  147    memset(amsg, 0, sizeof(amsg));
  148    memset(alinebuf, 0, sizeof(alinebuf));
  149    memset(alinebuf2, 0, sizeof(alinebuf2));
  150    ncurthrsid = 0xFFFFFFFF;
  151    ncurthridx = 0;
  152    nlockmode = 0;
  153 
  154    #ifdef MTK_TRACE_FULL_RING
  155    nringx = 0xFF;
  156    #else
  157    nringx = 0;
  158    #endif
  159 
  160    ntermx = 0;
  161    nfilex = 0;
  162 
  163    flog = 0;
  164    bflushlog = 1;
  165 
  166    const char *pszEnv = getenv("MTK_TRACE");
  167    if (pszEnv)
  168    {
  169       // by default, only some messages are stored in the ring.
  170       // allow extended settings here:
  171       const char *psz1 = strstr(pszEnv, "ring:");
  172       if (psz1)
  173       {
  174          psz1 += strlen("ring:");
  175          setRingTrace((char*)psz1);
  176       }
  177          
  178       // by default, only some messages are dumped onto terminal.
  179       // allow extended settings here:
  180       if ((psz1 = strstr(pszEnv, "term:")) != 0)
  181       {
  182          psz1 += strlen("term:");
  183          setTermTrace((char*)psz1);
  184       }
  185 
  186       if ((psz1 = strstr(pszEnv, "filename:")) != 0) {
  187          psz1 += strlen("filename:");
  188          pszFilename = psz1;
  189          flog = fopen(pszFilename, "w");
  190          if (!flog) {
  191             fprintf(stderr, "# # # MTKTRACE ERROR: UNABLE TO WRITE LOG: =>%s<= # # #\n", pszFilename);
  192             if (strchr(pszFilename, '\"') || strchr(pszFilename, '\''))
  193                fprintf(stderr, "... make sure that MTK_TRACE contains no \"\" or '' quotes.\n");
  194          } else {
  195             printf("MTKTrace: writing log output into %s\n", pszFilename);
  196             fflush(stdout);
  197             setFileTrace("twex,"); // may be overwritten by "file:" below
  198          }
  199       }
  200 
  201       if ((psz1 = strstr(pszEnv, "file:")) != 0)
  202       {
  203          psz1 += strlen("file:");
  204          setFileTrace((char*)psz1);
  205       }
  206    }
  207 
  208    glblMTKAlive = 1;
  209 }
  210 
  211 void MTKMain::setRingTrace(char *psz1) 
  212 {
  213    nringx = 0;
  214    while (*psz1 && (*psz1 != ','))
  215       switch (*psz1++) {
  216          case 't': nringx |=  1; break;
  217          case 'b': nringx |=  2; break;
  218          case 'x': nringx |=  4; break;
  219          case 'w': nringx |=  8; break;
  220          case 'e': nringx |= 16; break;
  221          default:
  222             fprintf(stderr, "ERROR: MTK unsupported ring settings, use tbxwe\n");
  223             break;
  224       }
  225 }
  226 
  227 void MTKMain::setTermTrace(char *psz1)
  228 {
  229    ntermx = 0;
  230    while (*psz1 && (*psz1 != ','))
  231       switch (*psz1++) {
  232          case 't': ntermx |=  1; break;
  233          case 'b': ntermx |=  2; break;
  234          case 'x': ntermx |=  4; break;
  235          case 'w': ntermx |=  8; break;
  236          case 'e': ntermx |= 16; break;
  237          default:
  238             fprintf(stderr, "ERROR: MTK unsupported term settings, use tbxwe\n");
  239             break;
  240       }
  241    nringx |= ntermx;
  242 }
  243 
  244 void MTKMain::setFileTrace(char *psz1)
  245 {
  246    nfilex = 0;
  247    while (*psz1 && (*psz1 != ','))
  248       switch (*psz1++) {
  249          case 't': nfilex |=  1; break;
  250          case 'b': nfilex |=  2; break;
  251          case 'x': nfilex |=  4; break;
  252          case 'w': nfilex |=  8; break;
  253          case 'e': nfilex |= 16; break;
  254          case 'f': bflushlog = 0; break; // 'f'ast mode: do not flush on every line
  255          default:
  256             fprintf(stderr, "ERROR: MTK unsupported file settings, use tbxwe\n");
  257             break;
  258       }
  259    nringx |= nfilex;
  260 }
  261 
  262 void MTKMain::traceMethodEntry(void *pRefInst, const char *pszFile, int nLine, const char *pszFunc)
  263 {
  264    if (nlockmode >= 2)
  265       return;
  266 
  267    ulong nthreadid = (ulong)(pthread_self());
  268 
  269    // assume we still have the same thread context
  270    ulong ithread = ncurthridx;
  271 
  272    // if thread context changed, find new index
  273    if (ncurthrsid != nthreadid)
  274    {
  275       for (ithread=0; ithread<nthreads; ithread++)
  276          if (asysid[ithread] == nthreadid)
  277             break;
  278 
  279       // if no index found, add a new thread index
  280       if (ithread==nthreads) {
  281          if (nthreads >= MTKMAXTHREADS) {
  282             fprintf(stderr, "ERROR: INSTR: too many threads, cannot trace\n");
  283             glblMTKAlive = 0;
  284             return;
  285          }
  286          nthreads++;
  287          asysid[ithread] = nthreadid;
  288          alevel[ithread] = 0;
  289       }
  290 
  291       // cache the current thread's system id and index mapping
  292       ncurthrsid = nthreadid;
  293       ncurthridx = ithread;
  294    }
  295 
  296    ulong ilevel = alevel[ithread];
  297    if (ilevel >= MTKMAXLEVEL) {
  298       fprintf(stderr, "ERROR: INSTR: max stack level reached, cannot trace\n");
  299       glblMTKAlive = 0;
  300       return;
  301    }
  302 
  303    // store current stack location
  304    apfile[ithread][ilevel] = pszFile;
  305    anline[ithread][ilevel] = nLine;
  306    apfunc[ithread][ilevel] = pszFunc;
  307    // if (strstr(pszFile, "BHDiag"))
  308    //   printf("tme %x %u %u %s %u\n", nthreadid, ithread, ilevel, pszFile, nLine);
  309 
  310    // increment and write back current stack level
  311    ilevel++;
  312    alevel[ithread] = ilevel;
  313 
  314    // trace block entry event?
  315    if (nringx & 2) 
  316    {
  317       char szBuf[120];
  318       szBuf[0] = '\0';
  319 
  320       // create formatted string: file,line,function
  321       //                           50   10    30
  322       long noff = 0, nlen = 0;
  323       const char *psz = 0;
  324 
  325       // block start marker
  326       szBuf[noff++] = '{';
  327       szBuf[noff] = '\0';
  328 
  329       // filename, if any
  330       psz  = pszFile ? pszFile : "";
  331       nlen = strlen(psz);
  332       if (nlen) {
  333          if (nlen > 50) { psz = psz + nlen - 50; nlen = 50; }
  334          if (noff+nlen < (long)sizeof(szBuf)-10) {
  335             memcpy(szBuf+noff, psz, nlen);
  336             szBuf[noff+nlen] = '\0';
  337             noff += nlen;
  338          }
  339       }
  340 
  341       // line number
  342       if (noff+20 < (long)sizeof(szBuf)-10) {
  343          szBuf[noff++] = ' ';
  344          #ifdef _WIN32
  345          _ultoa((unsigned long)nLine, szBuf+noff, 10);
  346          #else
  347          sprintf(szBuf+noff, "%lu", (unsigned long)nLine);
  348          #endif
  349          noff += strlen(szBuf+noff);
  350       }
  351 
  352       // function
  353       psz  = pszFunc ? pszFunc : "";
  354       nlen = strlen(psz);
  355       if (nlen) {
  356          if (nlen > 30) { psz = psz + nlen - 30; nlen = 30; }
  357          if (noff+nlen < (long)sizeof(szBuf)-10) {
  358             szBuf[noff++] = ' ';
  359             memcpy(szBuf+noff, psz, nlen);
  360             szBuf[noff+nlen] = '\0';
  361             noff += nlen;
  362          }
  363       }
  364 
  365       // block end marker
  366       szBuf[noff++] = '}';
  367       szBuf[noff] = '\0';
  368 
  369       // dump formatted string (file info is redundant)
  370       traceMessageRaw(pszFile, nLine, 'B', szBuf);
  371    }
  372 }
  373 
  374 void MTKMain::traceMethodExit(void *pRefInst, const char *pszFile, int nLine, const char *pszFunc)
  375 {
  376    if (nlockmode >= 2)
  377       return;
  378 
  379    ulong nthreadid = (ulong)(pthread_self());
  380 
  381    // assume we still have the same thread context
  382    ulong ithread = ncurthridx;
  383 
  384    // if thread context changed, find new index
  385    if (ncurthrsid != nthreadid)
  386    {
  387       for (ithread=0; ithread<nthreads; ithread++)
  388          if (asysid[ithread] == nthreadid)
  389             break;
  390 
  391       // if no index found, there is an internal error
  392       if (ithread==nthreads) {
  393          fprintf(stderr, "ERROR: INSTR: non-matching method exit, cannot trace\n");
  394          glblMTKAlive = 0;
  395          return;
  396       }
  397 
  398       // cache the current thread's system id and index mapping
  399       ncurthrsid = nthreadid;
  400       ncurthridx = ithread;
  401    }
  402 
  403    ulong ilevel = alevel[ithread];
  404    if (ilevel == 0) {
  405       fprintf(stderr, "ERROR: INSTR: stack underflow, cannot trace\n");
  406       glblMTKAlive = 0;
  407       return;
  408    }
  409 
  410    #ifdef MTK_TRACE_BLOCK_EXITS
  411    // trace block exit event?
  412    if ((nringx & 2) && apfile[ithread][ilevel] && anline[ithread][ilevel])
  413    {
  414       const char *pszFile = apfile[ithread][ilevel];
  415       int   nLine         = anline[ithread][ilevel];
  416       const char *pszFunc = apfunc[ithread][ilevel];
  417 
  418       char szBuf[120];
  419       szBuf[0] = '\0';
  420 
  421       // create formatted string: file,line,function
  422       //                           50   10    30
  423       long noff = 0, nlen = 0;
  424       const char *psz = 0;
  425 
  426       // block start marker
  427       szBuf[noff++] = ' ';
  428       szBuf[noff++] = '{';
  429       szBuf[noff] = '\0';
  430 
  431       // filename, if any
  432       psz  = pszFile ? pszFile : "";
  433       nlen = strlen(psz);
  434       if (nlen) {
  435          if (nlen > 50) { psz = psz + nlen - 50; nlen = 50; }
  436          if (noff+nlen < (long)sizeof(szBuf)-10) {
  437             memcpy(szBuf+noff, psz, nlen);
  438             szBuf[noff+nlen] = '\0';
  439             noff += nlen;
  440          }
  441       }
  442 
  443       // line number
  444       if (noff+20 < (long)sizeof(szBuf)-10) {
  445          szBuf[noff++] = ' ';
  446          #ifdef _WIN32
  447          _ultoa((unsigned long)nLine, szBuf+noff, 10);
  448          #else
  449          sprintf(szBuf+noff, "%lu", (unsigned long)nLine);
  450          #endif
  451          noff += strlen(szBuf+noff);
  452       }
  453 
  454       // function
  455       psz  = pszFunc ? pszFunc : "";
  456       nlen = strlen(psz);
  457       if (nlen) {
  458          if (nlen > 30) { psz = psz + nlen - 30; nlen = 30; }
  459          if (noff+nlen < (long)sizeof(szBuf)-10) {
  460             szBuf[noff++] = ' ';
  461             memcpy(szBuf+noff, psz, nlen);
  462             szBuf[noff+nlen] = '\0';
  463             noff += nlen;
  464          }
  465       }
  466 
  467       // block end marker
  468       szBuf[noff++] = '}';
  469       szBuf[noff++] = ' ';
  470       szBuf[noff++] = '<';
  471       szBuf[noff] = '\0';
  472 
  473       // dump formatted string (file info is redundant)
  474       traceMessageRaw(pszFile, nLine, 'B', szBuf);
  475    }
  476    #endif // MTK_TRACE_BLOCK_EXITS
  477 
  478    apfile[ithread][ilevel] = 0;
  479    anline[ithread][ilevel] = 0;
  480    apfunc[ithread][ilevel] = 0;
  481 
  482    ilevel--;
  483    alevel[ithread] = ilevel;
  484 }
  485 
  486 static unsigned long localGetCurrentTime()
  487 {
  488    #ifdef _WIN32
  489    return (unsigned long)GetTickCount();
  490    #else
  491    struct timeval tv;
  492    gettimeofday(&tv, NULL);
  493    return ((unsigned long)tv.tv_sec) * 1000 + ((unsigned long)tv.tv_usec) / 1000;
  494    #endif
  495 }
  496 
  497 void MTKMain::traceMessageRaw(const char *pszFile, int nLine, char cPrefix, char *pszRaw)
  498 {
  499    // IN: pszFile can be NULL!
  500 
  501    if (nlockmode >= 1)
  502       return;
  503 
  504    ulong nthreadid = (ulong)(pthread_self());
  505 
  506    // assume we still have the same thread context
  507    ulong ithread = ncurthridx;
  508 
  509    // if thread context changed, find new index
  510    if (ncurthrsid != nthreadid)
  511    {
  512       for (ithread=0; ithread<nthreads; ithread++)
  513          if (asysid[ithread] == nthreadid)
  514             break;
  515 
  516       // if no index found, add a new thread index
  517       if (ithread==nthreads) {
  518          if (nthreads >= MTKMAXTHREADS) {
  519             fprintf(stderr, "ERROR: INSTR: too many threads, cannot trace\n");
  520             glblMTKAlive = 0;
  521             return;
  522          }
  523          nthreads++;
  524          asysid[ithread] = nthreadid;
  525          alevel[ithread] = 0;
  526       }
  527 
  528       // cache the current thread's system id and index mapping
  529       ncurthrsid = nthreadid;
  530       ncurthridx = ithread;
  531    }
  532 
  533    ulong ilevel = alevel[ithread];
  534 
  535    // single nearly-atomic operation across all threads.
  536    // we expect that some messages can get lost.
  537    ulong imsg = (iclmsg++) % MTKMAXMSG;
  538 
  539    // if any context infos are missing, take them from the precache
  540    if (!pszFile) pszFile = apprefile[ithread];
  541    if (!nLine  ) nLine   = anpreline[ithread];
  542    if (!cPrefix) cPrefix = acpreprefix[ithread];
  543    if (!cPrefix) cPrefix = '?'; // shouldn't actually happen
  544 
  545    // store thread-index and nesting level
  546    ulong nprelen = 3;
  547    amsg[imsg][0] = (uchar)ithread;
  548    amsg[imsg][1] = (uchar)ilevel;
  549    amsg[imsg][2] = (uchar)cPrefix;
  550 
  551    // store actual message
  552    ulong imax = MTKMSGSIZE-nprelen-2;
  553    strncpy(&amsg[imsg][nprelen], pszRaw, imax);
  554    amsg[imsg][imax] = '\0';
  555 
  556    // strip cr/lf, if any
  557    char *pszMsg = &amsg[imsg][nprelen];
  558    long nLen = strlen(pszMsg);
  559    while (nLen > 0 && (pszMsg[nLen-1]=='\r' || pszMsg[nLen-1]=='\n')) {
  560       pszMsg[nLen-1] = '\0';
  561       nLen--;
  562    }
  563 
  564    // if there is space left, append location info with a linefeed
  565    if (pszFile != 0) {
  566       ulong ilen = strlen(&amsg[imsg][nprelen]);
  567       if (ilen < (imax-1)) {
  568          strcat(&amsg[imsg][nprelen+ilen+0], "\n");
  569          // make relative filename, full paths are too long
  570          const char *pszRel = 0;
  571          #ifdef _WIN32
  572          if ((pszRel = strrchr(pszFile, '\\')) != 0) pszFile = pszRel+1;
  573          #else
  574          if ((pszRel = strrchr(pszFile, '/')) != 0)  pszFile = pszRel+1;
  575          #endif
  576          strncpy(&amsg[imsg][nprelen+ilen+1], pszFile, (imax-1)-ilen);
  577          amsg[imsg][imax] = '\0';
  578       }
  579    }
  580 
  581    const char *pszAddInfo = "";
  582    if (cPrefix == 'E')
  583       pszAddInfo = "error: ";
  584    if (cPrefix == 'W')
  585       pszAddInfo = "warning: ";
  586 
  587    // tracing to file selected?
  588    if (nfilex != 0)
  589    {
  590       bool bSkip = 0;
  591       switch (cPrefix) {
  592          case 'X': if (!(nfilex &  4)) bSkip = 1; break; // extended input, general
  593          case 'W': if (!(nfilex &  8)) bSkip = 1; break; // extended input, warnings
  594          case 'E': if (!(nfilex & 16)) bSkip = 1; break; // extended input, errors
  595          case 'B': if (!(nfilex &  2)) bSkip = 1; break; // block entry
  596          default : if (!(nfilex &  1)) bSkip = 1; break; // all other messages
  597       }
  598       if (!bSkip && (flog != 0)) {
  599          // note that pszRaw still contains unwanted CR/LFs.
  600          long nLen = strlen(pszRaw);
  601          while (nLen > 0 && (pszRaw[nLen-1]=='\r' || pszRaw[nLen-1]=='\n'))
  602             nLen--;
  603          fprintf(flog, "%lu [%02lX:%c] %s%.*s%.*s\n", 
  604             (unsigned long)(localGetCurrentTime() & 65535UL),
  605             ithread, cPrefix, pszAddInfo,
  606             (int)ilevel, glblPszBlank, (int)nLen, pszRaw);
  607          if (bflushlog)
  608             fflush(flog);
  609       }
  610    }
  611 
  612    // instant tracing to terminal selected?
  613    if (ntermx != 0)
  614    {
  615       bool bRet = 0;
  616       switch (cPrefix) {
  617          case 'X': if (!(ntermx &  4)) bRet = 1; break; // extended input, general
  618          case 'W': if (!(ntermx &  8)) bRet = 1; break; // extended input, warnings
  619          case 'E': if (!(ntermx & 16)) bRet = 1; break; // extended input, errors
  620          case 'B': if (!(ntermx &  2)) bRet = 1; break; // block entry
  621          default : if (!(ntermx &  1)) bRet = 1; break; // all other messages
  622       }
  623       if (bRet) {
  624          // if (!strncmp(pszRaw, "P:", 2))
  625          //    printf("SKIP.1: %s %c %x\n", pszRaw, cPrefix, ntermx);
  626          return;
  627       }
  628       PROJECT_PRINTF("[%02lX:%c] %.*s%s\n", ithread, cPrefix, (int)ilevel, glblPszBlank, pszRaw);
  629    }
  630    else
  631    {
  632       // if (!strncmp(pszRaw, "P:", 2))
  633       //    printf("SKIP.2: %s\n", pszRaw);
  634    }
  635 
  636    // reset prelocation data to avoid reuse
  637    apprefile[ithread]   = 0;
  638    anpreline[ithread]   = 0;
  639    acpreprefix[ithread] = 0;
  640 }
  641 
  642 uchar MTKMain::tracePre(const char *pszFile, int nLine, char cPrefix)
  643 {
  644    if (nlockmode >= 1)
  645       return 0;
  646 
  647    ulong nthreadid = (ulong)(pthread_self());
  648 
  649    // assume we still have the same thread context
  650    ulong ithread = ncurthridx;
  651 
  652    // if thread context changed, find new index
  653    if (ncurthrsid != nthreadid)
  654    {
  655       for (ithread=0; ithread<nthreads; ithread++)
  656          if (asysid[ithread] == nthreadid)
  657             break;
  658 
  659       // if no index found, add a new thread index
  660       if (ithread==nthreads) {
  661          if (nthreads >= MTKMAXTHREADS) {
  662             fprintf(stderr, "ERROR: INSTR: too many threads, cannot trace\n");
  663             glblMTKAlive = 0;
  664             return 0;
  665          }
  666          nthreads++;
  667          asysid[ithread] = nthreadid;
  668          alevel[ithread] = 0;
  669       }
  670 
  671       // cache the current thread's system id and index mapping
  672       ncurthrsid = nthreadid;
  673       ncurthridx = ithread;
  674    }
  675 
  676    // we expect that this thread will issue a call to traceMessageRaw next,
  677    // where the following infos are used:
  678    apprefile[ithread]   = pszFile;
  679    anpreline[ithread]   = nLine;
  680    acpreprefix[ithread] = cPrefix;
  681 
  682    // are we currently interested in this kind of trace messages?
  683    bool bRC = 0, bRCSet = 0;
  684    if (cPrefix == 'X') { bRC = ((nringx &  4) != 0) ? 1 : 0; bRCSet = 1; }
  685    if (cPrefix == 'W') { bRC = ((nringx &  8) != 0) ? 1 : 0; bRCSet = 1; }
  686    if (cPrefix == 'E') { bRC = ((nringx & 16) != 0) ? 1 : 0; bRCSet = 1; }
  687    if (cPrefix == 'B') { bRC = ((nringx &  2) != 0) ? 1 : 0; bRCSet = 1; }
  688    if (bRCSet) {
  689       // if (cPrefix == 'E')
  690       //    printf("tracePre.1 %c %u\n", cPrefix, bRC);
  691       return bRC;
  692    }
  693 
  694    // all other message types: depends on general flag
  695    if (nringx & 1) {
  696       // if (cPrefix == 'E')
  697       //    printf("tracePre.2 %c 1\n", cPrefix);
  698       return 1;
  699    } else {
  700       // if (cPrefix == 'E')
  701       //    printf("tracePre.3 %c 0\n", cPrefix);
  702       return 0;
  703    }
  704 }
  705 
  706 void MTKMain::dumpStackTrace(bool bJustCurrentThread)
  707 {
  708    if (bJustCurrentThread) {
  709       // lockmode 1: do not accept further traceMessage calls.
  710       nlockmode = 1;
  711    } else {
  712       // lockmode 2: also do not accept block entry/exit events.
  713       // in most cases, mtk tracing cannot be used afterwards.
  714       nlockmode = 2;
  715    }
  716 
  717    ulong nthreadid = (ulong)(pthread_self());
  718    ulong ithread;
  719    for (ithread=0; ithread<nthreads; ithread++)
  720       if (asysid[ithread] == nthreadid)
  721          break;
  722    if (ithread==nthreads) {
  723       fprintf(stderr, "ERROR: INSTR: thread not found, cannot dump stack trace\n");
  724       nlockmode = 0;
  725       return;
  726    }
  727 
  728    FILE *fout = flog;
  729    if (!fout) fout = stdout;
  730 
  731    printf("> DUMPING STACKTRACE OF %s TO %s\n", bJustCurrentThread?"CURRENT THREAD":"ALL THREADS", pszFilename);
  732    fflush(stdout);
  733 
  734    if (!bJustCurrentThread) {
  735       fprintf(fout, "==================================================\n");
  736       fprintf(fout, "= current stack trace of ALL threads, %lu in total:\n", nthreads);
  737       fprintf(fout, "==================================================\n");
  738    }
  739 
  740    for (ulong ithread2=0; ithread2<nthreads; ithread2++)
  741    {
  742       if (bJustCurrentThread)
  743          ithread2 = ithread;
  744 
  745       ulong ilevel = alevel[ithread2];
  746       ulong nsysid = asysid[ithread2];
  747       fprintf(fout, "> ------- stack trace, thread %lx%s, sysid %lx, %lu levels -------\n", ithread2, (!bJustCurrentThread && (ithread2==ithread))?" (OWN)":"", nsysid, ilevel);
  748 
  749       for (ulong ilev=0; ilev<ilevel; ilev++)
  750       {
  751          const char *pszFile = apfile[ithread2][ilev];
  752          ulong nLine = anline[ithread2][ilev];
  753          const char *pszFunc = apfunc[ithread2][ilev];
  754          const char *pszRel = strrchr((char*)pszFile, '/');
  755          if (pszRel) pszRel++; else pszRel = pszFile;
  756          fprintf(fout, "> %02lX %.*s%s %s:%lu\n", ithread2, (int)ilev, glblPszBlank, pszFunc, pszRel, nLine);
  757       }
  758 
  759       if (bJustCurrentThread)
  760          break;
  761    }
  762    fprintf(fout, "> ---------------------- stack trace end ---------------------\n");
  763    fflush(fout);
  764 
  765    if (fout != stdout) {
  766       printf("> DUMPING OF STACKTRACE DONE.\n");
  767       fflush(stdout);
  768    }
  769 
  770    nlockmode = 0;
  771 }
  772 
  773 void MTKMain::dumpLastSteps(bool bJustCurrentThread)
  774 {
  775    nlockmode = 1;
  776 
  777    // identify our own thread
  778    ulong nthreadid = (ulong)(pthread_self());
  779    ulong iown;
  780    for (iown=0; iown<nthreads; iown++)
  781       if (asysid[iown] == nthreadid)
  782          break;
  783 
  784    FILE *fout = flog;
  785    if (!fout) fout = stdout;
  786 
  787    printf("> DUMPING LAST STEPS OF %s TO %s\n", bJustCurrentThread?"CURRENT THREAD":"PROCESS", pszFilename);
  788    fflush(stdout);
  789 
  790    if (bJustCurrentThread)
  791    fprintf(fout, "> ------ last steps of current thread, index %02lX sysid %04lX: -----\n", iown, nthreadid);
  792    else
  793    fprintf(fout, "> ------ last system steps, own thread = %02lX sysid %04lX: -----\n", iown, nthreadid);
  794 
  795    // read out ringbuffer backwards
  796    ulong imsg = iclmsg % MTKMAXMSG;
  797 
  798    // first, step FORWARD to oldest line, skipping empty entries.
  799    // NOTE: first three bytes of a record are prefix infos.
  800    ulong icur = (imsg+1) % MTKMAXMSG;
  801    while ((icur != imsg) && (!amsg[icur][3]))
  802       icur = (icur+1) % MTKMAXMSG;
  803 
  804    if (icur == imsg)
  805       fprintf(fout, "[ring buffer is empty.]\n");
  806 
  807    ulong nrec = 0;
  808 
  809    // now walk icur forward until imsg, dumping the messages
  810    while (icur != imsg)
  811    {
  812       uchar *pmsg = (uchar*)amsg[icur];
  813       ulong nprelen = 3;
  814       uchar ithr  = pmsg[0];
  815       uchar ilev  = pmsg[1];
  816       char  cPre  = (char)pmsg[2];
  817       if (bJustCurrentThread && (iown != ithr)) {
  818          // skip record
  819       } else {
  820          // stored messages are zero-terminated.
  821          // if there is a linefeed, there is location info afterwards.
  822          strncpy((char*)alinebuf, (const char*)pmsg+nprelen, MTKLBUFSIZE);
  823          alinebuf[MTKLBUFSIZE] = '\0';
  824          char *pszLoc = strchr(alinebuf, '\n');
  825          if (pszLoc) {
  826             *pszLoc++ = '\0';
  827          }
  828          // format main content, yet w/o location
  829          sprintf(alinebuf2, "[%02X:%c%c %.*s%s", ithr, cPre, (iown==ithr)?'*':']', (int)ilev, glblPszBlank, alinebuf);
  830          // if available, add location info
  831          if (pszLoc) {
  832             ulong ilen = strlen(alinebuf2);
  833             ulong iloc = 90;
  834             if (iloc < ilen)
  835                 iloc = ilen;
  836             iloc += 3;
  837             iloc += (6 - (iloc % 6));
  838             if (iloc < (MTKLBUFSIZE-10)) {
  839                for (ulong iins=ilen; iins<iloc; iins++)
  840                   alinebuf2[iins] = ' ';
  841                // add at least 3 chars of location info
  842                sprintf(&alinebuf2[iloc], "[%.*s]", (int)((MTKLBUFSIZE-5)-iloc), pszLoc);
  843             }
  844          }
  845          // finally, dump whole formatted line
  846          fprintf(fout, "%s\n", alinebuf2);
  847          nrec++;
  848       }
  849       icur = (icur+1) % MTKMAXMSG;
  850    }
  851 
  852    if (bJustCurrentThread)
  853    fprintf(fout, "> ------------- last thread steps end, %lu records ---------------\n", nrec);
  854    else
  855    fprintf(fout, "> ------------- last system steps end, %lu records ---------------\n", nrec);
  856    fflush(fout);
  857 
  858    if (fout != stdout) {
  859       printf("> DUMPING OF LAST STEPS DONE.\n");
  860       fflush(stdout);
  861    }
  862 
  863    nlockmode = 0;
  864 }
  865 
  866 // block entry registration, used by MTKBlock
  867 void mtkTraceMethodEntry(void *pRefInst, const char *pszFile, int nLine, const char *pszfunc) {
  868    if (glblMTKAlive)
  869       glblMTKInst.traceMethodEntry(pRefInst, pszFile, nLine, pszfunc);
  870 }
  871 
  872 // block exit registration, used by MTKBlock
  873 void mtkTraceMethodExit(void *pRefInst, const char *pszFile, int nLine, const char *pszfunc) {
  874    if (glblMTKAlive)
  875       glblMTKInst.traceMethodExit(pRefInst, pszFile, nLine, pszfunc);
  876 }
  877 
  878 // ============================================
  879 // ===== MTK public interface C functions =====
  880 // ============================================
  881 
  882 /*
  883 extern void mtkTraceRaw(const char *pszFile, int nLine, char cPrefix, char *pszRaw);
  884 extern void mtkTraceForm(const char *pszFile, int nLine, char cPrefix, const char *pszFormat, ...);
  885 extern int  mtkTracePre(const char *pszFile, int nLine, char cPrefix);
  886 extern void mtkTracePost(const char *pszFormat, ...);
  887 extern void mtkDumpStackTrace(int bOnlyOfCurrentThread);
  888 extern void mtkDumpLastSteps(int bOnlyOfCurrentThread);
  889 extern void mtkSetRingTrace(char *pszMask);
  890 extern void mtkSetTermTrace(char *pszMask);
  891 */
  892 
  893 // all-in-one tracing entry for preformatted message buffers
  894 void mtkTraceRaw(const char *pszFile, int nLine, char cPrefix, char *pszRaw) {
  895    if (glblMTKAlive)
  896       glblMTKInst.traceMessageRaw(pszFile, nLine, cPrefix, pszRaw);
  897 }
  898 
  899 // all-in-one tracing entry supporting printf-like parameters
  900 void mtkTraceForm(const char *pszFile, int nLine, char cPrefix, const char *pszFormat, ...)
  901 {
  902    if (glblMTKAlive)
  903    {
  904       va_list argList;
  905       va_start(argList, pszFormat);
  906       char szBuffer[1024];
  907       ::vsnprintf(szBuffer, sizeof(szBuffer)-10, pszFormat, argList);
  908       glblMTKInst.traceMessageRaw(pszFile, nLine, cPrefix, szBuffer);
  909    }
  910 }
  911 
  912 // the tracePre/Post combination is a workaround for complex caller macros
  913 // that are unable to provide location and prefix together with the message
  914 // in a single call. in this case, the caller first calls tracePre, storing
  915 // context info for the current thread, and then tracePost with the message.
  916 
  917 int mtkTracePre(const char *pszFile, int nLine, char cPrefix) {
  918    if (glblMTKAlive)
  919       return (int)glblMTKInst.tracePre(pszFile, nLine, cPrefix);
  920    else
  921       return (int)0;
  922 }
  923 
  924 void mtkTracePost(const char *pszFormat, ...)
  925 {
  926    if (glblMTKAlive)
  927    {
  928       va_list argList;
  929       va_start(argList, pszFormat);
  930       char szBuffer[1024];
  931       // szBuffer[0] = 'P';
  932       // szBuffer[1] = ':';
  933       ::vsnprintf(szBuffer, sizeof(szBuffer)-10, pszFormat, argList);
  934       // the 0,0,0 are expected to be set through tracePre before.
  935       glblMTKInst.traceMessageRaw(0,0,0, szBuffer);
  936    }
  937 }
  938 
  939 void mtkDumpStackTrace(int bOnlyOfCurrentThread) {
  940    if (glblMTKAlive)
  941       glblMTKInst.dumpStackTrace((uchar)bOnlyOfCurrentThread);
  942 }
  943 
  944 void mtkDumpLastSteps(int bOnlyOfCurrentThread) {
  945    if (glblMTKAlive)
  946       glblMTKInst.dumpLastSteps((uchar)bOnlyOfCurrentThread);
  947 }
  948 
  949 void mtkSetRingTrace(char *pszMask) {
  950    if (glblMTKAlive)
  951       glblMTKInst.setRingTrace(pszMask);
  952 }
  953 
  954 void mtkSetTermTrace(char *pszMask) {
  955    if (glblMTKAlive)
  956       glblMTKInst.setTermTrace(pszMask);
  957 }
  958 
  959 void mtkHexDump(const char *pszLinePrefix, void *pDataIn, long lSize, const char *pszFile, int nLine, char cPrefix)
  960 {
  961    char szBuf[128];
  962    char szPrn[32];
  963    uchar *pData = (uchar*)pDataIn;
  964    long iRead = 0;
  965    ulong noff = 0;
  966    for (long nRow=0; iRead<lSize; nRow++)
  967    {
  968       szBuf[0] = '\0';
  969 
  970       long nCol=0;
  971       for (; nCol<16 && iRead<lSize; nCol++)
  972       {
  973          uchar uc = pData[iRead++];
  974          sprintf(&szBuf[nCol*3], "%02X ", uc);
  975          szPrn[nCol] = isprint((char)uc) ? (char)uc : '.';
  976       }
  977       szPrn[nCol] = '\0';
  978 
  979       if (strlen(szBuf))
  980       {
  981          mtkTraceForm(pszFile, nLine, cPrefix, "%s%-48.48s %-16.16s  %04lX", pszLinePrefix, szBuf, szPrn, noff);
  982       }
  983 
  984       noff += 16;
  985    }
  986 }
  987