d33e4f3f75bfe11a7059fa22ee0c33f408136586
[platform/core/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
32 struct data_list_t {
33         union {
34                 void *next;
35                 void *first;
36         };
37         union {
38                 void *data;
39                 void *tail;
40         };
41         union {
42                 uint32_t hash;
43                 uint32_t count;
44         };
45 };
46
47 struct map_t {
48         uint32_t hash;
49         int is_instrument;
50         void *addr;
51         void *endaddr;
52         char permissions[20];
53         long long offset;
54         char device[256];
55         long long inode;
56         char filename[1024]; /* TODO dynamic allocation */
57 };
58
59 typedef int cmp_by_hash_and_name_t(void *a, void *b);
60
61 /* variables */
62
63 static pthread_mutex_t maps_lock = PTHREAD_MUTEX_INITIALIZER;
64
65 char **map_inst_list = NULL;
66 char *map_inst_list_set = NULL;
67 uint32_t map_inst_count = 0;
68
69 static struct data_list_t sections_list = {
70         {NULL}, /* head element */
71         {NULL}, /* tail for head list el */
72         {0},    /* count */
73 };
74
75 static uint32_t addr_hash_table_el_count_total = 0;
76 static uint32_t addr_hash_table_el_count_buzy = 0;
77 static struct map_t **addr_hash_table = NULL;
78
79 static uint32_t name_hash_table_el_count_total = 0;
80 static uint32_t name_hash_table_el_count_buzy = 0;
81 static struct map_t **name_hash_table = NULL;
82
83 #define MAX_READERS_COUNT 8
84 static sem_t readers_mutex;
85
86 /*
87  *
88  * Functions
89  *
90  */
91
92 static uint32_t calc_string_hash(char *str)
93 {
94         uint32_t res = 0;
95         while (*str != '\0') {
96                 res += *str;
97                 res <<= 1;
98                 str++;
99         }
100         return res;
101 }
102
103 static struct data_list_t *get_first_el(struct data_list_t *list)
104 {
105         return (struct data_list_t *)list->first;
106 }
107
108 static struct data_list_t *get_next_el(struct data_list_t *el)
109 {
110         return (struct data_list_t *)el->next;
111 }
112
113 static void print_map(struct map_t *map)
114 {
115         PRINTMSG("mapp-> %p-%p %s %llx %s %llx %s",
116                   map->addr,
117                   map->endaddr,
118                   map->permissions,
119                   map->offset,
120                   map->device,
121                   map->inode,
122                   map->filename);
123 }
124
125 static int read_mapping_line(FILE *mapfile, struct map_t *m)
126 {
127         char ch1, ch2;
128         int ret = fscanf(mapfile, "%p-%p %s %llx %s %llx%c%c",
129                          &m->addr,
130                          &m->endaddr,
131                          (char *)m->permissions,
132                          &m->offset,
133                          (char *)m->device,
134                          &m->inode,
135                          &ch1, &ch2);
136
137         m->is_instrument = 0;
138         if (ret > 0 && ret != EOF) {
139                 if (ch2 != '\n') {
140                         ret = (fgets((char *)m->filename, sizeof(m->filename), mapfile) != NULL);
141                         if (ret) {
142                                 int len;
143                                 /* remove leading white spaces */
144                                 if (m->filename[0] == ' ') {
145                                         char *p = m->filename;
146                                         while (*p == ' ')
147                                                 p++;
148                                         len = strlen(p);
149                                         memcpy(m->filename, p, len);
150                                 } else
151                                         len = strlen(m->filename);
152
153                                 m->filename[len-1] = '\0';
154                         }
155                 } else
156                         m->filename[0] = '\0';  /* no filename */
157
158                 return 1;
159         } else
160                 return 0;
161
162         return (ret != 0 && ret != EOF);
163 }
164
165 static void print_list()
166 {
167         struct data_list_t *p = NULL;
168         struct map_t *m = NULL;
169         uint32_t i = 0;
170
171         PRINTMSG("=====================================");
172         for (p = get_first_el(&sections_list); p != NULL && i < sections_list.count; p = p->next) {
173                 m = (struct map_t *)p->data;
174                 PRINTMSG("mapp[%03d]-> %p-%p %s %llx %s %llx %s",
175                          ++i,
176                          m->addr,
177                          m->endaddr,
178                          m->permissions,
179                          m->offset,
180                          m->device,
181                          m->inode,
182                          m->filename);
183         }
184
185 }
186
187 static struct data_list_t *new_data(void)
188 {
189         struct data_list_t *el = (*real_malloc)(sizeof(*el));
190         el->next = NULL;
191         el->hash = 0;
192         el->data = NULL;
193         return el;
194 }
195
196 static int data_list_append(struct data_list_t *head, struct data_list_t *el)
197 {
198         if (head->first == NULL) {
199                 // empty list
200                 head->first = el;
201                 head->tail = el;
202         } else {
203                 ((struct data_list_t *)head->tail)->next = el;
204                 head->tail = el;
205         }
206         head->count++;
207         return 0;
208 }
209
210 static int add_to_map_list(struct map_t **m)
211 {
212         struct data_list_t *el = new_data();
213         el->data = (void *)(*m);
214         el->hash = (*m)->hash;
215         data_list_append(&sections_list, el);
216         *m = NULL;
217
218         return 0;
219 }
220
221 static void print_list_sorted(struct map_t **list)
222 {
223         struct map_t *m = NULL;
224         uint32_t i = 0;
225
226         PRINTMSG("=====================================");
227         for (i = 0; i < sections_list.count; i++) {
228                 m = list[i];
229                 if (m->is_instrument)
230                 PRINTMSG("mapp[%03d]-> %016X %d <%s>",
231                          i + 1,
232                          m->hash,
233                          m->is_instrument,
234                          m->filename);
235         }
236 }
237
238 static int realloc_array(struct map_t ***p, uint32_t *count_total,
239                          uint32_t *count_buzy, cmp_by_hash_and_name_t cmp_func)
240 {
241         uint32_t i, j;
242         struct map_t **list;
243         struct data_list_t *el;
244
245         if (*p == NULL) {
246                 /* first init */
247                 if (sections_list.count != 0) {
248                         *p = (*real_malloc)(sizeof(**p) * sections_list.count);
249                         *count_total = sections_list.count;
250                 }
251         } else {
252                 /* not first init */
253                 if (sections_list.count > *count_total) {
254                         /* not enaught space in hash */
255                         *p = realloc(*p, sizeof(**p) * sections_list.count);
256                         *count_total = sections_list.count;
257                 }
258         }
259         *count_buzy = sections_list.count;
260
261         /* fill table */
262         list = *p;
263         el = get_first_el(&sections_list);
264
265         for (i = 0; i < sections_list.count; i++) {
266                 list[i] = (struct map_t *)el->data;
267                 el = get_next_el(el);
268         }
269
270         /* sort */
271         uint32_t max = *count_buzy;
272         struct map_t *tmp;
273         if (cmp_func) {
274                 for (i = 0; i < max; i++)
275                         for (j = i + 1; j < *count_buzy; j++) {
276                                 if (cmp_func(list[i], list[j]) > 0) {
277                                         tmp = list[i];
278                                         list[i] = list[j];
279                                         list[j] = tmp;
280                                 }
281                         }
282                 return 0;
283         }
284
285         return 0;
286 }
287
288 static int create_addr_hash_table()
289 {
290         realloc_array(&addr_hash_table, &addr_hash_table_el_count_total,
291                       &addr_hash_table_el_count_buzy, NULL);
292
293         return 0;
294 }
295
296 static int cmp_by_hash_and_name(void *a, void *b)
297 {
298         if (((struct map_t *)a)->hash != ((struct map_t *)b)->hash) {
299                 /* hash value is major priority */
300                 if (((struct map_t *)a)->hash > ((struct map_t *)b)->hash)
301                         return 1;
302                 else /* if i(a->hash < b->hash) */
303                         return -1;
304         } else {
305                 /* hash equel */
306                 /* retun filename cmp */
307                 return strcmp(((struct map_t *)a)->filename,
308                               ((struct map_t *)b)->filename);
309         }
310
311         /* never happens */
312         return 0;
313 }
314
315 static int create_name_hash_table()
316 {
317         realloc_array(&name_hash_table, &name_hash_table_el_count_total,
318                       &name_hash_table_el_count_buzy, cmp_by_hash_and_name);
319
320         return 0;
321 }
322
323 static struct map_t *get_map_by_filename(char *filename)
324 {
325         uint32_t hash = calc_string_hash(filename);
326         struct map_t *res = NULL;
327         uint32_t left, right, cur;
328
329         if (name_hash_table_el_count_buzy == 0 ||
330             name_hash_table[0]->hash > hash ||
331             name_hash_table[name_hash_table_el_count_buzy - 1]->hash < hash) {
332                 goto find_exit;
333         }
334
335         left = 0;
336         right = name_hash_table_el_count_buzy - 1;
337         cur = (left + right) >> 1;
338         while (left < right) {
339                 if (hash < name_hash_table[cur]->hash) {
340                         right = cur - 1;
341                         cur = (left + right) >> 1;
342                         continue;
343                 }
344                 if (hash > name_hash_table[cur]->hash) {
345                         left = cur + 1;
346                         cur = (left + right) >> 1;
347                         continue;
348                 }
349                 break;
350         }
351
352         /* resolve collisions */
353         if (name_hash_table[cur]->hash == hash) {
354                 while (1) {
355                         if (cur == 0)
356                                 /* top of list */
357                                 break;
358                         if (name_hash_table[cur - 1]->hash != hash)
359                                 /* previous element have different hash */
360                                 break;
361                         /* previous element have the same hash */
362                         cur--;
363                 }
364                 res = name_hash_table[cur];
365         }
366
367 find_exit:
368         return res;
369 }
370
371 static int update_is_instrument_lib_attr_nolock()
372 {
373         uint32_t i;
374         struct map_t *map;
375
376         /* clear is_instrument fields */
377         for (i = 0; i < name_hash_table_el_count_buzy; i++)
378                 name_hash_table[i]->is_instrument = 0;
379
380         /* set is_instrument fields */
381         for (i = 0; i < map_inst_count; i++) {
382                 map = get_map_by_filename(map_inst_list[i]);
383                 if (map) {
384                         PRINTMSG("set 1!!! = %s [%p:%p]", map->filename,
385                                   map->addr, map->endaddr);
386                         map->is_instrument = 1;
387                 }
388         }
389
390         return 0;
391         print_list_sorted(name_hash_table);
392 }
393
394 static struct map_t *get_map_by_addr(void *addr)
395 {
396         struct map_t *d = NULL;
397         struct map_t *res = NULL;
398         uint32_t left, right, cur;
399
400         if (addr_hash_table_el_count_buzy == 0)
401                 goto find_exit;
402
403         if (addr < addr_hash_table[0]->addr)
404                 goto find_exit;
405
406         if (addr > addr_hash_table[addr_hash_table_el_count_buzy - 1]->addr)
407                 goto find_exit;
408
409         left = 0;
410         right = addr_hash_table_el_count_buzy - 1;
411         cur = 0;
412         while (left < right) {
413                 cur = (left + right) >> 1;
414
415                 d = (struct map_t *)addr_hash_table[cur];
416                 if (addr < d->addr) {
417                         right = cur - 1;
418                         continue;
419                 }
420                 if (addr > d->endaddr) {
421                         left = cur + 1;
422                         continue;
423                 }
424
425                 /* success */
426                 return (addr_hash_table[cur]);
427         }
428
429         if (left == right) {
430                 d = (struct map_t *)addr_hash_table[left];
431                 if ((d->addr <= addr) && (addr <= d->endaddr))
432                         res = addr_hash_table[left];
433         }
434
435 find_exit:
436         return res;
437 }
438
439 static void maps_reader_lock_all()
440 {
441         int i;
442         for (i = 0; i < MAX_READERS_COUNT; i++)
443                 sem_wait(&readers_mutex);
444 }
445
446 static void maps_reader_unlock_all()
447 {
448         int i;
449         for (i = 0; i < MAX_READERS_COUNT; i++)
450                 sem_post(&readers_mutex);
451 }
452
453 static inline void maps_reader_lock()
454 {
455         sem_wait(&readers_mutex);       /* down semaphore */
456 }
457
458 static inline void maps_reader_unlock()
459 {
460         sem_post(&readers_mutex);       /* up semaphore */
461 }
462
463 static inline void maps_writer_lock()
464 {
465         pthread_mutex_lock(&maps_lock);
466 }
467
468 static inline void maps_writer_unlock()
469 {
470         pthread_mutex_unlock(&maps_lock);
471 }
472
473 // WARNING! this function use maps_set and set it to NULL
474 // so first param must be malloced and do not free maps_set after call
475 int set_map_inst_list(char **maps_set, uint32_t maps_count)
476 {
477         int res = 0;
478         uint32_t i = 0;
479         char *p;
480
481         maps_writer_lock();
482         maps_reader_lock_all();
483
484         if (map_inst_list != NULL) {
485                 free(map_inst_list);
486                 map_inst_list = NULL;
487         }
488
489         if (map_inst_list_set != NULL) {
490                 free(map_inst_list_set);
491                 map_inst_list_set = NULL;
492         }
493
494         map_inst_list = real_malloc(sizeof(*map_inst_list) * maps_count);
495         if (maps_set != NULL && *maps_set != NULL)
496                 map_inst_list_set = *maps_set;
497         map_inst_count = maps_count;
498
499         /* add library mapping names */
500         p = map_inst_list_set + sizeof(maps_count);
501         for (i = 0; i < maps_count; i++) {
502                 map_inst_list[i] = p;
503                 p += strlen(p) + 1;
504                 PRINTMSG("-------> %s", map_inst_list[i]);
505         }
506
507         res = update_is_instrument_lib_attr_nolock();
508
509         if (maps_set != NULL)
510                 *maps_set = NULL;
511
512         maps_reader_unlock_all();
513         maps_writer_unlock();
514
515         return res;
516 }
517
518 void maps_make()
519 {
520         maps_writer_lock();
521         maps_reader_lock_all();
522         FILE *f = fopen("/proc/self/maps", "r");
523         struct map_t *map = NULL;
524
525         /* read to exists locations */
526         struct data_list_t *cur = get_first_el(&sections_list);
527         sections_list.count = 0;
528         while (cur != NULL) {
529                 map = (struct map_t *)cur->data;
530                 if (!read_mapping_line(f, map))
531                         break;
532                 if (map->permissions[2] == 'x') {
533                         map->hash = calc_string_hash(map->filename);
534                         cur->hash = map->hash;
535                         sections_list.count++;
536                         cur = get_next_el(cur);
537                 }
538         }
539
540         /* add  locations */
541         map = (*real_malloc)(sizeof(*map));
542         while (read_mapping_line(f, map)) {
543                 if (map->permissions[2] == 'x') {
544                         map->hash = calc_string_hash(map->filename);
545                         add_to_map_list(&map);
546                         if (map == NULL)
547                                 map = (*real_malloc)(sizeof(*map));
548                 }
549         }
550
551         if (map != NULL)
552                 free(map);
553
554         fclose(f);
555
556         create_addr_hash_table();
557         create_name_hash_table();
558
559         update_is_instrument_lib_attr_nolock();
560
561         maps_reader_unlock_all();
562         maps_writer_unlock();
563 }
564
565 int maps_print_maps_by_addr(const void *addr)
566 {
567         int res = -1;
568         struct map_t *map = NULL;
569
570         maps_reader_lock();
571         map = get_map_by_addr((void *)addr);
572         if (map != NULL)
573                 print_map(map);
574         maps_reader_unlock();
575
576         return res;
577 }
578
579 int maps_is_instrument_section_by_addr_no_remap(const void *addr)
580 {
581         int res = -1;
582         struct map_t *map;
583
584         maps_reader_lock();
585         map = get_map_by_addr((void *)addr);
586         if (map != NULL)
587                 res = map->is_instrument;
588         maps_reader_unlock();
589
590         return res;
591
592 }
593
594 /* interface function */
595 int maps_is_instrument_section_by_addr(const void *addr)
596 {
597         int ret = maps_is_instrument_section_by_addr_no_remap(addr);
598
599         if (ret == -1) {
600                 PRINTMSG("========> hz addr %p. remap", addr);
601                 maps_make();
602                 ret = maps_is_instrument_section_by_addr_no_remap(addr);
603                 if (ret == -1) {
604                         print_list();
605                         PRINTERR("!!!!unknown addr %p", addr);
606                         get_map_by_addr((void *)addr);
607                         sleep(2);
608                         exit(0);
609                         ret = 0;
610                 }
611         }
612
613         return ret;
614 }
615
616 /* must be called ones */
617 int maps_init()
618 {
619         int res = 0;
620
621         probeBlockStart();
622         res = sem_init(&readers_mutex, 0, MAX_READERS_COUNT);
623         set_map_inst_list(NULL, 0);
624         probeBlockEnd();
625
626         return res;
627 }