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_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
)) {
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
)) {
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 hashesg
= freeg
= keysg
= datag
= extrag
= uncoalg
= NULL
;
166 ecode
= tdb_allrecord_lock(tdb
, F_RDLCK
, TDB_LOCK_WAIT
, false);
167 if (ecode
!= TDB_SUCCESS
) {
168 return tdb
->last_error
= ecode
;
171 ecode
= tdb_lock_expand(tdb
, F_RDLCK
);
172 if (ecode
!= TDB_SUCCESS
) {
173 tdb_allrecord_unlock(tdb
, F_RDLCK
);
174 return tdb
->last_error
= ecode
;
177 /* Start stats off empty. */
178 ftables
= tally_new(HISTO_HEIGHT
);
179 hashes
= tally_new(HISTO_HEIGHT
);
180 freet
= tally_new(HISTO_HEIGHT
);
181 keys
= tally_new(HISTO_HEIGHT
);
182 data
= tally_new(HISTO_HEIGHT
);
183 extra
= tally_new(HISTO_HEIGHT
);
184 uncoal
= tally_new(HISTO_HEIGHT
);
185 chains
= tally_new(HISTO_HEIGHT
);
186 if (!ftables
|| !hashes
|| !freet
|| !keys
|| !data
|| !extra
187 || !uncoal
|| !chains
) {
188 ecode
= tdb_logerr(tdb
, TDB_ERR_OOM
, TDB_LOG_ERROR
,
189 "tdb_summary: failed to allocate"
190 " tally structures");
194 ecode
= summarize(tdb
, hashes
, ftables
, freet
, keys
, data
, extra
,
196 if (ecode
!= TDB_SUCCESS
) {
200 if (flags
& TDB_SUMMARY_HISTOGRAMS
) {
201 hashesg
= tally_histogram(hashes
, HISTO_WIDTH
, HISTO_HEIGHT
);
202 freeg
= tally_histogram(freet
, HISTO_WIDTH
, HISTO_HEIGHT
);
203 keysg
= tally_histogram(keys
, HISTO_WIDTH
, HISTO_HEIGHT
);
204 datag
= tally_histogram(data
, HISTO_WIDTH
, HISTO_HEIGHT
);
205 extrag
= tally_histogram(extra
, HISTO_WIDTH
, HISTO_HEIGHT
);
206 uncoalg
= tally_histogram(uncoal
, HISTO_WIDTH
, HISTO_HEIGHT
);
209 /* 20 is max length of a %llu. */
210 len
= strlen(SUMMARY_FORMAT
) + 33*20 + 1
211 + (hashesg
? strlen(hashesg
) : 0)
212 + (freeg
? strlen(freeg
) : 0)
213 + (keysg
? strlen(keysg
) : 0)
214 + (datag
? strlen(datag
) : 0)
215 + (extrag
? strlen(extrag
) : 0)
216 + (uncoalg
? strlen(uncoalg
) : 0);
218 *summary
= malloc(len
);
220 ecode
= tdb_logerr(tdb
, TDB_ERR_OOM
, TDB_LOG_ERROR
,
221 "tdb_summary: failed to allocate string");
225 sprintf(*summary
, SUMMARY_FORMAT
,
226 (size_t)tdb
->file
->map_size
,
227 tally_total(keys
, NULL
) + tally_total(data
, NULL
),
229 tally_min(keys
), tally_mean(keys
), tally_max(keys
),
231 tally_min(data
), tally_mean(data
), tally_max(data
),
233 tally_min(extra
), tally_mean(extra
), tally_max(extra
),
234 extrag
? extrag
: "",
236 tally_min(freet
), tally_mean(freet
), tally_max(freet
),
238 tally_total(uncoal
, NULL
),
239 tally_min(uncoal
), tally_mean(uncoal
), tally_max(uncoal
),
240 uncoalg
? uncoalg
: "",
241 (unsigned)count_hash(tdb
, offsetof(struct tdb_header
,
243 TDB_TOPLEVEL_HASH_BITS
),
244 1 << TDB_TOPLEVEL_HASH_BITS
,
247 tally_min(hashes
), tally_mean(hashes
), tally_max(hashes
),
248 hashesg
? hashesg
: "",
249 tally_total(keys
, NULL
) * 100.0 / tdb
->file
->map_size
,
250 tally_total(data
, NULL
) * 100.0 / tdb
->file
->map_size
,
251 tally_total(extra
, NULL
) * 100.0 / tdb
->file
->map_size
,
252 tally_total(freet
, NULL
) * 100.0 / tdb
->file
->map_size
,
253 (tally_num(keys
) + tally_num(freet
) + tally_num(hashes
))
254 * sizeof(struct tdb_used_record
) * 100.0 / tdb
->file
->map_size
,
255 tally_num(ftables
) * sizeof(struct tdb_freetable
)
256 * 100.0 / tdb
->file
->map_size
,
258 * (sizeof(tdb_off_t
) << TDB_SUBLEVEL_HASH_BITS
)
259 + (sizeof(tdb_off_t
) << TDB_TOPLEVEL_HASH_BITS
)
260 + sizeof(struct tdb_chain
) * tally_num(chains
))
261 * 100.0 / tdb
->file
->map_size
);
279 tdb_allrecord_unlock(tdb
, F_RDLCK
);
280 tdb_unlock_expand(tdb
, F_RDLCK
);
281 return tdb
->last_error
= ecode
;