/* * ovdb_stat.c * print information about ovdb database */ #include "config.h" #include "clibrary.h" #include #include #include #include #include "inn/innconf.h" #include "inn/messages.h" #include "libinn.h" #include "ov.h" #include "paths.h" #include "storage.h" #include "../storage/ovdb/ovdb.h" #include "../storage/ovdb/ovdb-private.h" #ifndef USE_BERKELEY_DB int main(int argc UNUSED, char **argv UNUSED) { die("BerkeleyDB support not compiled"); } #else /* USE_BERKELEY_DB */ static int signalled = 0; static void sigfunc(int signum UNUSED) { signalled = 1; } static int html = 0; typedef enum { END, INT32, /* 'a' points to u_int32_t */ HEX32, /* 'a' printed in hex */ DIFF32, /* 'a' - 'b' - 'c' */ PCT32, /* 100 * 'a' / ('a' + 'b') */ FF, /* 'a' = freebytes, 'b' = npages, 'c' = pagesize */ BYTES, /* 'a' = bytes, 'b' = mbytes, 'c' = gbytes */ MODE, /* 'a' points to int, printed as octal mode */ TIME, /* 'a' points to time_t, printed as date/time */ LSN, /* 'a' points to DB_LSN */ STR, /* 'a' points to char* */ SIZE /* 'a' points to size_t */ } DATATYPE; struct datatab { DATATYPE type; ssize_t a; ssize_t b; ssize_t c; const char *desc; }; static void display_heading(const char *str) { if(html) printf("

%s

\n", str); else printf("%s\n", str); } static void getval(int i, void *p, struct datatab *tab, char *val, char *sufx) { int mode = 0; u_int32_t a = 0, b = 0, c = 0, bytes = 0, mbytes = 0, gbytes = 0; char *cp = p; char *tmp = NULL; time_t tm = 0; size_t sz = 0; DB_LSN *dl = NULL; val[0] = 0; sufx[0] = 0; switch(tab[i].type) { case INT32: /* 'a' points to u_int32_t */ memcpy(&a, cp + tab[i].a, sizeof(a)); sprintf(val, "%u", a); break; case HEX32: /* 'a' printed in hex */ memcpy(&a, cp + tab[i].a, sizeof(a)); sprintf(val, "%x", a); break; case DIFF32: /* 'a' - 'b' - 'c' */ memcpy(&a, cp + tab[i].a, sizeof(a)); memcpy(&b, cp + tab[i].b, sizeof(b)); if(tab[i].c != -1) { memcpy(&c, cp + tab[i].c, sizeof(c)); sprintf(val, "%d", a - b - c); } else { sprintf(val, "%d", a - b); } break; case PCT32: /* 100 * 'a' / ('a' + 'b') */ memcpy(&a, cp + tab[i].a, sizeof(a)); memcpy(&b, cp + tab[i].b, sizeof(b)); sprintf(val, "%.0f", (double) a / (a + b) * 100.0); strcpy(sufx, "%"); break; case FF: /* 'a' = freebytes, 'b' = npages, 'c' = pagesize */ memcpy(&a, cp + tab[i].a, sizeof(a)); memcpy(&b, cp + tab[i].b, sizeof(b)); memcpy(&c, cp + tab[i].c, sizeof(c)); if(b == 0) { sprintf(val, "%.0f", 0.0); } else { sprintf(val, "%.0f", (double)((b * c) - a) / (b * c) * 100); } strcpy(sufx, "%"); break; case BYTES: /* 'a' = bytes, 'b' = mbytes, 'c' = gbytes */ if(tab[i].a != -1) memcpy(&bytes, cp + tab[i].a, sizeof(bytes)); else bytes = 0; if(tab[i].b != -1) memcpy(&mbytes, cp + tab[i].b, sizeof(mbytes)); else mbytes = 0; if(tab[i].c != -1) memcpy(&gbytes, cp + tab[i].c, sizeof(gbytes)); else gbytes = 0; if(gbytes > 0 || mbytes > 0) { mbytes += gbytes * 1024; if(bytes > (1024*1024)) mbytes += bytes / (1024*1024); sprintf(val, "%u", mbytes); strcpy(sufx, "MB"); } else { sprintf(val, "%u", bytes); } break; case MODE: /* 'a' points to int, printed as octal mode */ memcpy(&mode, cp + tab[i].a, sizeof(mode)); sprintf(val, "%04o", mode); break; case TIME: /* 'a' points to time_t, printed as date/time */ memcpy(&tm, cp + tab[i].a, sizeof(tm)); if(tm == 0) { strcpy(val, "none"); } else { strftime(val, SMBUF, "%Y-%m-%d %T %Z", localtime(&tm)); } break; case LSN: /* 'a' points to DB_LSN */ dl = (DB_LSN *)(cp + tab[i].a); if(dl->file == 0) { strcpy(val, "none"); } else { sprintf(val, "%u/%u", dl->file, dl->offset); } break; case STR: /* 'a' points to char* */ memcpy(&tmp, cp + tab[i].a, sizeof(tmp)); strcpy(val, tmp); break; case SIZE: /* 'a' points to size_t */ memcpy(&sz, cp + tab[i].a, sizeof(sz)); sprintf(val, "%lu", (unsigned long) sz); break; case END: break; } } static char *myctime(time_t *tm) { static char val[SMBUF]; strftime(val, SMBUF, "%Y-%m-%d %T %Z", localtime(tm)); return val; } static void display_data(void *p, struct datatab *tab) { int i; char val[SMBUF], sufx[SMBUF]; if(html) puts(""); for(i = 0; tab[i].type != END; i++) { getval(i, p, tab, val, sufx); if(html) printf("
%s%s%s\n", val, sufx, tab[i].desc); else printf("%16s%-2s %s\n", val, sufx, tab[i].desc); } if(html) puts("

"); } static void start_table(const char *label, struct datatab *tab) { int i; if(html) { printf("

%s

\n", label); puts("\n"); for(i = 0; tab[i].type != END; i++) printf(""); for(i = 0; tab[i].type != END; i++) { getval(i, p, tab, val, sufx); printf("
%s\n", tab[i].desc); } } static void display_row(void *p, struct datatab *tab) { int i; char val[SMBUF], sufx[SMBUF]; if(html) { puts("
%s%s\n", val, sufx); } } else { puts("---------------------------------------------"); display_data(p, tab); } } static void end_table(void) { if(html) puts("

"); } #define OFFSETOF(type, f) ((char *)&(((type *)0)->f) - (char *)0) #define F(f) OFFSETOF(DB_LOCK_STAT, f) static struct datatab LOCK_tab[] = { #if DB_VERSION_MAJOR >= 3 { INT32, #if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1) F(st_id), #else F(st_lastid), #endif -1, -1, "Last allocated locker ID" }, #endif { INT32, F(st_maxlocks), -1, -1, "Maximum number of locks possible" }, #if DB_VERSION_MAJOR >= 4 || (DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR >= 2) { INT32, F(st_maxlockers), -1, -1, "Maximum number of lockers possible" }, { INT32, F(st_maxobjects), -1, -1, "Maximum number of objects possible" }, #endif { INT32, F(st_nmodes), -1, -1, "Lock modes" }, #if DB_VERSION_MAJOR >= 4 || (DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR >= 2) { INT32, F(st_nlocks), -1, -1, "Current locks" }, { INT32, F(st_maxnlocks), -1, -1, "Maximum locks" }, #endif { INT32, F(st_nlockers), -1, -1, "Current lockers" }, #if DB_VERSION_MAJOR >= 3 { INT32, F(st_maxnlockers), -1, -1, "Maximum lockers" }, #else { INT32, F(st_numobjs), -1, -1, "Lock objects" }, #endif #if DB_VERSION_MAJOR >= 4 || (DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR >= 2) { INT32, F(st_nobjects), -1, -1, "Current objects" }, { INT32, F(st_maxnobjects), -1, -1, "Maximum objects" }, #endif { INT32, F(st_nconflicts), -1, -1, "Lock conflicts" }, { INT32, F(st_nrequests), -1, -1, "Lock requests" }, { INT32, F(st_nreleases), -1, -1, "Lock releases" }, { DIFF32, F(st_nrequests), F(st_nreleases), F(st_ndeadlocks), "Outstanding locks" }, #if DB_VERSION_MAJOR >= 4 || (DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR > 0) { INT32, F(st_nnowaits), -1, -1, "Lock requests that would have waited" }, #endif { INT32, F(st_ndeadlocks), -1, -1, "Deadlocks" }, #if DB_VERSION_MAJOR >= 4 { INT32, F(st_nlocktimeouts), -1, -1, "Lock timeouts" }, { INT32, F(st_ntxntimeouts), -1, -1, "Transaction timeouts" }, #endif { INT32, F(st_region_nowait), -1, -1, "Region locks granted without waiting" }, { INT32, F(st_region_wait), -1, -1, "Region locks granted after waiting" }, { BYTES, F(st_regsize), -1, -1, "Lock region size" }, { END, -1, -1, -1, NULL } }; static int display_lock(void) { DB_LOCK_STAT *sp; #if DB_VERSION_MAJOR == 2 if(lock_stat(OVDBenv->lk_info, &sp, NULL) != 0) #elif DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR <= 2 if(lock_stat(OVDBenv, &sp, NULL) != 0) #elif DB_VERSION_MAJOR == 3 if(lock_stat(OVDBenv, &sp) != 0) #else if(OVDBenv->lock_stat(OVDBenv, &sp, 0) != 0) #endif return 1; display_heading("Lock Region Statistics"); display_data(sp, LOCK_tab); free(sp); return 0; } #undef F #define F(f) OFFSETOF(DB_LOG_STAT, f) static struct datatab LOG_tab[] = { { HEX32, F(st_magic), -1, -1, "Log magic number" }, { INT32, F(st_version), -1, -1, "Log version number" }, { MODE, F(st_mode), -1, -1, "Log file mode" }, #if DB_VERSION_MAJOR >= 3 { BYTES, F(st_lg_bsize), -1, -1, "Log record cache size" }, #endif #if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1) { BYTES, F(st_lg_size), -1, -1, "The current log file size" }, #else { BYTES, F(st_lg_max), -1, -1, "Max log file size" }, #endif { BYTES, F(st_w_bytes), F(st_w_mbytes), -1, "Log bytes written" }, { BYTES, F(st_wc_bytes), F(st_wc_mbytes), -1, "Log bytes written since last checkpoint" }, { INT32, F(st_wcount), -1, -1, "Total log writes" }, #if DB_VERSION_MAJOR >= 3 { INT32, F(st_wcount_fill), -1, -1, "Total log writes due to overflow" }, #endif { INT32, F(st_scount), -1, -1, "Total log flushes" }, { INT32, F(st_region_nowait), -1, -1, "Region locks granted without waiting" }, { INT32, F(st_region_wait), -1, -1, "Region locks granted after waiting" }, { INT32, F(st_cur_file), -1, -1, "Current log file number" }, { INT32, F(st_cur_offset), -1, -1, "Current log file offset" }, #if DB_VERSION_MAJOR >= 4 || (DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR >= 3) { INT32, F(st_disk_file), -1, -1, "Known on disk log file number" }, { INT32, F(st_disk_offset), -1, -1, "Known on disk log file offset" }, #endif { BYTES, F(st_regsize), -1, -1, "Log region size" }, #if DB_VERSION_MAJOR >= 4 #if DB_VERSION_MINOR < 1 { INT32, F(st_flushcommit), -1, -1, "Flushes containing a commit"}, #endif { INT32, F(st_maxcommitperflush), -1, -1, "Max number of commits in a flush"}, { INT32, F(st_mincommitperflush), -1, -1, "Min number of commits in a flush"}, #endif { END, -1, -1, -1, NULL } }; static int display_log(void) { DB_LOG_STAT *sp; #if DB_VERSION_MAJOR == 2 if(log_stat(OVDBenv->lg_info, &sp, NULL) != 0) #elif DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR <= 2 if(log_stat(OVDBenv, &sp, NULL) != 0) #elif DB_VERSION_MAJOR == 3 if(log_stat(OVDBenv, &sp) != 0) #else if(OVDBenv->log_stat(OVDBenv, &sp, 0) != 0) #endif return 1; display_heading("Log Region Statistics"); display_data(sp, LOG_tab); free(sp); return 0; } #undef F #define F(f) OFFSETOF(DB_MPOOL_STAT, f) static struct datatab MEM_tab[] = { { INT32, F(st_cache_hit), -1, -1, "Cache hits"}, { INT32, F(st_cache_miss), -1, -1, "Cache misses"}, { PCT32, F(st_cache_hit), F(st_cache_miss), -1, "Cache hit percentage"}, #if DB_VERSION_MAJOR == 2 { INT32, F(st_cachesize), -1, -1, "Total cache size"}, { INT32, F(st_regsize), -1, -1, "Pool region size"}, #else { BYTES, F(st_bytes), -1, F(st_gbytes), "Total cache size"}, #if DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR == 0 { INT32, F(st_regsize), -1, -1, "Pool region size"}, #else { INT32, F(st_ncache), -1, -1, "Number of caches"}, { INT32, F(st_regsize), -1, -1, "Pool individual cache size"}, #endif #endif { INT32, F(st_map), -1, -1, "Memory mapped pages"}, { INT32, F(st_page_create), -1, -1, "Pages created in the cache"}, { INT32, F(st_page_in), -1, -1, "Pages read into the cache"}, { INT32, F(st_page_out), -1, -1, "Pages written from the cache to the backing file"}, { INT32, F(st_ro_evict), -1, -1, "Clean pages forced from the cache"}, { INT32, F(st_rw_evict), -1, -1, "Dirty pages forced from the cache"}, { INT32, F(st_hash_buckets), -1, -1, "Hash buckets used for page location"}, { INT32, F(st_hash_searches), -1, -1, "Total hash chain searches"}, { INT32, F(st_hash_longest), -1, -1, "Longest hash chain searched"}, { INT32, F(st_hash_examined), -1, -1, "Total hash entries searched"}, { INT32, F(st_page_trickle), -1, -1, "Dirty buffers written by trickle-sync thread"}, { INT32, F(st_page_clean), -1, -1, "Current clean buffer count"}, { INT32, F(st_page_dirty), -1, -1, "Current dirty buffer count"}, { INT32, F(st_region_nowait), -1, -1, "Region locks granted without waiting"}, { INT32, F(st_region_wait), -1, -1, "Region locks granted after waiting"}, { END, -1, -1, -1, NULL } }; #undef F #define F(f) OFFSETOF(DB_MPOOL_FSTAT, f) static struct datatab MEMF_tab[] = { { STR, F(file_name), -1, -1, "Database"}, { SIZE, F(st_pagesize), -1, -1, "Page size"}, { INT32, F(st_cache_hit), -1, -1, "Cache hits"}, { INT32, F(st_cache_miss), -1, -1, "Cache misses"}, { PCT32, F(st_cache_hit), F(st_cache_miss), -1, "Cache hit percentage"}, { INT32, F(st_map), -1, -1, "Memory mapped pages"}, { INT32, F(st_page_create), -1, -1, "Pages created in the cache"}, { INT32, F(st_page_in), -1, -1, "Pages read into the cache"}, { INT32, F(st_page_out), -1, -1, "Pages written from the cache to the backing file"}, { END, -1, -1, -1, NULL } }; static int display_mem(int all) { DB_MPOOL_FSTAT **fsp; DB_MPOOL_STAT *gsp; #if DB_VERSION_MAJOR == 2 if(memp_stat(OVDBenv->mp_info, &gsp, &fsp, NULL) != 0) #elif DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR <= 2 if(memp_stat(OVDBenv, &gsp, &fsp, NULL) != 0) #elif DB_VERSION_MAJOR == 3 if(memp_stat(OVDBenv, &gsp, &fsp) != 0) #else if(OVDBenv->memp_stat(OVDBenv, &gsp, &fsp, 0) != 0) #endif return 1; display_heading("Memory Pool Statistics"); display_data(gsp, MEM_tab); if(all) { DB_MPOOL_FSTAT **p = fsp; start_table("Per-database Memory Pool Statistics", MEMF_tab); for(; p != NULL && *p != NULL; ++p) { display_row(*p, MEMF_tab); } end_table(); } free(fsp); free(gsp); return 0; } static int txn_compare(const void *a, const void *b) { if (((const DB_TXN_ACTIVE *)a)->txnid > ((const DB_TXN_ACTIVE *)b)->txnid) return 1; if (((const DB_TXN_ACTIVE *)a)->txnid < ((const DB_TXN_ACTIVE *)b)->txnid) return -1; return 0; } #undef F #define F(f) OFFSETOF(DB_TXN_STAT, f) static struct datatab TXN_tab[] = { { LSN, F(st_last_ckp), -1, -1, "File/offset for last checkpoint LSN" }, #if DB_VERSION_MAJOR < 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR < 1) { LSN, F(st_pending_ckp), -1, -1, "File/offset for last pending checkpoint LSN" }, #endif { TIME, F(st_time_ckp), -1, -1, "Checkpoint timestamp" }, { HEX32, F(st_last_txnid), -1, -1, "Last transaction ID allocated" }, { INT32, F(st_maxtxns), -1, -1, "Maximum active transactions possible" }, { INT32, F(st_nactive), -1, -1, "Active transactions" }, #if DB_VERSION_MAJOR >= 4 || (DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR >= 3) { INT32, F(st_nrestores), -1, -1, "Restored transactions after recovery" }, #endif #if DB_VERSION_MAJOR >= 3 { INT32, F(st_maxnactive), -1, -1, "Maximum active transactions" }, #endif { INT32, F(st_nbegins), -1, -1, "Transactions started" }, { INT32, F(st_ncommits), -1, -1, "Transactions committed" }, { INT32, F(st_naborts), -1, -1, "Transactions aborted" }, { INT32, F(st_region_nowait), -1, -1, "Region locks granted without waiting"}, { INT32, F(st_region_wait), -1, -1, "Region locks granted after waiting"}, { BYTES, F(st_regsize), -1, -1, "Transaction region size" }, { END, -1, -1, -1, NULL } }; #undef F #define F(f) OFFSETOF(DB_TXN_ACTIVE, f) static struct datatab TXNA_tab[] = { { INT32, F(txnid), -1, -1, "Transaction ID" }, #if DB_VERSION_MAJOR >= 3 { INT32, F(parentid), -1, -1, "Parent Transaction ID" }, #endif { LSN, F(lsn), -1, -1, "Initial LSN file/offset" }, { END, -1, -1, -1, NULL } }; static int display_txn(void) { DB_TXN_STAT *sp; u_int32_t i; #if DB_VERSION_MAJOR == 2 if(txn_stat(OVDBenv->tx_info, &sp, NULL) != 0) #elif DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR <= 2 if(txn_stat(OVDBenv, &sp, NULL) != 0) #elif DB_VERSION_MAJOR == 3 if(txn_stat(OVDBenv, &sp) != 0) #else if(OVDBenv->txn_stat(OVDBenv, &sp, 0) != 0) #endif return 1; display_heading("Transaction Region Statistics"); display_data(sp, TXN_tab); if(sp->st_nactive) { qsort(sp->st_txnarray, sp->st_nactive, sizeof(sp->st_txnarray[0]), txn_compare); start_table("Active Transactions", TXNA_tab); for (i = 0; i < sp->st_nactive; ++i) display_row(&(sp->st_txnarray[i]), TXNA_tab); end_table(); } free(sp); return 0; } static int display_ver(void) { if(html) puts("

"); printf("ovdb data version: %d\n", DATA_VERSION); if(html) puts("
"); printf("BerkeleyDB version: %s\n", db_version(NULL,NULL,NULL)); if(html) puts("

"); return 0; } #undef F #define F(f) OFFSETOF(DB_BTREE_STAT, f) static struct datatab BTREE_tab[] = { { HEX32, F(bt_magic), -1, -1, "Btree magic number" }, { INT32, F(bt_version), -1, -1, "Btree version number" }, { INT32, F(bt_minkey), -1, -1, "Minimum keys per page (minkey)" }, { INT32, F(bt_pagesize), -1, -1, "Database page size" }, { INT32, F(bt_levels), -1, -1, "Levels in the tree" }, #if DB_VERSION_MAJOR == 2 || (DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR == 0) { INT32, F(bt_nrecs), -1, -1, "Keys in the tree" }, #else { INT32, F(bt_nkeys), -1, -1, "Unique keys in the tree" }, { INT32, F(bt_ndata), -1, -1, "Data items in the tree" }, #endif { INT32, F(bt_int_pg), -1, -1, "Tree internal pages" }, { BYTES, F(bt_int_pgfree), -1, -1, "Bytes free in internal pages" }, { FF, F(bt_int_pgfree), F(bt_int_pg), F(bt_pagesize), "Internal page fill factor" }, { INT32, F(bt_leaf_pg), -1, -1, "Tree leaf pages" }, { BYTES, F(bt_leaf_pgfree), -1, -1, "Bytes free in leaf pages" }, { FF, F(bt_leaf_pgfree), F(bt_leaf_pg), F(bt_pagesize), "Leaf page fill factor" }, { INT32, F(bt_dup_pg), -1, -1, "Tree duplicate pages" }, { BYTES, F(bt_dup_pgfree), -1, -1, "Bytes free in duplicate pages" }, { FF, F(bt_dup_pgfree), F(bt_dup_pg), F(bt_pagesize), "Duplicate page fill factor" }, { INT32, F(bt_over_pg), -1, -1, "Tree overflow pages" }, { BYTES, F(bt_over_pgfree), -1, -1, "Bytes free overflow pages" }, { FF, F(bt_over_pgfree), F(bt_over_pg), F(bt_pagesize), "Overflow page fill factor" }, #if DB_VERSION_MAJOR >= 3 { INT32, F(bt_free), -1, -1, "Pages on the free list" }, #endif { END, -1, -1, -1, NULL } }; static int display_btree(DB *db) { DB_BTREE_STAT *sp; #if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3) if(db->stat(db, NULL, &sp, 0)) #else #if DB_VERSION_MAJOR == 4 || (DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR >= 3) if(db->stat(db, &sp, 0)) #else if(db->stat(db, &sp, NULL, 0)) #endif #endif return 1; display_heading("Btree Statistics"); display_data(sp, BTREE_tab); free(sp); return 0; } #if DB_VERSION_MAJOR >= 3 #undef F #define F(f) OFFSETOF(DB_HASH_STAT, f) static struct datatab HASH_tab[] = { { HEX32, F(hash_magic), -1, -1, "Hash magic number" }, { INT32, F(hash_version), -1, -1, "Hash version number" }, { INT32, F(hash_pagesize), -1, -1, "Database page size" }, #if DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR == 0 { INT32, F(hash_nrecs), -1, -1, "Keys in the database" }, #else { INT32, F(hash_nkeys), -1, -1, "Keys in the database" }, { INT32, F(hash_ndata), -1, -1, "Data items in the database" }, #endif { INT32, F(hash_buckets), -1, -1, "Hash buckets" }, { BYTES, F(hash_bfree), -1, -1, "Bytes free on bucket pages" }, { FF, F(hash_buckets), F(hash_bfree), F(hash_pagesize), "Bucket page fill factor" }, { INT32, F(hash_bigpages), -1, -1, "Overflow pages" }, { BYTES, F(hash_big_bfree), -1, -1, "Bytes free on Overflow pages" }, { FF, F(hash_bigpages), F(hash_big_bfree), F(hash_pagesize), "Overflow page fill factor" }, { INT32, F(hash_overflows), -1, -1, "Bucket overflow pages" }, { BYTES, F(hash_ovfl_free), -1, -1, "Bytes free on bucket overflow pages" }, { FF, F(hash_overflows), F(hash_ovfl_free), F(hash_pagesize), "Bucket overflow page fill factor" }, { INT32, F(hash_dup), -1, -1, "Duplicate pages" }, { BYTES, F(hash_dup_free), -1, -1, "Bytes free in duplicate pages" }, { FF, F(hash_dup), F(hash_dup_free), F(hash_pagesize), "Duplicate page fill factor" }, { INT32, F(hash_free), -1, -1, "Pages on the free list"}, { END, -1, -1, -1, NULL } }; #endif static int display_hash(DB *db UNUSED) { #if DB_VERSION_MAJOR == 2 printf("Hash statistics not available.\n"); return 0; #else DB_HASH_STAT *sp; #if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3) if(db->stat(db, NULL, &sp, 0)) #else #if DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR <= 2 if(db->stat(db, &sp, NULL, 0)) #else if(db->stat(db, &sp, 0)) #endif #endif return 1; display_heading("Hash Information"); display_data(sp, HASH_tab); return 0; #endif } static int display_db(char *dbfile) { int ret; DB *db; #if DB_VERSION_MAJOR == 2 if(db_open(dbfile, DB_UNKNOWN, DB_RDONLY, 0, OVDBenv, NULL, &db)) return 1; #else if(db_create(&db, OVDBenv, 0)) return 1; #if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1) if(db->open(db, NULL, dbfile, NULL, DB_UNKNOWN, DB_RDONLY, 0)) #else if(db->open(db, dbfile, NULL, DB_UNKNOWN, DB_RDONLY, 0)) #endif return 1; #endif switch(db->type) { case DB_BTREE: case DB_RECNO: ret = display_btree(db); break; case DB_HASH: ret = display_hash(db); break; default: ret = 1; break; } db->close(db, 0); return ret; } static int parse_artrange(char *str, ARTNUM *start, ARTNUM *stop) { char *c; int i; c = strchr(str, '-'); if(c == NULL) { i = atoi(str); if(i == 0) { return 1; } *start = *stop = i; return 0; } if(c == str) { *start = 0; *stop = atoi(str+1); return (*stop == 0); } if (strlen(str) == (size_t)(c - str + 1)) { *start = atoi(str); *stop = 0xffffffff; return (*start == 0); } *start = atoi(str); *stop = atoi(c+1); if(*start == 0 || *stop == 0 || *start > *stop) return 1; return 0; } static void htwrite(char *data, int len) { int i; for(i = 0; i < len; i++) { switch(data[i]) { case '<': case '>': case '&': printf("&#%d;", (int)data[i]); break; default: putchar(data[i]); } } } int main(int argc, char *argv[]) { void *s; ARTNUM a, start=0, stop=0, low, high; char *data, *disp_db = NULL; int len, c, count, flag, lowi, highi; int getgs=0, getcount=0, getinfo=0, err=0, gotone=0; int disp_lock=0, disp_log=0, disp_mem=0, disp_mem_all=0, disp_txn=0, disp_ver=0; int needng=0, o; openlog("ovdb_stat", L_OPENLOG_FLAGS | LOG_PID, LOG_INN_PROG); message_program_name = "ovdb_stat"; if (!innconf_read(NULL)) exit(1); if(!ovdb_check_user()) die("command must be run as user " NEWSUSER); if(!ovdb_getlock(OVDB_LOCK_ADMIN)) sysdie("cannot lock database"); if(!ovdb_open(OV_READ|OVDB_SERVER)) sysdie("cannot open overview; check syslog for OVDB messages"); xsignal(SIGINT, sigfunc); xsignal(SIGTERM, sigfunc); xsignal(SIGHUP, sigfunc); while((c = getopt(argc, argv, ":Hgcir:klmMtvd:")) != -1) { switch(c) { case 'H': html = 1; break; case 'g': getgs = 1; needng = 1; gotone++; break; case 'c': getcount = 1; needng = 1; gotone++; break; case 'i': getinfo = 1; needng = 1; gotone++; break; case 'r': if(parse_artrange(optarg, &start, &stop)) err++; needng = 1; gotone++; break; case 'k': disp_lock = 1; gotone++; break; case 'l': disp_log = 1; gotone++; break; case 'm': disp_mem = 1; gotone++; break; case 'M': disp_mem = 1; disp_mem_all = 1; gotone++; break; case 't': disp_txn = 1; gotone++; break; case 'v': disp_ver = 1; gotone++; break; case 'd': disp_db = optarg; gotone++; break; case ':': warn("option -%c requires an argument", optopt); err++; break; case '?': warn("unrecognized option -%c", optopt); err++; break; } } if(!gotone) { err++; } else if(optind == argc && needng) { warn("missing newsgroup argument(s)"); err++; } if(err) { fprintf(stderr, "\ Usage:\n\ ovdb_stat -Hgci [-r artnum] newsgroup [newsgroup ...]\n\ -H : output in HTML\n\ -g : show groupstats info\n\ -c : show groupstats info by counting actual records\n\ -i : show additional group info\n\ -r artnum-range : retrieve OV records for article number range\n\ \n\ ovdb_stat -Hklmtv [-d ]\n\ -H : output in HTML\n\ -k : Display lock region statistics\n\ -l : Display log region statistics\n\ -m : Display global memory cache statistics\n\ -M : Display all memory cache statistics\n\ -t : Display transaction statistics\n\ -v : Display version information\n\ -d database : Display statistics of specified database\n"); goto out; } if(html) puts("ovdb_stat

"); if(disp_lock) display_lock(); if(disp_log) display_log(); if(disp_mem) display_mem(disp_mem_all); if(disp_txn) display_txn(); if(disp_ver) display_ver(); if(disp_db) display_db(disp_db); if(getgs || getcount || getinfo) { if(html) { puts("\n"); puts(""); if(getgs) puts("
Group"); if(getgs) puts("Groupstats"); if(getcount) puts("Counted"); if(getinfo) puts("StatusCurrentPending"); puts("ExpiredExpire PID
LowHighCountFlag"); if(getcount) puts("LowHighCount"); if(getinfo) puts("FlagsGroupIDDBGroupIDDB"); } for(o = optind ; o < argc; o++) { if(html) printf("
%s", argv[o]); if(getgs) { if(ovdb_groupstats(argv[o], &lowi, &highi, &count, &flag)) { if(html) printf("%d%d%d%c", lowi, highi, count, flag); else printf("%s: groupstats: low: %d, high: %d, count: %d, flag: %c\n", argv[o], lowi, highi, count, flag); } } if(getcount) { low = high = count = 0; s = ovdb_opensearch(argv[o], 1, 0xffffffff); if (s != NULL) { while(ovdb_search(s, &a, NULL, NULL, NULL, NULL)) { if(low == 0 || a < low) low = a; if(a > high) high = a; count++; if(signalled) break; } ovdb_closesearch(s); if(signalled) goto out; if(html) printf("%ld%ld%d", low, high, count); else printf("%s: counted: low: %ld, high: %ld, count: %d\n", argv[o], low, high, count); } } if(getinfo) { int ret; struct groupinfo gi; ret = ovdb_getgroupinfo(argv[o], &gi, false, NULL, 0); if (ret != 0) { warn("%s: ovdb_getgroupinfo error: %s", argv[o], db_strerror(ret)); continue; } if(html) { printf("%s%s%s%s", (gi.status & GROUPINFO_DELETED) ? "D ":"", (gi.status & GROUPINFO_EXPIRING) ? "E ":"", (gi.status & GROUPINFO_MOVING) ? "M":"", (gi.status == 0) ? " ":""); printf("%dov%05d", gi.current_gid, gi.current_db); if(gi.status & GROUPINFO_MOVING) printf("%dov%05d", gi.new_gid, gi.new_db); else printf("  "); if(gi.expired) printf("%s%lu", myctime(&gi.expired), (unsigned long) gi.expiregrouppid); else printf("  "); putchar('\n'); } else { printf("%s: flags: %s%s%s%s\n", argv[o], (gi.status & GROUPINFO_DELETED) ? "DELETED ":"", (gi.status & GROUPINFO_EXPIRING) ? "EXPIRING ":"", (gi.status & GROUPINFO_MOVING) ? "MOVING":"", (gi.status == 0) ? "none":""); printf("%s: gid: %d; Stored in: ov%05d\n", argv[o], gi.current_gid, gi.current_db); if(gi.status & GROUPINFO_MOVING) printf("%s: pending gid: %d; pending db: ov%05d\n", argv[o], gi.new_gid, gi.new_db); if(gi.expired) { printf("%s: last expired: %s\n", argv[o], myctime(&gi.expired)); printf("%s: by process id: %lu\n", argv[o], (unsigned long) gi.expiregrouppid); } } } if(signalled) goto out; } if(html) puts("

"); } if(start || stop) { if(html) puts("

");
	for(o = optind ; o < argc; o++) {
            s = ovdb_opensearch(argv[o], start, stop);
	    if (s != NULL) {
		while(ovdb_search(s, &a, &data, &len, NULL, NULL)) {
		    if(html)
			htwrite(data, len);
		    else
			fwrite(data, len, 1, stdout);
		    if(signalled)
			break;
		}
		ovdb_closesearch(s);
		if(signalled)
		    goto out;
	    }
	    if(signalled)
		goto out;
	}
	if(html)
	    puts("
"); } out: if(html) puts("

"); ovdb_close(); return 0; } #endif /* USE_BERKELEY_DB */