root/src/timed_circle.cc

Revision 5, 7.1 KB (checked in by nick, 3 years ago)
Line 
1#include "apachetop.h"
2
3/* Timed_Circle class; stores all the hits in the last $X seconds.
4 *
5 * tab is a timed_circle_struct pointer, which we malloc to be $X; thus each
6 * struct is one second.
7 *
8 * tab[x].hits is a hit_struct pointer, malloc'ed to roughly how many
9 * hits we expect to see per second. This can be intelligently guessed from
10 * past data where possible. Each struct within this is then one hit.
11*/
12
13#define MAX_BUCKET (bucketsize)
14
15extern time_t now; /* global ApacheTop-wide to save on time() calls */
16extern struct gstat gstats;
17
18extern map *hm, *um, *rm;
19
20int Timed_Circle::create(unsigned int size)
21{
22        /* increment size by 1, because one bucket is always being used for
23         * a transitional second; thus could be empty. So we always have the
24         * requested amount of data (or perhaps slightly more) */
25        size++;
26
27        /* malloc the buckets */
28        tab = (struct timed_circle_struct *)
29            malloc(size * sizeof(struct timed_circle_struct));
30
31        if (tab == NULL)
32                return -1;
33
34        this->initbuckets(0, size);
35
36        bucketsize = size;
37        bucketpos = 0; /* start at the beginning */
38
39        walk_hitpos = walk_bucketpos = 0;
40
41        return 0;
42}
43
44int Timed_Circle::initbuckets(const unsigned int from, const unsigned int to)
45{
46        unsigned int x;
47        struct timed_circle_struct *ptr;
48
49        /* clear some values to 0 */
50        for(x = from, ptr = &tab[x] ; x < to; ++x, ++ptr)
51        {
52                /* set up hits array for this bucket */
53
54                ptr->hitsize = 5;
55                ptr->hits = (HIT *)malloc(sizeof(HIT) * ptr->hitsize);
56                if (!ptr->hits) abort();
57
58                memset(ptr->hits, 0, sizeof(HIT) * ptr->hitsize);
59
60                this->resetbucketstats(x);
61        }
62
63        return 0;
64}
65
66void Timed_Circle::resetbucketstats(const unsigned int r)
67{
68        int x;
69        HIT *hit;
70        struct timed_circle_struct *bucket;
71        //dprintf("resetting bucket stats %d\n", r);
72
73        bucket = &tab[r];
74
75        /* reset statistics for this bucket */
76        bucket->time = now;
77        bucket->bytecount = 0;
78        bucket->reqcount = 0;
79
80        bucket->rc_summary[2] = 0;
81        bucket->rc_summary[3] = 0;
82        bucket->rc_summary[4] = 0;
83        bucket->rc_summary[5] = 0;
84
85
86        /* free up any storage associated with the hit previously in this
87         * circle position. At the moment this is basically removing a
88         * refcount from the maps in it */
89        for(x = 0 ; x < bucket->hitsize ; x++)
90        {
91                hit = &bucket->hits[x];
92                if (hit->host_pos) hm->sub_ref(hit->host_pos);
93                if (hit->url_pos) um->sub_ref(hit->url_pos);
94                if (hit->ref_pos) rm->sub_ref(hit->ref_pos);
95        }
96
97        /* start at the beginning of the HIT array */
98        bucket->hitpos = 0;
99
100        /* and ensure they're all zero'ed */
101        memset(bucket->hits, 0, sizeof(HIT) * bucket->hitsize);
102}
103
104int Timed_Circle::insert(struct logbits lb)
105{
106        short rc_tmp;
107
108        HIT *hit;
109        struct timed_circle_struct *bucket;
110
111        /* if we're in a new second than the current bucket is for.. */
112        // also updates bucketpos
113        this->garbagecollection();
114
115        /* for the relevant bucket for this second.. */
116        bucket = &tab[bucketpos];
117
118        /* see if we have a free slot */
119        if (bucket->hitpos == bucket->hitsize)
120        {
121                /* we don't, increase the size of the hits array */
122                bucket->hits = (HIT *)
123                    realloc(bucket->hits, sizeof(HIT) * bucket->hitsize*2);
124                if (!bucket->hits)
125                {
126                        dprintf("realloc: %s\n", strerror(errno));
127                        abort(); /* FIXME: handle a bit better */
128                }
129
130                /* clear out the new hit positions */
131                memset(&bucket->hits[bucket->hitpos],
132                    0, sizeof(HIT) * bucket->hitsize);
133
134                bucket->hitsize *= 2;
135        }
136
137        /* we do (now) */
138        hit = &(bucket->hits[bucket->hitpos]);
139
140
141        /* free up any storage associated with the hit previously
142         * in this circle position. At the moment this is basically
143         * removing a refcount from the maps in it */
144        if (hit->host_pos) hm->sub_ref(hit->host_pos);
145        if (hit->url_pos) um->sub_ref(hit->url_pos);
146        if (hit->ref_pos) rm->sub_ref(hit->ref_pos);
147
148        /* store the data itself */
149        memcpy(hit, &lb, sizeof(lb));
150
151        /* update stats for this bucket */
152        bucket->reqcount++;
153        bucket->bytecount += lb.bytes;
154        rc_tmp = (int)lb.retcode/100;
155        bucket->rc_summary[rc_tmp]++;
156
157        /* ready for our next hitpos */
158        bucket->hitpos++;
159
160        return 0;
161}
162
163//int Timed_Circle::walk(unsigned int *url_pos, unsigned int *ip_pos,
164//   int *bytes, time_t *time, unsigned int *ipl, unsigned int *retcode)
165int Timed_Circle::walk(struct logbits **lb)
166{
167        struct timed_circle_struct *bucket;
168
169        *lb = NULL;
170
171        //dprintf("entering walk at bucket %d, hit %d\n", walk_bucketpos, walk_hitpos);
172
173        /* see if we're at the end of this bucket */
174        bucket = &tab[walk_bucketpos];
175        if (walk_hitpos == bucket->hitpos)
176        {
177                //dprintf("end of bucket %d\n", walk_bucketpos);
178                /* next bucket */
179                ++walk_bucketpos;
180                walk_hitpos = 0;
181        }
182
183        /* see if we're out of buckets;
184         * bucketsize-1 is the highest we'll ever use */
185        if (walk_bucketpos == MAX_BUCKET)
186        {
187                /* done */
188                walk_bucketpos = 0;
189                walk_hitpos = 0;
190                return -1;
191        }
192
193        bucket = &tab[walk_bucketpos];
194
195        /* anything in this bucket? */
196        while (walk_bucketpos < MAX_BUCKET && bucket->hitpos == 0)
197        {
198                //dprintf("skipping bucket %d\n", walk_bucketpos);
199                /* try the next one */
200                ++walk_bucketpos;
201                walk_hitpos = 0;
202                bucket = &tab[walk_bucketpos];
203                //dprintf("skipped to bucket %d\n", walk_bucketpos);
204
205                /* see if we're out of buckets;
206                 * bucketsize-1 is the highest we'll ever use */
207                if (walk_bucketpos == MAX_BUCKET)
208                {
209                        /* done */
210                        walk_bucketpos = 0;
211                        walk_hitpos = 0;
212                        return -1;
213                }
214        }
215
216        *lb = &(bucket->hits[walk_hitpos]);
217
218        ++walk_hitpos;
219
220        return 0;
221}
222
223void Timed_Circle::updatestats(void)
224{
225        unsigned int x;
226        struct timed_circle_struct *bucket;
227
228        // clear any buckets between last hit and now
229        this->garbagecollection();
230
231        /* now update stats from all buckets with something in them */
232        reqcount = 0; bytecount = 0;
233        rc_summary[2] = rc_summary[3] = rc_summary[4] = rc_summary[5] = 0;
234
235        for(x = 0, bucket = &tab[0] ; x < bucketsize ; ++x, ++bucket)
236        {
237                reqcount += bucket->reqcount;
238                bytecount += bucket->bytecount;
239
240                rc_summary[2] += bucket->rc_summary[2];
241                rc_summary[3] += bucket->rc_summary[3];
242                rc_summary[4] += bucket->rc_summary[4];
243                rc_summary[5] += bucket->rc_summary[5];
244        }
245}
246
247void Timed_Circle::garbagecollection(void)
248{
249        unsigned int x, prevbucket;
250
251        /* clear out the hits from any buckets which we've passed since our
252         * last garbage collection. Basically, if a period of time passes in
253         * between hits, we may go from bucket 1 .. 5 and skip 2/3/4. We
254         * need to empty them here.
255        */
256
257        if (tab[bucketpos].time < now)
258        {
259                prevbucket = bucketpos;
260                bucketpos = (now - gstats.start) % bucketsize;
261
262                if (bucketpos < prevbucket)
263                {
264                        /* we've gone from end of array to start again */
265
266                        /* so first, 0 to wherever */
267                        for (x = 0 ; x <= bucketpos ; ++x)
268                                resetbucketstats(x);
269
270                        /* now from old position to end */
271                        for (x = prevbucket+1 ; x < MAX_BUCKET ; ++x)
272                                resetbucketstats(x);
273                }
274                else
275                {
276                        /* simpler, just (eg) 7 to 9 */
277                        for(x = prevbucket+1 ; x <= bucketpos ; ++x)
278                                resetbucketstats(x);
279                }
280
281                // if we're overrunning, time to loop round
282        //      if (bucketpos == bucketsize)
283        //              bucketpos = 0; // back to first bucket
284        }
285}
286
287
288time_t Timed_Circle::oldest(void)
289{
290        time_t t = now;
291        unsigned int x;
292        struct timed_circle_struct *ptr;
293
294        /* this may take up some considerable time with a large circle? */
295        for(x = 0, ptr = &tab[0] ; x < MAX_BUCKET ; ++x, ++ptr)
296                t = getMIN(ptr->time, t);
297
298        return t;
299}
Note: See TracBrowser for help on using the browser.