/* * __handle_error_default -- * Default WT_EVENT_HANDLER->handle_error implementation: send to stderr. */ static int __handle_error_default(WT_EVENT_HANDLER *handler, WT_SESSION *wt_session, int error, const char *errmsg) { WT_SESSION_IMPL *session; WT_UNUSED(handler); WT_UNUSED(error); session = (WT_SESSION_IMPL *)wt_session; WT_RET(__wt_fprintf(session, stderr, "%s\n", errmsg)); WT_RET(__wt_fflush(session, stderr)); return (0); }
/* * __backup_list_uri_append -- * Append a new file name to the list, allocate space as necessary. * Called via the schema_worker function. */ static int __backup_list_uri_append( WT_SESSION_IMPL *session, const char *name, bool *skip) { WT_CURSOR_BACKUP *cb; char *value; cb = session->bkp_cursor; WT_UNUSED(skip); /* * While reading the metadata file, check there are no data sources * that can't support hot backup. This checks for a data source that's * non-standard, which can't be backed up, but is also sanity checking: * if there's an entry backed by anything other than a file or lsm * entry, we're confused. */ if (WT_PREFIX_MATCH(name, "log:")) { WT_RET(__backup_log_append(session, cb, false)); return (0); } if (!WT_PREFIX_MATCH(name, "file:") && !WT_PREFIX_MATCH(name, "colgroup:") && !WT_PREFIX_MATCH(name, "index:") && !WT_PREFIX_MATCH(name, "lsm:") && !WT_PREFIX_MATCH(name, "table:")) WT_RET_MSG(session, ENOTSUP, "hot backup is not supported for objects of type %s", name); /* Ignore the lookaside table. */ if (strcmp(name, WT_LAS_URI) == 0) return (0); /* Add the metadata entry to the backup file. */ WT_RET(__wt_metadata_search(session, name, &value)); WT_RET(__wt_fprintf(cb->bfp, "%s\n%s\n", name, value)); __wt_free(session, value); /* Add file type objects to the list of files to be copied. */ if (WT_PREFIX_MATCH(name, "file:")) WT_RET(__backup_list_append(session, cb, name)); return (0); }
/* * __txn_printlog -- * Print a log record in a human-readable format. */ static int __txn_printlog(WT_SESSION_IMPL *session, WT_ITEM *rawrec, WT_LSN *lsnp, WT_LSN *next_lsnp, void *cookie, int firstrecord) { WT_LOG_RECORD *logrec; WT_TXN_PRINTLOG_ARGS *args; const uint8_t *end, *p; const char *msg; uint64_t txnid; uint32_t fileid, lsnfile, lsnoffset, rectype; int32_t start; bool compressed; WT_UNUSED(next_lsnp); args = cookie; p = WT_LOG_SKIP_HEADER(rawrec->data); end = (const uint8_t *)rawrec->data + rawrec->size; logrec = (WT_LOG_RECORD *)rawrec->data; compressed = F_ISSET(logrec, WT_LOG_RECORD_COMPRESSED); /* First, peek at the log record type. */ WT_RET(__wt_logrec_read(session, &p, end, &rectype)); if (!firstrecord) WT_RET(__wt_fprintf(session, WT_STDOUT(session), ",\n")); WT_RET(__wt_fprintf(session, WT_STDOUT(session), " { \"lsn\" : [%" PRIu32 ",%" PRIu32 "],\n", lsnp->l.file, lsnp->l.offset)); WT_RET(__wt_fprintf(session, WT_STDOUT(session), " \"hdr_flags\" : \"%s\",\n", compressed ? "compressed" : "")); WT_RET(__wt_fprintf(session, WT_STDOUT(session), " \"rec_len\" : %" PRIu32 ",\n", logrec->len)); WT_RET(__wt_fprintf(session, WT_STDOUT(session), " \"mem_len\" : %" PRIu32 ",\n", compressed ? logrec->mem_len : logrec->len)); switch (rectype) { case WT_LOGREC_CHECKPOINT: WT_RET(__wt_struct_unpack(session, p, WT_PTRDIFF(end, p), WT_UNCHECKED_STRING(II), &lsnfile, &lsnoffset)); WT_RET(__wt_fprintf(session, WT_STDOUT(session), " \"type\" : \"checkpoint\",\n")); WT_RET(__wt_fprintf(session, WT_STDOUT(session), " \"ckpt_lsn\" : [%" PRIu32 ",%" PRIu32 "]\n", lsnfile, lsnoffset)); break; case WT_LOGREC_COMMIT: WT_RET(__wt_vunpack_uint(&p, WT_PTRDIFF(end, p), &txnid)); WT_RET(__wt_fprintf(session, WT_STDOUT(session), " \"type\" : \"commit\",\n")); WT_RET(__wt_fprintf(session, WT_STDOUT(session), " \"txnid\" : %" PRIu64 ",\n", txnid)); WT_RET(__txn_oplist_printlog(session, &p, end, args->flags)); break; case WT_LOGREC_FILE_SYNC: WT_RET(__wt_struct_unpack(session, p, WT_PTRDIFF(end, p), WT_UNCHECKED_STRING(Ii), &fileid, &start)); WT_RET(__wt_fprintf(session, WT_STDOUT(session), " \"type\" : \"file_sync\",\n")); WT_RET(__wt_fprintf(session, WT_STDOUT(session), " \"fileid\" : %" PRIu32 ",\n", fileid)); WT_RET(__wt_fprintf(session, WT_STDOUT(session), " \"start\" : %" PRId32 "\n", start)); break; case WT_LOGREC_MESSAGE: WT_RET(__wt_struct_unpack(session, p, WT_PTRDIFF(end, p), WT_UNCHECKED_STRING(S), &msg)); WT_RET(__wt_fprintf(session, WT_STDOUT(session), " \"type\" : \"message\",\n")); WT_RET(__wt_fprintf(session, WT_STDOUT(session), " \"message\" : \"%s\"\n", msg)); break; case WT_LOGREC_SYSTEM: WT_RET(__wt_struct_unpack(session, p, WT_PTRDIFF(end, p), WT_UNCHECKED_STRING(II), &lsnfile, &lsnoffset)); WT_RET(__wt_fprintf(session, WT_STDOUT(session), " \"type\" : \"system\",\n")); WT_RET(__txn_oplist_printlog(session, &p, end, args->flags)); break; } WT_RET(__wt_fprintf(session, WT_STDOUT(session), " }")); return (0); }
WT_RET(__txn_oplist_printlog(session, &p, end, args->flags)); break; } WT_RET(__wt_fprintf(session, WT_STDOUT(session), " }")); return (0); } /* * __wt_txn_printlog -- * Print the log in a human-readable format. */ int __wt_txn_printlog(WT_SESSION *wt_session, uint32_t flags) WT_GCC_FUNC_ATTRIBUTE((visibility("default"))) { WT_SESSION_IMPL *session; WT_TXN_PRINTLOG_ARGS args; session = (WT_SESSION_IMPL *)wt_session; args.flags = flags; WT_RET(__wt_fprintf(session, WT_STDOUT(session), "[\n")); WT_RET(__wt_log_scan( session, NULL, WT_LOGSCAN_FIRST, __txn_printlog, &args)); WT_RET(__wt_fprintf(session, WT_STDOUT(session), "\n]\n")); return (0); }
/* * __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); }
/* * __wt_statlog_dump_spinlock -- * Log the spin-lock statistics. */ int __wt_statlog_dump_spinlock(WT_CONNECTION_IMPL *conn, const char *tag) { WT_SPINLOCK *spin; WT_CONNECTION_STATS_SPINLOCK *p, *t; uint64_t block_manager, btree_page, ignore; u_int i, j; /* * Ignore rare acquisition of a spinlock using a base value of 10 per * second so we don't create graphs we don't care about. */ ignore = (uint64_t)(conn->stat_usecs / 1000000) * 10; /* Output the number of times each spinlock was acquired. */ block_manager = btree_page = 0; for (i = 0; i < WT_ELEMENTS(conn->spinlock_list); ++i) { if ((spin = conn->spinlock_list[i]) == NULL) continue; /* * There are two sets of spinlocks we aggregate, the btree page * locks and the block manager per-file locks. The reason is * the block manager locks grow with the number of files open * (and LSM and bloom filters can open a lot of files), and * there are 16 btree page locks and splitting them out has not * historically been that informative. */ if (strcmp(spin->name, "block manager") == 0) { block_manager += spin->counter; if (FLD_ISSET(conn->stat_flags, WT_CONN_STAT_CLEAR)) spin->counter = 0; continue; } if (strcmp(spin->name, "btree page") == 0) { btree_page += spin->counter; if (FLD_ISSET(conn->stat_flags, WT_CONN_STAT_CLEAR)) spin->counter = 0; continue; } WT_RET(__wt_fprintf(session, conn->stat_fp, "%s %" PRIu64 " %s spinlock %s: acquisitions\n", conn->stat_stamp, spin->counter <= ignore ? 0 : spin->counter, tag, spin->name)); if (FLD_ISSET(conn->stat_flags, WT_CONN_STAT_CLEAR)) spin->counter = 0; } WT_RET(__wt_fprintf(session, conn->stat_fp, "%s %" PRIu64 " %s spinlock %s: acquisitions\n", conn->stat_stamp, block_manager <= ignore ? 0 : block_manager, tag, "block manager")); WT_RET(__wt_fprintf(session, conn->stat_fp, "%s %" PRIu64 " %s spinlock %s: acquisitions\n", conn->stat_stamp, btree_page <= ignore ? 0 : btree_page, tag, "btree page")); /* * Output the number of times each location acquires its spinlock and * the blocking matrix. */ for (i = 0; i < WT_ELEMENTS(conn->spinlock_block); ++i) { p = &conn->spinlock_block[i]; if (p->name == NULL) continue; WT_RET(__wt_fprintf(session, conn->stat_fp, "%s %d %s spinlock %s acquired by %s(%d)\n", conn->stat_stamp, p->total <= ignore ? 0 : p->total, tag, p->name, p->file, p->line)); if (FLD_ISSET(conn->stat_flags, WT_CONN_STAT_CLEAR)) p->total = 0; for (j = 0; j < WT_ELEMENTS(conn->spinlock_block); ++j) { t = &conn->spinlock_block[j]; if (t->name == NULL) continue; WT_RET(__wt_fprintf(session, conn->stat_fp, "%s %d %s spinlock %s: %s(%d) blocked by %s(%d)\n", conn->stat_stamp, p->blocked[j] <= ignore ? 0 : p->blocked[j], tag, p->name, p->file, p->line, t->file, t->line)); if (FLD_ISSET(conn->stat_flags, WT_CONN_STAT_CLEAR)) p->blocked[j] = 0; } } WT_FULL_BARRIER(); /* Minimize the window. */ return (0); }