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