summary.c 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  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 <ccan/tally/tally.h>
  17. #define SUMMARY_FORMAT \
  18. "Size of file/data: %zu/%zu\n" \
  19. "Number of records: %zu\n" \
  20. "Smallest/average/largest keys: %zu/%zu/%zu\n%s" \
  21. "Smallest/average/largest data: %zu/%zu/%zu\n%s" \
  22. "Smallest/average/largest padding: %zu/%zu/%zu\n%s" \
  23. "Number of free records: %zu\n" \
  24. "Smallest/average/largest free records: %zu/%zu/%zu\n%s" \
  25. "Number of uncoalesced records: %zu\n" \
  26. "Smallest/average/largest uncoalesced runs: %zu/%zu/%zu\n%s" \
  27. "Toplevel hash used: %u of %u\n" \
  28. "Number of hashes: %zu\n" \
  29. "Smallest/average/largest hash chains: %zu/%zu/%zu\n%s" \
  30. "Percentage keys/data/padding/free/rechdrs/freehdrs/hashes: %.0f/%.0f/%.0f/%.0f/%.0f/%.0f/%.0f\n"
  31. #define BUCKET_SUMMARY_FORMAT_A \
  32. "Free bucket %zu: total entries %zu.\n" \
  33. "Smallest/average/largest length: %zu/%zu/%zu\n%s"
  34. #define BUCKET_SUMMARY_FORMAT_B \
  35. "Free bucket %zu-%zu: total entries %zu.\n" \
  36. "Smallest/average/largest length: %zu/%zu/%zu\n%s"
  37. #define CAPABILITY_FORMAT \
  38. "Capability %llu%s\n"
  39. #define HISTO_WIDTH 70
  40. #define HISTO_HEIGHT 20
  41. static ntdb_off_t count_hash(struct ntdb_context *ntdb,
  42. ntdb_off_t hash_off,
  43. ntdb_off_t num)
  44. {
  45. const ntdb_off_t *h;
  46. ntdb_off_t i, count = 0;
  47. h = ntdb_access_read(ntdb, hash_off, sizeof(*h) * num, true);
  48. if (NTDB_PTR_IS_ERR(h)) {
  49. return NTDB_ERR_TO_OFF(NTDB_PTR_ERR(h));
  50. }
  51. for (i = 0; i < num; i++)
  52. count += (h[i] != 0);
  53. ntdb_access_release(ntdb, h);
  54. return count;
  55. }
  56. static enum NTDB_ERROR summarize(struct ntdb_context *ntdb,
  57. struct tally *ftables,
  58. struct tally *fr,
  59. struct tally *keys,
  60. struct tally *data,
  61. struct tally *extra,
  62. struct tally *uncoal,
  63. struct tally *hashes,
  64. size_t *num_caps)
  65. {
  66. ntdb_off_t off;
  67. ntdb_len_t len;
  68. ntdb_len_t unc = 0;
  69. for (off = sizeof(struct ntdb_header);
  70. off < ntdb->file->map_size;
  71. off += len) {
  72. const union {
  73. struct ntdb_used_record u;
  74. struct ntdb_free_record f;
  75. struct ntdb_recovery_record r;
  76. } *p;
  77. /* We might not be able to get the whole thing. */
  78. p = ntdb_access_read(ntdb, off, sizeof(p->f), true);
  79. if (NTDB_PTR_IS_ERR(p)) {
  80. return NTDB_PTR_ERR(p);
  81. }
  82. if (frec_magic(&p->f) != NTDB_FREE_MAGIC) {
  83. if (unc > 1) {
  84. tally_add(uncoal, unc);
  85. unc = 0;
  86. }
  87. }
  88. if (p->r.magic == NTDB_RECOVERY_INVALID_MAGIC
  89. || p->r.magic == NTDB_RECOVERY_MAGIC) {
  90. len = sizeof(p->r) + p->r.max_len;
  91. } else if (frec_magic(&p->f) == NTDB_FREE_MAGIC) {
  92. len = frec_len(&p->f);
  93. tally_add(fr, len);
  94. len += sizeof(p->u);
  95. unc++;
  96. } else if (rec_magic(&p->u) == NTDB_USED_MAGIC) {
  97. len = sizeof(p->u)
  98. + rec_key_length(&p->u)
  99. + rec_data_length(&p->u)
  100. + rec_extra_padding(&p->u);
  101. tally_add(keys, rec_key_length(&p->u));
  102. tally_add(data, rec_data_length(&p->u));
  103. tally_add(extra, rec_extra_padding(&p->u));
  104. } else if (rec_magic(&p->u) == NTDB_HTABLE_MAGIC) {
  105. ntdb_off_t count = count_hash(ntdb,
  106. off + sizeof(p->u),
  107. 1 << ntdb->hash_bits);
  108. if (NTDB_OFF_IS_ERR(count)) {
  109. return NTDB_OFF_TO_ERR(count);
  110. }
  111. tally_add(hashes, count);
  112. tally_add(extra, rec_extra_padding(&p->u));
  113. len = sizeof(p->u)
  114. + rec_data_length(&p->u)
  115. + rec_extra_padding(&p->u);
  116. } else if (rec_magic(&p->u) == NTDB_FTABLE_MAGIC) {
  117. len = sizeof(p->u)
  118. + rec_data_length(&p->u)
  119. + rec_extra_padding(&p->u);
  120. tally_add(ftables, rec_data_length(&p->u));
  121. tally_add(extra, rec_extra_padding(&p->u));
  122. } else if (rec_magic(&p->u) == NTDB_CHAIN_MAGIC) {
  123. len = sizeof(p->u)
  124. + rec_data_length(&p->u)
  125. + rec_extra_padding(&p->u);
  126. tally_add(hashes,
  127. rec_data_length(&p->u)/sizeof(ntdb_off_t));
  128. tally_add(extra, rec_extra_padding(&p->u));
  129. } else if (rec_magic(&p->u) == NTDB_CAP_MAGIC) {
  130. len = sizeof(p->u)
  131. + rec_data_length(&p->u)
  132. + rec_extra_padding(&p->u);
  133. (*num_caps)++;
  134. } else {
  135. len = dead_space(ntdb, off);
  136. if (NTDB_OFF_IS_ERR(len)) {
  137. return NTDB_OFF_TO_ERR(len);
  138. }
  139. }
  140. ntdb_access_release(ntdb, p);
  141. }
  142. if (unc)
  143. tally_add(uncoal, unc);
  144. return NTDB_SUCCESS;
  145. }
  146. static void add_capabilities(struct ntdb_context *ntdb, char *summary)
  147. {
  148. ntdb_off_t off, next;
  149. const struct ntdb_capability *cap;
  150. size_t count = 0;
  151. /* Append to summary. */
  152. summary += strlen(summary);
  153. off = ntdb_read_off(ntdb, offsetof(struct ntdb_header, capabilities));
  154. if (NTDB_OFF_IS_ERR(off))
  155. return;
  156. /* Walk capability list. */
  157. for (; off; off = next) {
  158. cap = ntdb_access_read(ntdb, off, sizeof(*cap), true);
  159. if (NTDB_PTR_IS_ERR(cap)) {
  160. break;
  161. }
  162. count++;
  163. sprintf(summary, CAPABILITY_FORMAT,
  164. cap->type & NTDB_CAP_TYPE_MASK,
  165. /* Noopen? How did we get here? */
  166. (cap->type & NTDB_CAP_NOOPEN) ? " (unopenable)"
  167. : ((cap->type & NTDB_CAP_NOWRITE)
  168. && (cap->type & NTDB_CAP_NOCHECK)) ? " (uncheckable,read-only)"
  169. : (cap->type & NTDB_CAP_NOWRITE) ? " (read-only)"
  170. : (cap->type & NTDB_CAP_NOCHECK) ? " (uncheckable)"
  171. : "");
  172. summary += strlen(summary);
  173. next = cap->next;
  174. ntdb_access_release(ntdb, cap);
  175. }
  176. }
  177. _PUBLIC_ enum NTDB_ERROR ntdb_summary(struct ntdb_context *ntdb,
  178. enum ntdb_summary_flags flags,
  179. char **summary)
  180. {
  181. ntdb_len_t len;
  182. size_t num_caps = 0;
  183. struct tally *ftables, *freet, *keys, *data, *extra, *uncoal, *hashes;
  184. char *freeg, *keysg, *datag, *extrag, *uncoalg, *hashesg;
  185. enum NTDB_ERROR ecode;
  186. freeg = keysg = datag = extrag = uncoalg = hashesg = NULL;
  187. ecode = ntdb_allrecord_lock(ntdb, F_RDLCK, NTDB_LOCK_WAIT, false);
  188. if (ecode != NTDB_SUCCESS) {
  189. return ecode;
  190. }
  191. ecode = ntdb_lock_expand(ntdb, F_RDLCK);
  192. if (ecode != NTDB_SUCCESS) {
  193. ntdb_allrecord_unlock(ntdb, F_RDLCK);
  194. return ecode;
  195. }
  196. /* Start stats off empty. */
  197. ftables = tally_new(HISTO_HEIGHT);
  198. freet = tally_new(HISTO_HEIGHT);
  199. keys = tally_new(HISTO_HEIGHT);
  200. data = tally_new(HISTO_HEIGHT);
  201. extra = tally_new(HISTO_HEIGHT);
  202. uncoal = tally_new(HISTO_HEIGHT);
  203. hashes = tally_new(HISTO_HEIGHT);
  204. if (!ftables || !freet || !keys || !data || !extra
  205. || !uncoal || !hashes) {
  206. ecode = ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_ERROR,
  207. "ntdb_summary: failed to allocate"
  208. " tally structures");
  209. goto unlock;
  210. }
  211. ecode = summarize(ntdb, ftables, freet, keys, data, extra,
  212. uncoal, hashes, &num_caps);
  213. if (ecode != NTDB_SUCCESS) {
  214. goto unlock;
  215. }
  216. if (flags & NTDB_SUMMARY_HISTOGRAMS) {
  217. freeg = tally_histogram(freet, HISTO_WIDTH, HISTO_HEIGHT);
  218. keysg = tally_histogram(keys, HISTO_WIDTH, HISTO_HEIGHT);
  219. datag = tally_histogram(data, HISTO_WIDTH, HISTO_HEIGHT);
  220. extrag = tally_histogram(extra, HISTO_WIDTH, HISTO_HEIGHT);
  221. uncoalg = tally_histogram(uncoal, HISTO_WIDTH, HISTO_HEIGHT);
  222. hashesg = tally_histogram(hashes, HISTO_WIDTH, HISTO_HEIGHT);
  223. }
  224. /* 20 is max length of a %llu. */
  225. len = strlen(SUMMARY_FORMAT) + 33*20 + 1
  226. + (freeg ? strlen(freeg) : 0)
  227. + (keysg ? strlen(keysg) : 0)
  228. + (datag ? strlen(datag) : 0)
  229. + (extrag ? strlen(extrag) : 0)
  230. + (uncoalg ? strlen(uncoalg) : 0)
  231. + (hashesg ? strlen(hashesg) : 0)
  232. + num_caps * (strlen(CAPABILITY_FORMAT) + 20
  233. + strlen(" (uncheckable,read-only)"));
  234. *summary = ntdb->alloc_fn(ntdb, len, ntdb->alloc_data);
  235. if (!*summary) {
  236. ecode = ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_ERROR,
  237. "ntdb_summary: failed to allocate string");
  238. goto unlock;
  239. }
  240. sprintf(*summary, SUMMARY_FORMAT,
  241. (size_t)ntdb->file->map_size,
  242. tally_total(keys, NULL) + tally_total(data, NULL),
  243. tally_num(keys),
  244. tally_min(keys), tally_mean(keys), tally_max(keys),
  245. keysg ? keysg : "",
  246. tally_min(data), tally_mean(data), tally_max(data),
  247. datag ? datag : "",
  248. tally_min(extra), tally_mean(extra), tally_max(extra),
  249. extrag ? extrag : "",
  250. tally_num(freet),
  251. tally_min(freet), tally_mean(freet), tally_max(freet),
  252. freeg ? freeg : "",
  253. tally_total(uncoal, NULL),
  254. tally_min(uncoal), tally_mean(uncoal), tally_max(uncoal),
  255. uncoalg ? uncoalg : "",
  256. (unsigned)count_hash(ntdb, sizeof(struct ntdb_header),
  257. 1 << ntdb->hash_bits),
  258. 1 << ntdb->hash_bits,
  259. tally_num(hashes),
  260. tally_min(hashes), tally_mean(hashes), tally_max(hashes),
  261. hashesg ? hashesg : "",
  262. tally_total(keys, NULL) * 100.0 / ntdb->file->map_size,
  263. tally_total(data, NULL) * 100.0 / ntdb->file->map_size,
  264. tally_total(extra, NULL) * 100.0 / ntdb->file->map_size,
  265. tally_total(freet, NULL) * 100.0 / ntdb->file->map_size,
  266. (tally_num(keys) + tally_num(freet) + tally_num(hashes))
  267. * sizeof(struct ntdb_used_record) * 100.0 / ntdb->file->map_size,
  268. tally_num(ftables) * sizeof(struct ntdb_freetable)
  269. * 100.0 / ntdb->file->map_size,
  270. (tally_total(hashes, NULL) * sizeof(ntdb_off_t)
  271. + (sizeof(ntdb_off_t) << ntdb->hash_bits))
  272. * 100.0 / ntdb->file->map_size);
  273. add_capabilities(ntdb, *summary);
  274. unlock:
  275. ntdb->free_fn(freeg, ntdb->alloc_data);
  276. ntdb->free_fn(keysg, ntdb->alloc_data);
  277. ntdb->free_fn(datag, ntdb->alloc_data);
  278. ntdb->free_fn(extrag, ntdb->alloc_data);
  279. ntdb->free_fn(uncoalg, ntdb->alloc_data);
  280. ntdb->free_fn(hashesg, ntdb->alloc_data);
  281. ntdb->free_fn(freet, ntdb->alloc_data);
  282. ntdb->free_fn(keys, ntdb->alloc_data);
  283. ntdb->free_fn(data, ntdb->alloc_data);
  284. ntdb->free_fn(extra, ntdb->alloc_data);
  285. ntdb->free_fn(uncoal, ntdb->alloc_data);
  286. ntdb->free_fn(ftables, ntdb->alloc_data);
  287. ntdb->free_fn(hashes, ntdb->alloc_data);
  288. ntdb_allrecord_unlock(ntdb, F_RDLCK);
  289. ntdb_unlock_expand(ntdb, F_RDLCK);
  290. return ecode;
  291. }