1 /*-
2 * Copyright (c) 2014-2018 MongoDB, Inc.
3 * Copyright (c) 2008-2014 WiredTiger, Inc.
4 * All rights reserved.
5 *
6 * See the file LICENSE for redistribution information.
7 */
8
9 #include "wt_internal.h"
10
11 #ifdef __GNUC__
12 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 1)
13 /*
14 * !!!
15 * GCC with -Wformat-nonliteral complains about calls to strftime in this file.
16 * There's nothing wrong, this makes the warning go away.
17 */
18 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
19 #endif
20 #endif
21
22 /*
23 * __stat_sources_free --
24 * Free the array of statistics sources.
25 */
26 static void
__stat_sources_free(WT_SESSION_IMPL * session,char *** sources)27 __stat_sources_free(WT_SESSION_IMPL *session, char ***sources)
28 {
29 char **p;
30
31 if ((p = (*sources)) != NULL) {
32 for (; *p != NULL; ++p)
33 __wt_free(session, *p);
34 __wt_free(session, *sources);
35 }
36 }
37
38 /*
39 * __stat_config_discard --
40 * Discard all statistics-log configuration.
41 */
42 static int
__stat_config_discard(WT_SESSION_IMPL * session)43 __stat_config_discard(WT_SESSION_IMPL *session)
44 {
45 WT_CONNECTION_IMPL *conn;
46 WT_DECL_RET;
47
48 conn = S2C(session);
49
50 /*
51 * Discard all statistics-log configuration information, called when
52 * reconfiguring or destroying the statistics logging setup,
53 */
54 __wt_free(session, conn->stat_format);
55 ret = __wt_fclose(session, &conn->stat_fs);
56 __wt_free(session, conn->stat_path);
57 __stat_sources_free(session, &conn->stat_sources);
58 conn->stat_stamp = NULL;
59 conn->stat_usecs = 0;
60 return (ret);
61 }
62
63 /*
64 * __wt_conn_stat_init --
65 * Initialize the per-connection statistics.
66 */
67 void
__wt_conn_stat_init(WT_SESSION_IMPL * session)68 __wt_conn_stat_init(WT_SESSION_IMPL *session)
69 {
70 WT_CONNECTION_IMPL *conn;
71 WT_CONNECTION_STATS **stats;
72
73 conn = S2C(session);
74 stats = conn->stats;
75
76 __wt_async_stats_update(session);
77 __wt_cache_stats_update(session);
78 __wt_las_stats_update(session);
79 __wt_txn_stats_update(session);
80
81 WT_STAT_SET(session, stats, file_open, conn->open_file_count);
82 WT_STAT_SET(session,
83 stats, cursor_open_count, conn->open_cursor_count);
84 WT_STAT_SET(session, stats, dh_conn_handle_count, conn->dhandle_count);
85 WT_STAT_SET(session,
86 stats, rec_split_stashed_objects, conn->stashed_objects);
87 WT_STAT_SET(session,
88 stats, rec_split_stashed_bytes, conn->stashed_bytes);
89 }
90
91 /*
92 * __statlog_config --
93 * Parse and setup the statistics server options.
94 */
95 static int
__statlog_config(WT_SESSION_IMPL * session,const char ** cfg,bool * runp)96 __statlog_config(WT_SESSION_IMPL *session, const char **cfg, bool *runp)
97 {
98 WT_CONFIG objectconf;
99 WT_CONFIG_ITEM cval, k, v;
100 WT_CONNECTION_IMPL *conn;
101 WT_DECL_ITEM(tmp);
102 WT_DECL_RET;
103 int cnt;
104 char **sources;
105
106 /*
107 * A note on reconfiguration: the standard "is this configuration string
108 * allowed" checks should fail if reconfiguration has invalid strings,
109 * for example, "log=(enabled)", or "statistics_log=(path=XXX)", because
110 * the connection reconfiguration method doesn't allow those strings.
111 * Additionally, the base configuration values during reconfiguration
112 * are the currently configured values (so we don't revert to default
113 * values when repeatedly reconfiguring), and configuration processing
114 * of a currently set value should not change the currently set value.
115 *
116 * In this code path, a previous statistics log server reconfiguration
117 * may have stopped the server (and we're about to restart it). Because
118 * stopping the server discarded the configured information stored in
119 * the connection structure, we have to re-evaluate all configuration
120 * values, reconfiguration can't skip any of them.
121 */
122
123 conn = S2C(session);
124 sources = NULL;
125
126 /* Only start the server if wait time is non-zero */
127 WT_RET(__wt_config_gets(session, cfg, "statistics_log.wait", &cval));
128 *runp = cval.val != 0;
129 conn->stat_usecs = (uint64_t)cval.val * WT_MILLION;
130
131 /*
132 * Only set the JSON flag when stats are enabled, otherwise setting
133 * this flag can implicitly enable statistics gathering.
134 */
135 WT_RET(__wt_config_gets(session, cfg, "statistics_log.json", &cval));
136 if (cval.val != 0 && WT_STAT_ENABLED(session))
137 FLD_SET(conn->stat_flags, WT_STAT_JSON);
138
139 WT_RET(__wt_config_gets(
140 session, cfg, "statistics_log.on_close", &cval));
141 if (cval.val != 0)
142 FLD_SET(conn->stat_flags, WT_STAT_ON_CLOSE);
143
144 /*
145 * We don't allow the log path to be reconfigured for security reasons.
146 * (Applications passing input strings directly to reconfigure would
147 * expose themselves to a potential security problem, the utility of
148 * reconfiguring a statistics log path isn't worth the security risk.)
149 *
150 * See above for the details, but during reconfiguration we're loading
151 * the path value from the saved configuration information, and it's
152 * required during reconfiguration because we potentially stopped and
153 * are restarting, the server.
154 */
155 WT_RET(__wt_config_gets(session, cfg, "statistics_log.path", &cval));
156 WT_ERR(__wt_scr_alloc(session, 0, &tmp));
157 WT_ERR(__wt_buf_fmt(session,
158 tmp, "%.*s/%s", (int)cval.len, cval.str, WT_STATLOG_FILENAME));
159 WT_ERR(__wt_filename(session, tmp->data, &conn->stat_path));
160
161 WT_ERR(__wt_config_gets(session, cfg, "statistics_log.sources", &cval));
162 __wt_config_subinit(session, &objectconf, &cval);
163 for (cnt = 0; (ret = __wt_config_next(&objectconf, &k, &v)) == 0; ++cnt)
164 ;
165 WT_ERR_NOTFOUND_OK(ret);
166 if (cnt != 0) {
167 WT_ERR(__wt_calloc_def(session, cnt + 1, &sources));
168 __wt_config_subinit(session, &objectconf, &cval);
169 for (cnt = 0;
170 (ret = __wt_config_next(&objectconf, &k, &v)) == 0; ++cnt) {
171 /*
172 * XXX
173 * Only allow "file:" and "lsm:" for now: "file:" works
174 * because it's been converted to data handles, "lsm:"
175 * works because we can easily walk the list of open LSM
176 * objects, even though it hasn't been converted.
177 */
178 if (!WT_PREFIX_MATCH(k.str, "file:") &&
179 !WT_PREFIX_MATCH(k.str, "lsm:"))
180 WT_ERR_MSG(session, EINVAL,
181 "statistics_log sources configuration only "
182 "supports objects of type \"file\" or "
183 "\"lsm\"");
184 WT_ERR(
185 __wt_strndup(session, k.str, k.len, &sources[cnt]));
186 }
187 WT_ERR_NOTFOUND_OK(ret);
188
189 conn->stat_sources = sources;
190 sources = NULL;
191 }
192
193 /*
194 * When using JSON format, use the same timestamp format as MongoDB by
195 * default. This requires caution: the user might have set the timestamp
196 * in a previous reconfigure call and we don't want to override that, so
197 * compare the retrieved value with the default value to decide if we
198 * should use the JSON default.
199 *
200 * (This still implies if the user explicitly sets the timestamp to the
201 * default value, then sets the JSON flag in a separate reconfigure
202 * call, or vice-versa, we will incorrectly switch to the JSON default
203 * timestamp. But there's no way to detect that, and this is all a low
204 * probability path.)
205 *
206 * !!!
207 * Don't rewrite in the compressed "%FT%T.000Z" form, MSVC13 segfaults.
208 */
209 #define WT_TIMESTAMP_DEFAULT "%b %d %H:%M:%S"
210 #define WT_TIMESTAMP_JSON_DEFAULT "%Y-%m-%dT%H:%M:%S.000Z"
211 WT_ERR(__wt_config_gets(
212 session, cfg, "statistics_log.timestamp", &cval));
213 if (FLD_ISSET(conn->stat_flags, WT_STAT_JSON) &&
214 WT_STRING_MATCH(WT_TIMESTAMP_DEFAULT, cval.str, cval.len))
215 WT_ERR(__wt_strdup(
216 session, WT_TIMESTAMP_JSON_DEFAULT, &conn->stat_format));
217 else
218 WT_ERR(__wt_strndup(
219 session, cval.str, cval.len, &conn->stat_format));
220
221 err: __stat_sources_free(session, &sources);
222 __wt_scr_free(session, &tmp);
223
224 return (ret);
225 }
226
227 /*
228 * __statlog_print_header --
229 * Write the header for statistics when running in JSON mode.
230 */
231 static int
__statlog_print_header(WT_SESSION_IMPL * session)232 __statlog_print_header(WT_SESSION_IMPL *session)
233 {
234 WT_CONNECTION_IMPL *conn;
235
236 conn = S2C(session);
237
238 if (!FLD_ISSET(conn->stat_flags, WT_STAT_JSON))
239 return (0);
240
241 /*
242 * This flag is required in order to generate correct JSON when printing
243 * out stats for individual tables. When we are about to print the first
244 * table's stats we must print out the wiredTigerTables header once
245 * only and add a correct closing brace when we finish the tables
246 * section. To do this we maintain a flag variable to note when we have
247 * printed the first table. Unfortunately, the mechanism which we use
248 * to print stats for each table does not allow passing of variables
249 * by reference, this necessitates the use of a variable on the
250 * connection. The variable is safe as the JSON printing logic is only
251 * performed by the single threaded stat server.
252 */
253 conn->stat_json_tables = false;
254 WT_RET(__wt_fprintf(session, conn->stat_fs,
255 "{\"version\":\"%s\",\"localTime\":\"%s\"",
256 WIREDTIGER_VERSION_STRING, conn->stat_stamp));
257 return (0);
258 }
259
260 /*
261 * __statlog_print_table_name --
262 * Write the header for the wiredTigerTables section of statistics if
263 * running in JSON mode and the header has not been written this round,
264 * then print the name of the table.
265 */
266 static int
__statlog_print_table_name(WT_SESSION_IMPL * session,const char * name,bool conn_stats)267 __statlog_print_table_name(
268 WT_SESSION_IMPL *session, const char *name, bool conn_stats)
269 {
270 WT_CONNECTION_IMPL *conn;
271
272 conn = S2C(session);
273
274 if (!FLD_ISSET(conn->stat_flags, WT_STAT_JSON))
275 return (0);
276
277 /*
278 * If printing the connection stats, write that header and we are done.
279 */
280 if (conn_stats) {
281 WT_RET(__wt_fprintf(
282 session, conn->stat_fs, ",\"wiredTiger\":{"));
283 return (0);
284 }
285
286 /*
287 * If this is the first table we are printing stats for print the header
288 * for the wiredTigerTables section. Otherwise print a comma as this is
289 * a subsequent table.
290 */
291 if (conn->stat_json_tables)
292 WT_RET(__wt_fprintf(session, conn->stat_fs,","));
293 else {
294 conn->stat_json_tables = true;
295 WT_RET(__wt_fprintf(session,
296 conn->stat_fs,",\"wiredTigerTables\":{"));
297 }
298 WT_RET(__wt_fprintf(session, conn->stat_fs, "\"%s\":{", name));
299 return (0);
300 }
301
302 /*
303 * __statlog_print_footer --
304 * Write the footer for statistics when running in JSON mode.
305 */
306 static int
__statlog_print_footer(WT_SESSION_IMPL * session)307 __statlog_print_footer(WT_SESSION_IMPL *session)
308 {
309 WT_CONNECTION_IMPL *conn;
310
311 conn = S2C(session);
312
313 if (!FLD_ISSET(conn->stat_flags, WT_STAT_JSON))
314 return (0);
315
316 /* If we have printed a tables stats, then close that section. */
317 if (conn->stat_json_tables) {
318 WT_RET(__wt_fprintf(session, conn->stat_fs, "}"));
319 conn->stat_json_tables = false;
320 }
321 WT_RET(__wt_fprintf(session, conn->stat_fs, "}\n"));
322 return (0);
323 }
324
325 /*
326 * __statlog_dump --
327 * Dump out handle/connection statistics.
328 */
329 static int
__statlog_dump(WT_SESSION_IMPL * session,const char * name,bool conn_stats)330 __statlog_dump(WT_SESSION_IMPL *session, const char *name, bool conn_stats)
331 {
332 WT_CONNECTION_IMPL *conn;
333 WT_CURSOR *cursor;
334 WT_DECL_ITEM(tmp);
335 WT_DECL_RET;
336 size_t prefixlen;
337 int64_t val;
338 const char *desc, *endprefix, *valstr, *uri;
339 const char *cfg[] = {
340 WT_CONFIG_BASE(session, WT_SESSION_open_cursor), NULL };
341 bool first, groupfirst;
342
343 conn = S2C(session);
344 cursor = NULL;
345
346 WT_RET(__wt_scr_alloc(session, 0, &tmp));
347 first = groupfirst = true;
348
349 /* Build URI and configuration string. */
350 if (conn_stats)
351 uri = "statistics:";
352 else {
353 WT_ERR(__wt_buf_fmt(session, tmp, "statistics:%s", name));
354 uri = tmp->data;
355 }
356
357 /*
358 * Open the statistics cursor and dump the statistics.
359 *
360 * If we don't find an underlying object, silently ignore it, the object
361 * may exist only intermittently.
362 */
363 if ((ret = __wt_curstat_open(session, uri, NULL, cfg, &cursor)) != 0) {
364 if (ret == EBUSY || ret == ENOENT || ret == WT_NOTFOUND)
365 ret = 0;
366 goto err;
367 }
368
369 WT_ERR(__statlog_print_table_name(session, name, conn_stats));
370 while ((ret = cursor->next(cursor)) == 0) {
371 WT_ERR(cursor->get_value(cursor, &desc, &valstr, &val));
372 if (FLD_ISSET(conn->stat_flags, WT_STAT_JSON)) {
373 /* Check if we are starting a new section. */
374 endprefix = strchr(desc, ':');
375 prefixlen = WT_PTRDIFF(endprefix, desc);
376 WT_ASSERT(session, endprefix != NULL);
377 if (first ||
378 tmp->size != prefixlen ||
379 strncmp(desc, tmp->data, tmp->size) != 0) {
380 WT_ERR(__wt_buf_set(
381 session, tmp, desc, prefixlen));
382 WT_ERR(__wt_fprintf(session, conn->stat_fs,
383 "%s\"%.*s\":{", first ? "" : "},",
384 (int)prefixlen, desc));
385 first = false;
386 groupfirst = true;
387 }
388 WT_ERR(__wt_fprintf(session, conn->stat_fs,
389 "%s\"%s\":%" PRId64,
390 groupfirst ? "" : ",", endprefix + 2, val));
391 groupfirst = false;
392 } else
393 WT_ERR(__wt_fprintf(session, conn->stat_fs,
394 "%s %" PRId64 " %s %s\n",
395 conn->stat_stamp, val, name, desc));
396 }
397 WT_ERR_NOTFOUND_OK(ret);
398 if (FLD_ISSET(conn->stat_flags, WT_STAT_JSON))
399 WT_ERR(__wt_fprintf(session, conn->stat_fs, "}}"));
400
401 err: __wt_scr_free(session, &tmp);
402 if (cursor != NULL)
403 WT_TRET(cursor->close(cursor));
404 return (ret);
405 }
406
407 /*
408 * __statlog_apply --
409 * Review a single open handle and dump statistics on demand.
410 */
411 static int
__statlog_apply(WT_SESSION_IMPL * session,const char * cfg[])412 __statlog_apply(WT_SESSION_IMPL *session, const char *cfg[])
413 {
414 WT_DATA_HANDLE *dhandle;
415 WT_DECL_RET;
416 char **p;
417
418 WT_UNUSED(cfg);
419
420 dhandle = session->dhandle;
421
422 /* Check for a match on the set of sources. */
423 for (p = S2C(session)->stat_sources; *p != NULL; ++p)
424 if (WT_PREFIX_MATCH(dhandle->name, *p)) {
425 WT_WITHOUT_DHANDLE(session, ret =
426 __statlog_dump(session, dhandle->name, false));
427 return (ret);
428 }
429 return (0);
430 }
431
432 /*
433 * __statlog_lsm_apply --
434 * Review the list open LSM trees, and dump statistics on demand.
435 *
436 * XXX
437 * This code should be removed when LSM objects are converted to data handles.
438 */
439 static int
__statlog_lsm_apply(WT_SESSION_IMPL * session)440 __statlog_lsm_apply(WT_SESSION_IMPL *session)
441 {
442 #define WT_LSM_TREE_LIST_SLOTS 100
443 WT_LSM_TREE *lsm_tree, *list[WT_LSM_TREE_LIST_SLOTS];
444 WT_DECL_RET;
445 int cnt;
446 bool locked;
447 char **p;
448
449 cnt = locked = 0;
450
451 /*
452 * Walk the list of LSM trees, checking for a match on the set of
453 * sources.
454 *
455 * XXX
456 * We can't hold the schema lock for the traversal because the LSM
457 * statistics code acquires the tree lock, and the LSM cursor code
458 * acquires the tree lock and then acquires the schema lock, it's a
459 * classic deadlock. This is temporary code so I'm not going to do
460 * anything fancy.
461 * It is OK to not keep holding the schema lock after populating
462 * the list of matching LSM trees, since the __wt_lsm_tree_get call
463 * will bump a reference count, so the tree won't go away.
464 */
465 __wt_spin_lock(session, &S2C(session)->schema_lock);
466 locked = true;
467 TAILQ_FOREACH(lsm_tree, &S2C(session)->lsmqh, q) {
468 if (cnt == WT_LSM_TREE_LIST_SLOTS)
469 break;
470 for (p = S2C(session)->stat_sources; *p != NULL; ++p)
471 if (WT_PREFIX_MATCH(lsm_tree->name, *p)) {
472 WT_ERR(__wt_lsm_tree_get(session,
473 lsm_tree->name, false, &list[cnt++]));
474 break;
475 }
476 }
477 __wt_spin_unlock(session, &S2C(session)->schema_lock);
478 locked = false;
479
480 while (cnt > 0) {
481 --cnt;
482 WT_TRET(__statlog_dump(session, list[cnt]->name, false));
483 __wt_lsm_tree_release(session, list[cnt]);
484 }
485
486 err: if (locked)
487 __wt_spin_unlock(session, &S2C(session)->schema_lock);
488 /* Release any LSM trees on error. */
489 while (cnt > 0) {
490 --cnt;
491 __wt_lsm_tree_release(session, list[cnt]);
492 }
493 return (ret);
494 }
495
496 /*
497 * __statlog_log_one --
498 * Output a set of statistics into the current log file.
499 */
500 static int
__statlog_log_one(WT_SESSION_IMPL * session,WT_ITEM * path,WT_ITEM * tmp)501 __statlog_log_one(WT_SESSION_IMPL *session, WT_ITEM *path, WT_ITEM *tmp)
502 {
503 struct timespec ts;
504 struct tm localt;
505 WT_CONNECTION_IMPL *conn;
506
507 conn = S2C(session);
508
509 /* Get the current local time of day. */
510 __wt_epoch(session, &ts);
511 WT_RET(__wt_localtime(session, &ts.tv_sec, &localt));
512
513 /* Create the logging path name for this time of day. */
514 if (strftime(tmp->mem, tmp->memsize, conn->stat_path, &localt) == 0)
515 WT_RET_MSG(session, ENOMEM, "strftime path conversion");
516
517 /* If the path has changed, cycle the log file. */
518 if (conn->stat_fs == NULL ||
519 path == NULL || strcmp(tmp->mem, path->mem) != 0) {
520 WT_RET(__wt_fclose(session, &conn->stat_fs));
521 WT_RET(__wt_fopen(session, tmp->mem,
522 WT_FS_OPEN_CREATE | WT_FS_OPEN_FIXED, WT_STREAM_APPEND,
523 &conn->stat_fs));
524
525 if (path != NULL)
526 WT_RET(__wt_buf_setstr(session, path, tmp->mem));
527 }
528
529 /* Create the entry prefix for this time of day. */
530 if (strftime(tmp->mem, tmp->memsize, conn->stat_format, &localt) == 0)
531 WT_RET_MSG(session, ENOMEM, "strftime timestamp conversion");
532 conn->stat_stamp = tmp->mem;
533 WT_RET(__statlog_print_header(session));
534
535 /* Dump the connection statistics. */
536 WT_RET(__statlog_dump(session, conn->home, true));
537
538 /*
539 * Lock the schema and walk the list of open handles, dumping
540 * any that match the list of object sources.
541 */
542 if (conn->stat_sources != NULL)
543 WT_RET(__wt_conn_btree_apply(
544 session, NULL, __statlog_apply, NULL, NULL));
545
546 /*
547 * Walk the list of open LSM trees, dumping any that match the
548 * the list of object sources.
549 *
550 * XXX
551 * This code should be removed when LSM objects are converted to
552 * data handles.
553 */
554 if (conn->stat_sources != NULL)
555 WT_RET(__statlog_lsm_apply(session));
556 WT_RET(__statlog_print_footer(session));
557
558 /* Flush. */
559 return (__wt_fflush(session, conn->stat_fs));
560 }
561
562 /*
563 * __statlog_on_close --
564 * Log a set of statistics at close. Requires the server is not currently
565 * running.
566 */
567 static int
__statlog_on_close(WT_SESSION_IMPL * session)568 __statlog_on_close(WT_SESSION_IMPL *session)
569 {
570 WT_CONNECTION_IMPL *conn;
571 WT_DECL_ITEM(tmp);
572 WT_DECL_RET;
573
574 conn = S2C(session);
575
576 if (!FLD_ISSET(conn->stat_flags, WT_STAT_ON_CLOSE))
577 return (0);
578
579 if (F_ISSET(conn, WT_CONN_SERVER_STATISTICS))
580 WT_RET_MSG(session, EINVAL,
581 "Attempt to log statistics while a server is running");
582
583 WT_RET(__wt_scr_alloc(session, strlen(conn->stat_path) + 128, &tmp));
584 WT_ERR(__wt_buf_setstr(session, tmp, ""));
585 WT_ERR(__statlog_log_one(session, NULL, tmp));
586
587 err: __wt_scr_free(session, &tmp);
588 return (ret);
589 }
590
591 /*
592 * __statlog_server_run_chk --
593 * Check to decide if the statistics log server should continue running.
594 */
595 static bool
__statlog_server_run_chk(WT_SESSION_IMPL * session)596 __statlog_server_run_chk(WT_SESSION_IMPL *session)
597 {
598 return (F_ISSET(S2C(session), WT_CONN_SERVER_STATISTICS));
599 }
600
601 /*
602 * __statlog_server --
603 * The statistics server thread.
604 */
605 static WT_THREAD_RET
__statlog_server(void * arg)606 __statlog_server(void *arg)
607 {
608 WT_CONNECTION_IMPL *conn;
609 WT_DECL_RET;
610 WT_ITEM path, tmp;
611 WT_SESSION_IMPL *session;
612
613 session = arg;
614 conn = S2C(session);
615
616 /*
617 * We need a temporary place to build a path and an entry prefix.
618 * The length of the path plus 128 should be more than enough.
619 *
620 * We also need a place to store the current path, because that's
621 * how we know when to close/re-open the file.
622 */
623 WT_CLEAR(path);
624 WT_ERR(__wt_buf_init(session, &path, strlen(conn->stat_path) + 128));
625 WT_ERR(__wt_buf_setstr(session, &path, ""));
626 WT_CLEAR(tmp);
627 WT_ERR(__wt_buf_init(session, &tmp, strlen(conn->stat_path) + 128));
628 WT_ERR(__wt_buf_setstr(session, &tmp, ""));
629
630 for (;;) {
631 /* Wait until the next event. */
632 __wt_cond_wait(session, conn->stat_cond,
633 conn->stat_usecs, __statlog_server_run_chk);
634
635 /* Check if we're quitting or being reconfigured. */
636 if (!__statlog_server_run_chk(session))
637 break;
638
639 if (WT_STAT_ENABLED(session))
640 WT_ERR(__statlog_log_one(session, &path, &tmp));
641 }
642
643 if (0) {
644 err: WT_PANIC_MSG(session, ret, "statistics log server error");
645 }
646 __wt_buf_free(session, &path);
647 __wt_buf_free(session, &tmp);
648 return (WT_THREAD_RET_VALUE);
649 }
650
651 /*
652 * __statlog_start --
653 * Start the statistics server thread.
654 */
655 static int
__statlog_start(WT_CONNECTION_IMPL * conn)656 __statlog_start(WT_CONNECTION_IMPL *conn)
657 {
658 WT_SESSION_IMPL *session;
659
660 /* Nothing to do if the server is already running. */
661 if (conn->stat_session != NULL)
662 return (0);
663
664 F_SET(conn, WT_CONN_SERVER_STATISTICS);
665
666 /* The statistics log server gets its own session. */
667 WT_RET(__wt_open_internal_session(
668 conn, "statlog-server", true, 0, &conn->stat_session));
669 session = conn->stat_session;
670
671 WT_RET(__wt_cond_alloc(
672 session, "statistics log server", &conn->stat_cond));
673
674 /*
675 * Start the thread.
676 *
677 * Statistics logging creates a thread per database, rather than using
678 * a single thread to do logging for all of the databases. If we ever
679 * see lots of databases at a time, doing statistics logging, and we
680 * want to reduce the number of threads, there's no reason we have to
681 * have more than one thread, I just didn't feel like writing the code
682 * to figure out the scheduling.
683 */
684 WT_RET(__wt_thread_create(
685 session, &conn->stat_tid, __statlog_server, session));
686 conn->stat_tid_set = true;
687
688 return (0);
689 }
690
691 /*
692 * __wt_statlog_create --
693 * Start the statistics server thread.
694 */
695 int
__wt_statlog_create(WT_SESSION_IMPL * session,const char * cfg[])696 __wt_statlog_create(WT_SESSION_IMPL *session, const char *cfg[])
697 {
698 WT_CONNECTION_IMPL *conn;
699 bool start;
700
701 conn = S2C(session);
702
703 /*
704 * Stop any server that is already running. This means that each time
705 * reconfigure is called we'll bounce the server even if there are no
706 * configuration changes. This makes our life easier as the underlying
707 * configuration routine doesn't have to worry about freeing objects
708 * in the connection structure (it's guaranteed to always start with a
709 * blank slate), and we don't have to worry about races where a running
710 * server is reading configuration information that we're updating, and
711 * it's not expected that reconfiguration will happen a lot.
712 *
713 * If there's no server running, discard any configuration information
714 * so we don't leak memory during reconfiguration.
715 */
716 if (conn->stat_session == NULL)
717 WT_RET(__stat_config_discard(session));
718 else
719 WT_RET(__wt_statlog_destroy(session, false));
720
721 WT_RET(__statlog_config(session, cfg, &start));
722 if (start)
723 WT_RET(__statlog_start(conn));
724
725 return (0);
726 }
727
728 /*
729 * __wt_statlog_destroy --
730 * Destroy the statistics server thread.
731 */
732 int
__wt_statlog_destroy(WT_SESSION_IMPL * session,bool is_close)733 __wt_statlog_destroy(WT_SESSION_IMPL *session, bool is_close)
734 {
735 WT_CONNECTION_IMPL *conn;
736 WT_DECL_RET;
737 WT_SESSION *wt_session;
738
739 conn = S2C(session);
740
741 /* Stop the server thread. */
742 F_CLR(conn, WT_CONN_SERVER_STATISTICS);
743 if (conn->stat_tid_set) {
744 __wt_cond_signal(session, conn->stat_cond);
745 WT_TRET(__wt_thread_join(session, &conn->stat_tid));
746 conn->stat_tid_set = false;
747 }
748 __wt_cond_destroy(session, &conn->stat_cond);
749
750 /* Log a set of statistics on shutdown if configured. */
751 if (is_close)
752 WT_TRET(__statlog_on_close(session));
753
754 /* Discard all configuration information. */
755 WT_TRET(__stat_config_discard(session));
756
757 /* Close the server thread's session. */
758 if (conn->stat_session != NULL) {
759 wt_session = &conn->stat_session->iface;
760 WT_TRET(wt_session->close(wt_session, NULL));
761 conn->stat_session = NULL;
762 }
763
764 return (ret);
765 }
766