]> jfr.im git - irc/quakenet/newserv.git/blob - nsmstats/nsmstats.c
LUA: add function for channel chanop notice
[irc/quakenet/newserv.git] / nsmstats / nsmstats.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <math.h>
4 #include <string.h>
5
6 #include "../core/nsmalloc.h"
7 #include "../core/hooks.h"
8 #include "../control/control.h"
9 #include "../lib/version.h"
10
11 MODULE_VERSION("");
12
13 void nsmstats(int hookhum, void *arg);
14 int nsmhistogram(void *sender, int cargc, char **cargv);
15
16 void _init(void) {
17 registerhook(HOOK_CORE_STATSREQUEST, &nsmstats);
18 registercontrolhelpcmd("nsmhistogram", NO_DEVELOPER,1,&nsmhistogram,"Usage: nsmhistogram [pool id]\nDisplays memory information for given pool.");
19 }
20
21 void _fini(void) {
22 deregisterhook(HOOK_CORE_STATSREQUEST, &nsmstats);
23 deregistercontrolcmd("nsmhistogram", &nsmhistogram);
24 }
25
26 static char *formatmbuf(unsigned long count, size_t size, size_t realsize) {
27 static char buf[1024];
28
29 snprintf(buf, sizeof(buf), "%lu items, %luKb allocated for %luKb, %luKb (%.2f%%) overhead", count, (unsigned long)size / 1024, (unsigned long)realsize / 1024, (unsigned long)(realsize - size) / 1024, (double)(realsize - size) / (double)size * 100);
30 return buf;
31 }
32
33 void nsmgenstats(struct nsmpool *pool, double *mean, double *stddev) {
34 unsigned long long int sumsq = 0;
35 struct nsminfo *np;
36
37 *mean = (double)pool->size / pool->count;
38
39 for (np=pool->blocks;np;np=np->next)
40 sumsq+=np->size * np->size;
41
42 *stddev = sqrtf((double)sumsq / pool->count - *mean * *mean);
43 }
44
45 void nsmstats(int hookhum, void *arg) {
46 int i;
47 char buf[1024], extra[1024];
48 unsigned long totalcount = 0;
49 size_t totalsize = 0, totalrealsize = 0;
50 long level = (long)arg;
51
52 for (i=0;i<MAXPOOL;i++) {
53 struct nsmpool *pool=&nsmpools[i];
54 size_t realsize;
55
56 if (!pool->count)
57 continue;
58
59 realsize=pool->size + pool->count * sizeof(struct nsminfo) + sizeof(struct nsmpool);
60
61 totalsize+=pool->size;
62 totalrealsize+=realsize;
63 totalcount+=pool->count;
64
65 if(level > 10) {
66 extra[0] = '\0';
67 if(level > 100) {
68 double mean, stddev;
69 nsmgenstats(pool, &mean, &stddev);
70
71 snprintf(extra, sizeof(extra), ", mean: %.2fKb stddev: %.2fKb", mean / 1024, stddev / 1024);
72 }
73
74 snprintf(buf, sizeof(buf), "NSMalloc: pool %2d (%10s): %s%s", i, nsmpoolnames[i]?nsmpoolnames[i]:"??", formatmbuf(pool->count, pool->size, realsize), extra);
75 triggerhook(HOOK_CORE_STATSREPLY, buf);
76 }
77 }
78
79 snprintf(buf, sizeof(buf), "NSMalloc: pool totals: %s", formatmbuf(totalcount, totalsize, totalrealsize));
80 triggerhook(HOOK_CORE_STATSREPLY, buf);
81 }
82
83 struct nsmhistogram_s {
84 size_t size;
85 unsigned long freq;
86 } nsmhistogram_s;
87
88 static int hcompare_size(const void *a, const void *b) {
89 return ((struct nsmhistogram_s *)a)->size - ((struct nsmhistogram_s *)b)->size;
90 }
91
92 static int hcompare_freq(const void *a, const void *b) {
93 return ((struct nsmhistogram_s *)a)->freq - ((struct nsmhistogram_s *)b)->freq;
94 }
95
96 /*
97 * since this is gonna process over a million allocations, we can't just do
98 * it the simple O(n^2) way, so here's the crazy fast(er) way:
99 * we create a list of all sizes and sort them, then store unique items
100 * we can then calculate frequencies in O(n log n) time by searching this
101 * sorted list using a binary chop...
102 */
103 int nsmhistogram(void *sender, int cargc, char **cargv) {
104 int i, max;
105 unsigned int poolid;
106 struct nsmpool *pool;
107 struct nsminfo *np;
108 struct nsmhistogram_s *freqs;
109 size_t cval;
110 unsigned long dst;
111
112 if(cargc < 1)
113 return CMD_USAGE;
114
115 poolid = atoi(cargv[0]);
116 if(poolid > MAXPOOL) {
117 controlreply(sender, "Bad pool id.");
118 return CMD_ERROR;
119 }
120
121 pool = &nsmpools[poolid];
122 if(pool->count == 0) {
123 controlreply(sender, "Pool is empty.");
124 return CMD_ERROR;
125 }
126
127 freqs = (struct nsmhistogram_s *)malloc(sizeof(struct nsmhistogram_s) * pool->count);
128 if(!freqs) {
129 controlreply(sender, "Error allocating first BIG array.");
130 return CMD_ERROR;
131 }
132
133 /* O(n) */
134 memset(freqs, 0, sizeof(struct nsmhistogram_s) * pool->count);
135 for(i=0,np=pool->blocks;np;np=np->next)
136 freqs[i++].size = np->size;
137
138 /* O(n log n) */
139 qsort(freqs, pool->count, sizeof(struct nsmhistogram_s), hcompare_size);
140
141 /* O(n) */
142 cval = freqs[0].size;
143 dst = 1;
144 for(i=1;i<pool->count;i++) {
145 if(cval != freqs[i].size) {
146 cval = freqs[i].size;
147 freqs[dst++].size = freqs[i].size;
148 }
149 }
150
151 /* outer loop is O(n), inner loop is O(1 + log n) => O(n + n log n) => O(n log n) */
152 for(np=pool->blocks;np;np=np->next) {
153 unsigned long low = 0, high = dst;
154 while(low < high) {
155 unsigned long mid = (low + high) / 2;
156 if (freqs[mid].size < np->size) {
157 low = mid + 1;
158 } else {
159 high = mid;
160 }
161 }
162 if((low > np->size) || (freqs[low].size != np->size)) {
163 controlreply(sender, "ERROR");
164 free(freqs);
165 return CMD_ERROR;
166 } else {
167 freqs[low].freq++;
168 }
169 }
170
171 /* O(n log n) */
172 qsort(freqs, dst, sizeof(struct nsmhistogram_s), hcompare_freq);
173 max = 2000;
174
175 for(i=dst-1;i>=0&&max-->0;i--)
176 controlreply(sender, "%10lu: %10lu", (unsigned long)freqs[i].size, (unsigned long)freqs[i].freq);
177
178 free(freqs);
179 return CMD_OK;
180 }