/* * __dmsg -- * Debug message. */ static void __dmsg(WT_DBG *ds, const char *fmt, ...) { va_list ap; WT_ITEM *msg; WT_SESSION_IMPL *session; size_t len, space; char *p; session = ds->session; /* * Debug output chunks are not necessarily terminated with a newline * character. It's easy if we're dumping to a stream, but if we're * dumping to an event handler, which is line-oriented, we must buffer * the output chunk, and pass it to the event handler once we see a * terminating newline. */ if (ds->fp == NULL) { msg = ds->msg; for (;;) { p = (char *)msg->mem + msg->size; space = msg->memsize - msg->size; va_start(ap, fmt); len = (size_t)vsnprintf(p, space, fmt, ap); va_end(ap); /* Check if there was enough space. */ if (len < space) { msg->size += len; break; } /* * There's not much to do on error without checking for * an error return on every single printf. Anyway, it's * pretty unlikely and this is debugging output, I'm not * going to worry about it. */ if (__wt_buf_grow( session, msg, msg->memsize + len + 128) != 0) return; } if (((uint8_t *)msg->mem)[msg->size - 1] == '\n') { ((uint8_t *)msg->mem)[msg->size - 1] = '\0'; (void)__wt_msg(session, "%s", (char *)msg->mem); msg->size = 0; } } else { va_start(ap, fmt); (void)__wt_vfprintf(ds->fp, fmt, ap); va_end(ap); } }
return (vfprintf(fp, fmt, ap) < 0 ? __wt_errno() : 0); } /* * __wt_fprintf -- * Fprintf for a FILE handle. */ int __wt_fprintf(FILE *fp, const char *fmt, ...) WT_GCC_FUNC_ATTRIBUTE((format (printf, 2, 3))) { WT_DECL_RET; va_list ap; va_start(ap, fmt); ret = __wt_vfprintf(fp, fmt, ap); va_end(ap); return (ret); } /* * __wt_fflush -- * Flush a FILE handle. */ int __wt_fflush(FILE *fp) { /* Flush the handle. */ return (fflush(fp) == 0 ? 0 : __wt_errno()); }
/* * __wt_eventv -- * Report a message to an event handler. */ int __wt_eventv(WT_SESSION_IMPL *session, bool msg_event, int error, const char *file_name, int line_number, const char *fmt, va_list ap) { WT_EVENT_HANDLER *handler; WT_DECL_RET; WT_SESSION *wt_session; struct timespec ts; size_t len, remain, wlen; int prefix_cnt; const char *err, *prefix; char *end, *p, tid[128]; /* * We're using a stack buffer because we want error messages no matter * what, and allocating a WT_ITEM, or the memory it needs, might fail. * * !!! * SECURITY: * Buffer placed at the end of the stack in case snprintf overflows. */ char s[2048]; /* * !!! * This function MUST handle a NULL WT_SESSION_IMPL handle. * * Without a session, we don't have event handlers or prefixes for the * error message. Write the error to stderr and call it a day. (It's * almost impossible for that to happen given how early we allocate the * first session, but if the allocation of the first session fails, for * example, we can end up here without a session.) */ if (session == NULL) { WT_RET(__wt_fprintf(session, stderr, "WiredTiger Error%s%s: ", error == 0 ? "" : ": ", error == 0 ? "" : __wt_strerror(session, error, NULL, 0))); WT_RET(__wt_vfprintf(session, stderr, fmt, ap)); WT_RET(__wt_fprintf(session, stderr, "\n")); return (__wt_fflush(session, stderr)); } p = s; end = s + sizeof(s); /* * We have several prefixes for the error message: * a timestamp and the process and thread ids, the database error * prefix, the data-source's name, and the session's name. Write them * as a comma-separate list, followed by a colon. */ prefix_cnt = 0; if (__wt_epoch(session, &ts) == 0) { __wt_thread_id(tid, sizeof(tid)); remain = WT_PTRDIFF(end, p); wlen = (size_t)snprintf(p, remain, "[%" PRIuMAX ":%" PRIuMAX "][%s]", (uintmax_t)ts.tv_sec, (uintmax_t)ts.tv_nsec / 1000, tid); p = wlen >= remain ? end : p + wlen; prefix_cnt = 1; } if ((prefix = S2C(session)->error_prefix) != NULL) { remain = WT_PTRDIFF(end, p); wlen = (size_t)snprintf(p, remain, "%s%s", prefix_cnt == 0 ? "" : ", ", prefix); p = wlen >= remain ? end : p + wlen; prefix_cnt = 1; } prefix = session->dhandle == NULL ? NULL : session->dhandle->name; if (prefix != NULL) { remain = WT_PTRDIFF(end, p); wlen = (size_t)snprintf(p, remain, "%s%s", prefix_cnt == 0 ? "" : ", ", prefix); p = wlen >= remain ? end : p + wlen; prefix_cnt = 1; } if ((prefix = session->name) != NULL) { remain = WT_PTRDIFF(end, p); wlen = (size_t)snprintf(p, remain, "%s%s", prefix_cnt == 0 ? "" : ", ", prefix); p = wlen >= remain ? end : p + wlen; prefix_cnt = 1; } if (prefix_cnt != 0) { remain = WT_PTRDIFF(end, p); wlen = (size_t)snprintf(p, remain, ": "); p = wlen >= remain ? end : p + wlen; } if (file_name != NULL) { remain = WT_PTRDIFF(end, p); wlen = (size_t) snprintf(p, remain, "%s, %d: ", file_name, line_number); p = wlen >= remain ? end : p + wlen; } remain = WT_PTRDIFF(end, p); wlen = (size_t)vsnprintf(p, remain, fmt, ap); p = wlen >= remain ? end : p + wlen; if (error != 0) { /* * When the engine calls __wt_err on error, it often outputs an * error message including the string associated with the error * it's returning. We could change the calls to call __wt_errx, * but it's simpler to not append an error string if all we are * doing is duplicating an existing error string. * * Use strcmp to compare: both strings are nul-terminated, and * we don't want to run past the end of the buffer. */ err = __wt_strerror(session, error, NULL, 0); len = strlen(err); if (WT_PTRDIFF(p, s) < len || strcmp(p - len, err) != 0) { remain = WT_PTRDIFF(end, p); (void)snprintf(p, remain, ": %s", err); } } /* * If a handler fails, return the error status: if we're in the process * of handling an error, any return value we provide will be ignored by * our caller, our caller presumably already has an error value it will * be returning. * * If an application-specified or default informational message handler * fails, complain using the application-specified or default error * handler. * * If an application-specified error message handler fails, complain * using the default error handler. If the default error handler fails, * there's nothing to do. */ wt_session = (WT_SESSION *)session; handler = session->event_handler; if (msg_event) { ret = handler->handle_message(handler, wt_session, s); if (ret != 0) __handler_failure(session, ret, "message", false); } else { ret = handler->handle_error(handler, wt_session, error, s); if (ret != 0 && handler->handle_error != __handle_error_default) __handler_failure(session, ret, "error", true); } return (ret); }