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, ®ion->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(®ion->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(®ion->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", ®ion->last_ckp); 342 __db_msg(env, 343 "%.24s\tLast checkpoint timestamp", 344 region->time_ckp == 0 ? "0" : 345 __os_ctime(®ion->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