Changeset: 1b2865035ec4 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=1b2865035ec4
Modified Files:
monetdb5/mal/mal_profiler.c
Branch: Oct2020
Log Message:
Reallocate profiler output buffer if it is not enough
This fixes bug 6998.
diffs (170 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
@@ -60,17 +60,51 @@ static struct rusage prevUsage;
#define LOGLEN 8192
#define lognew() loglen = 0; logbase = logbuffer; *logbase = 0;
+/*
+ * 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` macro 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 beginnig.
+ *
+ * 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.
+ */
#define logadd(...)
\
do {
\
char tmp_buff[LOGLEN];
\
- int tmp_len = 0;
\
+ size_t 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 {
\
+ if (loglen + tmp_len < logcap)
\
+ loglen += snprintf(logbase+loglen, logcap - loglen,
__VA_ARGS__); \
+ else if (tmp_len < logcap) {
\
logjsonInternal(logbuffer);
\
lognew();
\
- loglen += snprintf(logbase+loglen, LOGLEN - loglen,
__VA_ARGS__); \
+ loglen += snprintf(logbase+loglen, logcap - loglen,
__VA_ARGS__); \
+ }
\
+ else {
\
+ char *alloc_buff;
\
+ logjsonInternal(logbuffer);
\
+ logcap = tmp_len + tmp_len/2;
\
+ alloc_buff = (char *)realloc(logbuffer, logcap);
\
+ if (alloc_buff == NULL) {
\
+ TRC_ERROR(MAL_SERVER, "Profiler JSON buffer
reallocation failure\n"); \
+ free(logbuffer);
\
+ return;
\
+ }
\
+ logbuffer = alloc_buff;
\
+ lognew();
\
+ loglen += snprintf(logbase+loglen, logcap - loglen,
__VA_ARGS__); \
}
\
} while (0)
@@ -89,27 +123,6 @@ static void logjsonInternal(char *logbuf
MT_lock_unset(&mal_profileLock);
}
-static char *
-truncate_string(char *inp)
-{
- size_t len;
- char *ret;
- size_t ret_len = LOGLEN/2;
- size_t padding = 64;
-
- len = strlen(inp);
- ret = (char *)GDKmalloc(ret_len + 1);
- if (ret == NULL) {
- return NULL;
- }
-
- 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;
-}
-
/* JSON rendering method of performance data.
* The eventparser may assume this layout for ease of parsing
EXAMPLE:
@@ -127,8 +140,8 @@ EXAMPLE:
static void
renderProfilerEvent(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci,
int start)
{
- char logbuffer[LOGLEN], *logbase;
- size_t loglen;
+ char *logbuffer = NULL, *logbase;
+ size_t loglen, logcap = LOGLEN;
str c;
str stmtq;
lng usec;
@@ -150,6 +163,12 @@ renderProfilerEvent(Client cntxt, MalBlk
if(malprofileruser!= MAL_ADMIN && malprofileruser != cntxt->user)
return;
+ logbuffer = (char *)malloc(logcap);
+ if (logbuffer == NULL) {
+ TRC_ERROR(MAL_SERVER, "Profiler JSON buffer allocation
failure\n");
+ return;
+ }
+
usec= pci->clock;
microseconds = (uint64_t)usec - ((uint64_t)startup_time.tv_sec*1000000
- (uint64_t)startup_time.tv_usec);
/* make profile event tuple */
@@ -275,17 +294,11 @@ This information can be used to determin
logadd(",\"count\":"BUNFMT, cnt);
logadd(",\"size\":" LLFMT, total);
} else{
- char *truncated = NULL;
tname = getTypeName(tpe);
logadd(",\"type\":\"%s\"", tname);
logadd(",\"const\":%d",
isVarConstant(mb, getArg(pci,j)));
cv =
VALformat(&stk->stk[getArg(pci,j)]);
stmtq = cv ? mal_quote(cv, strlen(cv))
: NULL;
- if (stmtq != NULL && strlen(stmtq) >
LOGLEN/2) {
- truncated =
truncate_string(stmtq);
- GDKfree(stmtq);
- stmtq = truncated;
- }
if (stmtq)
logadd(",\"value\":\"%s\"",
stmtq);
GDKfree(cv);
@@ -302,6 +315,7 @@ This information can be used to determin
}
logadd("}\n"); // end marker
logjsonInternal(logbuffer);
+ free(logbuffer);
}
/* the OS details on cpu load are read from /proc/stat
@@ -381,8 +395,8 @@ void
profilerHeartbeatEvent(char *alter)
{
char cpuload[BUFSIZ];
- char logbuffer[LOGLEN], *logbase;
- int loglen;
+ char *logbuffer = NULL, *logbase;
+ size_t loglen, logcap = LOGLEN;
lng usec;
uint64_t microseconds;
@@ -395,6 +409,12 @@ profilerHeartbeatEvent(char *alter)
if (getCPULoad(cpuload))
return;
+ logbuffer = (char *)malloc(logcap);
+ if (logbuffer == NULL) {
+ TRC_ERROR(MAL_SERVER, "Profiler JSON buffer allocation
failure\n");
+ return;
+ }
+
lognew();
logadd("{"); // fill in later with the event counter
if (!GDKinmemory() && !GDKembedded()) {
@@ -426,6 +446,7 @@ profilerHeartbeatEvent(char *alter)
logadd("\"cpuload\":%s",cpuload);
logadd("}\n"); // end marker
logjsonInternal(logbuffer);
+ free(logbuffer);
}
void
_______________________________________________
checkin-list mailing list
[email protected]
https://www.monetdb.org/mailman/listinfo/checkin-list