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 static tdb_off_t
count_hash(struct tdb_context
*tdb
,
23 tdb_off_t hash_off
, unsigned bits
)
29 h
= tdb_access_read(tdb
, hash_off
, sizeof(*h
) << bits
, true);
30 if (TDB_PTR_IS_ERR(h
)) {
31 return TDB_ERR_TO_OFF(TDB_PTR_ERR(h
));
33 for (i
= 0; i
< (1 << bits
); i
++)
36 tdb_access_release(tdb
, h
);
40 static enum TDB_ERROR
summarize(struct tdb_context
*tdb
,
42 struct tally
*ftables
,
54 for (off
= sizeof(struct tdb_header
);
55 off
< tdb
->file
->map_size
;
58 struct tdb_used_record u
;
59 struct tdb_free_record f
;
60 struct tdb_recovery_record r
;
62 /* We might not be able to get the whole thing. */
63 p
= tdb_access_read(tdb
, off
, sizeof(p
->f
), true);
64 if (TDB_PTR_IS_ERR(p
)) {
65 return TDB_PTR_ERR(p
);
67 if (frec_magic(&p
->f
) != TDB_FREE_MAGIC
) {
69 tally_add(uncoal
, unc
);
74 if (p
->r
.magic
== TDB_RECOVERY_INVALID_MAGIC
75 || p
->r
.magic
== TDB_RECOVERY_MAGIC
) {
76 len
= sizeof(p
->r
) + p
->r
.max_len
;
77 } else if (frec_magic(&p
->f
) == TDB_FREE_MAGIC
) {
78 len
= frec_len(&p
->f
);
82 } else if (rec_magic(&p
->u
) == TDB_USED_MAGIC
) {
84 + rec_key_length(&p
->u
)
85 + rec_data_length(&p
->u
)
86 + rec_extra_padding(&p
->u
);
88 tally_add(keys
, rec_key_length(&p
->u
));
89 tally_add(data
, rec_data_length(&p
->u
));
90 tally_add(extra
, rec_extra_padding(&p
->u
));
91 } else if (rec_magic(&p
->u
) == TDB_HTABLE_MAGIC
) {
92 tdb_off_t count
= count_hash(tdb
,
94 TDB_SUBLEVEL_HASH_BITS
);
95 if (TDB_OFF_IS_ERR(count
)) {
96 return TDB_OFF_TO_ERR(count
);
98 tally_add(hashes
, count
);
99 tally_add(extra
, rec_extra_padding(&p
->u
));
101 + rec_data_length(&p
->u
)
102 + rec_extra_padding(&p
->u
);
103 } else if (rec_magic(&p
->u
) == TDB_FTABLE_MAGIC
) {
105 + rec_data_length(&p
->u
)
106 + rec_extra_padding(&p
->u
);
107 tally_add(ftables
, rec_data_length(&p
->u
));
108 tally_add(extra
, rec_extra_padding(&p
->u
));
109 } else if (rec_magic(&p
->u
) == TDB_CHAIN_MAGIC
) {
111 + rec_data_length(&p
->u
)
112 + rec_extra_padding(&p
->u
);
113 tally_add(chains
, 1);
114 tally_add(extra
, rec_extra_padding(&p
->u
));
116 len
= dead_space(tdb
, off
);
117 if (TDB_OFF_IS_ERR(len
)) {
118 return TDB_OFF_TO_ERR(len
);
121 tdb_access_release(tdb
, p
);
124 tally_add(uncoal
, unc
);
128 #define SUMMARY_FORMAT \
129 "Size of file/data: %zu/%zu\n" \
130 "Number of records: %zu\n" \
131 "Smallest/average/largest keys: %zu/%zu/%zu\n%s" \
132 "Smallest/average/largest data: %zu/%zu/%zu\n%s" \
133 "Smallest/average/largest padding: %zu/%zu/%zu\n%s" \
134 "Number of free records: %zu\n" \
135 "Smallest/average/largest free records: %zu/%zu/%zu\n%s" \
136 "Number of uncoalesced records: %zu\n" \
137 "Smallest/average/largest uncoalesced runs: %zu/%zu/%zu\n%s" \
138 "Toplevel hash used: %u of %u\n" \
139 "Number of chains: %zu\n" \
140 "Number of subhashes: %zu\n" \
141 "Smallest/average/largest subhash entries: %zu/%zu/%zu\n%s" \
142 "Percentage keys/data/padding/free/rechdrs/freehdrs/hashes: %.0f/%.0f/%.0f/%.0f/%.0f/%.0f/%.0f\n"
144 #define BUCKET_SUMMARY_FORMAT_A \
145 "Free bucket %zu: total entries %zu.\n" \
146 "Smallest/average/largest length: %zu/%zu/%zu\n%s"
147 #define BUCKET_SUMMARY_FORMAT_B \
148 "Free bucket %zu-%zu: total entries %zu.\n" \
149 "Smallest/average/largest length: %zu/%zu/%zu\n%s"
151 #define HISTO_WIDTH 70
152 #define HISTO_HEIGHT 20
154 enum TDB_ERROR
tdb_summary(struct tdb_context
*tdb
,
155 enum tdb_summary_flags flags
,
159 struct tally
*ftables
, *hashes
, *freet
, *keys
, *data
, *extra
, *uncoal
,
161 char *hashesg
, *freeg
, *keysg
, *datag
, *extrag
, *uncoalg
;
162 enum TDB_ERROR ecode
;
164 if (tdb
->flags
& TDB_VERSION1
) {
165 /* tdb1 doesn't do graphs. */
166 *summary
= tdb1_summary(tdb
);
168 return tdb
->last_error
;
172 hashesg
= freeg
= keysg
= datag
= extrag
= uncoalg
= NULL
;
174 ecode
= tdb_allrecord_lock(tdb
, F_RDLCK
, TDB_LOCK_WAIT
, false);
175 if (ecode
!= TDB_SUCCESS
) {
176 return tdb
->last_error
= ecode
;
179 ecode
= tdb_lock_expand(tdb
, F_RDLCK
);
180 if (ecode
!= TDB_SUCCESS
) {
181 tdb_allrecord_unlock(tdb
, F_RDLCK
);
182 return tdb
->last_error
= ecode
;
185 /* Start stats off empty. */
186 ftables
= tally_new(HISTO_HEIGHT
);
187 hashes
= tally_new(HISTO_HEIGHT
);
188 freet
= tally_new(HISTO_HEIGHT
);
189 keys
= tally_new(HISTO_HEIGHT
);
190 data
= tally_new(HISTO_HEIGHT
);
191 extra
= tally_new(HISTO_HEIGHT
);
192 uncoal
= tally_new(HISTO_HEIGHT
);
193 chains
= tally_new(HISTO_HEIGHT
);
194 if (!ftables
|| !hashes
|| !freet
|| !keys
|| !data
|| !extra
195 || !uncoal
|| !chains
) {
196 ecode
= tdb_logerr(tdb
, TDB_ERR_OOM
, TDB_LOG_ERROR
,
197 "tdb_summary: failed to allocate"
198 " tally structures");
202 ecode
= summarize(tdb
, hashes
, ftables
, freet
, keys
, data
, extra
,
204 if (ecode
!= TDB_SUCCESS
) {
208 if (flags
& TDB_SUMMARY_HISTOGRAMS
) {
209 hashesg
= tally_histogram(hashes
, HISTO_WIDTH
, HISTO_HEIGHT
);
210 freeg
= tally_histogram(freet
, HISTO_WIDTH
, HISTO_HEIGHT
);
211 keysg
= tally_histogram(keys
, HISTO_WIDTH
, HISTO_HEIGHT
);
212 datag
= tally_histogram(data
, HISTO_WIDTH
, HISTO_HEIGHT
);
213 extrag
= tally_histogram(extra
, HISTO_WIDTH
, HISTO_HEIGHT
);
214 uncoalg
= tally_histogram(uncoal
, HISTO_WIDTH
, HISTO_HEIGHT
);
217 /* 20 is max length of a %llu. */
218 len
= strlen(SUMMARY_FORMAT
) + 33*20 + 1
219 + (hashesg
? strlen(hashesg
) : 0)
220 + (freeg
? strlen(freeg
) : 0)
221 + (keysg
? strlen(keysg
) : 0)
222 + (datag
? strlen(datag
) : 0)
223 + (extrag
? strlen(extrag
) : 0)
224 + (uncoalg
? strlen(uncoalg
) : 0);
226 *summary
= malloc(len
);
228 ecode
= tdb_logerr(tdb
, TDB_ERR_OOM
, TDB_LOG_ERROR
,
229 "tdb_summary: failed to allocate string");
233 sprintf(*summary
, SUMMARY_FORMAT
,
234 (size_t)tdb
->file
->map_size
,
235 tally_total(keys
, NULL
) + tally_total(data
, NULL
),
237 tally_min(keys
), tally_mean(keys
), tally_max(keys
),
239 tally_min(data
), tally_mean(data
), tally_max(data
),
241 tally_min(extra
), tally_mean(extra
), tally_max(extra
),
242 extrag
? extrag
: "",
244 tally_min(freet
), tally_mean(freet
), tally_max(freet
),
246 tally_total(uncoal
, NULL
),
247 tally_min(uncoal
), tally_mean(uncoal
), tally_max(uncoal
),
248 uncoalg
? uncoalg
: "",
249 (unsigned)count_hash(tdb
, offsetof(struct tdb_header
,
251 TDB_TOPLEVEL_HASH_BITS
),
252 1 << TDB_TOPLEVEL_HASH_BITS
,
255 tally_min(hashes
), tally_mean(hashes
), tally_max(hashes
),
256 hashesg
? hashesg
: "",
257 tally_total(keys
, NULL
) * 100.0 / tdb
->file
->map_size
,
258 tally_total(data
, NULL
) * 100.0 / tdb
->file
->map_size
,
259 tally_total(extra
, NULL
) * 100.0 / tdb
->file
->map_size
,
260 tally_total(freet
, NULL
) * 100.0 / tdb
->file
->map_size
,
261 (tally_num(keys
) + tally_num(freet
) + tally_num(hashes
))
262 * sizeof(struct tdb_used_record
) * 100.0 / tdb
->file
->map_size
,
263 tally_num(ftables
) * sizeof(struct tdb_freetable
)
264 * 100.0 / tdb
->file
->map_size
,
266 * (sizeof(tdb_off_t
) << TDB_SUBLEVEL_HASH_BITS
)
267 + (sizeof(tdb_off_t
) << TDB_TOPLEVEL_HASH_BITS
)
268 + sizeof(struct tdb_chain
) * tally_num(chains
))
269 * 100.0 / tdb
->file
->map_size
);
287 tdb_allrecord_unlock(tdb
, F_RDLCK
);
288 tdb_unlock_expand(tdb
, F_RDLCK
);
289 return tdb
->last_error
= ecode
;