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/>.
19 #include <ccan/tally/tally.h>
21 #define SUMMARY_FORMAT \
22 "Size of file/data: %zu/%zu\n" \
23 "Number of records: %zu\n" \
24 "Smallest/average/largest keys: %zu/%zu/%zu\n%s" \
25 "Smallest/average/largest data: %zu/%zu/%zu\n%s" \
26 "Smallest/average/largest padding: %zu/%zu/%zu\n%s" \
27 "Number of free records: %zu\n" \
28 "Smallest/average/largest free records: %zu/%zu/%zu\n%s" \
29 "Number of uncoalesced records: %zu\n" \
30 "Smallest/average/largest uncoalesced runs: %zu/%zu/%zu\n%s" \
31 "Toplevel hash used: %u of %u\n" \
32 "Number of hashes: %zu\n" \
33 "Smallest/average/largest hash chains: %zu/%zu/%zu\n%s" \
34 "Percentage keys/data/padding/free/rechdrs/freehdrs/hashes: %.0f/%.0f/%.0f/%.0f/%.0f/%.0f/%.0f\n"
36 #define BUCKET_SUMMARY_FORMAT_A \
37 "Free bucket %zu: total entries %zu.\n" \
38 "Smallest/average/largest length: %zu/%zu/%zu\n%s"
39 #define BUCKET_SUMMARY_FORMAT_B \
40 "Free bucket %zu-%zu: total entries %zu.\n" \
41 "Smallest/average/largest length: %zu/%zu/%zu\n%s"
42 #define CAPABILITY_FORMAT \
45 #define HISTO_WIDTH 70
46 #define HISTO_HEIGHT 20
48 static ntdb_off_t
count_hash(struct ntdb_context
*ntdb
,
53 ntdb_off_t i
, count
= 0;
55 h
= ntdb_access_read(ntdb
, hash_off
, sizeof(*h
) * num
, true);
56 if (NTDB_PTR_IS_ERR(h
)) {
57 return NTDB_ERR_TO_OFF(NTDB_PTR_ERR(h
));
59 for (i
= 0; i
< num
; i
++)
62 ntdb_access_release(ntdb
, h
);
66 static enum NTDB_ERROR
summarize(struct ntdb_context
*ntdb
,
67 struct tally
*ftables
,
80 for (off
= sizeof(struct ntdb_header
);
81 off
< ntdb
->file
->map_size
;
84 struct ntdb_used_record u
;
85 struct ntdb_free_record f
;
86 struct ntdb_recovery_record r
;
88 /* We might not be able to get the whole thing. */
89 p
= ntdb_access_read(ntdb
, off
, sizeof(p
->f
), true);
90 if (NTDB_PTR_IS_ERR(p
)) {
91 return NTDB_PTR_ERR(p
);
93 if (frec_magic(&p
->f
) != NTDB_FREE_MAGIC
) {
95 tally_add(uncoal
, unc
);
100 if (p
->r
.magic
== NTDB_RECOVERY_INVALID_MAGIC
101 || p
->r
.magic
== NTDB_RECOVERY_MAGIC
) {
102 len
= sizeof(p
->r
) + p
->r
.max_len
;
103 } else if (frec_magic(&p
->f
) == NTDB_FREE_MAGIC
) {
104 len
= frec_len(&p
->f
);
108 } else if (rec_magic(&p
->u
) == NTDB_USED_MAGIC
) {
110 + rec_key_length(&p
->u
)
111 + rec_data_length(&p
->u
)
112 + rec_extra_padding(&p
->u
);
114 tally_add(keys
, rec_key_length(&p
->u
));
115 tally_add(data
, rec_data_length(&p
->u
));
116 tally_add(extra
, rec_extra_padding(&p
->u
));
117 } else if (rec_magic(&p
->u
) == NTDB_HTABLE_MAGIC
) {
118 ntdb_off_t count
= count_hash(ntdb
,
120 1 << ntdb
->hash_bits
);
121 if (NTDB_OFF_IS_ERR(count
)) {
122 return NTDB_OFF_TO_ERR(count
);
124 tally_add(hashes
, count
);
125 tally_add(extra
, rec_extra_padding(&p
->u
));
127 + rec_data_length(&p
->u
)
128 + rec_extra_padding(&p
->u
);
129 } else if (rec_magic(&p
->u
) == NTDB_FTABLE_MAGIC
) {
131 + rec_data_length(&p
->u
)
132 + rec_extra_padding(&p
->u
);
133 tally_add(ftables
, rec_data_length(&p
->u
));
134 tally_add(extra
, rec_extra_padding(&p
->u
));
135 } else if (rec_magic(&p
->u
) == NTDB_CHAIN_MAGIC
) {
137 + rec_data_length(&p
->u
)
138 + rec_extra_padding(&p
->u
);
140 rec_data_length(&p
->u
)/sizeof(ntdb_off_t
));
141 tally_add(extra
, rec_extra_padding(&p
->u
));
142 } else if (rec_magic(&p
->u
) == NTDB_CAP_MAGIC
) {
144 + rec_data_length(&p
->u
)
145 + rec_extra_padding(&p
->u
);
148 len
= dead_space(ntdb
, off
);
149 if (NTDB_OFF_IS_ERR(len
)) {
150 return NTDB_OFF_TO_ERR(len
);
153 ntdb_access_release(ntdb
, p
);
156 tally_add(uncoal
, unc
);
160 static void add_capabilities(struct ntdb_context
*ntdb
, char *summary
)
162 ntdb_off_t off
, next
;
163 const struct ntdb_capability
*cap
;
166 /* Append to summary. */
167 summary
+= strlen(summary
);
169 off
= ntdb_read_off(ntdb
, offsetof(struct ntdb_header
, capabilities
));
170 if (NTDB_OFF_IS_ERR(off
))
173 /* Walk capability list. */
174 for (; off
; off
= next
) {
175 cap
= ntdb_access_read(ntdb
, off
, sizeof(*cap
), true);
176 if (NTDB_PTR_IS_ERR(cap
)) {
180 sprintf(summary
, CAPABILITY_FORMAT
,
181 cap
->type
& NTDB_CAP_TYPE_MASK
,
182 /* Noopen? How did we get here? */
183 (cap
->type
& NTDB_CAP_NOOPEN
) ? " (unopenable)"
184 : ((cap
->type
& NTDB_CAP_NOWRITE
)
185 && (cap
->type
& NTDB_CAP_NOCHECK
)) ? " (uncheckable,read-only)"
186 : (cap
->type
& NTDB_CAP_NOWRITE
) ? " (read-only)"
187 : (cap
->type
& NTDB_CAP_NOCHECK
) ? " (uncheckable)"
189 summary
+= strlen(summary
);
191 ntdb_access_release(ntdb
, cap
);
195 _PUBLIC_
enum NTDB_ERROR
ntdb_summary(struct ntdb_context
*ntdb
,
196 enum ntdb_summary_flags flags
,
201 struct tally
*ftables
, *freet
, *keys
, *data
, *extra
, *uncoal
, *hashes
;
202 char *freeg
, *keysg
, *datag
, *extrag
, *uncoalg
, *hashesg
;
203 enum NTDB_ERROR ecode
;
205 freeg
= keysg
= datag
= extrag
= uncoalg
= hashesg
= NULL
;
207 ecode
= ntdb_allrecord_lock(ntdb
, F_RDLCK
, NTDB_LOCK_WAIT
, false);
208 if (ecode
!= NTDB_SUCCESS
) {
212 ecode
= ntdb_lock_expand(ntdb
, F_RDLCK
);
213 if (ecode
!= NTDB_SUCCESS
) {
214 ntdb_allrecord_unlock(ntdb
, F_RDLCK
);
218 /* Start stats off empty. */
219 ftables
= tally_new(HISTO_HEIGHT
);
220 freet
= tally_new(HISTO_HEIGHT
);
221 keys
= tally_new(HISTO_HEIGHT
);
222 data
= tally_new(HISTO_HEIGHT
);
223 extra
= tally_new(HISTO_HEIGHT
);
224 uncoal
= tally_new(HISTO_HEIGHT
);
225 hashes
= tally_new(HISTO_HEIGHT
);
226 if (!ftables
|| !freet
|| !keys
|| !data
|| !extra
227 || !uncoal
|| !hashes
) {
228 ecode
= ntdb_logerr(ntdb
, NTDB_ERR_OOM
, NTDB_LOG_ERROR
,
229 "ntdb_summary: failed to allocate"
230 " tally structures");
234 ecode
= summarize(ntdb
, ftables
, freet
, keys
, data
, extra
,
235 uncoal
, hashes
, &num_caps
);
236 if (ecode
!= NTDB_SUCCESS
) {
240 if (flags
& NTDB_SUMMARY_HISTOGRAMS
) {
241 freeg
= tally_histogram(freet
, HISTO_WIDTH
, HISTO_HEIGHT
);
242 keysg
= tally_histogram(keys
, HISTO_WIDTH
, HISTO_HEIGHT
);
243 datag
= tally_histogram(data
, HISTO_WIDTH
, HISTO_HEIGHT
);
244 extrag
= tally_histogram(extra
, HISTO_WIDTH
, HISTO_HEIGHT
);
245 uncoalg
= tally_histogram(uncoal
, HISTO_WIDTH
, HISTO_HEIGHT
);
246 hashesg
= tally_histogram(hashes
, HISTO_WIDTH
, HISTO_HEIGHT
);
249 /* 20 is max length of a %llu. */
250 len
= strlen(SUMMARY_FORMAT
) + 33*20 + 1
251 + (freeg
? strlen(freeg
) : 0)
252 + (keysg
? strlen(keysg
) : 0)
253 + (datag
? strlen(datag
) : 0)
254 + (extrag
? strlen(extrag
) : 0)
255 + (uncoalg
? strlen(uncoalg
) : 0)
256 + (hashesg
? strlen(hashesg
) : 0)
257 + num_caps
* (strlen(CAPABILITY_FORMAT
) + 20
258 + strlen(" (uncheckable,read-only)"));
260 *summary
= ntdb
->alloc_fn(ntdb
, len
, ntdb
->alloc_data
);
262 ecode
= ntdb_logerr(ntdb
, NTDB_ERR_OOM
, NTDB_LOG_ERROR
,
263 "ntdb_summary: failed to allocate string");
267 sprintf(*summary
, SUMMARY_FORMAT
,
268 (size_t)ntdb
->file
->map_size
,
269 tally_total(keys
, NULL
) + tally_total(data
, NULL
),
271 tally_min(keys
), tally_mean(keys
), tally_max(keys
),
273 tally_min(data
), tally_mean(data
), tally_max(data
),
275 tally_min(extra
), tally_mean(extra
), tally_max(extra
),
276 extrag
? extrag
: "",
278 tally_min(freet
), tally_mean(freet
), tally_max(freet
),
280 tally_total(uncoal
, NULL
),
281 tally_min(uncoal
), tally_mean(uncoal
), tally_max(uncoal
),
282 uncoalg
? uncoalg
: "",
283 (unsigned)count_hash(ntdb
, sizeof(struct ntdb_header
),
284 1 << ntdb
->hash_bits
),
285 1 << ntdb
->hash_bits
,
287 tally_min(hashes
), tally_mean(hashes
), tally_max(hashes
),
288 hashesg
? hashesg
: "",
289 tally_total(keys
, NULL
) * 100.0 / ntdb
->file
->map_size
,
290 tally_total(data
, NULL
) * 100.0 / ntdb
->file
->map_size
,
291 tally_total(extra
, NULL
) * 100.0 / ntdb
->file
->map_size
,
292 tally_total(freet
, NULL
) * 100.0 / ntdb
->file
->map_size
,
293 (tally_num(keys
) + tally_num(freet
) + tally_num(hashes
))
294 * sizeof(struct ntdb_used_record
) * 100.0 / ntdb
->file
->map_size
,
295 tally_num(ftables
) * sizeof(struct ntdb_freetable
)
296 * 100.0 / ntdb
->file
->map_size
,
297 (tally_total(hashes
, NULL
) * sizeof(ntdb_off_t
)
298 + (sizeof(ntdb_off_t
) << ntdb
->hash_bits
))
299 * 100.0 / ntdb
->file
->map_size
);
301 add_capabilities(ntdb
, *summary
);
304 ntdb
->free_fn(freeg
, ntdb
->alloc_data
);
305 ntdb
->free_fn(keysg
, ntdb
->alloc_data
);
306 ntdb
->free_fn(datag
, ntdb
->alloc_data
);
307 ntdb
->free_fn(extrag
, ntdb
->alloc_data
);
308 ntdb
->free_fn(uncoalg
, ntdb
->alloc_data
);
309 ntdb
->free_fn(hashesg
, ntdb
->alloc_data
);
310 ntdb
->free_fn(freet
, ntdb
->alloc_data
);
311 ntdb
->free_fn(keys
, ntdb
->alloc_data
);
312 ntdb
->free_fn(data
, ntdb
->alloc_data
);
313 ntdb
->free_fn(extra
, ntdb
->alloc_data
);
314 ntdb
->free_fn(uncoal
, ntdb
->alloc_data
);
315 ntdb
->free_fn(ftables
, ntdb
->alloc_data
);
316 ntdb
->free_fn(hashes
, ntdb
->alloc_data
);
318 ntdb_allrecord_unlock(ntdb
, F_RDLCK
);
319 ntdb_unlock_expand(ntdb
, F_RDLCK
);