LCOV - code coverage report
Current view: top level - monetdb5/mal - mal_profiler.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 287 490 58.6 %
Date: 2021-09-14 22:17:06 Functions: 23 34 67.6 %

          Line data    Source code
       1             : /*
       2             :  * This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0.  If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       5             :  *
       6             :  * Copyright 1997 - July 2008 CWI, August 2008 - 2021 MonetDB B.V.
       7             :  */
       8             : 
       9             : /* (c) M.L. Kersten
      10             :  * Performance tracing
      11             :  * The stethoscope/tachograph and tomograph performance monitors have exclusive access
      12             :  * to a single event stream, which avoids concurrency conflicts amongst clients.
      13             :  * It also avoid cluthered event records on the stream. Since this event stream is owned
      14             :  * by a client, we should ensure that the profiler is automatically
      15             :  * reset once the owner leaves.
      16             :  */
      17             : #include "monetdb_config.h"
      18             : #include "mutils.h"         /* mercurial_revision */
      19             : #include "msabaoth.h"         /* msab_getUUID */
      20             : #include "mal_authorize.h"
      21             : #include "mal_function.h"
      22             : #include "mal_listing.h"
      23             : #include "mal_profiler.h"
      24             : #include "mal_runtime.h"
      25             : #include "mal_utils.h"
      26             : #include "mal_resource.h"
      27             : 
      28             : #ifdef HAVE_SYS_TIME_H
      29             : #include <sys/time.h>
      30             : #endif
      31             : 
      32             : #include <string.h>
      33             : 
      34             : static const char *myname = 0;  // avoid tracing the profiler module
      35             : 
      36             : /* The JSON rendering can be either using '\n' separators between
      37             :  * each key:value pair or as a single line.
      38             :  * The current stethoscope implementation requires the first option and
      39             :  * also the term rendering  to be set to ''
      40             :  */
      41             : 
      42             : /* When the MAL block contains a BARRIER block we may end up with tons
      43             :  * of profiler events. To avoid this, we stop emitting the events
      44             :  * when we reached the HIGHWATERMARK. Leaving a message in the log. */
      45             : #define HIGHWATERMARK 5
      46             : 
      47             : 
      48             : int malProfileMode = 0;     /* global flag to indicate profiling mode */
      49             : static oid malprofileruser;     /* keep track on who has claimed the channel */
      50             : 
      51             : static struct timeval startup_time;
      52             : 
      53             : static ATOMIC_TYPE hbdelay = ATOMIC_VAR_INIT(0);
      54             : 
      55             : #ifdef HAVE_SYS_RESOURCE_H
      56             : struct rusage infoUsage;
      57             : static struct rusage prevUsage;
      58             : #endif
      59             : 
      60             : #define LOGLEN 8192
      61             : 
      62             : // The heart beat events should be sent to all outstanding channels.
      63           2 : static void logjsonInternal(char *logbuffer)
      64             : {
      65             :         size_t len;
      66           2 :         len = strlen(logbuffer);
      67             : 
      68           2 :         MT_lock_set(&mal_profileLock);
      69           2 :         if (maleventstream) {
      70             :         // upon request the log record is sent over the profile stream
      71           0 :                 (void) mnstr_write(maleventstream, logbuffer, 1, len);
      72           0 :                 (void) mnstr_flush(maleventstream, MNSTR_FLUSH_DATA);
      73             :         }
      74           2 :         MT_lock_unset(&mal_profileLock);
      75           2 : }
      76             : 
      77             : /*
      78             :  * We use a buffer (`logbuffer`) where we incrementally create the output JSON object. Initially we allocate LOGLEN (8K)
      79             :  * bytes and we keep the capacity of the buffer (`logcap`) and the length of the current string (`loglen`).
      80             :  *
      81             :  * We use the `logadd` function to add data to our buffer (usually key-value pairs). This macro offers an interface similar
      82             :  * to printf.
      83             :  *
      84             :  * The first snprintf bellow happens in a statically allocated buffer that might be much smaller than logcap. We do not
      85             :  * care. We only need to perform this snprintf to get the actual length of the string that is to be produced.
      86             :  *
      87             :  * There are three cases:
      88             :  *
      89             :  * 1. The new string fits in the current buffer -> we just update the buffer
      90             :  *
      91             :  * 2. The new string does not fit in the current buffer, but is smaller than the capacity of the buffer -> we output the
      92             :  * current contents of the buffer and start at the beginning.
      93             :  *
      94             :  * 3. The new string exceeds the current capacity of the buffer -> we output the current contents and reallocate the
      95             :  * buffer. The new capacity is 1.5 times the length of the new string.
      96             :  */
      97             : struct logbuf {
      98             :         char *logbuffer;
      99             :         char *logbase;
     100             :         size_t loglen;
     101             :         size_t logcap;
     102             : };
     103             : 
     104             : static inline void
     105             : lognew(struct logbuf *logbuf)
     106             : {
     107         924 :         logbuf->loglen = 0;
     108         924 :         logbuf->logbase = logbuf->logbuffer;
     109         924 :         *logbuf->logbase = 0;
     110           0 : }
     111             : 
     112             : static inline void
     113           0 : logdel(struct logbuf *logbuf)
     114             : {
     115           0 :         free(logbuf->logbuffer);
     116           0 : }
     117             : 
     118             : static bool logadd(struct logbuf *logbuf,
     119             :                                    _In_z_ _Printf_format_string_ const char *fmt, ...)
     120             :         __attribute__((__format__(__printf__, 2, 3)))
     121             :         __attribute__((__warn_unused_result__));
     122             : static bool
     123       37565 : logadd(struct logbuf *logbuf, const char *fmt, ...)
     124             : {
     125             :         char tmp_buff[LOGLEN];
     126             :         int tmp_len;
     127             :         va_list va;
     128             :         va_list va2;
     129             : 
     130       37565 :         va_start(va, fmt);
     131       37565 :         va_copy(va2, va);                       /* we will need it again */
     132       37565 :         tmp_len = vsnprintf(tmp_buff, sizeof(tmp_buff), fmt, va);
     133       37565 :         if (tmp_len < 0) {
     134           0 :                 logdel(logbuf);
     135           0 :                 va_end(va);
     136           0 :                 va_end(va2);
     137           0 :                 return false;
     138             :         }
     139       37565 :         if (logbuf->loglen + (size_t) tmp_len >= logbuf->logcap) {
     140         924 :                 if ((size_t) tmp_len >= logbuf->logcap) {
     141             :                         /* includes first time when logbuffer == NULL and logcap = 0 */
     142             :                         char *alloc_buff;
     143         924 :                         if (logbuf->loglen > 0)
     144           1 :                                 logjsonInternal(logbuf->logbuffer);
     145         924 :                         logbuf->logcap = (size_t) tmp_len + (size_t) tmp_len/2;
     146         924 :                         if (logbuf->logcap < LOGLEN)
     147         917 :                                 logbuf->logcap = LOGLEN;
     148         924 :                         alloc_buff = realloc(logbuf->logbuffer, logbuf->logcap);
     149         924 :                         if (alloc_buff == NULL) {
     150           0 :                                 TRC_ERROR(MAL_SERVER, "Profiler JSON buffer reallocation failure\n");
     151           0 :                                 logdel(logbuf);
     152           0 :                                 va_end(va);
     153           0 :                                 va_end(va2);
     154           0 :                                 return false;
     155             :                         }
     156         924 :                         logbuf->logbuffer = alloc_buff;
     157             :                         lognew(logbuf);
     158             :                 } else {
     159           0 :                         logjsonInternal(logbuf->logbuffer);
     160             :                         lognew(logbuf);
     161             :                 }
     162             :         }
     163       37565 :         logbuf->loglen += vsnprintf(logbuf->logbase + logbuf->loglen,
     164       37565 :                                                                 logbuf->logcap - logbuf->loglen,
     165             :                                                                 fmt, va2);
     166       37565 :         va_end(va);
     167       37565 :         va_end(va2);
     168       37565 :         return true;
     169             : }
     170             : 
     171             : /* JSON rendering method of performance data.
     172             :  * The eventparser may assume this layout for ease of parsing
     173             : EXAMPLE:
     174             : {
     175             : "event":6        ,
     176             : "thread":3,
     177             : "function":"user.s3_1",
     178             : "pc":1,
     179             : "tag":10397,
     180             : "state":"start",
     181             : "usec":0,
     182             : }
     183             : "stmt":"X_41=0@0:void := querylog.define(\"select count(*) from tables;\":str,\"default_pipe\":str,30:int);",
     184             : */
     185             : static str
     186         957 : prepareProfilerEvent(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci, int start)
     187             : {
     188             :         struct logbuf logbuf;
     189             :         str c;
     190             :         str stmtq;
     191             :         lng usec;
     192             :         uint64_t microseconds;
     193             :         bool ok;
     194         957 :         const char *algo = MT_thread_getalgorithm();
     195             : 
     196             :         /* ignore generation of events for instructions that are called too often
     197             :          * they may appear when BARRIER blocks are executed
     198             :          * The default parameter should be sufficient for most practical cases.
     199             :          */
     200         957 :         if( !start && pci->calls > HIGHWATERMARK){
     201          36 :                 if( pci->calls == 10000 || pci->calls == 100000 || pci->calls == 1000000 || pci->calls == 10000000)
     202           0 :                         TRC_WARNING(MAL_SERVER, "Too many calls: %d\n", pci->calls);
     203          36 :                 return NULL;
     204             :         }
     205             : 
     206             : /* The stream of events can be complete read by the DBA,
     207             :  * all other users can only see events assigned to their account
     208             :  */
     209         921 :         if(malprofileruser!= MAL_ADMIN && malprofileruser != cntxt->user)
     210             :                 return NULL;
     211             : 
     212             : /* align the variable namings with EXPLAIN and TRACE */
     213         921 :         if( pci->pc == 1 && start)
     214           0 :                 renameVariables(mb);
     215             : 
     216         921 :         logbuf = (struct logbuf) {0};
     217             : 
     218         921 :         usec= pci->clock;
     219         921 :         microseconds = (uint64_t)usec - ((uint64_t)startup_time.tv_sec*1000000 - (uint64_t)startup_time.tv_usec);
     220             :         /* make profile event tuple  */
     221             :         /* TODO: This could probably be optimized somehow to avoid the
     222             :          * function call to mercurial_revision().
     223             :          */
     224             :         // No comma at the beginning
     225        1842 :         if (!logadd(&logbuf,
     226             :                                 "{"                           // fill in later with the event counter
     227             :                                 "\"version\":\""MONETDB_VERSION" (hg id: %s)\""
     228             :                                 ",\"user\":"OIDFMT
     229             :                                 ",\"clk\":"LLFMT
     230             :                                 ",\"mclk\":%"PRIu64""
     231             :                                 ",\"thread\":%d"
     232             :                                 ",\"program\":\"%s.%s\""
     233             :                                 ",\"pc\":%d"
     234             :                                 ",\"tag\":"OIDFMT,
     235             :                                 mercurial_revision(),
     236             :                                 cntxt->user,
     237             :                                 usec,
     238             :                                 microseconds,
     239             :                                 THRgettid(),
     240         924 :                                 getModuleId(getInstrPtr(mb, 0)), getFunctionId(getInstrPtr(mb, 0)),
     241         921 :                                 mb?getPC(mb,pci):0,
     242             :                                 stk?stk->tag:0))
     243           0 :                 goto cleanup_and_exit;
     244         920 :         if( pci->modname && !logadd(&logbuf, ",\"module\":\"%s\"", pci->modname ? pci->modname : ""))
     245           0 :                 goto cleanup_and_exit;
     246         919 :         if( pci->fcnname && !logadd(&logbuf, ",\"function\":\"%s\"", pci->fcnname ? pci->fcnname : ""))
     247           0 :                 goto cleanup_and_exit;
     248         918 :         if( pci->barrier && !logadd(&logbuf, ",\"barrier\":\"%s\"", operatorName(pci->barrier)))
     249           0 :                 goto cleanup_and_exit;
     250         954 :         if ((pci->token < FCNcall || pci->token > PATcall) &&
     251          36 :                 !logadd(&logbuf, ",\"operator\":\"%s\"", operatorName(pci->token)))
     252           0 :                 goto cleanup_and_exit;
     253         918 :         if (!GDKinmemory(0) && !GDKembedded()) {
     254         917 :                 char *uuid = NULL;
     255             :                 str c;
     256         917 :                 if ((c = msab_getUUID(&uuid)) == NULL) {
     257         918 :                         ok = logadd(&logbuf, ",\"session\":\"%s\"", uuid);
     258         923 :                         free(uuid);
     259         923 :                         if (!ok)
     260           0 :                                 goto cleanup_and_exit;
     261             :                 } else
     262           0 :                         free(c);
     263             :         }
     264        1844 :         if (!logadd(&logbuf, ",\"state\":\"%s\",\"usec\":"LLFMT,
     265             :                                 start?"start":"done", pci->ticks))
     266           0 :                 goto cleanup_and_exit;
     267         920 :         if (algo && !logadd(&logbuf, ",\"algorithm\":\"%s\"", algo))
     268           0 :                 goto cleanup_and_exit;
     269             : 
     270             : /* EXAMPLE MAL statement argument decomposition
     271             :  * The eventparser may assume this layout for ease of parsing
     272             :  {
     273             :  ... as above ...
     274             :  "result":{"clk":"173297139,"pc":1,"index":0,,"name":"X_6","type":"void","value":"0@0","eol":0}
     275             :  ...
     276             :  "argument":{"clk":173297139,"pc":1,"index":"2","type":"str","value":"\"default_pipe\"","eol":0},
     277             :  }
     278             :  This information can be used to determine memory footprint and variable life times.
     279             : */
     280             : 
     281             :         // Also show details of the arguments for modelling
     282         920 :         if(mb && pci->modname && pci->fcnname){
     283             :                 int j;
     284             : 
     285         884 :                 if (!logadd(&logbuf, ",\"args\":["))
     286           0 :                         goto cleanup_and_exit;
     287        4735 :                 for(j=0; j< pci->argc; j++){
     288        3848 :                         int tpe = getVarType(mb, getArg(pci,j));
     289             :                         str tname = 0, cv;
     290             :                         lng total = 0;
     291             :                         BUN cnt = 0;
     292             :                         bat bid=0;
     293             : 
     294        3848 :                         if (j == 0) {
     295             :                                 // No comma at the beginning
     296         886 :                                 if (!logadd(&logbuf, "{"))
     297           0 :                                         goto cleanup_and_exit;
     298             :                         }
     299             :                         else {
     300        2962 :                                 if (!logadd(&logbuf, ",{"))
     301           0 :                                         goto cleanup_and_exit;
     302             :                         }
     303        7697 :                         if (!logadd(&logbuf, "\"%s\":%d,\"var\":\"%s\"",
     304        3851 :                                                 j < pci->retc ? "ret" : "arg", j,
     305             :                                                 getVarName(mb, getArg(pci,j))))
     306           0 :                                 goto cleanup_and_exit;
     307        3841 :                         c =getVarName(mb, getArg(pci,j));
     308        3839 :                         if(getVarSTC(mb,getArg(pci,j))){
     309           0 :                                 InstrPtr stc = getInstrPtr(mb, getVarSTC(mb,getArg(pci,j)));
     310           0 :                                 if (stc &&
     311           0 :                                         strcmp(getModuleId(stc),"sql") ==0 &&
     312           0 :                                         strncmp(getFunctionId(stc),"bind",4)==0 &&
     313           0 :                                         !logadd(&logbuf, ",\"alias\":\"%s.%s.%s\"",
     314           0 :                                                         getVarConstant(mb, getArg(stc,stc->retc +1)).val.sval,
     315           0 :                                                         getVarConstant(mb, getArg(stc,stc->retc +2)).val.sval,
     316           0 :                                                         getVarConstant(mb, getArg(stc,stc->retc +3)).val.sval))
     317           0 :                                         goto cleanup_and_exit;
     318             :                         }
     319        3839 :                         if(isaBatType(tpe)){
     320        1767 :                                 BAT *d= BATdescriptor(bid = stk->stk[getArg(pci,j)].val.bval);
     321        1778 :                                 tname = getTypeName(getBatType(tpe));
     322        1780 :                                 ok = logadd(&logbuf, ",\"type\":\"bat[:%s]\"", tname);
     323        1775 :                                 GDKfree(tname);
     324        1778 :                                 if (!ok) {
     325           0 :                                         if (d)
     326           0 :                                                 BBPunfix(d->batCacheid);
     327           0 :                                         goto cleanup_and_exit;
     328             :                                 }
     329        1778 :                                 if(d) {
     330             :                                         BAT *v;
     331        1770 :                                         MT_lock_set(&d->theaplock);
     332        1770 :                                         BATiter di = bat_iterator_nolock(d);
     333             :                                         /* outside the lock we cannot dereference di.h or di.vh,
     334             :                                          * but we can use all values without dereference and
     335             :                                          * without further locking */
     336        1768 :                                         MT_lock_unset(&d->theaplock);
     337        1768 :                                         cnt = di.count;
     338        1768 :                                         if(isVIEW(d)){
     339         161 :                                                 v= BBP_cache(VIEWtparent(d));
     340         483 :                                                 if (!logadd(&logbuf,
     341             :                                                                         ",\"view\":\"true\""
     342             :                                                                         ",\"parent\":%d"
     343             :                                                                         ",\"seqbase\":"BUNFMT
     344             :                                                                         ",\"mode\":\"%s\"",
     345         161 :                                                                         VIEWtparent(d),
     346             :                                                                         d->hseqbase,
     347         135 :                                                                         v && !v->batTransient ? "persistent" : "transient")) {
     348           0 :                                                         BBPunfix(d->batCacheid);
     349           0 :                                                         goto cleanup_and_exit;
     350             :                                                 }
     351             :                                         } else {
     352        1607 :                                                 if (!logadd(&logbuf, ",\"mode\":\"%s\"", (d->batTransient ? "transient" : "persistent"))) {
     353           0 :                                                         BBPunfix(d->batCacheid);
     354           0 :                                                         goto cleanup_and_exit;
     355             :                                                 }
     356             :                                         }
     357        1764 :                                         if (!logadd(&logbuf,
     358             :                                                                 ",\"sorted\":%d"
     359             :                                                                 ",\"revsorted\":%d"
     360             :                                                                 ",\"nonil\":%d"
     361             :                                                                 ",\"nil\":%d"
     362             :                                                                 ",\"key\":%d",
     363        1768 :                                                                 d->tsorted,
     364        1768 :                                                                 d->trevsorted,
     365        1768 :                                                                 d->tnonil,
     366        1768 :                                                                 d->tnil,
     367        1768 :                                                                 d->tkey)) {
     368           0 :                                                 BBPunfix(d->batCacheid);
     369           0 :                                                 goto cleanup_and_exit;
     370             :                                         }
     371             : #define keepprop(NME, LNME)                                                                                             \
     372             :         do {                                                                                                                            \
     373             :                 const void *valp = BATgetprop(d, NME);                                                  \
     374             :                 if ( valp){                                                                                                             \
     375             :                         cv = VALformat(valp);                                                                           \
     376             :                         if (cv) {                                                                                                       \
     377             :                                 char *cvquote = mal_quote(cv, strlen(cv));                              \
     378             :                                 ok = logadd(&logbuf, ",\"%s\":\"%s\"", LNME, cvquote);        \
     379             :                                 GDKfree(cv);                                                                                    \
     380             :                                 GDKfree(cvquote);                                                                               \
     381             :                                 if (!ok) {                                                                                              \
     382             :                                         BBPunfix(d->batCacheid);                                                     \
     383             :                                         goto cleanup_and_exit;                                                          \
     384             :                                 }                                                                                                               \
     385             :                         }                                                                                                                       \
     386             :                 }                                                                                                                               \
     387             :         } while (0)
     388        1972 :                                         if ((di.minpos != BUN_NONE &&
     389        1972 :                                                  !logadd(&logbuf, ",\"minpos\":\""BUNFMT"\"", di.minpos)) ||
     390         211 :                                                 (di.maxpos != BUN_NONE &&
     391        1976 :                                                  !logadd(&logbuf, ",\"maxpos\":\""BUNFMT"\"", di.maxpos)) ||
     392           0 :                                                 (di.unique_est != 0 &&
     393           0 :                                                  !logadd(&logbuf, ",\"nestimate\":\"%g\"", di.unique_est))) {
     394           0 :                                                 BBPunfix(d->batCacheid);
     395           0 :                                                 goto cleanup_and_exit;
     396             :                                         }
     397             : 
     398        1765 :                                         cv = VALformat(&stk->stk[getArg(pci,j)]);
     399        1772 :                                         c = strchr(cv, '>');
     400        1772 :                                         if (c)          /* unlikely that this isn't true */
     401        1772 :                                                 *c = 0;
     402        1772 :                                         ok = logadd(&logbuf, ",\"file\":\"%s\"", cv + 1);
     403        1767 :                                         GDKfree(cv);
     404        1771 :                                         if (!ok) {
     405           0 :                                                 BBPunfix(d->batCacheid);
     406           0 :                                                 goto cleanup_and_exit;
     407             :                                         }
     408        1771 :                                         total += cnt << di.shift;
     409        1771 :                                         if (!logadd(&logbuf, ",\"width\":%d", di.width)) {
     410           0 :                                                 BBPunfix(d->batCacheid);
     411           0 :                                                 goto cleanup_and_exit;
     412             :                                         }
     413             :                                         /* keeping information about the individual auxiliary heaps is helpful during analysis. */
     414        1758 :                                         MT_rwlock_rdlock(&d->thashlock);
     415        1770 :                                         if( d->thash && !logadd(&logbuf, ",\"hash\":" LLFMT, (lng) hashinfo(d->thash, d->batCacheid))) {
     416           0 :                                                 MT_rwlock_rdunlock(&d->thashlock);
     417           0 :                                                 BBPunfix(d->batCacheid);
     418           0 :                                                 goto cleanup_and_exit;
     419             :                                         }
     420        1770 :                                         MT_rwlock_rdunlock(&d->thashlock);
     421        1770 :                                         if( di.vh && !logadd(&logbuf, ",\"vheap\":" BUNFMT, di.vhfree)) {
     422           0 :                                                 BBPunfix(d->batCacheid);
     423           0 :                                                 goto cleanup_and_exit;
     424             :                                         }
     425        1771 :                                         if( d->timprints && !logadd(&logbuf, ",\"imprints\":" LLFMT, (lng) IMPSimprintsize(d))) {
     426           0 :                                                 BBPunfix(d->batCacheid);
     427           0 :                                                 goto cleanup_and_exit;
     428             :                                         }
     429             :                                         /* if (!logadd(&logbuf, "\"debug\":\"%s\",", d->debugmessages)) goto cleanup_and_exit; */
     430        1771 :                                         BBPunfix(d->batCacheid);
     431             :                                 }
     432        1776 :                                 if (!logadd(&logbuf,
     433             :                                                         ",\"bid\":%d"
     434             :                                                         ",\"count\":"BUNFMT
     435             :                                                         ",\"size\":" LLFMT,
     436             :                                                         bid, cnt, total))
     437           0 :                                         goto cleanup_and_exit;
     438             :                         } else{
     439        2072 :                                 tname = getTypeName(tpe);
     440        2080 :                                 ok = logadd(&logbuf,
     441             :                                                         ",\"type\":\"%s\""
     442             :                                                         ",\"const\":%d",
     443        2080 :                                                         tname, isVarConstant(mb, getArg(pci,j)));
     444        2074 :                                 GDKfree(tname);
     445        2084 :                                 if (!ok)
     446           0 :                                         goto cleanup_and_exit;
     447        2084 :                                 cv = VALformat(&stk->stk[getArg(pci,j)]);
     448        2081 :                                 stmtq = cv ? mal_quote(cv, strlen(cv)) : NULL;
     449        2082 :                                 if (stmtq)
     450        2082 :                                         ok = logadd(&logbuf, ",\"value\":\"%s\"", stmtq);
     451        2077 :                                 GDKfree(cv);
     452        2085 :                                 GDKfree(stmtq);
     453        2084 :                                 if (!ok)
     454           0 :                                         goto cleanup_and_exit;
     455             :                         }
     456        3855 :                         if (!logadd(&logbuf, ",\"eol\":%d", getVarEolife(mb,getArg(pci,j))))
     457           0 :                                 goto cleanup_and_exit;
     458             :                         // if (!logadd(&logbuf, ",\"fixed\":%d", isVarFixed(mb,getArg(pci,j)))) return NULL;
     459        3839 :                         if (!logadd(&logbuf, "}"))
     460           0 :                                 goto cleanup_and_exit;
     461             :                 }
     462         887 :                 if (!logadd(&logbuf, "]")) // end marker for arguments
     463           0 :                         goto cleanup_and_exit;
     464             :         }
     465         922 :         if (!logadd(&logbuf, "}\n")) // end marker
     466           0 :                 goto cleanup_and_exit;
     467         922 :         return logbuf.logbuffer;
     468           0 :  cleanup_and_exit:
     469           0 :         logdel(&logbuf);
     470           0 :         return NULL;
     471             : }
     472             : 
     473             : static void
     474           0 : renderProfilerEvent(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci, int start)
     475             : {
     476             :         str ev;
     477           0 :         ev = prepareProfilerEvent(cntxt, mb, stk, pci, start);
     478           0 :         if( ev ){
     479           0 :                 logjsonInternal(ev);
     480           0 :                 free(ev);
     481             :         }
     482           0 : }
     483             : 
     484             : /* the OS details on cpu load are read from /proc/stat
     485             :  * We should use an OS define to react to the maximal cores
     486             :  */
     487             : 
     488             : #define MAXCPU          256
     489             : #define LASTCPU         (MAXCPU - 1)
     490             : static struct{
     491             :         lng user, nice, system, idle, iowait;
     492             :         double load;
     493             : } corestat[MAXCPU];
     494             : 
     495             : static int
     496          10 : getCPULoad(char cpuload[BUFSIZ]){
     497             :     int cpu, len = 0, i;
     498             :         lng user, nice, system, idle, iowait;
     499             :         size_t n;
     500             :     char buf[512], *s;
     501             :         static FILE *proc= NULL;
     502             :         lng newload;
     503             : 
     504          10 :         if (proc == NULL) {
     505           1 :                 proc = fopen("/proc/stat", "r");
     506           1 :                 if (proc == NULL) {
     507             :                         /* unexpected */
     508             :                         return -1;
     509             :                 }
     510             :         } else
     511           9 :                 rewind(proc);
     512             : 
     513         170 :         while (fgets(buf, (int) sizeof(buf), proc) != NULL) {
     514         160 :                 n = strlen(buf);
     515         160 :                 if (strncmp(buf, "cpu", 3) == 0) {
     516             :                         s = buf + 3;
     517          90 :                         if (*s == ' ') {
     518             :                                 cpu = LASTCPU; // the cpu totals stored here
     519             :                         }  else {
     520             :                                 cpu = atoi(s);
     521          80 :                                 if (cpu < 0 || cpu > LASTCPU)
     522             :                                         cpu = LASTCPU;
     523             :                         }
     524          90 :                         s = strchr(s,' ');
     525          90 :                         if (s == NULL)          /* unexpected format of file */
     526             :                                 break;
     527         190 :                         while (*s && isspace((unsigned char)*s))
     528         100 :                                 s++;
     529          90 :                         i= sscanf(s, LLSCN" "LLSCN" "LLSCN" "LLSCN" "LLSCN,  &user, &nice, &system, &idle, &iowait);
     530          90 :                         if (i == 5) {
     531          90 :                                 newload = (user - corestat[cpu].user + nice - corestat[cpu].nice + system - corestat[cpu].system);
     532          90 :                                 if (newload)
     533          10 :                                         corestat[cpu].load = (double) newload / (newload + idle - corestat[cpu].idle + iowait - corestat[cpu].iowait);
     534          90 :                                 corestat[cpu].user = user;
     535          90 :                                 corestat[cpu].nice = nice;
     536          90 :                                 corestat[cpu].system = system;
     537          90 :                                 corestat[cpu].idle = idle;
     538          90 :                                 corestat[cpu].iowait = iowait;
     539             :                         }
     540             :                 }
     541             : 
     542         180 :                 while (buf[n - 1] != '\n') {
     543          20 :                         if (fgets(buf, (int) sizeof(buf), proc) == NULL)
     544           0 :                                 goto exitloop;
     545          20 :                         n = strlen(buf);
     546             :                 }
     547             :         }
     548          10 :   exitloop:
     549             : 
     550          10 :         if (cpuload == NULL)
     551             :                 return 0;
     552             :         // identify core processing
     553           0 :         len += snprintf(cpuload, BUFSIZ, "[");
     554           0 :         for (cpu = 0; cpuload && cpu < LASTCPU && corestat[cpu].user; cpu++) {
     555           0 :                 len +=snprintf(cpuload + len, BUFSIZ - len, "%s%.2f", (cpu?",":""), corestat[cpu].load);
     556             :         }
     557           0 :         (void) snprintf(cpuload + len, BUFSIZ - len, "]");
     558           0 :         return 0;
     559             : }
     560             : 
     561             : void
     562           0 : profilerHeartbeatEvent(char *alter)
     563             : {
     564             :         char cpuload[BUFSIZ];
     565             :         struct logbuf logbuf;
     566             :         lng usec;
     567             :         uint64_t microseconds;
     568             : 
     569           0 :         if (ATOMIC_GET(&hbdelay) == 0 || maleventstream  == 0)
     570           0 :                 return;
     571           0 :         usec = GDKusec();
     572           0 :         microseconds = (uint64_t)startup_time.tv_sec*1000000 + (uint64_t)startup_time.tv_usec + (uint64_t)usec;
     573             : 
     574             :         /* get CPU load on beat boundaries only */
     575           0 :         if (getCPULoad(cpuload))
     576             :                 return;
     577             : 
     578           0 :         logbuf = (struct logbuf) {0};
     579             : 
     580           0 :         if (!logadd(&logbuf, "{"))        // fill in later with the event counter
     581             :                 return;
     582           0 :         if (!GDKinmemory(0) && !GDKembedded()) {
     583           0 :                 char *uuid = NULL, *err;
     584           0 :                 if ((err = msab_getUUID(&uuid)) == NULL) {
     585           0 :                         bool ok = logadd(&logbuf, "\"session\":\"%s\",", uuid);
     586           0 :                         free(uuid);
     587           0 :                         if (!ok)
     588           0 :                                 return;
     589             :                 } else
     590           0 :                         free(err);
     591             :         }
     592           0 :         if (!logadd(&logbuf, "\"clk\":"LLFMT",\"ctime\":%"PRIu64",\"rss\":%zu,",
     593             :                                 usec,
     594             :                                 microseconds,
     595           0 :                                 MT_getrss()/1024/1024))
     596             :                 return;
     597             : #ifdef HAVE_SYS_RESOURCE_H
     598           0 :         getrusage(RUSAGE_SELF, &infoUsage);
     599           0 :         if(infoUsage.ru_inblock - prevUsage.ru_inblock && !logadd(&logbuf, "\"inblock\":%ld,", infoUsage.ru_inblock - prevUsage.ru_inblock))
     600             :                 return;
     601           0 :         if(infoUsage.ru_oublock - prevUsage.ru_oublock && !logadd(&logbuf, "\"oublock\":%ld,", infoUsage.ru_oublock - prevUsage.ru_oublock))
     602             :                 return;
     603           0 :         if(infoUsage.ru_majflt - prevUsage.ru_majflt && !logadd(&logbuf, "\"majflt\":%ld,", infoUsage.ru_majflt - prevUsage.ru_majflt))
     604             :                 return;
     605           0 :         if(infoUsage.ru_nswap - prevUsage.ru_nswap && !logadd(&logbuf, "\"nswap\":%ld,", infoUsage.ru_nswap - prevUsage.ru_nswap))
     606             :                 return;
     607           0 :         if(infoUsage.ru_nvcsw - prevUsage.ru_nvcsw && !logadd(&logbuf, "\"nvcsw\":%ld,", infoUsage.ru_nvcsw - prevUsage.ru_nvcsw +infoUsage.ru_nivcsw - prevUsage.ru_nivcsw))
     608             :                 return;
     609           0 :         prevUsage = infoUsage;
     610             : #endif
     611           0 :         if (!logadd(&logbuf,
     612             :                                 "\"state\":\"%s\","
     613             :                                 "\"cpuload\":%s"
     614             :                                 "}\n",                        // end marker
     615             :                                 alter, cpuload))
     616             :                 return;
     617           0 :         logjsonInternal(logbuf.logbuffer);
     618           0 :         logdel(&logbuf);
     619             : }
     620             : 
     621             : void
     622          90 : profilerEvent(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci, int start)
     623             : {
     624             :         (void) cntxt;
     625          90 :         if (stk == NULL) return;
     626          90 :         if (pci == NULL) return;
     627          90 :         if (getModuleId(pci) == myname) // ignore profiler commands from monitoring
     628             :                 return;
     629             : 
     630          88 :         if(maleventstream) {
     631           0 :                 renderProfilerEvent(cntxt, mb, stk, pci, start);
     632           0 :                 if (!start && pci->pc ==0)
     633           0 :                         profilerHeartbeatEvent("ping");
     634           0 :                 if (start && pci->token == ENDsymbol)
     635           0 :                         profilerHeartbeatEvent("ping");
     636             :         }
     637             : }
     638             : 
     639             : /* The first scheme dumps the events on a stream (and in the pool)
     640             :  */
     641             : str
     642           0 : openProfilerStream(Client cntxt)
     643             : {
     644             :         int j;
     645             : 
     646             : #ifdef HAVE_SYS_RESOURCE_H
     647           0 :         getrusage(RUSAGE_SELF, &infoUsage);
     648           0 :         prevUsage = infoUsage;
     649             : #endif
     650           0 :         if (myname == 0){
     651           0 :                 myname = putName("profiler");
     652           0 :                 logjsonInternal(monet_characteristics);
     653             :         }
     654           0 :         if(maleventstream){
     655             :                 /* The DBA can always grab the stream, others have to wait */
     656           0 :                 if (cntxt->user == MAL_ADMIN)
     657           0 :                         closeProfilerStream(cntxt);
     658             :                 else
     659           0 :                         throw(MAL,"profiler.start","Profiler already running, stream not available");
     660             :         }
     661           0 :         malProfileMode = -1;
     662           0 :         maleventstream = cntxt->fdout;
     663           0 :         malprofileruser = cntxt->user;
     664             : 
     665             :         // Ignore the JSON rendering mode, use compiled time version
     666             : 
     667             :         /* show all in progress instructions for stethoscope startup */
     668             :         /* wait a short time for instructions to finish updating there thread admin
     669             :          * and then follow the locking scheme */
     670             : 
     671           0 :         MT_sleep_ms(200);
     672             : 
     673           0 :         MT_lock_set(&mal_profileLock);
     674           0 :         for(j = 0; j <THREADS; j++){
     675             :                 Client c = 0; MalBlkPtr m=0; MalStkPtr s = 0; InstrPtr p = 0;
     676           0 :                 c = workingset[j].cntxt;
     677           0 :                 m = workingset[j].mb;
     678           0 :                 s = workingset[j].stk;
     679           0 :                 p =  workingset[j].pci;
     680           0 :                 if( c && m && s && p )
     681             :                         /* show the event  assuming the quadruple is aligned*/
     682           0 :                         profilerEvent(c, m, s, p, 1);
     683             :         }
     684           0 :         MT_lock_unset(&mal_profileLock);
     685           0 :         return MAL_SUCCEED;
     686             : }
     687             : 
     688             : str
     689           1 : closeProfilerStream(Client cntxt)
     690             : {
     691             :         (void) cntxt;
     692           1 :         maleventstream = NULL;
     693           1 :         malProfileMode = 0;
     694           1 :         malprofileruser = 0;
     695           1 :         return MAL_SUCCEED;
     696             : }
     697             : 
     698             : /* the second scheme is to collect the profile
     699             :  * events in a local table for direct SQL inspection
     700             :  */
     701             : str
     702           1 : startProfiler(Client cntxt)
     703             : {
     704             : #ifdef HAVE_SYS_RESOURCE_H
     705           1 :         getrusage(RUSAGE_SELF, &infoUsage);
     706           1 :         prevUsage = infoUsage;
     707             : #endif
     708             :         (void) cntxt;
     709             : 
     710           1 :         if(maleventstream){
     711           0 :                 throw(MAL,"profiler.start","Profiler already running, stream not available");
     712             :         }
     713           1 :         MT_lock_set(&mal_profileLock);
     714           1 :         if (myname == 0){
     715           1 :                 myname = putName("profiler");
     716             :         }
     717           1 :         malProfileMode = 1;
     718           1 :         MT_lock_unset(&mal_profileLock);
     719           1 :         logjsonInternal(monet_characteristics);
     720             :         // reset the trace table
     721           1 :         clearTrace(cntxt);
     722             : 
     723           1 :         return MAL_SUCCEED;
     724             : }
     725             : 
     726             : /* SQL tracing is simplified, because it only collects the events in the temporary table.
     727             :  */
     728             : str
     729          20 : startTrace(Client cntxt)
     730             : {
     731          20 :         cntxt->sqlprofiler = TRUE;
     732          20 :         clearTrace(cntxt);
     733          20 :         return MAL_SUCCEED;
     734             : }
     735             : 
     736             : str
     737          22 : stopTrace(Client cntxt)
     738             : {
     739          22 :         cntxt->sqlprofiler = FALSE;
     740          22 :         return MAL_SUCCEED;
     741             : }
     742             : 
     743             : str
     744         265 : stopProfiler(Client cntxt)
     745             : {
     746         265 :         MT_lock_set(&mal_profileLock);
     747         265 :         malProfileMode = 0;
     748         265 :         setHeartbeat(0); // stop heartbeat
     749         265 :         if(cntxt)
     750           1 :                 closeProfilerStream(cntxt);
     751         265 :         MT_lock_unset(&mal_profileLock);
     752         265 :         return MAL_SUCCEED;
     753             : }
     754             : 
     755             : /*
     756             :  * SQL profile traces
     757             :  * The events being captured are stored in client specific BATs.
     758             :  * They are made persistent to accumate information over
     759             :  * multiple sessions. This means it has to be explicitly reset
     760             :  * to avoid disc overflow using profiler.reset().
     761             :  *
     762             :  * The information returned for the trace is purposely limited
     763             :  * to the ticks and the final MAL instruction.
     764             :  * For more detailed analysis, the stethoscope should be used.
     765             :  */
     766             : 
     767             : static void
     768          12 : _cleanupProfiler(Client cntxt)
     769             : {
     770          12 :         if (cntxt->profticks)
     771          12 :                 BBPunfix(cntxt->profticks->batCacheid);
     772          12 :         if (cntxt->profstmt)
     773          12 :                 BBPunfix(cntxt->profstmt->batCacheid);
     774          12 :         if (cntxt->profevents)
     775          12 :                 BBPunfix(cntxt->profevents->batCacheid);
     776          12 :         cntxt->profticks = cntxt->profstmt = cntxt->profevents = NULL;
     777          12 : }
     778             : 
     779             : static BAT *
     780         111 : TRACEcreate(int tt)
     781             : {
     782             :         BAT *b;
     783             : 
     784         111 :         b = COLnew(0, tt, 1 << 10, TRANSIENT);
     785         111 :         if (b == NULL)
     786           0 :                 return NULL;
     787             :         return b;
     788             : }
     789             : 
     790             : static void
     791          86 : initTrace(Client cntxt)
     792             : {
     793          86 :         MT_lock_set(&mal_profileLock);
     794          86 :         if (cntxt->profticks) {
     795          49 :                 MT_lock_unset(&mal_profileLock);
     796          49 :                 return;       /* already initialized */
     797             :         }
     798          37 :         cntxt->profticks = TRACEcreate(TYPE_lng);
     799          37 :         cntxt->profstmt = TRACEcreate(TYPE_str);
     800          37 :         cntxt->profevents = TRACEcreate(TYPE_str);
     801          37 :         if (cntxt->profticks == NULL || cntxt->profstmt == NULL || cntxt->profevents == NULL)
     802           0 :                 _cleanupProfiler(cntxt);
     803          37 :         MT_lock_unset(&mal_profileLock);
     804             : }
     805             : 
     806             : int
     807          63 : TRACEtable(Client cntxt, BAT **r)
     808             : {
     809          63 :         initTrace(cntxt);
     810          63 :         MT_lock_set(&mal_profileLock);
     811          63 :         if (cntxt->profticks == NULL) {
     812           0 :                 MT_lock_unset(&mal_profileLock);
     813           0 :                 return -1;       /* not initialized */
     814             :         }
     815          63 :         r[0] = COLcopy(cntxt->profticks, cntxt->profticks->ttype, false, TRANSIENT);
     816          63 :         r[1] = COLcopy(cntxt->profstmt, cntxt->profstmt->ttype, false, TRANSIENT);
     817          63 :         r[2] = COLcopy(cntxt->profevents, cntxt->profevents->ttype, false, TRANSIENT);
     818          63 :         MT_lock_unset(&mal_profileLock);
     819          63 :         return 3;
     820             : }
     821             : 
     822             : BAT *
     823           4 : getTrace(Client cntxt, const char *nme)
     824             : {
     825             :         BAT *bn = NULL;
     826             : 
     827           4 :         MT_lock_set(&mal_profileLock);
     828           4 :         if (cntxt->profticks) {
     829           4 :                 if (strcmp(nme, "usec") == 0){
     830           2 :                         bn = COLcopy(cntxt->profticks, cntxt->profticks->ttype, false, TRANSIENT);
     831           2 :                 } else if (strcmp(nme, "stmt") == 0){
     832           2 :                         bn = COLcopy(cntxt->profstmt, cntxt->profstmt->ttype, false, TRANSIENT);
     833           0 :                 } else if (strcmp(nme, "events") == 0){
     834           0 :                         bn = COLcopy(cntxt->profevents, cntxt->profevents->ttype, false, TRANSIENT);
     835             :                 }
     836             :         }
     837           4 :         MT_lock_unset(&mal_profileLock);
     838           4 :         return bn;
     839             : }
     840             : 
     841             : void
     842          23 : clearTrace(Client cntxt)
     843             : {
     844             :         (void) cntxt;
     845          23 :         MT_lock_set(&mal_profileLock);
     846          23 :         if (cntxt->profticks == NULL) {
     847          11 :                 MT_lock_unset(&mal_profileLock);
     848          11 :                 initTrace(cntxt);
     849          11 :                 return;     /* not initialized */
     850             :         }
     851             :         /* drop all trace tables */
     852          12 :         _cleanupProfiler(cntxt);
     853          12 :         MT_lock_unset(&mal_profileLock);
     854          12 :         initTrace(cntxt);
     855             : }
     856             : 
     857             : str
     858           0 : cleanupTraces(Client cntxt)
     859             : {
     860           0 :         clearTrace(cntxt);
     861           0 :         return MAL_SUCCEED;
     862             : }
     863             : 
     864             : void
     865         958 : sqlProfilerEvent(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
     866             : {
     867             :         str stmt, c, ev;
     868             :         int errors = 0;
     869             : 
     870         958 :         if (cntxt->profticks == NULL)
     871             :                 return;
     872             : 
     873             :         /* generate actual call statement */
     874         958 :         stmt = instruction2str(mb, stk, pci, LIST_MAL_ALL | LIST_MAL_ALGO);
     875             :         c = stmt;
     876             : 
     877             : /* unclear why we needed this. OLD?
     878             :         while (c && *c && (isspace((unsigned char)*c) || *c == '!'))
     879             :                 c++;
     880             : */
     881             : 
     882         957 :         ev = prepareProfilerEvent(cntxt, mb, stk, pci, 0);
     883             :         // keep it a short transaction
     884         958 :         MT_lock_set(&mal_profileLock);
     885         961 :         if (cntxt->profticks == NULL) {
     886           0 :                 MT_lock_unset(&mal_profileLock);
     887           0 :                 GDKfree(stmt);
     888           0 :                 return;
     889             :         }
     890         961 :         errors += BUNappend(cntxt->profticks, &pci->ticks, false) != GDK_SUCCEED;
     891         961 :         errors += BUNappend(cntxt->profstmt, c, false) != GDK_SUCCEED;
     892         961 :         if( ev)
     893         925 :                 errors += BUNappend(cntxt->profevents, ev, false) != GDK_SUCCEED;
     894         961 :         if (errors > 0) {
     895             :                 /* stop profiling if an error occurred */
     896           0 :                 cntxt->sqlprofiler = FALSE;
     897             :         }
     898             : 
     899         961 :         MT_lock_unset(&mal_profileLock);
     900         961 :         GDKfree(stmt);
     901         961 :         if(ev) free(ev);
     902             : }
     903             : 
     904             : lng
     905           0 : getDiskWrites(void)
     906             : {
     907             : #ifdef HAVE_SYS_RESOURCE_H
     908             :         struct rusage infoUsage;
     909           0 :         getrusage(RUSAGE_SELF, &infoUsage);
     910           0 :         return infoUsage.ru_oublock;
     911             : #else
     912             :         return 0;
     913             : #endif
     914             : }
     915             : 
     916             : lng
     917           0 : getDiskReads(void)
     918             : {
     919             : #ifdef HAVE_SYS_RESOURCE_H
     920             :         struct rusage infoUsage;
     921           0 :         getrusage(RUSAGE_SELF, &infoUsage);
     922           0 :         return infoUsage.ru_inblock;
     923             : #else
     924             :         return 0;
     925             : #endif
     926             : }
     927             : 
     928             : lng
     929           0 : getUserTime(void)
     930             : {
     931             : #ifdef HAVE_TIMES
     932             :         struct tms newTms;
     933           0 :         times(&newTms);
     934           0 :         return newTms.tms_utime;
     935             : #else
     936             :         return 0;
     937             : #endif
     938             : }
     939             : 
     940             : lng
     941           0 : getSystemTime(void)
     942             : {
     943             : #ifdef HAVE_TIMES
     944             :         struct tms newTms;
     945           0 :         times(&newTms);
     946           0 :         return newTms.tms_stime;
     947             : #else
     948             :         return 0;
     949             : #endif
     950             : }
     951             : 
     952             : /* Calculate a pessimistic size of the disk storage */
     953             : lng
     954           0 : getDiskSpace(void)
     955             : {
     956             :         BAT *b;
     957             :         bat i;
     958             :         lng size = 0;
     959             : 
     960           0 :         for (i = 1; i < getBBPsize(); i++)
     961           0 :                 if (BBP_logical(i) && (BBP_refs(i) || BBP_lrefs(i))) {
     962           0 :                         b = BATdescriptor(i);
     963           0 :                         if (b) {
     964           0 :                                 size += sizeof(BAT);
     965             : 
     966           0 :                                 MT_lock_set(&b->theaplock);
     967           0 :                                 if (!isVIEW(b)) {
     968           0 :                                         BUN cnt = BATcount(b);
     969             : 
     970             :                                         /* the upperbound is used for the heaps */
     971           0 :                                         if (b->tvheap)
     972           0 :                                                 size += HEAPvmsize(b->tvheap);
     973           0 :                                         MT_lock_unset(&b->theaplock);
     974             : 
     975           0 :                                         size += tailsize(b, cnt);
     976           0 :                                         if (b->thash)
     977           0 :                                                 size += sizeof(BUN) * cnt;
     978             :                                         /* also add the size of an imprint, ordered index or mosaic */
     979           0 :                                         if(b->timprints)
     980           0 :                                                 size += IMPSimprintsize(b);
     981           0 :                                         if(b->torderidx)
     982           0 :                                                 size += HEAPvmsize(b->torderidx);
     983             :                                 } else {
     984           0 :                                         MT_lock_unset(&b->theaplock);
     985             :                                 }
     986           0 :                                 BBPunfix(i);
     987             :                         }
     988             :                 }
     989           0 :         return size;
     990             : }
     991             : 
     992             : 
     993          10 : void profilerGetCPUStat(lng *user, lng *nice, lng *sys, lng *idle, lng *iowait)
     994             : {
     995          10 :         (void) getCPULoad(NULL);
     996          10 :         *user = corestat[LASTCPU].user;
     997          10 :         *nice = corestat[LASTCPU].nice;
     998          10 :         *sys = corestat[LASTCPU].system;
     999          10 :         *idle = corestat[LASTCPU].idle;
    1000          10 :         *iowait = corestat[LASTCPU].iowait;
    1001          10 : }
    1002             : 
    1003             : /* the heartbeat process produces a ping event once every X milliseconds */
    1004             : static MT_Id hbthread;
    1005             : static ATOMIC_TYPE hbrunning = ATOMIC_VAR_INIT(0);
    1006             : 
    1007         266 : static void profilerHeartbeat(void *dummy)
    1008             : {
    1009             :         int t;
    1010         266 :         const int timeout = GDKdebug & FORCEMITOMASK ? 10 : 25;
    1011             : 
    1012             :         (void) dummy;
    1013             :         for (;;) {
    1014             :                 /* wait until you need this info */
    1015         266 :                 MT_thread_setworking("sleeping");
    1016       87483 :                 while (ATOMIC_GET(&hbdelay) == 0 || maleventstream == NULL) {
    1017       87483 :                         if (GDKexiting() || !ATOMIC_GET(&hbrunning))
    1018         265 :                                 return;
    1019       87218 :                         MT_sleep_ms(timeout);
    1020             :                 }
    1021           0 :                 for (t = (int) ATOMIC_GET(&hbdelay); t > 0; t -= timeout) {
    1022           0 :                         if (GDKexiting() || !ATOMIC_GET(&hbrunning))
    1023           0 :                                 return;
    1024           0 :                         MT_sleep_ms(t > timeout ? timeout : t);
    1025             :                 }
    1026           0 :                 if (GDKexiting() || !ATOMIC_GET(&hbrunning))
    1027           0 :                         return;
    1028           0 :                 MT_thread_setworking("pinging");
    1029           0 :                 profilerHeartbeatEvent("ping");
    1030             :         }
    1031             : }
    1032             : 
    1033         529 : void setHeartbeat(int delay)
    1034             : {
    1035         529 :         if (delay < 0){
    1036         264 :                 ATOMIC_SET(&hbrunning, 0);
    1037         264 :                 if (hbthread)
    1038         264 :                         MT_join_thread(hbthread);
    1039         264 :                 return;
    1040             :         }
    1041         265 :         if (delay > 0 &&  delay <= 10)
    1042             :                 delay = 10;
    1043         265 :         ATOMIC_SET(&hbdelay, delay);
    1044             : }
    1045             : 
    1046             : /* TODO getprofilerlimit and setprofilerlimit functions */
    1047             : 
    1048           2 : int getprofilerlimit(void)
    1049             : {
    1050           2 :         return 0;
    1051             : }
    1052             : 
    1053           0 : void setprofilerlimit(int limit)
    1054             : {
    1055             :         (void) limit;
    1056           0 : }
    1057             : 
    1058         265 : void initProfiler(void)
    1059             : {
    1060         265 :         gettimeofday(&startup_time, NULL);
    1061         265 : }
    1062             : 
    1063         266 : void initHeartbeat(void)
    1064             : {
    1065         266 :         ATOMIC_SET(&hbrunning, 1);
    1066         266 :         if (MT_create_thread(&hbthread, profilerHeartbeat, NULL, MT_THR_JOINABLE,
    1067             :                                                  "heartbeat") < 0) {
    1068             :                 /* it didn't happen */
    1069           0 :                 hbthread = 0;
    1070           0 :                 ATOMIC_SET(&hbrunning, 0);
    1071             :         }
    1072         266 : }

Generated by: LCOV version 1.14