2 Trivial Database 2: human-readable summary code
3 Copyright (C) Rusty Russell 2010
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 3 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, see <http://www.gnu.org/licenses/>.
20 #include <ccan/tally/tally.h>
22 #define SUMMARY_FORMAT \
23 "Size of file/data: %zu/%zu\n" \
24 "Number of records: %zu\n" \
25 "Smallest/average/largest keys: %zu/%zu/%zu\n%s" \
26 "Smallest/average/largest data: %zu/%zu/%zu\n%s" \
27 "Smallest/average/largest padding: %zu/%zu/%zu\n%s" \
28 "Number of free records: %zu\n" \
29 "Smallest/average/largest free records: %zu/%zu/%zu\n%s" \
30 "Number of uncoalesced records: %zu\n" \
31 "Smallest/average/largest uncoalesced runs: %zu/%zu/%zu\n%s" \
32 "Toplevel hash used: %u of %u\n" \
33 "Number of chains: %zu\n" \
34 "Number of subhashes: %zu\n" \
35 "Smallest/average/largest subhash entries: %zu/%zu/%zu\n%s" \
36 "Percentage keys/data/padding/free/rechdrs/freehdrs/hashes: %.0f/%.0f/%.0f/%.0f/%.0f/%.0f/%.0f\n"
38 #define BUCKET_SUMMARY_FORMAT_A \
39 "Free bucket %zu: total entries %zu.\n" \
40 "Smallest/average/largest length: %zu/%zu/%zu\n%s"
41 #define BUCKET_SUMMARY_FORMAT_B \
42 "Free bucket %zu-%zu: total entries %zu.\n" \
43 "Smallest/average/largest length: %zu/%zu/%zu\n%s"
44 #define CAPABILITY_FORMAT \
47 #define HISTO_WIDTH 70
48 #define HISTO_HEIGHT 20
50 static tdb_off_t
count_hash(struct tdb_context
*tdb
,
51 tdb_off_t hash_off
, unsigned bits
)
57 h
= tdb_access_read(tdb
, hash_off
, sizeof(*h
) << bits
, true);
58 if (TDB_PTR_IS_ERR(h
)) {
59 return TDB_ERR_TO_OFF(TDB_PTR_ERR(h
));
61 for (i
= 0; i
< (1 << bits
); i
++)
64 tdb_access_release(tdb
, h
);
68 static enum TDB_ERROR
summarize(struct tdb_context
*tdb
,
70 struct tally
*ftables
,
83 for (off
= sizeof(struct tdb_header
);
84 off
< tdb
->file
->map_size
;
87 struct tdb_used_record u
;
88 struct tdb_free_record f
;
89 struct tdb_recovery_record r
;
91 /* We might not be able to get the whole thing. */
92 p
= tdb_access_read(tdb
, off
, sizeof(p
->f
), true);
93 if (TDB_PTR_IS_ERR(p
)) {
94 return TDB_PTR_ERR(p
);
96 if (frec_magic(&p
->f
) != TDB_FREE_MAGIC
) {
98 tally_add(uncoal
, unc
);
103 if (p
->r
.magic
== TDB_RECOVERY_INVALID_MAGIC
104 || p
->r
.magic
== TDB_RECOVERY_MAGIC
) {
105 len
= sizeof(p
->r
) + p
->r
.max_len
;
106 } else if (frec_magic(&p
->f
) == TDB_FREE_MAGIC
) {
107 len
= frec_len(&p
->f
);
111 } else if (rec_magic(&p
->u
) == TDB_USED_MAGIC
) {
113 + rec_key_length(&p
->u
)
114 + rec_data_length(&p
->u
)
115 + rec_extra_padding(&p
->u
);
117 tally_add(keys
, rec_key_length(&p
->u
));
118 tally_add(data
, rec_data_length(&p
->u
));
119 tally_add(extra
, rec_extra_padding(&p
->u
));
120 } else if (rec_magic(&p
->u
) == TDB_HTABLE_MAGIC
) {
121 tdb_off_t count
= count_hash(tdb
,
123 TDB_SUBLEVEL_HASH_BITS
);
124 if (TDB_OFF_IS_ERR(count
)) {
125 return TDB_OFF_TO_ERR(count
);
127 tally_add(hashes
, count
);
128 tally_add(extra
, rec_extra_padding(&p
->u
));
130 + rec_data_length(&p
->u
)
131 + rec_extra_padding(&p
->u
);
132 } else if (rec_magic(&p
->u
) == TDB_FTABLE_MAGIC
) {
134 + rec_data_length(&p
->u
)
135 + rec_extra_padding(&p
->u
);
136 tally_add(ftables
, rec_data_length(&p
->u
));
137 tally_add(extra
, rec_extra_padding(&p
->u
));
138 } else if (rec_magic(&p
->u
) == TDB_CHAIN_MAGIC
) {
140 + rec_data_length(&p
->u
)
141 + rec_extra_padding(&p
->u
);
142 tally_add(chains
, 1);
143 tally_add(extra
, rec_extra_padding(&p
->u
));
144 } else if (rec_magic(&p
->u
) == TDB_CAP_MAGIC
) {
146 + rec_data_length(&p
->u
)
147 + rec_extra_padding(&p
->u
);
150 len
= dead_space(tdb
, off
);
151 if (TDB_OFF_IS_ERR(len
)) {
152 return TDB_OFF_TO_ERR(len
);
155 tdb_access_release(tdb
, p
);
158 tally_add(uncoal
, unc
);
162 static void add_capabilities(struct tdb_context
*tdb
, char *summary
)
165 const struct tdb_capability
*cap
;
168 /* Append to summary. */
169 summary
+= strlen(summary
);
171 off
= tdb_read_off(tdb
, offsetof(struct tdb_header
, capabilities
));
172 if (TDB_OFF_IS_ERR(off
))
175 /* Walk capability list. */
176 for (; off
; off
= next
) {
177 cap
= tdb_access_read(tdb
, off
, sizeof(*cap
), true);
178 if (TDB_PTR_IS_ERR(cap
)) {
182 sprintf(summary
, CAPABILITY_FORMAT
,
183 cap
->type
& TDB_CAP_TYPE_MASK
,
184 /* Noopen? How did we get here? */
185 (cap
->type
& TDB_CAP_NOOPEN
) ? " (unopenable)"
186 : ((cap
->type
& TDB_CAP_NOWRITE
)
187 && (cap
->type
& TDB_CAP_NOCHECK
)) ? " (uncheckable,read-only)"
188 : (cap
->type
& TDB_CAP_NOWRITE
) ? " (read-only)"
189 : (cap
->type
& TDB_CAP_NOCHECK
) ? " (uncheckable)"
191 summary
+= strlen(summary
);
193 tdb_access_release(tdb
, cap
);
197 _PUBLIC_
enum TDB_ERROR
tdb_summary(struct tdb_context
*tdb
,
198 enum tdb_summary_flags flags
,
203 struct tally
*ftables
, *hashes
, *freet
, *keys
, *data
, *extra
, *uncoal
,
205 char *hashesg
, *freeg
, *keysg
, *datag
, *extrag
, *uncoalg
;
206 enum TDB_ERROR ecode
;
208 if (tdb
->flags
& TDB_VERSION1
) {
209 /* tdb1 doesn't do graphs. */
210 *summary
= tdb1_summary(tdb
);
212 return tdb
->last_error
;
216 hashesg
= freeg
= keysg
= datag
= extrag
= uncoalg
= NULL
;
218 ecode
= tdb_allrecord_lock(tdb
, F_RDLCK
, TDB_LOCK_WAIT
, false);
219 if (ecode
!= TDB_SUCCESS
) {
220 return tdb
->last_error
= ecode
;
223 ecode
= tdb_lock_expand(tdb
, F_RDLCK
);
224 if (ecode
!= TDB_SUCCESS
) {
225 tdb_allrecord_unlock(tdb
, F_RDLCK
);
226 return tdb
->last_error
= ecode
;
229 /* Start stats off empty. */
230 ftables
= tally_new(HISTO_HEIGHT
);
231 hashes
= tally_new(HISTO_HEIGHT
);
232 freet
= tally_new(HISTO_HEIGHT
);
233 keys
= tally_new(HISTO_HEIGHT
);
234 data
= tally_new(HISTO_HEIGHT
);
235 extra
= tally_new(HISTO_HEIGHT
);
236 uncoal
= tally_new(HISTO_HEIGHT
);
237 chains
= tally_new(HISTO_HEIGHT
);
238 if (!ftables
|| !hashes
|| !freet
|| !keys
|| !data
|| !extra
239 || !uncoal
|| !chains
) {
240 ecode
= tdb_logerr(tdb
, TDB_ERR_OOM
, TDB_LOG_ERROR
,
241 "tdb_summary: failed to allocate"
242 " tally structures");
246 ecode
= summarize(tdb
, hashes
, ftables
, freet
, keys
, data
, extra
,
247 uncoal
, chains
, &num_caps
);
248 if (ecode
!= TDB_SUCCESS
) {
252 if (flags
& TDB_SUMMARY_HISTOGRAMS
) {
253 hashesg
= tally_histogram(hashes
, HISTO_WIDTH
, HISTO_HEIGHT
);
254 freeg
= tally_histogram(freet
, HISTO_WIDTH
, HISTO_HEIGHT
);
255 keysg
= tally_histogram(keys
, HISTO_WIDTH
, HISTO_HEIGHT
);
256 datag
= tally_histogram(data
, HISTO_WIDTH
, HISTO_HEIGHT
);
257 extrag
= tally_histogram(extra
, HISTO_WIDTH
, HISTO_HEIGHT
);
258 uncoalg
= tally_histogram(uncoal
, HISTO_WIDTH
, HISTO_HEIGHT
);
261 /* 20 is max length of a %llu. */
262 len
= strlen(SUMMARY_FORMAT
) + 33*20 + 1
263 + (hashesg
? strlen(hashesg
) : 0)
264 + (freeg
? strlen(freeg
) : 0)
265 + (keysg
? strlen(keysg
) : 0)
266 + (datag
? strlen(datag
) : 0)
267 + (extrag
? strlen(extrag
) : 0)
268 + (uncoalg
? strlen(uncoalg
) : 0)
269 + num_caps
* (strlen(CAPABILITY_FORMAT
) + 20
270 + strlen(" (uncheckable,read-only)"));
272 *summary
= malloc(len
);
274 ecode
= tdb_logerr(tdb
, TDB_ERR_OOM
, TDB_LOG_ERROR
,
275 "tdb_summary: failed to allocate string");
279 sprintf(*summary
, SUMMARY_FORMAT
,
280 (size_t)tdb
->file
->map_size
,
281 tally_total(keys
, NULL
) + tally_total(data
, NULL
),
283 tally_min(keys
), tally_mean(keys
), tally_max(keys
),
285 tally_min(data
), tally_mean(data
), tally_max(data
),
287 tally_min(extra
), tally_mean(extra
), tally_max(extra
),
288 extrag
? extrag
: "",
290 tally_min(freet
), tally_mean(freet
), tally_max(freet
),
292 tally_total(uncoal
, NULL
),
293 tally_min(uncoal
), tally_mean(uncoal
), tally_max(uncoal
),
294 uncoalg
? uncoalg
: "",
295 (unsigned)count_hash(tdb
, offsetof(struct tdb_header
,
297 TDB_TOPLEVEL_HASH_BITS
),
298 1 << TDB_TOPLEVEL_HASH_BITS
,
301 tally_min(hashes
), tally_mean(hashes
), tally_max(hashes
),
302 hashesg
? hashesg
: "",
303 tally_total(keys
, NULL
) * 100.0 / tdb
->file
->map_size
,
304 tally_total(data
, NULL
) * 100.0 / tdb
->file
->map_size
,
305 tally_total(extra
, NULL
) * 100.0 / tdb
->file
->map_size
,
306 tally_total(freet
, NULL
) * 100.0 / tdb
->file
->map_size
,
307 (tally_num(keys
) + tally_num(freet
) + tally_num(hashes
))
308 * sizeof(struct tdb_used_record
) * 100.0 / tdb
->file
->map_size
,
309 tally_num(ftables
) * sizeof(struct tdb_freetable
)
310 * 100.0 / tdb
->file
->map_size
,
312 * (sizeof(tdb_off_t
) << TDB_SUBLEVEL_HASH_BITS
)
313 + (sizeof(tdb_off_t
) << TDB_TOPLEVEL_HASH_BITS
)
314 + sizeof(struct tdb_chain
) * tally_num(chains
))
315 * 100.0 / tdb
->file
->map_size
);
317 add_capabilities(tdb
, *summary
);
335 tdb_allrecord_unlock(tdb
, F_RDLCK
);
336 tdb_unlock_expand(tdb
, F_RDLCK
);
337 return tdb
->last_error
= ecode
;