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