|  | /** | 
|  | * @file jitsymbol.c | 
|  | * Handle symbols found in jitted code dump | 
|  | * | 
|  | * @remark Copyright 2007 OProfile authors | 
|  | * @remark Read the file COPYING | 
|  | * | 
|  | * @author Jens Wilke | 
|  | * @Modifications Maynard Johnson | 
|  | * @Modifications Philippe Elie | 
|  | * @Modifications Daniel Hansel | 
|  | * | 
|  | * Copyright IBM Corporation 2007 | 
|  | * | 
|  | */ | 
|  |  | 
|  | #include "opjitconv.h" | 
|  | #include "opd_printf.h" | 
|  | #include "op_libiberty.h" | 
|  | #include "op_types.h" | 
|  |  | 
|  | #include <stddef.h> | 
|  | #include <stdlib.h> | 
|  | #include <stdint.h> | 
|  | #include <stdio.h> | 
|  | #include <string.h> | 
|  | #include <unistd.h> | 
|  | #include <limits.h> | 
|  |  | 
|  | typedef int (*compare_symbol)(void const *, void const *); | 
|  |  | 
|  |  | 
|  | /* count the entries in the jitentry_list */ | 
|  | static u32 count_entries(void) | 
|  | { | 
|  | struct jitentry const * entry; | 
|  | u32 cnt = 0; | 
|  | for (entry = jitentry_list; entry; entry = entry->next) | 
|  | cnt++; | 
|  | return cnt; | 
|  | } | 
|  |  | 
|  |  | 
|  | static void fill_entry_array(struct jitentry * entries[]) | 
|  | { | 
|  | int i = 0; | 
|  | struct jitentry * entry; | 
|  | for (entry = jitentry_list; entry; entry = entry->next) | 
|  | entries[i++] = entry; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* create an array pointing to the jitentry structures which is sorted | 
|  | * according to the comparator rule given by parameter compar | 
|  | */ | 
|  | static struct jitentry ** create_sorted_array(compare_symbol compare) | 
|  | { | 
|  | struct jitentry ** array = | 
|  | xmalloc(sizeof(struct jitentry *) * entry_count); | 
|  | fill_entry_array(array); | 
|  | qsort(array, entry_count, sizeof(struct jitentry *), compare); | 
|  | return array; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* comparator method for qsort which sorts jitentries by symbol_name */ | 
|  | static int cmp_symbolname(void const * a, void const * b) | 
|  | { | 
|  | struct jitentry * a0 = *(struct jitentry **) a; | 
|  | struct jitentry * b0 = *(struct jitentry **) b; | 
|  | return strcmp(a0->symbol_name, b0->symbol_name); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* comparator method for qsort which sorts jitentries by address */ | 
|  | static int cmp_address(void const * a, void const * b) | 
|  | { | 
|  | struct jitentry * a0 = *(struct jitentry **) a; | 
|  | struct jitentry * b0 = *(struct jitentry **) b; | 
|  | if (a0->vma < b0->vma) | 
|  | return -1; | 
|  | if (a0->vma == b0->vma) | 
|  | return 0; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* resort address_ascending array */ | 
|  | static void resort_address(void) | 
|  | { | 
|  | u32 i; | 
|  |  | 
|  | qsort(entries_address_ascending, entry_count, | 
|  | sizeof(struct jitentry *), cmp_address); | 
|  |  | 
|  | // lower entry_count if entries are invalidated | 
|  | for (i = 0; i < entry_count; ++i) { | 
|  | if (entries_address_ascending[i]->vma) | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (i) { | 
|  | entry_count -= i; | 
|  | memmove(&entries_address_ascending[0], | 
|  | &entries_address_ascending[i], | 
|  | sizeof(struct jitentry *) * entry_count); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Copy address_ascending array to entries_symbols_ascending and resort it.  */ | 
|  | static void resort_symbol(void) | 
|  | { | 
|  | memcpy(entries_symbols_ascending, entries_address_ascending, | 
|  | sizeof(struct jitentry *) * entry_count); | 
|  | qsort(entries_symbols_ascending, entry_count, | 
|  | sizeof(struct jitentry *), cmp_symbolname); | 
|  | } | 
|  |  | 
|  | /* allocate, populate and sort the jitentry arrays */ | 
|  | void create_arrays(void) | 
|  | { | 
|  | max_entry_count = entry_count = count_entries(); | 
|  | entries_symbols_ascending = create_sorted_array(cmp_symbolname); | 
|  | entries_address_ascending = create_sorted_array(cmp_address); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* add a new create jitentry to the array. mallocs new arrays if space is | 
|  | * needed */ | 
|  | static void insert_entry(struct jitentry * entry) | 
|  | { | 
|  | if (entry_count == max_entry_count) { | 
|  | if (max_entry_count < UINT32_MAX - 18) | 
|  | max_entry_count += 18; | 
|  | else if (max_entry_count < UINT32_MAX) | 
|  | max_entry_count += 1; | 
|  | else { | 
|  | fprintf(stderr, "Amount of JIT dump file entries is too large.\n"); | 
|  | exit(EXIT_FAILURE); | 
|  | } | 
|  | entries_symbols_ascending = (struct jitentry **) | 
|  | xrealloc(entries_symbols_ascending, | 
|  | sizeof(struct jitentry *) * max_entry_count); | 
|  | entries_address_ascending = (struct jitentry **) | 
|  | xrealloc(entries_address_ascending, | 
|  | sizeof(struct jitentry *) * max_entry_count); | 
|  | } | 
|  | entries_address_ascending[entry_count++] = entry; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* add a suffix to the name to differenciate it */ | 
|  | static char * replacement_name(char * s, int i) | 
|  | { | 
|  | int cnt = 1; | 
|  | int j = i; | 
|  | char * res; | 
|  |  | 
|  | while ((j = j/10)) | 
|  | cnt++; | 
|  | cnt += 2 + strlen(s); | 
|  | res = xmalloc(cnt); | 
|  | snprintf(res, cnt, "%s~%i", s, i); | 
|  | return res; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Mark the entry so it is not included in the ELF file. We do this by | 
|  | * writing a 0 address as magic vma and sorting | 
|  | * it out later | 
|  | */ | 
|  | static void invalidate_entry(struct jitentry * e) | 
|  | { | 
|  | verbprintf(debug, "invalidate_entry: addr=%llx, name=%s\n", | 
|  | e->vma, e->symbol_name); | 
|  | e->vma = 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Invalidate all symbols that are not alive at sampling start time. | 
|  | */ | 
|  | static void invalidate_earlybirds(unsigned long long start_time) | 
|  | { | 
|  | u32 i; | 
|  | int flag; | 
|  | struct jitentry * a; | 
|  |  | 
|  | flag = 0; | 
|  | for (i = 0; i < entry_count; i++) { | 
|  | a = entries_address_ascending[i]; | 
|  | if (a->life_end < start_time) { | 
|  | invalidate_entry(a); | 
|  | flag = 1; | 
|  | } | 
|  | } | 
|  | if (flag) { | 
|  | resort_address(); | 
|  | resort_symbol(); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /* select the symbol with the longest life time in the index range */ | 
|  | static int select_one(int start_idx, int end_idx) | 
|  | { | 
|  | int i; | 
|  | int candidate = OP_JIT_CONV_FAIL; | 
|  | unsigned long long lifetime = 0; | 
|  | unsigned long long x; | 
|  | struct jitentry const * e; | 
|  |  | 
|  | for (i = start_idx; i <= end_idx; i++) { | 
|  | e = entries_address_ascending[i]; | 
|  | x = e->life_end - e->life_start; | 
|  | if (candidate == -1 || x > lifetime) { | 
|  | candidate = i; | 
|  | lifetime = x; | 
|  | } | 
|  | } | 
|  | return candidate; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * We decided to keep one entry in favor of the other. Instead of dropping | 
|  | * the overlapping entry we split or truncate it to not overlap any more. | 
|  | * | 
|  | * Looking on the address regions, we may have the following situation: | 
|  | * | 
|  | *  split:     |------------| | 
|  | *  keep:          |---| | 
|  | * | 
|  | * The split entry may be splitted in a left part and a right part. E.g.: | 
|  | * | 
|  | *  split0/1:  |--|     |---| | 
|  | *  keep:          |---| | 
|  | * | 
|  | * However, both parts may or may not exist. | 
|  | */ | 
|  | static void split_entry(struct jitentry * split, struct jitentry const * keep) | 
|  | { | 
|  | unsigned long long start_addr_keep = keep->vma; | 
|  | unsigned long long end_addr_keep = keep->vma + keep->code_size; | 
|  | unsigned long long end_addr_split = split->vma + split->code_size; | 
|  | unsigned long long start_addr_split = split->vma; | 
|  |  | 
|  | // do we need a right part? | 
|  | if (end_addr_split > end_addr_keep) { | 
|  | struct jitentry * new_entry = | 
|  | xcalloc(1, sizeof(struct jitentry)); | 
|  | char * s = NULL; | 
|  |  | 
|  | /* Check for max. length to avoid possible integer overflow. */ | 
|  | if (strlen(split->symbol_name) > SIZE_MAX - 3) { | 
|  | fprintf(stderr, "Length of symbol name is too large.\n"); | 
|  | exit(EXIT_FAILURE); | 
|  | } else { | 
|  | s = xmalloc(strlen(split->symbol_name) + 3); | 
|  | strcpy(s, split->symbol_name); | 
|  | strcat(s, "#1"); | 
|  | } | 
|  |  | 
|  | new_entry->vma = end_addr_keep; | 
|  | new_entry->code_size = end_addr_split - end_addr_keep; | 
|  | new_entry->symbol_name = s; | 
|  | new_entry->sym_name_malloced = 1; | 
|  | new_entry->life_start = split->life_start; | 
|  | new_entry->life_end = split->life_end; | 
|  | // the right part does not have an associated code, because we | 
|  | // don't know whether the split part begins at an opcode | 
|  | new_entry->code = NULL; | 
|  | verbprintf(debug, "split right (new) name=%s, start=%llx," | 
|  | " end=%llx\n", new_entry->symbol_name, | 
|  | new_entry->vma, | 
|  | new_entry->vma + new_entry->code_size); | 
|  | insert_entry(new_entry); | 
|  | } | 
|  | // do we need a left part? | 
|  | if (start_addr_split < start_addr_keep) { | 
|  | char * s = NULL; | 
|  |  | 
|  | /* Check for max. length to avoid possible integer overflow. */ | 
|  | if (strlen(split->symbol_name) > SIZE_MAX - 3) { | 
|  | fprintf(stderr, "Length of symbol name is too large.\n"); | 
|  | exit(EXIT_FAILURE); | 
|  | } else { | 
|  | s = xmalloc(strlen(split->symbol_name) + 3); | 
|  | strcpy(s, split->symbol_name); | 
|  | strcat(s, "#0"); | 
|  | } | 
|  |  | 
|  | split->code_size = start_addr_keep - start_addr_split; | 
|  | if (split->sym_name_malloced) | 
|  | free(split->symbol_name); | 
|  | split->symbol_name = s; | 
|  | split->sym_name_malloced = 1; | 
|  | verbprintf(debug, "split left name=%s, start=%llx, end=%llx\n", | 
|  | split->symbol_name, split->vma, | 
|  | split->vma + split->code_size); | 
|  | } else { | 
|  | invalidate_entry(split); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Scans through the index range and splits/truncates entries that overlap | 
|  | * with the one indexed by keep_idx. Returns the total lifetime of the symbols | 
|  | * found to overlap. | 
|  | * Returns ULONG_MAX on error. | 
|  | */ | 
|  | static unsigned long long eliminate_overlaps(int start_idx, int end_idx, | 
|  | int keep_idx) | 
|  | { | 
|  | unsigned long long retval; | 
|  | struct jitentry const * keep = entries_address_ascending[keep_idx]; | 
|  | struct jitentry * e; | 
|  | unsigned long long start_addr_keep = keep->vma; | 
|  | unsigned long long end_addr_keep = keep->vma + keep->code_size; | 
|  | unsigned long long start_addr_entry, end_addr_entry; | 
|  | int i; | 
|  | unsigned long long min_start = keep->life_start; | 
|  | unsigned long long max_end = keep->life_end; | 
|  |  | 
|  | for (i = start_idx; i <= end_idx; i++) { | 
|  | if (i == keep_idx) | 
|  | continue; | 
|  | e = entries_address_ascending[i]; | 
|  | start_addr_entry = e->vma; | 
|  | end_addr_entry = e->vma + e->code_size; | 
|  | if (debug) { | 
|  | if (!(start_addr_entry <= end_addr_entry)) { | 
|  | verbprintf(debug, "assert failed:" | 
|  | " start_addr_entry <=" | 
|  | " end_addr_entry\n"); | 
|  | retval = ULONG_MAX; | 
|  | goto out; | 
|  | } | 
|  | if (!(start_addr_keep <= end_addr_keep)) { | 
|  | verbprintf(debug, "assert failed: " | 
|  | "start_addr_keep <=" | 
|  | " end_addr_keep\n"); | 
|  | retval = ULONG_MAX; | 
|  | goto out; | 
|  | } | 
|  | } | 
|  | if (start_addr_entry < end_addr_keep && | 
|  | end_addr_entry > start_addr_keep) { | 
|  | if (e->life_start < min_start) | 
|  | min_start = e->life_start; | 
|  | if (e->life_end > max_end) | 
|  | max_end = e->life_end; | 
|  | split_entry(e, keep); | 
|  | } | 
|  | } | 
|  | retval = max_end - min_start; | 
|  | out: | 
|  | return retval; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Within the index range (into the array entries_address_ascending), find the | 
|  | * symbol with the maximal lifetime and split/truncate all symbols that overlap | 
|  | * with it (i.e. that there won't be any overlaps anymore). | 
|  | */ | 
|  | static int handle_overlap_region(int start_idx, int end_idx) | 
|  | { | 
|  | int rc = OP_JIT_CONV_OK; | 
|  | int idx; | 
|  | struct jitentry * e; | 
|  | int cnt; | 
|  | char * name; | 
|  | int i, j; | 
|  | unsigned long long totaltime; | 
|  |  | 
|  | if (debug) { | 
|  | for (i = start_idx; i <= end_idx; i++) { | 
|  | e = entries_address_ascending[i]; | 
|  | verbprintf(debug, "overlap idx=%i, name=%s, " | 
|  | "start=%llx, end=%llx, life_start=%lli, " | 
|  | "life_end=%lli, lifetime=%lli\n", | 
|  | i, e->symbol_name, e->vma, | 
|  | e->vma + e->code_size, e->life_start, | 
|  | e->life_end, e->life_end - e->life_start); | 
|  | } | 
|  | } | 
|  | idx = select_one(start_idx, end_idx); | 
|  | totaltime = eliminate_overlaps(start_idx, end_idx, idx); | 
|  | if (totaltime == ULONG_MAX) { | 
|  | rc = OP_JIT_CONV_FAIL; | 
|  | goto out; | 
|  | } | 
|  | e = entries_address_ascending[idx]; | 
|  |  | 
|  | cnt = 1; | 
|  | j = (e->life_end - e->life_start) * 100 / totaltime; | 
|  | while ((j = j/10)) | 
|  | cnt++; | 
|  |  | 
|  | // Mark symbol name with a %% to indicate the overlap. | 
|  | cnt += strlen(e->symbol_name) + 2 + 1; | 
|  | name = xmalloc(cnt); | 
|  | snprintf(name, cnt, "%s%%%llu", e->symbol_name, | 
|  | (e->life_end - e->life_start) * 100 / totaltime); | 
|  | if (e->sym_name_malloced) | 
|  | free(e->symbol_name); | 
|  | e->symbol_name = name; | 
|  | e->sym_name_malloced = 1; | 
|  | verbprintf(debug, "selected idx=%i, name=%s\n", idx, e->symbol_name); | 
|  | out: | 
|  | return rc; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * one scan through the symbols to find overlaps. | 
|  | * return 1 if an overlap is found. this is repeated until no more overlap | 
|  | * is there. | 
|  | * Process: There may be more than two overlapping symbols with each other. | 
|  | * The index range of symbols found to overlap are passed to | 
|  | * handle_overlap_region. | 
|  | */ | 
|  | static int scan_overlaps(void) | 
|  | { | 
|  | int i, j; | 
|  | unsigned long long end_addr, end_addr2; | 
|  | struct jitentry const * a; | 
|  | int flag = 0; | 
|  | // entry_count can be incremented by split_entry() during the loop, | 
|  | // save the inital value as loop count | 
|  | int loop_count = entry_count; | 
|  |  | 
|  | verbprintf(debug,"count=%i, scan overlaps...\n", entry_count); | 
|  | i = 0; | 
|  | end_addr = 0; | 
|  | for (j = 1; j < loop_count; j++) { | 
|  | /** | 
|  | * Take a symbol [i] and look for the next symbol(s) [j] that are overlapping | 
|  | * symbol [i]. If a symbol [j] is found that is not overlapping symbol [i] the | 
|  | * symbols [i]..[j-1] are handled to be not overlapping anymore. | 
|  | * See descriptions of handle_overlap_region() and eliminate_overlaps() for | 
|  | * further details of this handling. | 
|  | * E.g. possible cases to be handled could be: | 
|  | * | 
|  | *   sym1 |-----------| | 
|  | *   sym2     |---| | 
|  | * | 
|  | *   sym1 |--------| | 
|  | *   sym2     |--------| | 
|  | * | 
|  | *   sym1 |--------| | 
|  | *   sym2     |-------| | 
|  | *   sym3        |---------| | 
|  | * | 
|  | * But in the following case | 
|  | * | 
|  | *   sym1 |--------| | 
|  | *   sym2      |-------------| | 
|  | *   sym3              |----------| | 
|  | * | 
|  | * sym3 would not overlap with sym1. Therefore handle_overlap_regio() would | 
|  | * only be called for sym1 up to sym2. | 
|  | */ | 
|  | a = entries_address_ascending[j - 1]; | 
|  | end_addr2 = a->vma + a->code_size; | 
|  | if (end_addr2 > end_addr) | 
|  | end_addr = end_addr2; | 
|  | a = entries_address_ascending[j]; | 
|  | if (end_addr <= a->vma) { | 
|  | if (i != j - 1) { | 
|  | if (handle_overlap_region(i, j - 1) == | 
|  | OP_JIT_CONV_FAIL) { | 
|  | flag = OP_JIT_CONV_FAIL; | 
|  | goto out; | 
|  | } | 
|  | flag = 1; | 
|  | } | 
|  | i = j; | 
|  | } | 
|  | } | 
|  | if (i != j - 1) { | 
|  | if (handle_overlap_region(i, j - 1) == OP_JIT_CONV_FAIL) | 
|  | flag = OP_JIT_CONV_FAIL; | 
|  | else | 
|  | flag = 1; | 
|  | } | 
|  | out: | 
|  | return flag; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* search for symbols that have overlapping address ranges and decide for | 
|  | * one */ | 
|  | int resolve_overlaps(unsigned long long start_time) | 
|  | { | 
|  | int rc = OP_JIT_CONV_OK; | 
|  | int cnt = 0; | 
|  |  | 
|  | invalidate_earlybirds(start_time); | 
|  | while ((rc = scan_overlaps()) && rc != OP_JIT_CONV_FAIL) { | 
|  | resort_address(); | 
|  | if (cnt == 0) { | 
|  | verbprintf(debug, "WARNING: overlaps detected. " | 
|  | "Removing overlapping JIT methods\n"); | 
|  | } | 
|  | cnt++; | 
|  | } | 
|  | if (cnt > 0 && rc != OP_JIT_CONV_FAIL) | 
|  | resort_symbol(); | 
|  | return rc; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * scan through the sorted array and replace identical symbol names by unique | 
|  | * ones by adding a counter value. | 
|  | */ | 
|  | void disambiguate_symbol_names(void) | 
|  | { | 
|  | u32 j; | 
|  | int cnt, rep_cnt; | 
|  | struct jitentry * a; | 
|  | struct jitentry * b; | 
|  |  | 
|  | rep_cnt = 0; | 
|  | for (j = 1; j < entry_count; j++) { | 
|  | a = entries_symbols_ascending[j - 1]; | 
|  | cnt = 1; | 
|  | do { | 
|  | b = entries_symbols_ascending[j]; | 
|  | if (strcmp(a->symbol_name, b->symbol_name) == 0) { | 
|  | if (b->sym_name_malloced) | 
|  | free(b->symbol_name); | 
|  | b->symbol_name = | 
|  | replacement_name(a->symbol_name, cnt); | 
|  | b->sym_name_malloced = 1; | 
|  | j++; | 
|  | cnt++; | 
|  | rep_cnt++; | 
|  | } else { | 
|  | break; | 
|  | } | 
|  | } while (j < entry_count); | 
|  | } | 
|  | /* recurse to avoid that the added suffix also creates a collision */ | 
|  | if (rep_cnt) { | 
|  | qsort(entries_symbols_ascending, entry_count, | 
|  | sizeof(struct jitentry *), cmp_symbolname); | 
|  | disambiguate_symbol_names(); | 
|  | } | 
|  | } |