1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996,2008 Oracle.  All rights reserved.
5 *
6 * $Id: txn_stat.c,v 12.28 2008/01/08 20:59:00 bostic Exp $
7 */
8
9#include "db_config.h"
10
11#include "db_int.h"
12#include "dbinc/db_page.h"
13#include "dbinc/db_am.h"
14#include "dbinc/log.h"
15#include "dbinc/txn.h"
16
17#ifdef HAVE_STATISTICS
18static int  __txn_compare __P((const void *, const void *));
19static int  __txn_print_all __P((ENV *, u_int32_t));
20static int  __txn_print_stats __P((ENV *, u_int32_t));
21static int  __txn_stat __P((ENV *, DB_TXN_STAT **, u_int32_t));
22static char *__txn_status __P((DB_TXN_ACTIVE *));
23static void __txn_gid __P((ENV *, DB_MSGBUF *, DB_TXN_ACTIVE *));
24
25#define	XID_FIELD_IS_SET(p)						\
26	((p)->xa_status != 0 || (p)->status == TXN_PREPARED)
27
28/*
29 * __txn_stat_pp --
30 *	DB_ENV->txn_stat pre/post processing.
31 *
32 * PUBLIC: int __txn_stat_pp __P((DB_ENV *, DB_TXN_STAT **, u_int32_t));
33 */
34int
35__txn_stat_pp(dbenv, statp, flags)
36	DB_ENV *dbenv;
37	DB_TXN_STAT **statp;
38	u_int32_t flags;
39{
40	DB_THREAD_INFO *ip;
41	ENV *env;
42	int ret;
43
44	env = dbenv->env;
45
46	ENV_REQUIRES_CONFIG(env,
47	    env->tx_handle, "DB_ENV->txn_stat", DB_INIT_TXN);
48
49	if ((ret = __db_fchk(env,
50	    "DB_ENV->txn_stat", flags, DB_STAT_CLEAR)) != 0)
51		return (ret);
52
53	ENV_ENTER(env, ip);
54	REPLICATION_WRAP(env, (__txn_stat(env, statp, flags)), 0, ret);
55	ENV_LEAVE(env, ip);
56	return (ret);
57}
58
59/*
60 * __txn_stat --
61 *	ENV->txn_stat.
62 */
63static int
64__txn_stat(env, statp, flags)
65	ENV *env;
66	DB_TXN_STAT **statp;
67	u_int32_t flags;
68{
69	DB_TXNMGR *mgr;
70	DB_TXNREGION *region;
71	DB_TXN_STAT *stats;
72	TXN_DETAIL *td;
73	size_t nbytes;
74	u_int32_t maxtxn, ndx;
75	int ret;
76
77	*statp = NULL;
78	mgr = env->tx_handle;
79	region = mgr->reginfo.primary;
80
81	/*
82	 * Allocate for the maximum active transactions -- the DB_TXN_ACTIVE
83	 * struct is small and the maximum number of active transactions is
84	 * not going to be that large.  Don't have to lock anything to look
85	 * at the region's maximum active transactions value, it's read-only
86	 * and never changes after the region is created.
87	 *
88	 * The maximum active transactions isn't a hard limit, so allocate
89	 * some extra room, and don't walk off the end.
90	 */
91	maxtxn = region->maxtxns + (region->maxtxns / 10) + 10;
92	nbytes = sizeof(DB_TXN_STAT) + sizeof(DB_TXN_ACTIVE) * maxtxn;
93	if ((ret = __os_umalloc(env, nbytes, &stats)) != 0)
94		return (ret);
95
96	TXN_SYSTEM_LOCK(env);
97	memcpy(stats, &region->stat, sizeof(*stats));
98	stats->st_last_txnid = region->last_txnid;
99	stats->st_last_ckp = region->last_ckp;
100	stats->st_time_ckp = region->time_ckp;
101	stats->st_txnarray = (DB_TXN_ACTIVE *)&stats[1];
102
103	for (ndx = 0,
104	    td = SH_TAILQ_FIRST(&region->active_txn, __txn_detail);
105	    td != NULL && ndx < maxtxn;
106	    td = SH_TAILQ_NEXT(td, links, __txn_detail), ++ndx) {
107		stats->st_txnarray[ndx].txnid = td->txnid;
108		if (td->parent == INVALID_ROFF)
109			stats->st_txnarray[ndx].parentid = TXN_INVALID;
110		else
111			stats->st_txnarray[ndx].parentid =
112			    ((TXN_DETAIL *)R_ADDR(&mgr->reginfo,
113			    td->parent))->txnid;
114		stats->st_txnarray[ndx].pid = td->pid;
115		stats->st_txnarray[ndx].tid = td->tid;
116		stats->st_txnarray[ndx].lsn = td->begin_lsn;
117		stats->st_txnarray[ndx].read_lsn = td->read_lsn;
118		stats->st_txnarray[ndx].mvcc_ref = td->mvcc_ref;
119		stats->st_txnarray[ndx].status = td->status;
120		stats->st_txnarray[ndx].xa_status = td->xa_status;
121		if (XID_FIELD_IS_SET(td))
122			memcpy(stats->st_txnarray[ndx].xid,
123			    td->xid, sizeof(td->xid));
124		if (td->name != INVALID_ROFF) {
125			(void)strncpy(stats->st_txnarray[ndx].name,
126			    R_ADDR(&mgr->reginfo, td->name),
127			    sizeof(stats->st_txnarray[ndx].name) - 1);
128			stats->st_txnarray[ndx].name[
129			    sizeof(stats->st_txnarray[ndx].name) - 1] = '\0';
130		} else
131			stats->st_txnarray[ndx].name[0] = '\0';
132	}
133
134	__mutex_set_wait_info(env, region->mtx_region,
135	    &stats->st_region_wait, &stats->st_region_nowait);
136	stats->st_regsize = mgr->reginfo.rp->size;
137	if (LF_ISSET(DB_STAT_CLEAR)) {
138		if (!LF_ISSET(DB_STAT_SUBSYSTEM))
139			__mutex_clear(env, region->mtx_region);
140		memset(&region->stat, 0, sizeof(region->stat));
141		region->stat.st_maxtxns = region->maxtxns;
142		region->stat.st_maxnactive =
143		    region->stat.st_nactive = stats->st_nactive;
144		region->stat.st_maxnsnapshot =
145		    region->stat.st_nsnapshot = stats->st_nsnapshot;
146	}
147
148	TXN_SYSTEM_UNLOCK(env);
149
150	*statp = stats;
151	return (0);
152}
153
154/*
155 * __txn_stat_print_pp --
156 *	DB_ENV->txn_stat_print pre/post processing.
157 *
158 * PUBLIC: int __txn_stat_print_pp __P((DB_ENV *, u_int32_t));
159 */
160int
161__txn_stat_print_pp(dbenv, flags)
162	DB_ENV *dbenv;
163	u_int32_t flags;
164{
165	DB_THREAD_INFO *ip;
166	ENV *env;
167	int ret;
168
169	env = dbenv->env;
170
171	ENV_REQUIRES_CONFIG(env,
172	    env->tx_handle, "DB_ENV->txn_stat_print", DB_INIT_TXN);
173
174	if ((ret = __db_fchk(env, "DB_ENV->txn_stat_print",
175	    flags, DB_STAT_ALL | DB_STAT_CLEAR)) != 0)
176		return (ret);
177
178	ENV_ENTER(env, ip);
179	REPLICATION_WRAP(env, (__txn_stat_print(env, flags)), 0, ret);
180	ENV_LEAVE(env, ip);
181	return (ret);
182}
183
184/*
185 * __txn_stat_print
186 *	ENV->txn_stat_print method.
187 *
188 * PUBLIC: int  __txn_stat_print __P((ENV *, u_int32_t));
189 */
190int
191__txn_stat_print(env, flags)
192	ENV *env;
193	u_int32_t flags;
194{
195	u_int32_t orig_flags;
196	int ret;
197
198	orig_flags = flags;
199	LF_CLR(DB_STAT_CLEAR | DB_STAT_SUBSYSTEM);
200	if (flags == 0 || LF_ISSET(DB_STAT_ALL)) {
201		ret = __txn_print_stats(env, orig_flags);
202		if (flags == 0 || ret != 0)
203			return (ret);
204	}
205
206	if (LF_ISSET(DB_STAT_ALL) &&
207	    (ret = __txn_print_all(env, orig_flags)) != 0)
208		return (ret);
209
210	return (0);
211}
212
213/*
214 * __txn_print_stats --
215 *	Display default transaction region statistics.
216 */
217static int
218__txn_print_stats(env, flags)
219	ENV *env;
220	u_int32_t flags;
221{
222	DB_ENV *dbenv;
223	DB_MSGBUF mb;
224	DB_TXN_ACTIVE *txn;
225	DB_TXN_STAT *sp;
226	u_int32_t i;
227	int ret;
228	char buf[DB_THREADID_STRLEN], time_buf[CTIME_BUFLEN];
229
230	dbenv = env->dbenv;
231
232	if ((ret = __txn_stat(env, &sp, flags)) != 0)
233		return (ret);
234
235	if (LF_ISSET(DB_STAT_ALL))
236		__db_msg(env, "Default transaction region information:");
237	__db_msg(env, "%lu/%lu\t%s",
238	    (u_long)sp->st_last_ckp.file, (u_long)sp->st_last_ckp.offset,
239	    sp->st_last_ckp.file == 0 ?
240	    "No checkpoint LSN" : "File/offset for last checkpoint LSN");
241	if (sp->st_time_ckp == 0)
242		__db_msg(env, "0\tNo checkpoint timestamp");
243	else
244		__db_msg(env, "%.24s\tCheckpoint timestamp",
245		    __os_ctime(&sp->st_time_ckp, time_buf));
246	__db_msg(env, "%#lx\tLast transaction ID allocated",
247	    (u_long)sp->st_last_txnid);
248	__db_dl(env, "Maximum number of active transactions configured",
249	    (u_long)sp->st_maxtxns);
250	__db_dl(env, "Active transactions", (u_long)sp->st_nactive);
251	__db_dl(env,
252	    "Maximum active transactions", (u_long)sp->st_maxnactive);
253	__db_dl(env,
254	    "Number of transactions begun", (u_long)sp->st_nbegins);
255	__db_dl(env,
256	    "Number of transactions aborted", (u_long)sp->st_naborts);
257	__db_dl(env,
258	    "Number of transactions committed", (u_long)sp->st_ncommits);
259	__db_dl(env, "Snapshot transactions", (u_long)sp->st_nsnapshot);
260	__db_dl(env, "Maximum snapshot transactions",
261	    (u_long)sp->st_maxnsnapshot);
262	__db_dl(env,
263	    "Number of transactions restored", (u_long)sp->st_nrestores);
264
265	__db_dlbytes(env, "Transaction region size",
266	    (u_long)0, (u_long)0, (u_long)sp->st_regsize);
267	__db_dl_pct(env,
268	    "The number of region locks that required waiting",
269	    (u_long)sp->st_region_wait, DB_PCT(sp->st_region_wait,
270	    sp->st_region_wait + sp->st_region_nowait), NULL);
271
272	qsort(sp->st_txnarray,
273	    sp->st_nactive, sizeof(sp->st_txnarray[0]), __txn_compare);
274	__db_msg(env, "Active transactions:");
275	DB_MSGBUF_INIT(&mb);
276	for (i = 0; i < sp->st_nactive; ++i) {
277		txn = &sp->st_txnarray[i];
278		__db_msgadd(env, &mb,
279	    "\t%lx: %s; pid/thread %s; begin LSN: file/offset %lu/%lu",
280		    (u_long)txn->txnid, __txn_status(txn),
281		    dbenv->thread_id_string(dbenv, txn->pid, txn->tid, buf),
282		    (u_long)txn->lsn.file, (u_long)txn->lsn.offset);
283		if (txn->parentid != 0)
284			__db_msgadd(env, &mb,
285			    "; parent: %lx", (u_long)txn->parentid);
286		if (!IS_MAX_LSN(txn->read_lsn))
287			__db_msgadd(env, &mb, "; read LSN: %lu/%lu",
288			    (u_long)txn->read_lsn.file,
289			    (u_long)txn->read_lsn.offset);
290		if (txn->mvcc_ref != 0)
291			__db_msgadd(env, &mb,
292			    "; mvcc refcount: %lu", (u_long)txn->mvcc_ref);
293		if (txn->name[0] != '\0')
294			__db_msgadd(env, &mb, "; \"%s\"", txn->name);
295		if (XID_FIELD_IS_SET(txn))
296			__txn_gid(env, &mb, txn);
297		DB_MSGBUF_FLUSH(env, &mb);
298	}
299
300	__os_ufree(env, sp);
301
302	return (0);
303}
304
305/*
306 * __txn_print_all --
307 *	Display debugging transaction region statistics.
308 */
309static int
310__txn_print_all(env, flags)
311	ENV *env;
312	u_int32_t flags;
313{
314	static const FN fn[] = {
315		{ TXN_IN_RECOVERY,	"TXN_IN_RECOVERY" },
316		{ 0,			NULL }
317	};
318	DB_TXNMGR *mgr;
319	DB_TXNREGION *region;
320	char time_buf[CTIME_BUFLEN];
321
322	mgr = env->tx_handle;
323	region = mgr->reginfo.primary;
324
325	TXN_SYSTEM_LOCK(env);
326
327	__db_print_reginfo(env, &mgr->reginfo, "Transaction", flags);
328
329	__db_msg(env, "%s", DB_GLOBAL(db_line));
330	__db_msg(env, "DB_TXNMGR handle information:");
331	__mutex_print_debug_single(env, "DB_TXNMGR mutex", mgr->mutex, flags);
332	__db_dl(env,
333	    "Number of transactions discarded", (u_long)mgr->n_discards);
334
335	__db_msg(env, "%s", DB_GLOBAL(db_line));
336	__db_msg(env, "DB_TXNREGION handle information:");
337	__mutex_print_debug_single(
338	    env, "DB_TXNREGION region mutex", region->mtx_region, flags);
339	STAT_ULONG("Maximum number of active txns", region->maxtxns);
340	STAT_HEX("Last transaction ID allocated", region->last_txnid);
341	STAT_HEX("Current maximum unused ID", region->cur_maxid);
342
343	__mutex_print_debug_single(
344	    env, "checkpoint mutex", region->mtx_ckp, flags);
345	STAT_LSN("Last checkpoint LSN", &region->last_ckp);
346	__db_msg(env,
347	    "%.24s\tLast checkpoint timestamp",
348	    region->time_ckp == 0 ? "0" :
349	    __os_ctime(&region->time_ckp, time_buf));
350
351	__db_prflags(env, NULL, region->flags, fn, NULL, "\tFlags");
352
353	__db_msg(env, "%s", DB_GLOBAL(db_line));
354	__db_msg(env, "XA information:");
355	STAT_LONG("XA RMID", env->xa_rmid);
356	/*
357	 * XXX
358	 * Display list of XA transactions in the ENV handle.
359	 */
360
361	TXN_SYSTEM_UNLOCK(env);
362
363	return (0);
364}
365
366static char *
367__txn_status(txn)
368	DB_TXN_ACTIVE *txn;
369{
370	switch (txn->xa_status) {
371	case 0:
372		switch (txn->status) {
373		case TXN_ABORTED:
374			return ("aborted");
375		case TXN_COMMITTED:
376			return ("committed");
377		case TXN_PREPARED:
378			return ("prepared");
379		case TXN_RUNNING:
380			return ("running");
381		default:
382			break;
383		}
384		break;
385	case TXN_XA_ABORTED:
386		return ("XA aborted");
387	case TXN_XA_DEADLOCKED:
388		return ("XA deadlocked");
389	case TXN_XA_ENDED:
390		return ("XA ended");
391	case TXN_XA_PREPARED:
392		return ("XA prepared");
393	case TXN_XA_STARTED:
394		return ("XA started");
395	case TXN_XA_SUSPENDED:
396		return ("XA suspended");
397	default:
398		break;
399	}
400	return ("unknown state");
401}
402
403static void
404__txn_gid(env, mbp, txn)
405	ENV *env;
406	DB_MSGBUF *mbp;
407	DB_TXN_ACTIVE *txn;
408{
409	u_int32_t v, *xp;
410	u_int i;
411	int cnt;
412
413	__db_msgadd(env, mbp, "\n\tGID/XID:");
414	for (cnt = 0, xp = (u_int32_t *)txn->xid, i = 0;;) {
415		memcpy(&v, xp++, sizeof(u_int32_t));
416		__db_msgadd(env, mbp, "%#lx ", (u_long)v);
417		if ((i += sizeof(u_int32_t)) >= DB_XIDDATASIZE)
418			break;
419		if (++cnt == 4) {
420			DB_MSGBUF_FLUSH(env, mbp);
421			__db_msgadd(env, mbp, "\t\t");
422			cnt = 0;
423		}
424	}
425}
426
427static int
428__txn_compare(a1, b1)
429	const void *a1, *b1;
430{
431	const DB_TXN_ACTIVE *a, *b;
432
433	a = a1;
434	b = b1;
435
436	if (a->txnid > b->txnid)
437		return (1);
438	if (a->txnid < b->txnid)
439		return (-1);
440	return (0);
441}
442
443#else /* !HAVE_STATISTICS */
444
445int
446__txn_stat_pp(dbenv, statp, flags)
447	DB_ENV *dbenv;
448	DB_TXN_STAT **statp;
449	u_int32_t flags;
450{
451	COMPQUIET(statp, NULL);
452	COMPQUIET(flags, 0);
453
454	return (__db_stat_not_built(dbenv->env));
455}
456
457int
458__txn_stat_print_pp(dbenv, flags)
459	DB_ENV *dbenv;
460	u_int32_t flags;
461{
462	COMPQUIET(flags, 0);
463
464	return (__db_stat_not_built(dbenv->env));
465}
466#endif
467