Changeset: 854ec21bfdcc for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/854ec21bfdcc
Branch: Jun2020-mmt
Log Message:

Merge with Jun2020 branch.


diffs (truncated from 742 to 300 lines):

diff --git a/monetdb5/mal/mal_profiler.c b/monetdb5/mal/mal_profiler.c
--- a/monetdb5/mal/mal_profiler.c
+++ b/monetdb5/mal/mal_profiler.c
@@ -31,17 +31,18 @@
 
 #include <string.h>
 
-static str myname = 0; // avoid tracing the profiler module
+static const char *myname = 0; // avoid tracing the profiler module
 
 /* The JSON rendering can be either using '\n' separators between
  * each key:value pair or as a single line.
  * The current stethoscope implementation requires the first option and
- * also the term rendering  to be set to ''
+ * also the term rendering to be set to ''
  */
 
 /* When the MAL block contains a BARRIER block we may end up with tons
  * of profiler events. To avoid this, we stop emitting the events
- * when we reached the HIGHWATERMARK. Leaving a message in the log. */
+ * when we reached the HIGHWATERMARK. Leaving a message in the log.
+ */
 #define HIGHWATERMARK 5
 
 
@@ -58,242 +59,378 @@ static struct rusage prevUsage;
 #endif
 
 #define LOGLEN 8192
-#define lognew()  loglen = 0; logbase = logbuffer; *logbase = 0;
-
-#define logadd(...)                                                            
                                                \
-       do {                                                                    
                                                        \
-               char tmp_buff[LOGLEN];                                          
                                        \
-               int tmp_len = 0;                                                
                                                \
-               tmp_len = snprintf(tmp_buff, LOGLEN, __VA_ARGS__);              
                \
-               if (loglen + tmp_len < LOGLEN)                                  
                                \
-                       loglen += snprintf(logbase+loglen, LOGLEN - loglen, 
__VA_ARGS__); \
-               else {                                                          
                                                        \
-                       logjsonInternal(logbuffer);                             
                                        \
-                       lognew();                                               
                                                        \
-                       loglen += snprintf(logbase+loglen, LOGLEN - loglen, 
__VA_ARGS__); \
-               }                                                               
                                                                \
-       } while (0)
 
 // The heart beat events should be sent to all outstanding channels.
-static void logjsonInternal(char *logbuffer)
+static void logjsonInternal(char *logbuffer, bool flush)
 {
        size_t len;
        len = strlen(logbuffer);
 
-       MT_lock_set(&mal_profileLock);
        if (maleventstream) {
-       // upon request the log record is sent over the profile stream
+               // upon request the log record is sent over the profile stream
                (void) mnstr_write(maleventstream, logbuffer, 1, len);
-               (void) mnstr_flush(maleventstream);
+               if (flush)
+                       (void) mnstr_flush(maleventstream);
        }
-       MT_lock_unset(&mal_profileLock);
+}
+
+/*
+ * We use a buffer (`logbuffer`) where we incrementally create the output JSON 
object. Initially we allocate LOGLEN (8K)
+ * bytes and we keep the capacity of the buffer (`logcap`) and the length of 
the current string (`loglen`).
+ *
+ * We use the `logadd` function to add data to our buffer (usually key-value 
pairs). This macro offers an interface similar
+ * to printf.
+ *
+ * The first snprintf bellow happens in a statically allocated buffer that 
might be much smaller than logcap. We do not
+ * care. We only need to perform this snprintf to get the actual length of the 
string that is to be produced.
+ *
+ * There are three cases:
+ *
+ * 1. The new string fits in the current buffer -> we just update the buffer
+ *
+ * 2. The new string does not fit in the current buffer, but is smaller than 
the capacity of the buffer -> we output the
+ * current contents of the buffer and start at the beginning.
+ *
+ * 3. The new string exceeds the current capacity of the buffer -> we output 
the current contents and reallocate the
+ * buffer. The new capacity is 1.5 times the length of the new string.
+ */
+struct logbuf {
+       char *logbuffer;
+       char *logbase;
+       size_t loglen;
+       size_t logcap;
+};
+
+static inline void
+lognew(struct logbuf *logbuf)
+{
+       logbuf->loglen = 0;
+       logbuf->logbase = logbuf->logbuffer;
+       *logbuf->logbase = 0;
+}
+
+static inline void
+logdel(struct logbuf *logbuf)
+{
+       free(logbuf->logbuffer);
 }
 
-static char *
-truncate_string(char *inp)
+static bool logadd(struct logbuf *logbuf,
+                                  _In_z_ _Printf_format_string_ const char 
*fmt, ...)
+       __attribute__((__format__(__printf__, 2, 3)))
+       __attribute__((__warn_unused_result__));
+static bool
+logadd(struct logbuf *logbuf, const char *fmt, ...)
 {
-       size_t len;
-       char *ret;
-       size_t ret_len = LOGLEN/2;
-       size_t padding = 64;
+       char tmp_buff[LOGLEN];
+       int tmp_len;
+       va_list va;
+       va_list va2;
 
-       len = strlen(inp);
-       ret = (char *)GDKmalloc(ret_len + 1);
-       if (ret == NULL) {
-               return NULL;
+       va_start(va, fmt);
+       va_copy(va2, va);                       /* we will need it again */
+       tmp_len = vsnprintf(tmp_buff, sizeof(tmp_buff), fmt, va);
+       if (tmp_len < 0) {
+               logdel(logbuf);
+               va_end(va);
+               va_end(va2);
+               return false;
        }
-
-       snprintf(ret, ret_len + 1, "%.*s...<truncated>...%.*s",
-                        (int) (ret_len/2), inp, (int) (ret_len/2 - padding),
-                        inp + (len - ret_len/2 + padding));
-
-       return ret;
+       if (logbuf->loglen + (size_t) tmp_len >= logbuf->logcap) {
+               if ((size_t) tmp_len >= logbuf->logcap) {
+                       /* includes first time when logbuffer == NULL and 
logcap = 0 */
+                       char *alloc_buff;
+                       if (logbuf->loglen > 0)
+                               logjsonInternal(logbuf->logbuffer, false);
+                       logbuf->logcap = (size_t) tmp_len + (size_t) tmp_len/2;
+                       if (logbuf->logcap < LOGLEN)
+                               logbuf->logcap = LOGLEN;
+                       alloc_buff = realloc(logbuf->logbuffer, logbuf->logcap);
+                       if (alloc_buff == NULL) {
+                               TRC_ERROR(MAL_SERVER, "Profiler JSON buffer 
reallocation failure\n");
+                               logdel(logbuf);
+                               va_end(va);
+                               va_end(va2);
+                               return false;
+                       }
+                       logbuf->logbuffer = alloc_buff;
+                       lognew(logbuf);
+               } else {
+                       logjsonInternal(logbuf->logbuffer, false);
+                       lognew(logbuf);
+               }
+       }
+       logbuf->loglen += vsnprintf(logbuf->logbase + logbuf->loglen,
+                                                               logbuf->logcap 
- logbuf->loglen,
+                                                               fmt, va2);
+       va_end(va);
+       va_end(va2);
+       return true;
 }
 
 /* JSON rendering method of performance data.
  * The eventparser may assume this layout for ease of parsing
-EXAMPLE:
-{
-"event":6        ,
-"thread":3,
-"function":"user.s3_1",
-"pc":1,
-"tag":10397,
-"state":"start",
-"usec":0,
-}
-"stmt":"X_41=0@0:void := querylog.define(\"select count(*) from 
tables;\":str,\"default_pipe\":str,30:int);",
+ EXAMPLE:
+ {
+ "event":6        ,
+ "thread":3,
+ "function":"user.s3_1",
+ "pc":1,
+ "tag":10397,
+ "state":"start",
+ "usec":0,
+ }
+ "stmt":"X_41=0@0:void := querylog.define(\"select count(*) from 
tables;\":str,\"default_pipe\":str,30:int);",
 */
 static void
-renderProfilerEvent(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci, 
int start)
+prepareProfilerEvent(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci, 
int start)
 {
-       char logbuffer[LOGLEN], *logbase;
-       size_t loglen;
+       struct logbuf logbuf;
        str c;
        str stmtq;
        lng usec;
        uint64_t microseconds;
+       bool ok;
 
        /* ignore generation of events for instructions that are called too 
often
         * they may appear when BARRIER blocks are executed
         * The default parameter should be sufficient for most practical cases.
-       */
+        */
        if( !start && pci->calls > HIGHWATERMARK){
                if( pci->calls == 10000 || pci->calls == 100000 || pci->calls 
== 1000000 || pci->calls == 10000000)
                        TRC_WARNING(MAL_SERVER, "Too many calls: %d\n", 
pci->calls);
                return;
        }
 
-/* The stream of events can be complete read by the DBA,
- * all other users can only see events assigned to their account
- */
+       /* The stream of events can be complete read by the DBA,
+        * all other users can only see events assigned to their account
+        */
        if(malprofileruser!= MAL_ADMIN && malprofileruser != cntxt->user)
                return;
 
+       logbuf = (struct logbuf) {0};
+
        usec= pci->clock;
        microseconds = (uint64_t)usec - ((uint64_t)startup_time.tv_sec*1000000 
- (uint64_t)startup_time.tv_usec);
        /* make profile event tuple  */
-       lognew();
-       logadd("{"); // fill in later with the event counter
        /* TODO: This could probably be optimized somehow to avoid the
         * function call to mercurial_revision().
         */
        // No comma at the beginning
-       logadd("\"version\":\""VERSION" (hg id: %s)\"", mercurial_revision());
-       logadd(",\"user\":"OIDFMT, cntxt->user);
-       logadd(",\"clk\":"LLFMT, usec);
-       logadd(",\"mclk\":%"PRIu64"", microseconds);
-       logadd(",\"thread\":%d", THRgettid());
-       logadd(",\"program\":\"%s.%s\"", getModuleId(getInstrPtr(mb, 0)), 
getFunctionId(getInstrPtr(mb, 0)));
-       logadd(",\"pc\":%d", mb?getPC(mb,pci):0);
-       logadd(",\"tag\":"OIDFMT, stk?stk->tag:0);
-       if( pci->modname)
-               logadd(",\"module\":\"%s\"", pci->modname ? pci->modname : "");
-       if( pci->fcnname)
-               logadd(",\"function\":\"%s\"", pci->fcnname ? pci->fcnname : 
"");
-       if( pci->barrier)
-               logadd(",\"barrier\":\"%s\"", operatorName(pci->barrier));
-       if( pci->token < FCNcall || pci->token > PATcall)
-               logadd(",\"operator\":\"%s\"", operatorName(pci->token));
-    if (!GDKinmemory()) {
-        char *uuid;
+       if (!logadd(&logbuf,
+                               "{"                             // fill in 
later with the event counter
+                               "\"version\":\""VERSION" (hg id: %s)\""
+                               ",\"user\":"OIDFMT
+                               ",\"clk\":"LLFMT
+                               ",\"mclk\":%"PRIu64""
+                               ",\"thread\":%d"
+                               ",\"program\":\"%s.%s\""
+                               ",\"pc\":%d"
+                               ",\"tag\":"OIDFMT,
+                               mercurial_revision(),
+                               cntxt->user,
+                               usec,
+                               microseconds,
+                               THRgettid(),
+                               getModuleId(getInstrPtr(mb, 0)), 
getFunctionId(getInstrPtr(mb, 0)),
+                               mb?getPC(mb,pci):0,
+                               stk?stk->tag:0))
+               return;
+       if( pci->modname && !logadd(&logbuf, ",\"module\":\"%s\"", pci->modname 
? pci->modname : ""))
+               return;
+       if( pci->fcnname && !logadd(&logbuf, ",\"function\":\"%s\"", 
pci->fcnname ? pci->fcnname : ""))
+               return;
+       if( pci->barrier && !logadd(&logbuf, ",\"barrier\":\"%s\"", 
operatorName(pci->barrier)))
+               return;
+       if ((pci->token < FCNcall || pci->token > PATcall) &&
+               !logadd(&logbuf, ",\"operator\":\"%s\"", 
operatorName(pci->token)))
+               return;
+       if (!GDKinmemory()) {
+               char *uuid = NULL;
                str c;
                if ((c = msab_getUUID(&uuid)) == NULL) {
-                       logadd(",\"session\":\"%s\"", uuid);
+                       ok = logadd(&logbuf, ",\"session\":\"%s\"", uuid);
                        free(uuid);
+                       if (!ok)
+                               return;
                } else
                        free(c);
_______________________________________________
checkin-list mailing list -- checkin-list@monetdb.org
To unsubscribe send an email to checkin-list-le...@monetdb.org

Reply via email to