]> sourceware.org Git - systemtap.git/blame - runtime/map.c
pre-release update-docs
[systemtap.git] / runtime / map.c
CommitLineData
b4e1e09c
MH
1/* -*- linux-c -*-
2 * Map Functions
73fcca6f 3 * Copyright (C) 2005-2018 Red Hat Inc.
b4e1e09c
MH
4 *
5 * This file is part of systemtap, and is free software. You can
6 * redistribute it and/or modify it under the terms of the GNU General
7 * Public License (GPL); either version 2, or (at your option) any
8 * later version.
9 */
10
11#ifndef _MAP_C_
e32551b1
MH
12#define _MAP_C_
13
b9c556e4
MH
14/** @file map.c
15 * @brief Implements maps (associative arrays) and lists
16 */
17
549b9c3b
MH
18#include "stat-common.c"
19#include "map-stat.c"
e32551b1 20
4c2732a1 21static int int64_eq_p (int64_t key1, int64_t key2)
43614f5d
MH
22{
23 return key1 == key2;
24}
43614f5d 25
4c2732a1 26static void str_copy(char *dest, char *src)
43614f5d 27{
4aa2079a
JS
28 if (src)
29 strlcpy(dest, src, MAP_STRING_LENGTH);
30 else
31 *dest = 0;
43614f5d 32}
43614f5d 33
4c2732a1 34static void str_add(void *dest, char *val)
66f95fac
MH
35{
36 char *dst = (char *)dest;
4aa2079a 37 strlcat(dst, val, MAP_STRING_LENGTH);
66f95fac
MH
38}
39
4c2732a1 40static int str_eq_p (char *key1, char *key2)
43614f5d
MH
41{
42 return strncmp(key1, key2, MAP_STRING_LENGTH - 1) == 0;
43}
44
204b456c 45
e5d2abb5
MH
46/** @addtogroup maps
47 * Implements maps (associative arrays) and lists
48 * @{
49 */
50
7ba70af1 51
204b456c
MH
52/** Get the first element in a map.
53 * @param map
54 * @returns a pointer to the first element.
55 * This is typically used with _stp_map_iter(). See the foreach() macro
56 * for typical usage. It probably does what you want anyway.
57 * @sa foreach
58 */
59
4c2732a1 60static struct map_node *_stp_map_start(MAP map)
204b456c 61{
d4d2275a 62 //dbug ("%lx\n", (long)mlist_next(&map->head));
204b456c 63
d4d2275a 64 if (mlist_empty(&map->head))
204b456c
MH
65 return NULL;
66
d4d2275a 67 return mlist_map_node(mlist_next(&map->head));
204b456c
MH
68}
69
70/** Get the next element in a map.
71 * @param map
72 * @param m a pointer to the current element, returned from _stp_map_start()
73 * or _stp_map_iter().
74 * @returns a pointer to the next element.
75 * This is typically used with _stp_map_start(). See the foreach() macro
76 * for typical usage. It probably does what you want anyway.
77 * @sa foreach
78 */
79
4c2732a1 80static struct map_node *_stp_map_iter(MAP map, struct map_node *m)
204b456c 81{
d4d2275a 82 if (mlist_next(&m->lnode) == &map->head)
204b456c
MH
83 return NULL;
84
d4d2275a 85 return mlist_map_node(mlist_next(&m->lnode));
204b456c
MH
86}
87
a98c930b
AJ
88static struct map_node *_stp_map_iterdel(MAP map, struct map_node *m)
89{
90 struct map_node *r_map;
3a7fec94 91
3a7fec94
AJ
92 r_map = _stp_map_iter(map, m);
93 _new_map_del_node(map, m);
a98c930b 94
a98c930b
AJ
95 return r_map;
96}
97
89ad3fb0
MH
98/** Clears all the elements in a map.
99 * @param map
100 */
101
4c2732a1 102static void _stp_map_clear(MAP map)
89ad3fb0
MH
103{
104 struct map_node *m;
105
89ad3fb0
MH
106 map->num = 0;
107
d4d2275a
JS
108 while (!mlist_empty(&map->head)) {
109 m = mlist_map_node(mlist_next(&map->head));
110
89ad3fb0 111 /* remove node from old hash list */
d4d2275a
JS
112 mhlist_del_init(&m->hnode);
113
89ad3fb0 114 /* remove from entry list */
d4d2275a
JS
115 mlist_del(&m->lnode);
116
89ad3fb0 117 /* add to free pool */
d4d2275a 118 mlist_add(&m->lnode, &map->pool);
89ad3fb0
MH
119 }
120}
121
4c2732a1 122static void _stp_pmap_clear(PMAP pmap)
c5a2dde1
MH
123{
124 int i;
125
1d0e697d 126 for_each_possible_cpu(i) {
db45dfde 127 MAP m = _stp_pmap_get_map (pmap, i);
9f804a24
YZ
128 if (likely(m != NULL))
129 _stp_map_clear(m);
e3575828 130 }
db45dfde 131 _stp_map_clear(_stp_pmap_get_agg(pmap));
c5a2dde1 132}
953c5ad1 133
953c5ad1 134
9262e814 135/* sort keynum values */
e9b2a22d 136#define SORT_COUNT -5 /* see also translate.cxx:visit_foreach_loop */
9262e814
MH
137#define SORT_SUM -4
138#define SORT_MIN -3
139#define SORT_MAX -2
140#define SORT_AVG -1
141
4a61d600 142/* comparison function for sorts. */
1841d2dd
JS
143static int _stp_cmp (struct mlist_head *h1, struct mlist_head *h2,
144 int keynum, int dir, map_get_key_fn get_key)
145{
146 int64_t a = 0, b = 0;
147 int type = END;
148 key_data k1 = (*get_key)(mlist_map_node(h1), keynum, &type);
149 key_data k2 = (*get_key)(mlist_map_node(h2), keynum, NULL);
150 if (type == INT64) {
151 a = k1.val;
152 b = k2.val;
153 } else if (type == STRING) {
154 a = strcmp(k1.strp, k2.strp);
155 b = 0;
156 } else if (type == STAT) {
157 stat_data *sd1 = k1.statp;
158 stat_data *sd2 = k2.statp;
159 switch (keynum) {
160 case SORT_COUNT:
161 a = sd1->count;
162 b = sd2->count;
163 break;
164 case SORT_SUM:
165 a = sd1->sum;
166 b = sd2->sum;
167 break;
168 case SORT_MIN:
169 a = sd1->min;
170 b = sd2->min;
171 break;
172 case SORT_MAX:
173 a = sd1->max;
174 b = sd2->max;
175 break;
176 case SORT_AVG:
22563440
MC
177 a = _stp_div64 (NULL, sd1->sum, sd1->count);
178 b = _stp_div64 (NULL, sd2->sum, sd2->count);
1841d2dd
JS
179 break;
180 default:
181 /* should never happen */
182 a = b = 0;
4a61d600 183 }
4a61d600 184 }
1841d2dd
JS
185 if ((a < b && dir > 0) || (a > b && dir < 0))
186 return 1;
187 return 0;
4a61d600
MH
188}
189
190/* swap function for bubble sort */
d4d2275a 191static inline void _stp_swap (struct mlist_head *a, struct mlist_head *b)
4a61d600 192{
d4d2275a
JS
193 mlist_del(a);
194 mlist_add(a, b);
4a61d600
MH
195}
196
4a61d600
MH
197
198/** Sort an entire array.
199 * Sorts an entire array using merge sort.
200 *
201 * @param map Map
202 * @param keynum 0 for the value, or a positive number for the key number to sort on.
203 * @param dir Sort Direction. -1 for low-to-high. 1 for high-to-low.
204 * @sa _stp_map_sortn()
205 */
206
2473ac1b
JS
207static void _stp_map_sort (MAP map, int keynum, int dir,
208 map_get_key_fn get_key)
4a61d600 209{
d4d2275a 210 struct mlist_head *p, *q, *e, *tail;
1841d2dd 211 int nmerges, psize, qsize, i, insize = 1;
d4d2275a 212 struct mlist_head *head = &map->head;
4a61d600 213
d4d2275a 214 if (mlist_empty(head))
4a61d600
MH
215 return;
216
4a61d600
MH
217 do {
218 tail = head;
d4d2275a 219 p = mlist_next(head);
4a61d600
MH
220 nmerges = 0;
221
222 while (p) {
223 nmerges++;
224 q = p;
225 psize = 0;
226 for (i = 0; i < insize; i++) {
227 psize++;
d4d2275a 228 q = mlist_next(q) == head ? NULL : mlist_next(q);
4a61d600
MH
229 if (!q)
230 break;
231 }
232
233 qsize = insize;
234 while (psize > 0 || (qsize > 0 && q)) {
2473ac1b 235 if (psize && (!qsize || !q ||
1841d2dd 236 !_stp_cmp(p, q, keynum, dir, get_key))) {
4a61d600 237 e = p;
d4d2275a 238 p = mlist_next(p) == head ? NULL : mlist_next(p);
4a61d600
MH
239 psize--;
240 } else {
241 e = q;
d4d2275a 242 q = mlist_next(q) == head ? NULL : mlist_next(q);
4a61d600
MH
243 qsize--;
244 }
d4d2275a 245
4a61d600 246 /* now put 'e' on tail of list and make it our new tail */
d4d2275a
JS
247 mlist_del(e);
248 mlist_add(e, tail);
4a61d600
MH
249 tail = e;
250 }
251 p = q;
252 }
253 insize += insize;
254 } while (nmerges > 1);
255}
256
fbb24b89
MH
257/** Get the top values from an array.
258 * Sorts an array such that the start of the array contains the top
259 * or bottom 'n' values. Use this when sorting the entire array
260 * would be too time-consuming and you are only interested in the
261 * highest or lowest values.
262 *
263 * @param map Map
264 * @param n Top (or bottom) number of elements. 0 sorts the entire array.
265 * @param keynum 0 for the value, or a positive number for the key number to sort on.
266 * @param dir Sort Direction. -1 for low-to-high. 1 for high-to-low.
267 * @sa _stp_map_sort()
268 */
2473ac1b
JS
269static void _stp_map_sortn(MAP map, int n, int keynum, int dir,
270 map_get_key_fn get_key)
fbb24b89 271{
083384f0 272 if (n == 0 || n > 30) {
2473ac1b 273 _stp_map_sort(map, keynum, dir, get_key);
fbb24b89 274 } else {
d4d2275a 275 struct mlist_head *head = &map->head;
97a9ceb2 276 struct mlist_head *c, *a = 0, *last, *tmp;
1841d2dd 277 int num, swaps = 1;
d4d2275a
JS
278
279 if (mlist_empty(head))
fbb24b89 280 return;
d4d2275a 281
fbb24b89
MH
282 /* start off with a modified bubble sort of the first n elements */
283 while (swaps) {
284 num = n;
285 swaps = 0;
d4d2275a
JS
286 a = mlist_next(head);
287 c = mlist_next(mlist_next(a));
288 while ((mlist_next(a) != head) && (--num > 0)) {
1841d2dd 289 if (_stp_cmp(a, mlist_next(a), keynum, dir, get_key)) {
fbb24b89 290 swaps++;
d4d2275a 291 _stp_swap(a, mlist_next(a));
fbb24b89 292 }
d4d2275a
JS
293 a = mlist_prev(c);
294 c = mlist_next(c);
fbb24b89
MH
295 }
296 }
d4d2275a 297
fbb24b89
MH
298 /* Now use a kind of insertion sort for the rest of the array. */
299 /* Each element is tested to see if it should be be in the top 'n' */
300 last = a;
d4d2275a 301 a = mlist_next(a);
fbb24b89 302 while (a != head) {
d4d2275a 303 tmp = mlist_next(a);
fbb24b89 304 c = last;
1841d2dd 305 while (c != head && _stp_cmp(c, a, keynum, dir, get_key))
d4d2275a 306 c = mlist_prev(c);
fbb24b89 307 if (c != last) {
d4d2275a
JS
308 mlist_del(a);
309 mlist_add(a, c);
310 last = mlist_prev(last);
fbb24b89
MH
311 }
312 a = tmp;
313 }
314 }
315}
4a61d600 316
2473ac1b 317static struct map_node *_stp_new_agg(MAP agg, struct mhlist_head *ahead,
b17e1e1e 318 struct map_node *ptr, map_update_fn update)
7ba70af1
MH
319{
320 struct map_node *aptr;
321 /* copy keys and aggregate */
7ba70af1
MH
322 aptr = _new_map_create(agg, ahead);
323 if (aptr == NULL)
51761586 324 return NULL;
b17e1e1e 325 (*update)(agg, aptr, ptr, 0);
51761586 326 return aptr;
7ba70af1
MH
327}
328
8d948813
MH
329/** Aggregate per-cpu maps.
330 * This function aggregates the per-cpu maps into an aggregated
331 * map. A pointer to that aggregated map is returned.
332 *
333 * A write lock must be held on the map during this function.
334 *
335 * @param map A pointer to a pmap.
f903d01c 336 * @returns a pointer to the aggregated map. Null on failure.
8d948813 337 */
b17e1e1e 338static MAP _stp_pmap_agg (PMAP pmap, map_update_fn update, map_cmp_fn cmp)
7ba70af1
MH
339{
340 int i, hash;
341 MAP m, agg;
508e476d 342 struct map_node *ptr, *aptr = NULL;
d4d2275a
JS
343 struct mhlist_head *head, *ahead;
344 struct mhlist_node *e, *f;
82523f19 345 int quit = 0;
7ba70af1 346
db45dfde 347 agg = _stp_pmap_get_agg(pmap);
b17e1e1e 348
7ba70af1
MH
349 /* FIXME. we either clear the aggregation map or clear each local map */
350 /* every time we aggregate. which would be best? */
351 _stp_map_clear (agg);
352
1d0e697d 353 for_each_possible_cpu(i) {
db45dfde 354 m = _stp_pmap_get_map (pmap, i);
9f804a24
YZ
355 if (unlikely(m == NULL)) {
356 /* offline CPU or a newly-added online CPU */
357 continue;
358 }
359
7ba70af1 360 /* walk the hash chains. */
9a38b89c 361 for (hash = 0; hash <= m->hash_table_mask; hash++) {
7ba70af1
MH
362 head = &m->hashes[hash];
363 ahead = &agg->hashes[hash];
d4d2275a 364 mhlist_for_each_entry(ptr, e, head, hnode) {
7ba70af1 365 int match = 0;
d4d2275a 366 mhlist_for_each_entry(aptr, f, ahead, hnode) {
2473ac1b 367 if ((*cmp)(ptr, aptr)) {
7ba70af1
MH
368 match = 1;
369 break;
370 }
371 }
372 if (match)
b17e1e1e 373 (*update)(agg, aptr, ptr, 1);
f903d01c 374 else {
b17e1e1e 375 if (!_stp_new_agg(agg, ahead, ptr, update)) {
961f1254
FCE
376 agg = NULL;
377 goto out;
378 // NB: break would head out to the for (hash...)
379 // loop, which behaves badly with an agg==NULL.
f903d01c
MH
380 }
381 }
7ba70af1
MH
382 }
383 }
384 }
961f1254 385
810762b4 386out:
8d948813 387 return agg;
7ba70af1
MH
388}
389
d4d2275a 390static struct map_node *_new_map_create (MAP map, struct mhlist_head *head)
af5dd76b
MH
391{
392 struct map_node *m;
d4d2275a 393 if (mlist_empty(&map->pool)) {
af5dd76b
MH
394 if (!map->wrap) {
395 /* ERROR. no space left */
396 return NULL;
397 }
d4d2275a
JS
398 m = mlist_map_node(mlist_next(&map->head));
399 mhlist_del_init(&m->hnode);
af5dd76b 400 } else {
d4d2275a 401 m = mlist_map_node(mlist_next(&map->pool));
99675700 402 map->num++;
af5dd76b 403 }
d4d2275a
JS
404 mlist_move_tail(&m->lnode, &map->head);
405
af5dd76b 406 /* add node to new hash list */
d4d2275a 407 mhlist_add_head(&m->hnode, head);
af5dd76b
MH
408 return m;
409}
410
411static void _new_map_del_node (MAP map, struct map_node *n)
412{
413 /* remove node from old hash list */
d4d2275a
JS
414 mhlist_del_init(&n->hnode);
415
af5dd76b 416 /* remove from entry list */
d4d2275a
JS
417 mlist_del(&n->lnode);
418
af5dd76b 419 /* add it back to the pool */
d4d2275a
JS
420 mlist_add(&n->lnode, &map->pool);
421
af5dd76b
MH
422 map->num--;
423}
424
b17e1e1e 425static int _new_map_set_int64 (MAP map, int64_t *dst, int64_t val, int add)
af5dd76b 426{
3c2a749a 427 if (add)
b17e1e1e 428 *dst += val;
3c2a749a 429 else
b17e1e1e 430 *dst = val;
3c2a749a 431
af5dd76b
MH
432 return 0;
433}
434
b17e1e1e 435static int _new_map_set_str (MAP map, char *dst, char *val, int add)
af5dd76b 436{
3c2a749a 437 if (add)
b17e1e1e 438 str_add(dst, val);
3c2a749a 439 else
b17e1e1e 440 str_copy(dst, val);
3c2a749a 441
af5dd76b
MH
442 return 0;
443}
444
26382d61 445static int _new_map_set_stat (MAP map, struct stat_data *sd, int64_t val, int add, int s1, int s2, int s3, int s4, int s5)
af5dd76b 446{
7ba70af1
MH
447 if (!add) {
448 Hist st = &map->hist;
449 sd->count = 0;
450 if (st->type != HIST_NONE) {
451 int j;
452 for (j = 0; j < st->buckets; j++)
af5dd76b
MH
453 sd->histogram[j] = 0;
454 }
455 }
63ead7fa
MC
456 (&map->hist)->bit_shift = map->bit_shift;
457 (&map->hist)->stat_ops = map->stat_ops;
26382d61 458 __stp_stat_add (&map->hist, sd, val, s1, s2, s3, s4, s5);
af5dd76b
MH
459 return 0;
460}
461
b17e1e1e
JS
462static int _new_map_copy_stat (MAP map, struct stat_data *sd1, struct stat_data *sd2, int add)
463{
464 Hist st = &map->hist;
63ead7fa
MC
465 int64_t S11, S12, S21, S22;
466 int64_t sd1_count, sd1_avg_s;
467 S11 = S12 = S21 = S22 = 0;
468 sd1_count = sd1_avg_s = 0;
b17e1e1e 469
226188f2
FCE
470 if (sd2 == NULL) {
471 sd1->count = 0;
472 if (st->type != HIST_NONE) {
473 int j;
474 for (j = 0; j < st->buckets; j++)
475 sd1->histogram[j] = 0;
476 }
477 } else if (add && sd1->count > 0 && sd2->count > 0) {
63ead7fa
MC
478 sd1_count = sd1->count;
479 sd1_avg_s = sd1->avg_s;
480
b17e1e1e
JS
481 sd1->count += sd2->count;
482 sd1->sum += sd2->sum;
483 if (sd2->min < sd1->min)
484 sd1->min = sd2->min;
485 if (sd2->max > sd1->max)
486 sd1->max = sd2->max;
0ec5ddf5
FCE
487
488 if (sd2->stat_ops & STAT_OP_VARIANCE) {
489 sd1->shift = sd2->shift;
490 sd1->avg_s = _stp_div64(NULL, sd1->sum << sd2->shift, sd1->count);
0ec5ddf5
FCE
491
492 /*
493 * For aggregating variance over available CPUs, the Total Variance
494 * formula is being used. This formula is mentioned in following
495 * paper: Niranjan Kamat, Arnab Nandi: A Closer Look at Variance
496 * Implementations In Modern Database Systems: SIGMOD Record 2015.
497 * Available at: http://web.cse.ohio-state.edu/~kamatn/variance.pdf
498 */
499 S11 = sd1_count * (sd1_avg_s - sd1->avg_s) * (sd1_avg_s - sd1->avg_s);
500 S12 = (sd1_count - 1) * sd1->variance_s;
501 S21 = sd2->count * (sd2->avg_s - sd1->avg_s) * (sd2->avg_s - sd1->avg_s);
502 S22 = (sd2->count - 1) * sd2->variance_s;
503
504 sd1->variance_s = _stp_div64(NULL, (S11 + S12 + S21 + S22), (sd1->count - 1));
505 sd1->variance = sd1->variance_s >> (2 * sd2->shift);
506 }
b17e1e1e
JS
507 if (st->type != HIST_NONE) {
508 int j;
509 for (j = 0; j < st->buckets; j++)
510 sd1->histogram[j] += sd2->histogram[j];
511 }
512 } else {
513 sd1->count = sd2->count;
514 sd1->sum = sd2->sum;
515 sd1->min = sd2->min;
516 sd1->max = sd2->max;
0ec5ddf5
FCE
517 if (sd2->stat_ops & STAT_OP_VARIANCE) {
518 sd1->shift = sd2->shift;
519 sd1->avg_s = sd2->avg_s;
0ec5ddf5
FCE
520 sd1->variance_s = sd2->variance_s;
521 sd1->variance = sd2->variance_s >> (2 * sd2->shift);
522 }
b17e1e1e
JS
523 if (st->type != HIST_NONE) {
524 int j;
525 for (j = 0; j < st->buckets; j++)
526 sd1->histogram[j] = sd2->histogram[j];
527 }
528 }
529 return 0;
530}
531
99675700
MH
532/** Return the number of elements in a map
533 * This function will return the number of active elements
534 * in a map.
535 * @param map
536 * @returns an int
537 */
538#define _stp_map_size(map) (map->num)
539
540/** Return the number of elements in a pmap
541 * This function will return the number of active elements
542 * in all the per-cpu maps in a pmap. This is a quick sum and is
543 * not the same as the number of unique elements that would
544 * be in the aggragated map.
545 * @param pmap
546 * @returns an int
547 */
4c2732a1 548static int _stp_pmap_size (PMAP pmap)
99675700
MH
549{
550 int i, num = 0;
551
1d0e697d 552 for_each_possible_cpu(i) {
db45dfde 553 MAP m = _stp_pmap_get_map (pmap, i);
9f804a24
YZ
554 if (unlikely(m == NULL)) {
555 /* offline CPU or a newly-added online CPU */
556 continue;
557 }
e3575828
DS
558 num += m->num;
559 }
99675700
MH
560 return num;
561}
af5dd76b 562#endif /* _MAP_C_ */
204b456c 563
This page took 0.313337 seconds and 6 git commands to generate.