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