summary.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. /*
  2. Trivial Database 2: human-readable summary code
  3. Copyright (C) Rusty Russell 2010
  4. This library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Lesser General Public
  6. License as published by the Free Software Foundation; either
  7. version 3 of the License, or (at your option) any later version.
  8. This library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public
  13. License along with this library; if not, see <http://www.gnu.org/licenses/>.
  14. */
  15. #include "private.h"
  16. #include <assert.h>
  17. #include <ccan/tally/tally.h>
  18. static tdb_off_t count_hash(struct tdb_context *tdb,
  19. tdb_off_t hash_off, unsigned bits)
  20. {
  21. const tdb_off_t *h;
  22. tdb_off_t count = 0;
  23. unsigned int i;
  24. h = tdb_access_read(tdb, hash_off, sizeof(*h) << bits, true);
  25. if (TDB_PTR_IS_ERR(h)) {
  26. return TDB_PTR_ERR(h);
  27. }
  28. for (i = 0; i < (1 << bits); i++)
  29. count += (h[i] != 0);
  30. tdb_access_release(tdb, h);
  31. return count;
  32. }
  33. static enum TDB_ERROR summarize(struct tdb_context *tdb,
  34. struct tally *hashes,
  35. struct tally *ftables,
  36. struct tally *fr,
  37. struct tally *keys,
  38. struct tally *data,
  39. struct tally *extra,
  40. struct tally *uncoal,
  41. struct tally *buckets,
  42. struct tally *chains)
  43. {
  44. tdb_off_t off;
  45. tdb_len_t len;
  46. tdb_len_t unc = 0;
  47. for (off = sizeof(struct tdb_header);
  48. off < tdb->file->map_size;
  49. off += len) {
  50. const union {
  51. struct tdb_used_record u;
  52. struct tdb_free_record f;
  53. struct tdb_recovery_record r;
  54. } *p;
  55. /* We might not be able to get the whole thing. */
  56. p = tdb_access_read(tdb, off, sizeof(p->f), true);
  57. if (TDB_PTR_IS_ERR(p)) {
  58. return TDB_PTR_ERR(p);
  59. }
  60. if (p->r.magic == TDB_RECOVERY_INVALID_MAGIC
  61. || p->r.magic == TDB_RECOVERY_MAGIC) {
  62. if (unc) {
  63. tally_add(uncoal, unc);
  64. unc = 0;
  65. }
  66. len = sizeof(p->r) + p->r.max_len;
  67. } else if (frec_magic(&p->f) == TDB_FREE_MAGIC) {
  68. len = frec_len(&p->f);
  69. tally_add(fr, len);
  70. tally_add(buckets, size_to_bucket(len));
  71. len += sizeof(p->u);
  72. unc++;
  73. } else if (rec_magic(&p->u) == TDB_USED_MAGIC) {
  74. if (unc) {
  75. tally_add(uncoal, unc);
  76. unc = 0;
  77. }
  78. len = sizeof(p->u)
  79. + rec_key_length(&p->u)
  80. + rec_data_length(&p->u)
  81. + rec_extra_padding(&p->u);
  82. tally_add(keys, rec_key_length(&p->u));
  83. tally_add(data, rec_data_length(&p->u));
  84. tally_add(extra, rec_extra_padding(&p->u));
  85. } else if (rec_magic(&p->u) == TDB_HTABLE_MAGIC) {
  86. tdb_off_t count = count_hash(tdb,
  87. off + sizeof(p->u),
  88. TDB_SUBLEVEL_HASH_BITS);
  89. if (TDB_OFF_IS_ERR(count)) {
  90. return count;
  91. }
  92. tally_add(hashes, count);
  93. tally_add(extra, rec_extra_padding(&p->u));
  94. len = sizeof(p->u)
  95. + rec_data_length(&p->u)
  96. + rec_extra_padding(&p->u);
  97. } else if (rec_magic(&p->u) == TDB_FTABLE_MAGIC) {
  98. len = sizeof(p->u)
  99. + rec_data_length(&p->u)
  100. + rec_extra_padding(&p->u);
  101. tally_add(ftables, rec_data_length(&p->u));
  102. tally_add(extra, rec_extra_padding(&p->u));
  103. } else if (rec_magic(&p->u) == TDB_CHAIN_MAGIC) {
  104. len = sizeof(p->u)
  105. + rec_data_length(&p->u)
  106. + rec_extra_padding(&p->u);
  107. tally_add(chains, 1);
  108. tally_add(extra, rec_extra_padding(&p->u));
  109. } else {
  110. len = dead_space(tdb, off);
  111. if (TDB_OFF_IS_ERR(len)) {
  112. return len;
  113. }
  114. }
  115. tdb_access_release(tdb, p);
  116. }
  117. if (unc)
  118. tally_add(uncoal, unc);
  119. return TDB_SUCCESS;
  120. }
  121. #define SUMMARY_FORMAT \
  122. "Size of file/data: %zu/%zu\n" \
  123. "Number of records: %zu\n" \
  124. "Smallest/average/largest keys: %zu/%zu/%zu\n%s" \
  125. "Smallest/average/largest data: %zu/%zu/%zu\n%s" \
  126. "Smallest/average/largest padding: %zu/%zu/%zu\n%s" \
  127. "Number of free records: %zu\n" \
  128. "Smallest/average/largest free records: %zu/%zu/%zu\n%s" \
  129. "Number of uncoalesced records: %zu\n" \
  130. "Smallest/average/largest uncoalesced runs: %zu/%zu/%zu\n%s" \
  131. "Number of free lists: %zu\n%s" \
  132. "Toplevel hash used: %u of %u\n" \
  133. "Number of chains: %zu\n" \
  134. "Number of subhashes: %zu\n" \
  135. "Smallest/average/largest subhash entries: %zu/%zu/%zu\n%s" \
  136. "Percentage keys/data/padding/free/rechdrs/freehdrs/hashes: %.0f/%.0f/%.0f/%.0f/%.0f/%.0f/%.0f\n"
  137. #define BUCKET_SUMMARY_FORMAT_A \
  138. "Free bucket %zu: total entries %zu.\n" \
  139. "Smallest/average/largest length: %zu/%zu/%zu\n%s"
  140. #define BUCKET_SUMMARY_FORMAT_B \
  141. "Free bucket %zu-%zu: total entries %zu.\n" \
  142. "Smallest/average/largest length: %zu/%zu/%zu\n%s"
  143. #define HISTO_WIDTH 70
  144. #define HISTO_HEIGHT 20
  145. enum TDB_ERROR tdb_summary(struct tdb_context *tdb,
  146. enum tdb_summary_flags flags,
  147. char **summary)
  148. {
  149. tdb_len_t len;
  150. struct tally *ftables, *hashes, *freet, *keys, *data, *extra, *uncoal,
  151. *buckets, *chains;
  152. char *hashesg, *freeg, *keysg, *datag, *extrag, *uncoalg, *bucketsg;
  153. enum TDB_ERROR ecode;
  154. hashesg = freeg = keysg = datag = extrag = uncoalg = bucketsg = NULL;
  155. ecode = tdb_allrecord_lock(tdb, F_RDLCK, TDB_LOCK_WAIT, false);
  156. if (ecode != TDB_SUCCESS) {
  157. return tdb->last_error = ecode;
  158. }
  159. ecode = tdb_lock_expand(tdb, F_RDLCK);
  160. if (ecode != TDB_SUCCESS) {
  161. tdb_allrecord_unlock(tdb, F_RDLCK);
  162. return tdb->last_error = ecode;
  163. }
  164. /* Start stats off empty. */
  165. ftables = tally_new(HISTO_HEIGHT);
  166. hashes = tally_new(HISTO_HEIGHT);
  167. freet = tally_new(HISTO_HEIGHT);
  168. keys = tally_new(HISTO_HEIGHT);
  169. data = tally_new(HISTO_HEIGHT);
  170. extra = tally_new(HISTO_HEIGHT);
  171. uncoal = tally_new(HISTO_HEIGHT);
  172. buckets = tally_new(HISTO_HEIGHT);
  173. chains = tally_new(HISTO_HEIGHT);
  174. if (!ftables || !hashes || !freet || !keys || !data || !extra
  175. || !uncoal || !buckets || !chains) {
  176. ecode = tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
  177. "tdb_summary: failed to allocate"
  178. " tally structures");
  179. goto unlock;
  180. }
  181. ecode = summarize(tdb, hashes, ftables, freet, keys, data, extra,
  182. uncoal, buckets, chains);
  183. if (ecode != TDB_SUCCESS) {
  184. goto unlock;
  185. }
  186. if (flags & TDB_SUMMARY_HISTOGRAMS) {
  187. hashesg = tally_histogram(hashes, HISTO_WIDTH, HISTO_HEIGHT);
  188. freeg = tally_histogram(freet, HISTO_WIDTH, HISTO_HEIGHT);
  189. keysg = tally_histogram(keys, HISTO_WIDTH, HISTO_HEIGHT);
  190. datag = tally_histogram(data, HISTO_WIDTH, HISTO_HEIGHT);
  191. extrag = tally_histogram(extra, HISTO_WIDTH, HISTO_HEIGHT);
  192. uncoalg = tally_histogram(uncoal, HISTO_WIDTH, HISTO_HEIGHT);
  193. bucketsg = tally_histogram(buckets, HISTO_WIDTH, HISTO_HEIGHT);
  194. }
  195. /* 20 is max length of a %llu. */
  196. len = strlen(SUMMARY_FORMAT) + 33*20 + 1
  197. + (hashesg ? strlen(hashesg) : 0)
  198. + (freeg ? strlen(freeg) : 0)
  199. + (keysg ? strlen(keysg) : 0)
  200. + (datag ? strlen(datag) : 0)
  201. + (extrag ? strlen(extrag) : 0)
  202. + (uncoalg ? strlen(uncoalg) : 0)
  203. + (bucketsg ? strlen(bucketsg) : 0);
  204. *summary = malloc(len);
  205. if (!*summary) {
  206. ecode = tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
  207. "tdb_summary: failed to allocate string");
  208. goto unlock;
  209. }
  210. sprintf(*summary, SUMMARY_FORMAT,
  211. (size_t)tdb->file->map_size,
  212. tally_num(keys) + tally_num(data),
  213. tally_num(keys),
  214. tally_min(keys), tally_mean(keys), tally_max(keys),
  215. keysg ? keysg : "",
  216. tally_min(data), tally_mean(data), tally_max(data),
  217. datag ? datag : "",
  218. tally_min(extra), tally_mean(extra), tally_max(extra),
  219. extrag ? extrag : "",
  220. tally_num(freet),
  221. tally_min(freet), tally_mean(freet), tally_max(freet),
  222. freeg ? freeg : "",
  223. tally_total(uncoal, NULL),
  224. tally_min(uncoal), tally_mean(uncoal), tally_max(uncoal),
  225. uncoalg ? uncoalg : "",
  226. tally_num(buckets),
  227. bucketsg ? bucketsg : "",
  228. (unsigned)count_hash(tdb, offsetof(struct tdb_header,
  229. hashtable),
  230. TDB_TOPLEVEL_HASH_BITS),
  231. 1 << TDB_TOPLEVEL_HASH_BITS,
  232. tally_num(chains),
  233. tally_num(hashes),
  234. tally_min(hashes), tally_mean(hashes), tally_max(hashes),
  235. hashesg ? hashesg : "",
  236. tally_total(keys, NULL) * 100.0 / tdb->file->map_size,
  237. tally_total(data, NULL) * 100.0 / tdb->file->map_size,
  238. tally_total(extra, NULL) * 100.0 / tdb->file->map_size,
  239. tally_total(freet, NULL) * 100.0 / tdb->file->map_size,
  240. (tally_num(keys) + tally_num(freet) + tally_num(hashes))
  241. * sizeof(struct tdb_used_record) * 100.0 / tdb->file->map_size,
  242. tally_num(ftables) * sizeof(struct tdb_freetable)
  243. * 100.0 / tdb->file->map_size,
  244. (tally_num(hashes)
  245. * (sizeof(tdb_off_t) << TDB_SUBLEVEL_HASH_BITS)
  246. + (sizeof(tdb_off_t) << TDB_TOPLEVEL_HASH_BITS)
  247. + sizeof(struct tdb_chain) * tally_num(chains))
  248. * 100.0 / tdb->file->map_size);
  249. unlock:
  250. free(hashesg);
  251. free(freeg);
  252. free(keysg);
  253. free(datag);
  254. free(extrag);
  255. free(uncoalg);
  256. free(bucketsg);
  257. free(hashes);
  258. free(buckets);
  259. free(freet);
  260. free(keys);
  261. free(data);
  262. free(extra);
  263. free(uncoal);
  264. free(ftables);
  265. free(chains);
  266. tdb_allrecord_unlock(tdb, F_RDLCK);
  267. tdb_unlock_expand(tdb, F_RDLCK);
  268. return tdb->last_error = ecode;
  269. }