tizen 2.3 release
[framework/system/swap-probe.git] / helper / damaps.c
1 /*
2  *  DA probe
3  *
4  * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact:
7  *
8  * Cherepanov Vitaliy <v.cherepanov@samsung.com>
9  *
10  * This library is free software; you can redistribute it and/or modify it under
11  * the terms of the GNU Lesser General Public License as published by the
12  * Free Software Foundation; either version 2.1 of the License, or (at your option)
13  * any later version.
14  *
15  * This library is distributed in the hope that it will be useful, but WITHOUT ANY
16  * WARRANTY; without even the implied warranty of MERCHANTABILITY or
17  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
18  * License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this library; if not, write to the Free Software Foundation, Inc., 51
22  * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23  *
24  * Contributors:
25  * - S-Core Co., Ltd
26  *
27  */
28
29 #include <unistd.h>
30 #include "dahelper.h"
31 #include "real_functions.h"
32
33 struct data_list_t {
34         union {
35                 void *next;
36                 void *first;
37         };
38         union {
39                 void *data;
40                 void *tail;
41         };
42         union {
43                 uint32_t hash;
44                 uint32_t count;
45         };
46 };
47
48 struct map_t {
49         uint32_t hash;
50         int is_instrument;
51         void *addr;
52         void *endaddr;
53         char permissions[20];
54         long long offset;
55         char device[256];
56         long long inode;
57         char filename[1024]; /* TODO dynamic allocation */
58 };
59
60 typedef int cmp_by_hash_and_name_t(void *a, void *b);
61
62 /* variables */
63
64 static pthread_mutex_t maps_lock = PTHREAD_MUTEX_INITIALIZER;
65
66 char **map_inst_list = NULL;
67 char *map_inst_list_set = NULL;
68 uint32_t map_inst_count = 0;
69
70 static struct data_list_t sections_list = {
71         {NULL}, /* head element */
72         {NULL}, /* tail for head list el */
73         {0},    /* count */
74 };
75
76 static uint32_t addr_hash_table_el_count_total = 0;
77 static uint32_t addr_hash_table_el_count_buzy = 0;
78 static struct map_t **addr_hash_table = NULL;
79
80 static uint32_t name_hash_table_el_count_total = 0;
81 static uint32_t name_hash_table_el_count_buzy = 0;
82 static struct map_t **name_hash_table = NULL;
83
84 #define MAX_READERS_COUNT 8
85 static sem_t readers_mutex;
86
87 /*
88  *
89  * Functions
90  *
91  */
92
93 static uint32_t calc_string_hash(char *str)
94 {
95         uint32_t res = 0;
96         while (*str != '\0') {
97                 res += *str;
98                 res <<= 1;
99                 str++;
100         }
101         return res;
102 }
103
104 static struct data_list_t *get_first_el(struct data_list_t *list)
105 {
106         return (struct data_list_t *)list->first;
107 }
108
109 static struct data_list_t *get_next_el(struct data_list_t *el)
110 {
111         return (struct data_list_t *)el->next;
112 }
113
114 static void print_map(struct map_t *map)
115 {
116         PRINTMSG("mapp-> %p-%p %s %llx %s %llx %s",
117                   map->addr,
118                   map->endaddr,
119                   map->permissions,
120                   map->offset,
121                   map->device,
122                   map->inode,
123                   map->filename);
124 }
125
126 static int read_mapping_line(FILE *mapfile, struct map_t *m)
127 {
128         char ch1, ch2;
129         int ret = 0;
130
131         if (m == NULL) {
132                 PRINTERR("map_t param is NULL\n");
133                 return 0;
134         }
135
136         ret = fscanf(mapfile, "%p-%p %s %llx %s %llx%c%c",
137                      &m->addr,
138                      &m->endaddr,
139                      (char *)m->permissions,
140                      &m->offset,
141                      (char *)m->device,
142                      &m->inode,
143                      &ch1, &ch2);
144
145         m->is_instrument = 0;
146         if (ret > 0 && ret != EOF) {
147                 int len = 0;
148
149                 if (ch2 != '\n') {
150                         ret = (fgets((char *)m->filename, sizeof(m->filename), mapfile) != NULL);
151                         if (ret) {
152                                 /* remove leading white spaces */
153                                 if (m->filename[0] == ' ') {
154                                         char *p = m->filename;
155                                         while (*p == ' ')
156                                                 p++;
157                                         len = strlen(p);
158                                         memmove(m->filename, p, len);
159                                 } else
160                                         len = strlen(m->filename);
161                                 if (len > 0)
162                                         len--;
163                         }
164                 }
165
166                 m->filename[len] = '\0';
167
168                 return 1;
169         } else
170                 return 0;
171
172         return (ret != 0 && ret != EOF);
173 }
174
175 static void print_list()
176 {
177         struct data_list_t *p = NULL;
178         struct map_t *m = NULL;
179         uint32_t i = 0;
180
181         PRINTMSG("=====================================");
182         for (p = get_first_el(&sections_list); p != NULL && i < sections_list.count; p = p->next) {
183                 m = (struct map_t *)p->data;
184                 PRINTMSG("mapp[%03d]-> %p-%p %s %llx %s %llx %s",
185                          ++i,
186                          m->addr,
187                          m->endaddr,
188                          m->permissions,
189                          m->offset,
190                          m->device,
191                          m->inode,
192                          m->filename);
193                 usleep(500);
194         }
195
196 }
197
198 static struct data_list_t *new_data(void)
199 {
200         struct data_list_t *el = (*real_malloc)(sizeof(*el));
201         el->next = NULL;
202         el->hash = 0;
203         el->data = NULL;
204         return el;
205 }
206
207 static int data_list_append(struct data_list_t *head, struct data_list_t *el)
208 {
209         if (head->first == NULL) {
210                 // empty list
211                 head->first = el;
212                 head->tail = el;
213         } else {
214                 ((struct data_list_t *)head->tail)->next = el;
215                 head->tail = el;
216         }
217         head->count++;
218         return 0;
219 }
220
221 static int add_to_map_list(struct map_t **m)
222 {
223         struct data_list_t *el = new_data();
224         el->data = (void *)(*m);
225         el->hash = (*m)->hash;
226         data_list_append(&sections_list, el);
227         *m = NULL;
228
229         return 0;
230 }
231
232 static void print_list_sorted(struct map_t **list)
233 {
234         struct map_t *m = NULL;
235         uint32_t i = 0;
236
237         PRINTMSG("=====================================");
238         for (i = 0; i < sections_list.count; i++) {
239                 m = list[i];
240                 if (m->is_instrument)
241                 PRINTMSG("mapp[%03d]-> %016X %d <%s>",
242                          i + 1,
243                          m->hash,
244                          m->is_instrument,
245                          m->filename);
246                 usleep(500);
247         }
248 }
249
250 static int realloc_array(struct map_t ***p, uint32_t *count_total,
251                          uint32_t *count_buzy, cmp_by_hash_and_name_t cmp_func)
252 {
253         uint32_t i, j;
254         struct map_t **list;
255         struct data_list_t *el;
256
257         if (*p == NULL) {
258                 /* first init */
259                 if (sections_list.count != 0) {
260                         *p = (*real_malloc)(sizeof(**p) * sections_list.count);
261                         *count_total = sections_list.count;
262                 }
263         } else {
264                 /* not first init */
265                 if (sections_list.count > *count_total) {
266                         /* not enaught space in hash */
267                         *p = realloc(*p, sizeof(**p) * sections_list.count);
268                         *count_total = sections_list.count;
269                 }
270         }
271         *count_buzy = sections_list.count;
272
273         /* fill table */
274         list = *p;
275         el = get_first_el(&sections_list);
276
277         for (i = 0; i < sections_list.count; i++) {
278                 list[i] = (struct map_t *)el->data;
279                 el = get_next_el(el);
280         }
281
282         /* sort */
283         uint32_t max = *count_buzy;
284         struct map_t *tmp;
285         if (cmp_func) {
286                 for (i = 0; i < max; i++)
287                         for (j = i + 1; j < *count_buzy; j++) {
288                                 if (cmp_func(list[i], list[j]) > 0) {
289                                         tmp = list[i];
290                                         list[i] = list[j];
291                                         list[j] = tmp;
292                                 }
293                         }
294                 return 0;
295         }
296
297         return 0;
298 }
299
300 static int create_addr_hash_table()
301 {
302         realloc_array(&addr_hash_table, &addr_hash_table_el_count_total,
303                       &addr_hash_table_el_count_buzy, NULL);
304
305         return 0;
306 }
307
308 static int cmp_by_hash_and_name(void *a, void *b)
309 {
310         if (((struct map_t *)a)->hash != ((struct map_t *)b)->hash) {
311                 /* hash value is major priority */
312                 if (((struct map_t *)a)->hash > ((struct map_t *)b)->hash)
313                         return 1;
314                 else /* if i(a->hash < b->hash) */
315                         return -1;
316         } else {
317                 /* hash equel */
318                 /* retun filename cmp */
319                 return strcmp(((struct map_t *)a)->filename,
320                               ((struct map_t *)b)->filename);
321         }
322
323         /* never happens */
324         return 0;
325 }
326
327 static int create_name_hash_table()
328 {
329         realloc_array(&name_hash_table, &name_hash_table_el_count_total,
330                       &name_hash_table_el_count_buzy, cmp_by_hash_and_name);
331
332         return 0;
333 }
334
335 static struct map_t **get_map_by_filename(char *filename, int *count)
336 {
337         uint32_t hash = calc_string_hash(filename);
338         struct map_t **res = NULL;
339         uint32_t left, right, cur;
340
341         *count = 0;
342
343         if (name_hash_table_el_count_buzy == 0 ||
344             name_hash_table[0]->hash > hash ||
345             name_hash_table[name_hash_table_el_count_buzy - 1]->hash < hash) {
346                 goto find_exit;
347         }
348
349         left = 0;
350         right = name_hash_table_el_count_buzy - 1;
351         cur = (left + right) >> 1;
352         while (left < right) {
353                 if (hash < name_hash_table[cur]->hash) {
354                         right = cur - 1;
355                         cur = (left + right) >> 1;
356                         continue;
357                 }
358                 if (hash > name_hash_table[cur]->hash) {
359                         left = cur + 1;
360                         cur = (left + right) >> 1;
361                         continue;
362                 }
363                 break;
364         }
365
366         /* resolve collisions */
367         if (name_hash_table[cur]->hash == hash) {
368
369                 /* get first with same hash */
370                 while (1) {
371                         if (cur == 0)
372                                 /* top of list */
373                                 break;
374                         if (name_hash_table[cur - 1]->hash != hash)
375                                 /* previous element have different hash */
376                                 break;
377                         /* previous element have the same hash */
378                         cur--;
379                 }
380
381                 /* get first with same hash and filename */
382                 while (1) {
383                         if (cur > name_hash_table_el_count_buzy - 1)
384                                 /* top of list */
385                                 break;
386                         if (name_hash_table[cur]->hash != hash)
387                                 /* previous element have different hash */
388                                 break;
389                         if (strncmp(name_hash_table[cur]->filename, filename, strlen(filename)) == 0)
390                                 break;
391                         /* previous element have the same hash */
392                         cur++;
393                 }
394
395                 /* calculate sections count */
396                 while (cur <= (name_hash_table_el_count_buzy - 1) &&
397                        name_hash_table[cur]->hash == hash &&
398                        strncmp(name_hash_table[cur]->filename, filename, strlen(filename)) == 0) {
399                         if (res == NULL)
400                                 res = &name_hash_table[cur];
401                         cur++;
402                         *count = *count + 1;
403                 }
404         }
405
406 find_exit:
407         return res;
408 }
409
410 static int update_is_instrument_lib_attr_nolock()
411 {
412         uint32_t i;
413         struct map_t **map;
414
415         /* clear is_instrument fields */
416         for (i = 0; i < name_hash_table_el_count_buzy; i++)
417                 name_hash_table[i]->is_instrument = 0;
418
419         /* set is_instrument fields */
420         for (i = 0; i < map_inst_count; i++) {
421                 int count = 0;
422                 map = get_map_by_filename(map_inst_list[i], &count);
423                 for (;count > 0; count--) {
424                         PRINTMSG("set 1!!! = %s [%p:%p]", (*map)->filename,
425                                   (*map)->addr, (*map)->endaddr);
426                         (*map)->is_instrument = 1;
427                         map++;
428                 }
429         }
430
431         return 0;
432         print_list_sorted(name_hash_table);
433 }
434
435 static struct map_t *get_map_by_addr(void *addr)
436 {
437         struct map_t *d = NULL;
438         struct map_t *res = NULL;
439         uint32_t left, right, cur;
440
441         if (addr_hash_table_el_count_buzy == 0)
442                 goto find_exit;
443
444         if (addr < addr_hash_table[0]->addr)
445                 goto find_exit;
446
447         if (addr > addr_hash_table[addr_hash_table_el_count_buzy - 1]->endaddr)
448                 goto find_exit;
449
450         left = 0;
451         right = addr_hash_table_el_count_buzy - 1;
452         cur = 0;
453         while (left < right) {
454                 cur = (left + right) >> 1;
455
456                 d = (struct map_t *)addr_hash_table[cur];
457                 if (addr < d->addr) {
458                         right = cur - 1;
459                         continue;
460                 }
461                 if (addr > d->endaddr) {
462                         left = cur + 1;
463                         continue;
464                 }
465
466                 /* success */
467                 return (addr_hash_table[cur]);
468         }
469
470         if (left == right) {
471                 d = (struct map_t *)addr_hash_table[left];
472                 if ((d->addr <= addr) && (addr <= d->endaddr))
473                         res = addr_hash_table[left];
474         }
475
476 find_exit:
477         return res;
478 }
479
480 static void maps_reader_lock_all()
481 {
482         int i;
483         for (i = 0; i < MAX_READERS_COUNT; i++)
484                 sem_wait(&readers_mutex);
485 }
486
487 static void maps_reader_unlock_all()
488 {
489         int i;
490         for (i = 0; i < MAX_READERS_COUNT; i++)
491                 sem_post(&readers_mutex);
492 }
493
494 static inline void maps_reader_lock()
495 {
496         sem_wait(&readers_mutex);       /* down semaphore */
497 }
498
499 static inline void maps_reader_unlock()
500 {
501         sem_post(&readers_mutex);       /* up semaphore */
502 }
503
504 static inline void maps_writer_lock()
505 {
506         pthread_mutex_lock(&maps_lock);
507 }
508
509 static inline void maps_writer_unlock()
510 {
511         pthread_mutex_unlock(&maps_lock);
512 }
513
514 /* TODO refactor this function to alloc map_inst_list_set and copy it frm src*/
515 // WARNING! this function use maps_set and set it to NULL
516 // so first param must be malloced and do not free maps_set after call
517 int set_map_inst_list(char **maps_set, uint32_t maps_count)
518 {
519         int res = 0;
520         uint32_t i = 0;
521         char *p;
522
523         maps_writer_lock();
524         maps_reader_lock_all();
525
526         if (map_inst_list != NULL) {
527                 free(map_inst_list);
528                 map_inst_list = NULL;
529         }
530
531         if (map_inst_list_set != NULL) {
532                 free(map_inst_list_set);
533                 map_inst_list_set = NULL;
534         }
535
536         map_inst_list = real_malloc(sizeof(*map_inst_list) * maps_count);
537         if (maps_count != 0 && map_inst_list == NULL) {
538                 PRINTERR("Cannot allocate data for map_inst_list\n");
539                 res = -1;
540                 goto unlock_exit;
541         }
542
543         if (maps_set != NULL && *maps_set != NULL && map_inst_list != NULL) {
544                 map_inst_list_set = *maps_set;
545                 map_inst_count = maps_count;
546
547                 /* add library mapping names */
548                 p = map_inst_list_set + sizeof(maps_count);
549                 for (i = 0; i < maps_count; i++) {
550                         map_inst_list[i] = p;
551                         p += strlen(p) + 1;
552                         PRINTMSG("-------> %s", map_inst_list[i]);
553                 }
554         } else {
555                 map_inst_count = 0;
556         }
557
558         res = update_is_instrument_lib_attr_nolock();
559
560         if (maps_set != NULL)
561                 *maps_set = NULL;
562
563 unlock_exit:
564         maps_reader_unlock_all();
565         maps_writer_unlock();
566
567         return res;
568 }
569
570 int maps_make()
571 {
572         int res = 0;
573         maps_writer_lock();
574         maps_reader_lock_all();
575         FILE *f = fopen("/proc/self/maps", "r");
576         struct map_t *map = NULL;
577
578         /* read to exists locations */
579         struct data_list_t *cur = get_first_el(&sections_list);
580         sections_list.count = 0;
581         while (cur != NULL) {
582                 map = (struct map_t *)cur->data;
583                 if (!read_mapping_line(f, map))
584                         break;
585                 if (map->permissions[2] == 'x') {
586                         map->hash = calc_string_hash(map->filename);
587                         cur->hash = map->hash;
588                         sections_list.count++;
589                         cur = get_next_el(cur);
590                 }
591         }
592
593         /* add  locations */
594         map = (*real_malloc)(sizeof(*map));
595         if (map == NULL) {
596                 PRINTERR("Can not alloc data for map\n");
597                 res = -1;
598                 goto unlock_exit;
599         }
600         while (read_mapping_line(f, map)) {
601                 if (map->permissions[2] == 'x') {
602                         map->hash = calc_string_hash(map->filename);
603                         add_to_map_list(&map);
604                         if (map == NULL)
605                                 map = (*real_malloc)(sizeof(*map));
606                 }
607         }
608
609         if (map != NULL)
610                 free(map);
611
612         create_addr_hash_table();
613         create_name_hash_table();
614
615         update_is_instrument_lib_attr_nolock();
616
617 unlock_exit:
618         fclose(f);
619
620         maps_reader_unlock_all();
621         maps_writer_unlock();
622
623         return res;
624 }
625
626 int maps_print_maps_by_addr(const void *addr)
627 {
628         int res = -1;
629         struct map_t *map = NULL;
630
631         maps_reader_lock();
632         map = get_map_by_addr((void *)addr);
633         if (map != NULL)
634                 print_map(map);
635         maps_reader_unlock();
636
637         return res;
638 }
639
640 int maps_is_instrument_section_by_addr_no_remap(const void *addr)
641 {
642         int res = -1;
643         struct map_t *map;
644
645         maps_reader_lock();
646         map = get_map_by_addr((void *)addr);
647         if (map != NULL)
648                 res = map->is_instrument;
649         maps_reader_unlock();
650
651         return res;
652
653 }
654
655 /* interface function */
656 int maps_is_instrument_section_by_addr(const void *addr)
657 {
658         int ret = maps_is_instrument_section_by_addr_no_remap(addr);
659
660         if (ret == -1) {
661                 PRINTMSG("========> hz addr %p. remap", addr);
662                 if (maps_make() != 0) {
663                         PRINTERR("maps make failed\n");
664                         ret = -1;
665                         goto exit;
666                 }
667                 ret = maps_is_instrument_section_by_addr_no_remap(addr);
668                 if (ret == -1) {
669                         print_list();
670                         PRINTERR("!!!!unknown addr %p", addr);
671                         get_map_by_addr((void *)addr);
672                         sleep(2);
673                         exit(0);
674                         ret = 0;
675                 }
676         }
677
678 exit:
679         return ret;
680 }
681
682 /* must be called ones */
683 int maps_init()
684 {
685         int res = 0;
686
687         probeBlockStart();
688         res = sem_init(&readers_mutex, 0, MAX_READERS_COUNT);
689         set_map_inst_list(NULL, 0);
690         probeBlockEnd();
691
692         return res;
693 }