1 /*	$NetBSD: mdb_stat.c,v 1.3 2021/08/14 16:14:57 christos Exp $	*/
2 
3 /* mdb_stat.c - memory-mapped database status tool */
4 /*
5  * Copyright 2011-2021 Howard Chu, Symas Corp.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <unistd.h>
20 #include "lmdb.h"
21 
22 #ifdef	_WIN32
23 #define	Z	"I"
24 #else
25 #define	Z	"z"
26 #endif
27 
prstat(MDB_stat * ms)28 static void prstat(MDB_stat *ms)
29 {
30 #if 0
31 	printf("  Page size: %u\n", ms->ms_psize);
32 #endif
33 	printf("  Tree depth: %u\n", ms->ms_depth);
34 	printf("  Branch pages: %"Z"u\n", ms->ms_branch_pages);
35 	printf("  Leaf pages: %"Z"u\n", ms->ms_leaf_pages);
36 	printf("  Overflow pages: %"Z"u\n", ms->ms_overflow_pages);
37 	printf("  Entries: %"Z"u\n", ms->ms_entries);
38 }
39 
usage(char * prog)40 static void usage(char *prog)
41 {
42 	fprintf(stderr, "usage: %s [-V] [-n] [-e] [-r[r]] [-f[f[f]]] [-a|-s subdb] dbpath\n", prog);
43 	exit(EXIT_FAILURE);
44 }
45 
main(int argc,char * argv[])46 int main(int argc, char *argv[])
47 {
48 	int i, rc;
49 	MDB_env *env;
50 	MDB_txn *txn;
51 	MDB_dbi dbi;
52 	MDB_stat mst;
53 	MDB_envinfo mei;
54 	char *prog = argv[0];
55 	char *envname;
56 	char *subname = NULL;
57 	int alldbs = 0, envinfo = 0, envflags = 0, freinfo = 0, rdrinfo = 0;
58 
59 	if (argc < 2) {
60 		usage(prog);
61 	}
62 
63 	/* -a: print stat of main DB and all subDBs
64 	 * -s: print stat of only the named subDB
65 	 * -e: print env info
66 	 * -f: print freelist info
67 	 * -r: print reader info
68 	 * -n: use NOSUBDIR flag on env_open
69 	 * -V: print version and exit
70 	 * (default) print stat of only the main DB
71 	 */
72 	while ((i = getopt(argc, argv, "Vaefnrs:")) != EOF) {
73 		switch(i) {
74 		case 'V':
75 			printf("%s\n", MDB_VERSION_STRING);
76 			exit(0);
77 			break;
78 		case 'a':
79 			if (subname)
80 				usage(prog);
81 			alldbs++;
82 			break;
83 		case 'e':
84 			envinfo++;
85 			break;
86 		case 'f':
87 			freinfo++;
88 			break;
89 		case 'n':
90 			envflags |= MDB_NOSUBDIR;
91 			break;
92 		case 'r':
93 			rdrinfo++;
94 			break;
95 		case 's':
96 			if (alldbs)
97 				usage(prog);
98 			subname = optarg;
99 			break;
100 		default:
101 			usage(prog);
102 		}
103 	}
104 
105 	if (optind != argc - 1)
106 		usage(prog);
107 
108 	envname = argv[optind];
109 	rc = mdb_env_create(&env);
110 	if (rc) {
111 		fprintf(stderr, "mdb_env_create failed, error %d %s\n", rc, mdb_strerror(rc));
112 		return EXIT_FAILURE;
113 	}
114 
115 	if (alldbs || subname) {
116 		mdb_env_set_maxdbs(env, 4);
117 	}
118 
119 	rc = mdb_env_open(env, envname, envflags | MDB_RDONLY, 0664);
120 	if (rc) {
121 		fprintf(stderr, "mdb_env_open failed, error %d %s\n", rc, mdb_strerror(rc));
122 		goto env_close;
123 	}
124 
125 	if (envinfo) {
126 		(void)mdb_env_stat(env, &mst);
127 		(void)mdb_env_info(env, &mei);
128 		printf("Environment Info\n");
129 		printf("  Map address: %p\n", mei.me_mapaddr);
130 		printf("  Map size: %"Z"u\n", mei.me_mapsize);
131 		printf("  Page size: %u\n", mst.ms_psize);
132 		printf("  Max pages: %"Z"u\n", mei.me_mapsize / mst.ms_psize);
133 		printf("  Number of pages used: %"Z"u\n", mei.me_last_pgno+1);
134 		printf("  Last transaction ID: %"Z"u\n", mei.me_last_txnid);
135 		printf("  Max readers: %u\n", mei.me_maxreaders);
136 		printf("  Number of readers used: %u\n", mei.me_numreaders);
137 	}
138 
139 	if (rdrinfo) {
140 		printf("Reader Table Status\n");
141 		rc = mdb_reader_list(env, (MDB_msg_func *)fputs, stdout);
142 		if (rdrinfo > 1) {
143 			int dead;
144 			mdb_reader_check(env, &dead);
145 			printf("  %d stale readers cleared.\n", dead);
146 			rc = mdb_reader_list(env, (MDB_msg_func *)fputs, stdout);
147 		}
148 		if (!(subname || alldbs || freinfo))
149 			goto env_close;
150 	}
151 
152 	rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn);
153 	if (rc) {
154 		fprintf(stderr, "mdb_txn_begin failed, error %d %s\n", rc, mdb_strerror(rc));
155 		goto env_close;
156 	}
157 
158 	if (freinfo) {
159 		MDB_cursor *cursor;
160 		MDB_val key, data;
161 		size_t pages = 0, *iptr;
162 
163 		printf("Freelist Status\n");
164 		dbi = 0;
165 		rc = mdb_cursor_open(txn, dbi, &cursor);
166 		if (rc) {
167 			fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc));
168 			goto txn_abort;
169 		}
170 		rc = mdb_stat(txn, dbi, &mst);
171 		if (rc) {
172 			fprintf(stderr, "mdb_stat failed, error %d %s\n", rc, mdb_strerror(rc));
173 			goto txn_abort;
174 		}
175 		prstat(&mst);
176 		while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
177 			iptr = data.mv_data;
178 			pages += *iptr;
179 			if (freinfo > 1) {
180 				char *bad = "";
181 				size_t pg, prev;
182 				ssize_t i, j, span = 0;
183 				j = *iptr++;
184 				for (i = j, prev = 1; --i >= 0; ) {
185 					pg = iptr[i];
186 					if (pg <= prev)
187 						bad = " [bad sequence]";
188 					prev = pg;
189 					pg += span;
190 					for (; i >= span && iptr[i-span] == pg; span++, pg++) ;
191 				}
192 				printf("    Transaction %"Z"u, %"Z"d pages, maxspan %"Z"d%s\n",
193 					*(size_t *)key.mv_data, j, span, bad);
194 				if (freinfo > 2) {
195 					for (--j; j >= 0; ) {
196 						pg = iptr[j];
197 						for (span=1; --j >= 0 && iptr[j] == pg+span; span++) ;
198 						printf(span>1 ? "     %9"Z"u[%"Z"d]\n" : "     %9"Z"u\n",
199 							pg, span);
200 					}
201 				}
202 			}
203 		}
204 		mdb_cursor_close(cursor);
205 		printf("  Free pages: %"Z"u\n", pages);
206 	}
207 
208 	rc = mdb_open(txn, subname, 0, &dbi);
209 	if (rc) {
210 		fprintf(stderr, "mdb_open failed, error %d %s\n", rc, mdb_strerror(rc));
211 		goto txn_abort;
212 	}
213 
214 	rc = mdb_stat(txn, dbi, &mst);
215 	if (rc) {
216 		fprintf(stderr, "mdb_stat failed, error %d %s\n", rc, mdb_strerror(rc));
217 		goto txn_abort;
218 	}
219 	printf("Status of %s\n", subname ? subname : "Main DB");
220 	prstat(&mst);
221 
222 	if (alldbs) {
223 		MDB_cursor *cursor;
224 		MDB_val key;
225 
226 		rc = mdb_cursor_open(txn, dbi, &cursor);
227 		if (rc) {
228 			fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc));
229 			goto txn_abort;
230 		}
231 		while ((rc = mdb_cursor_get(cursor, &key, NULL, MDB_NEXT_NODUP)) == 0) {
232 			char *str;
233 			MDB_dbi db2;
234 			if (memchr(key.mv_data, '\0', key.mv_size))
235 				continue;
236 			str = malloc(key.mv_size+1);
237 			memcpy(str, key.mv_data, key.mv_size);
238 			str[key.mv_size] = '\0';
239 			rc = mdb_open(txn, str, 0, &db2);
240 			if (rc == MDB_SUCCESS)
241 				printf("Status of %s\n", str);
242 			free(str);
243 			if (rc) continue;
244 			rc = mdb_stat(txn, db2, &mst);
245 			if (rc) {
246 				fprintf(stderr, "mdb_stat failed, error %d %s\n", rc, mdb_strerror(rc));
247 				goto txn_abort;
248 			}
249 			prstat(&mst);
250 			mdb_close(env, db2);
251 		}
252 		mdb_cursor_close(cursor);
253 	}
254 
255 	if (rc == MDB_NOTFOUND)
256 		rc = MDB_SUCCESS;
257 
258 	mdb_close(env, dbi);
259 txn_abort:
260 	mdb_txn_abort(txn);
261 env_close:
262 	mdb_env_close(env);
263 
264 	return rc ? EXIT_FAILURE : EXIT_SUCCESS;
265 }
266