1 /**
2 * @file jitsymbol.c
3 * Handle symbols found in jitted code dump
4 *
5 * @remark Copyright 2007 OProfile authors
6 * @remark Read the file COPYING
7 *
8 * @author Jens Wilke
9 * @Modifications Maynard Johnson
10 * @Modifications Philippe Elie
11 * @Modifications Daniel Hansel
12 *
13 * Copyright IBM Corporation 2007
14 *
15 */
16
17 #include "opjitconv.h"
18 #include "opd_printf.h"
19 #include "op_libiberty.h"
20 #include "op_types.h"
21
22 #include <stddef.h>
23 #include <stdlib.h>
24 #include <stdint.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <limits.h>
29
30 typedef int (*compare_symbol)(void const *, void const *);
31
32
33 /* count the entries in the jitentry_list */
count_entries(void)34 static u32 count_entries(void)
35 {
36 struct jitentry const * entry;
37 u32 cnt = 0;
38 for (entry = jitentry_list; entry; entry = entry->next)
39 cnt++;
40 return cnt;
41 }
42
43
fill_entry_array(struct jitentry * entries[])44 static void fill_entry_array(struct jitentry * entries[])
45 {
46 int i = 0;
47 struct jitentry * entry;
48 for (entry = jitentry_list; entry; entry = entry->next)
49 entries[i++] = entry;
50 }
51
52
53 /* create an array pointing to the jitentry structures which is sorted
54 * according to the comparator rule given by parameter compar
55 */
create_sorted_array(compare_symbol compare)56 static struct jitentry ** create_sorted_array(compare_symbol compare)
57 {
58 struct jitentry ** array =
59 xmalloc(sizeof(struct jitentry *) * entry_count);
60 fill_entry_array(array);
61 qsort(array, entry_count, sizeof(struct jitentry *), compare);
62 return array;
63 }
64
65
66 /* comparator method for qsort which sorts jitentries by symbol_name */
cmp_symbolname(void const * a,void const * b)67 static int cmp_symbolname(void const * a, void const * b)
68 {
69 struct jitentry * a0 = *(struct jitentry **) a;
70 struct jitentry * b0 = *(struct jitentry **) b;
71 return strcmp(a0->symbol_name, b0->symbol_name);
72 }
73
74
75 /* comparator method for qsort which sorts jitentries by address */
cmp_address(void const * a,void const * b)76 static int cmp_address(void const * a, void const * b)
77 {
78 struct jitentry * a0 = *(struct jitentry **) a;
79 struct jitentry * b0 = *(struct jitentry **) b;
80 if (a0->vma < b0->vma)
81 return -1;
82 if (a0->vma == b0->vma)
83 return 0;
84 return 1;
85 }
86
87
88 /* resort address_ascending array */
resort_address(void)89 static void resort_address(void)
90 {
91 u32 i;
92
93 qsort(entries_address_ascending, entry_count,
94 sizeof(struct jitentry *), cmp_address);
95
96 // lower entry_count if entries are invalidated
97 for (i = 0; i < entry_count; ++i) {
98 if (entries_address_ascending[i]->vma)
99 break;
100 }
101
102 if (i) {
103 entry_count -= i;
104 memmove(&entries_address_ascending[0],
105 &entries_address_ascending[i],
106 sizeof(struct jitentry *) * entry_count);
107 }
108 }
109
110
111 /* Copy address_ascending array to entries_symbols_ascending and resort it. */
resort_symbol(void)112 static void resort_symbol(void)
113 {
114 memcpy(entries_symbols_ascending, entries_address_ascending,
115 sizeof(struct jitentry *) * entry_count);
116 qsort(entries_symbols_ascending, entry_count,
117 sizeof(struct jitentry *), cmp_symbolname);
118 }
119
120 /* allocate, populate and sort the jitentry arrays */
create_arrays(void)121 void create_arrays(void)
122 {
123 max_entry_count = entry_count = count_entries();
124 entries_symbols_ascending = create_sorted_array(cmp_symbolname);
125 entries_address_ascending = create_sorted_array(cmp_address);
126 }
127
128
129 /* add a new create jitentry to the array. mallocs new arrays if space is
130 * needed */
insert_entry(struct jitentry * entry)131 static void insert_entry(struct jitentry * entry)
132 {
133 if (entry_count == max_entry_count) {
134 if (max_entry_count < UINT32_MAX - 18)
135 max_entry_count += 18;
136 else if (max_entry_count < UINT32_MAX)
137 max_entry_count += 1;
138 else {
139 fprintf(stderr, "Amount of JIT dump file entries is too large.\n");
140 exit(EXIT_FAILURE);
141 }
142 entries_symbols_ascending = (struct jitentry **)
143 xrealloc(entries_symbols_ascending,
144 sizeof(struct jitentry *) * max_entry_count);
145 entries_address_ascending = (struct jitentry **)
146 xrealloc(entries_address_ascending,
147 sizeof(struct jitentry *) * max_entry_count);
148 }
149 entries_address_ascending[entry_count++] = entry;
150 }
151
152
153 /* add a suffix to the name to differenciate it */
replacement_name(char * s,int i)154 static char * replacement_name(char * s, int i)
155 {
156 int cnt = 1;
157 int j = i;
158 char * res;
159
160 while ((j = j/10))
161 cnt++;
162 cnt += 2 + strlen(s);
163 res = xmalloc(cnt);
164 snprintf(res, cnt, "%s~%i", s, i);
165 return res;
166 }
167
168
169 /*
170 * Mark the entry so it is not included in the ELF file. We do this by
171 * writing a 0 address as magic vma and sorting
172 * it out later
173 */
invalidate_entry(struct jitentry * e)174 static void invalidate_entry(struct jitentry * e)
175 {
176 verbprintf(debug, "invalidate_entry: addr=%llx, name=%s\n",
177 e->vma, e->symbol_name);
178 e->vma = 0;
179 }
180
181
182 /*
183 * Invalidate all symbols that are not alive at sampling start time.
184 */
invalidate_earlybirds(unsigned long long start_time)185 static void invalidate_earlybirds(unsigned long long start_time)
186 {
187 u32 i;
188 int flag;
189 struct jitentry * a;
190
191 flag = 0;
192 for (i = 0; i < entry_count; i++) {
193 a = entries_address_ascending[i];
194 if (a->life_end < start_time) {
195 invalidate_entry(a);
196 flag = 1;
197 }
198 }
199 if (flag) {
200 resort_address();
201 resort_symbol();
202 }
203 }
204
205
206 /* select the symbol with the longest life time in the index range */
select_one(int start_idx,int end_idx)207 static int select_one(int start_idx, int end_idx)
208 {
209 int i;
210 int candidate = OP_JIT_CONV_FAIL;
211 unsigned long long lifetime = 0;
212 unsigned long long x;
213 struct jitentry const * e;
214
215 for (i = start_idx; i <= end_idx; i++) {
216 e = entries_address_ascending[i];
217 x = e->life_end - e->life_start;
218 if (candidate == -1 || x > lifetime) {
219 candidate = i;
220 lifetime = x;
221 }
222 }
223 return candidate;
224 }
225
226
227 /*
228 * We decided to keep one entry in favor of the other. Instead of dropping
229 * the overlapping entry we split or truncate it to not overlap any more.
230 *
231 * Looking on the address regions, we may have the following situation:
232 *
233 * split: |------------|
234 * keep: |---|
235 *
236 * The split entry may be splitted in a left part and a right part. E.g.:
237 *
238 * split0/1: |--| |---|
239 * keep: |---|
240 *
241 * However, both parts may or may not exist.
242 */
split_entry(struct jitentry * split,struct jitentry const * keep)243 static void split_entry(struct jitentry * split, struct jitentry const * keep)
244 {
245 unsigned long long start_addr_keep = keep->vma;
246 unsigned long long end_addr_keep = keep->vma + keep->code_size;
247 unsigned long long end_addr_split = split->vma + split->code_size;
248 unsigned long long start_addr_split = split->vma;
249
250 // do we need a right part?
251 if (end_addr_split > end_addr_keep) {
252 struct jitentry * new_entry =
253 xcalloc(1, sizeof(struct jitentry));
254 char * s = NULL;
255
256 /* Check for max. length to avoid possible integer overflow. */
257 if (strlen(split->symbol_name) > SIZE_MAX - 3) {
258 fprintf(stderr, "Length of symbol name is too large.\n");
259 exit(EXIT_FAILURE);
260 } else {
261 s = xmalloc(strlen(split->symbol_name) + 3);
262 strcpy(s, split->symbol_name);
263 strcat(s, "#1");
264 }
265
266 new_entry->vma = end_addr_keep;
267 new_entry->code_size = end_addr_split - end_addr_keep;
268 new_entry->symbol_name = s;
269 new_entry->sym_name_malloced = 1;
270 new_entry->life_start = split->life_start;
271 new_entry->life_end = split->life_end;
272 // the right part does not have an associated code, because we
273 // don't know whether the split part begins at an opcode
274 new_entry->code = NULL;
275 verbprintf(debug, "split right (new) name=%s, start=%llx,"
276 " end=%llx\n", new_entry->symbol_name,
277 new_entry->vma,
278 new_entry->vma + new_entry->code_size);
279 insert_entry(new_entry);
280 }
281 // do we need a left part?
282 if (start_addr_split < start_addr_keep) {
283 char * s = NULL;
284
285 /* Check for max. length to avoid possible integer overflow. */
286 if (strlen(split->symbol_name) > SIZE_MAX - 3) {
287 fprintf(stderr, "Length of symbol name is too large.\n");
288 exit(EXIT_FAILURE);
289 } else {
290 s = xmalloc(strlen(split->symbol_name) + 3);
291 strcpy(s, split->symbol_name);
292 strcat(s, "#0");
293 }
294
295 split->code_size = start_addr_keep - start_addr_split;
296 if (split->sym_name_malloced)
297 free(split->symbol_name);
298 split->symbol_name = s;
299 split->sym_name_malloced = 1;
300 verbprintf(debug, "split left name=%s, start=%llx, end=%llx\n",
301 split->symbol_name, split->vma,
302 split->vma + split->code_size);
303 } else {
304 invalidate_entry(split);
305 }
306 }
307
308
309 /*
310 * Scans through the index range and splits/truncates entries that overlap
311 * with the one indexed by keep_idx. Returns the total lifetime of the symbols
312 * found to overlap.
313 * Returns ULONG_MAX on error.
314 */
eliminate_overlaps(int start_idx,int end_idx,int keep_idx)315 static unsigned long long eliminate_overlaps(int start_idx, int end_idx,
316 int keep_idx)
317 {
318 unsigned long long retval;
319 struct jitentry const * keep = entries_address_ascending[keep_idx];
320 struct jitentry * e;
321 unsigned long long start_addr_keep = keep->vma;
322 unsigned long long end_addr_keep = keep->vma + keep->code_size;
323 unsigned long long start_addr_entry, end_addr_entry;
324 int i;
325 unsigned long long min_start = keep->life_start;
326 unsigned long long max_end = keep->life_end;
327
328 for (i = start_idx; i <= end_idx; i++) {
329 if (i == keep_idx)
330 continue;
331 e = entries_address_ascending[i];
332 start_addr_entry = e->vma;
333 end_addr_entry = e->vma + e->code_size;
334 if (debug) {
335 if (!(start_addr_entry <= end_addr_entry)) {
336 verbprintf(debug, "assert failed:"
337 " start_addr_entry <="
338 " end_addr_entry\n");
339 retval = ULONG_MAX;
340 goto out;
341 }
342 if (!(start_addr_keep <= end_addr_keep)) {
343 verbprintf(debug, "assert failed: "
344 "start_addr_keep <="
345 " end_addr_keep\n");
346 retval = ULONG_MAX;
347 goto out;
348 }
349 }
350 if (start_addr_entry < end_addr_keep &&
351 end_addr_entry > start_addr_keep) {
352 if (e->life_start < min_start)
353 min_start = e->life_start;
354 if (e->life_end > max_end)
355 max_end = e->life_end;
356 split_entry(e, keep);
357 }
358 }
359 retval = max_end - min_start;
360 out:
361 return retval;
362 }
363
364
365 /*
366 * Within the index range (into the array entries_address_ascending), find the
367 * symbol with the maximal lifetime and split/truncate all symbols that overlap
368 * with it (i.e. that there won't be any overlaps anymore).
369 */
handle_overlap_region(int start_idx,int end_idx)370 static int handle_overlap_region(int start_idx, int end_idx)
371 {
372 int rc = OP_JIT_CONV_OK;
373 int idx;
374 struct jitentry * e;
375 int cnt;
376 char * name;
377 int i, j;
378 unsigned long long totaltime;
379
380 if (debug) {
381 for (i = start_idx; i <= end_idx; i++) {
382 e = entries_address_ascending[i];
383 verbprintf(debug, "overlap idx=%i, name=%s, "
384 "start=%llx, end=%llx, life_start=%lli, "
385 "life_end=%lli, lifetime=%lli\n",
386 i, e->symbol_name, e->vma,
387 e->vma + e->code_size, e->life_start,
388 e->life_end, e->life_end - e->life_start);
389 }
390 }
391 idx = select_one(start_idx, end_idx);
392 totaltime = eliminate_overlaps(start_idx, end_idx, idx);
393 if (totaltime == ULONG_MAX) {
394 rc = OP_JIT_CONV_FAIL;
395 goto out;
396 }
397 e = entries_address_ascending[idx];
398
399 cnt = 1;
400 j = (e->life_end - e->life_start) * 100 / totaltime;
401 while ((j = j/10))
402 cnt++;
403
404 // Mark symbol name with a %% to indicate the overlap.
405 cnt += strlen(e->symbol_name) + 2 + 1;
406 name = xmalloc(cnt);
407 snprintf(name, cnt, "%s%%%llu", e->symbol_name,
408 (e->life_end - e->life_start) * 100 / totaltime);
409 if (e->sym_name_malloced)
410 free(e->symbol_name);
411 e->symbol_name = name;
412 e->sym_name_malloced = 1;
413 verbprintf(debug, "selected idx=%i, name=%s\n", idx, e->symbol_name);
414 out:
415 return rc;
416 }
417
418
419 /*
420 * one scan through the symbols to find overlaps.
421 * return 1 if an overlap is found. this is repeated until no more overlap
422 * is there.
423 * Process: There may be more than two overlapping symbols with each other.
424 * The index range of symbols found to overlap are passed to
425 * handle_overlap_region.
426 */
scan_overlaps(void)427 static int scan_overlaps(void)
428 {
429 int i, j;
430 unsigned long long end_addr, end_addr2;
431 struct jitentry const * a;
432 int flag = 0;
433 // entry_count can be incremented by split_entry() during the loop,
434 // save the inital value as loop count
435 int loop_count = entry_count;
436
437 verbprintf(debug,"count=%i, scan overlaps...\n", entry_count);
438 i = 0;
439 end_addr = 0;
440 for (j = 1; j < loop_count; j++) {
441 /**
442 * Take a symbol [i] and look for the next symbol(s) [j] that are overlapping
443 * symbol [i]. If a symbol [j] is found that is not overlapping symbol [i] the
444 * symbols [i]..[j-1] are handled to be not overlapping anymore.
445 * See descriptions of handle_overlap_region() and eliminate_overlaps() for
446 * further details of this handling.
447 * E.g. possible cases to be handled could be:
448 *
449 * sym1 |-----------|
450 * sym2 |---|
451 *
452 * sym1 |--------|
453 * sym2 |--------|
454 *
455 * sym1 |--------|
456 * sym2 |-------|
457 * sym3 |---------|
458 *
459 * But in the following case
460 *
461 * sym1 |--------|
462 * sym2 |-------------|
463 * sym3 |----------|
464 *
465 * sym3 would not overlap with sym1. Therefore handle_overlap_regio() would
466 * only be called for sym1 up to sym2.
467 */
468 a = entries_address_ascending[j - 1];
469 end_addr2 = a->vma + a->code_size;
470 if (end_addr2 > end_addr)
471 end_addr = end_addr2;
472 a = entries_address_ascending[j];
473 if (end_addr <= a->vma) {
474 if (i != j - 1) {
475 if (handle_overlap_region(i, j - 1) ==
476 OP_JIT_CONV_FAIL) {
477 flag = OP_JIT_CONV_FAIL;
478 goto out;
479 }
480 flag = 1;
481 }
482 i = j;
483 }
484 }
485 if (i != j - 1) {
486 if (handle_overlap_region(i, j - 1) == OP_JIT_CONV_FAIL)
487 flag = OP_JIT_CONV_FAIL;
488 else
489 flag = 1;
490 }
491 out:
492 return flag;
493 }
494
495
496 /* search for symbols that have overlapping address ranges and decide for
497 * one */
resolve_overlaps(unsigned long long start_time)498 int resolve_overlaps(unsigned long long start_time)
499 {
500 int rc = OP_JIT_CONV_OK;
501 int cnt = 0;
502
503 invalidate_earlybirds(start_time);
504 while ((rc = scan_overlaps()) && rc != OP_JIT_CONV_FAIL) {
505 resort_address();
506 if (cnt == 0) {
507 verbprintf(debug, "WARNING: overlaps detected. "
508 "Removing overlapping JIT methods\n");
509 }
510 cnt++;
511 }
512 if (cnt > 0 && rc != OP_JIT_CONV_FAIL)
513 resort_symbol();
514 return rc;
515 }
516
517
518 /*
519 * scan through the sorted array and replace identical symbol names by unique
520 * ones by adding a counter value.
521 */
disambiguate_symbol_names(void)522 void disambiguate_symbol_names(void)
523 {
524 u32 j;
525 int cnt, rep_cnt;
526 struct jitentry * a;
527 struct jitentry * b;
528
529 rep_cnt = 0;
530 for (j = 1; j < entry_count; j++) {
531 a = entries_symbols_ascending[j - 1];
532 cnt = 1;
533 do {
534 b = entries_symbols_ascending[j];
535 if (strcmp(a->symbol_name, b->symbol_name) == 0) {
536 if (b->sym_name_malloced)
537 free(b->symbol_name);
538 b->symbol_name =
539 replacement_name(a->symbol_name, cnt);
540 b->sym_name_malloced = 1;
541 j++;
542 cnt++;
543 rep_cnt++;
544 } else {
545 break;
546 }
547 } while (j < entry_count);
548 }
549 /* recurse to avoid that the added suffix also creates a collision */
550 if (rep_cnt) {
551 qsort(entries_symbols_ascending, entry_count,
552 sizeof(struct jitentry *), cmp_symbolname);
553 disambiguate_symbol_names();
554 }
555 }
556