Mike Dodd | 8cfa702 | 2010-11-17 11:12:26 -0800 | [diff] [blame^] | 1 | /** |
| 2 | * @file parse_dump.c |
| 3 | * parse a jit dump file |
| 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 "jitdump.h" |
| 19 | #include "opd_printf.h" |
| 20 | #include "op_libiberty.h" |
| 21 | |
| 22 | #include <string.h> |
| 23 | #include <stdio.h> |
| 24 | |
| 25 | /* parse a code load record and add the entry to the jitentry list */ |
| 26 | static int parse_code_load(void const * ptr_arg, int size, |
| 27 | unsigned long long end_time) |
| 28 | { |
| 29 | struct jitentry * entry; |
| 30 | int rc = OP_JIT_CONV_OK; |
| 31 | char const * ptr = ptr_arg; |
| 32 | struct jr_code_load const * rec = ptr_arg; |
| 33 | char const * end; |
| 34 | size_t padding_count, rec_totalsize; |
| 35 | end = rec->code_addr ? ptr + size : NULL; |
| 36 | |
| 37 | entry = xcalloc(1, sizeof(struct jitentry)); |
| 38 | |
| 39 | // jitentry constructor |
| 40 | entry->next = NULL; |
| 41 | ptr += sizeof(*rec); |
| 42 | /* symbol_name can be malloced so we cast away the constness. */ |
| 43 | entry->symbol_name = (char *)ptr; |
| 44 | entry->sym_name_malloced = 0; |
| 45 | ptr += strlen(ptr) + 1; |
| 46 | entry->code = rec->code_addr ? ptr : NULL; |
| 47 | entry->vma = rec->vma; |
| 48 | entry->code_size = rec->code_size; |
| 49 | entry->section = NULL; |
| 50 | entry->life_start = rec->timestamp; |
| 51 | // if nothing else is known the symbol lives till the end of the |
| 52 | // sampling run, this value may be overwritten by an unload record1 |
| 53 | // later |
| 54 | entry->life_end = end_time; |
| 55 | |
| 56 | // build list |
| 57 | entry->next = jitentry_list; |
| 58 | jitentry_list = entry; |
| 59 | |
| 60 | /* padding bytes are calculated over the complete record |
| 61 | * (i.e. header + symbol name + code) |
| 62 | */ |
| 63 | rec_totalsize = sizeof(*rec) + strlen(entry->symbol_name) + 1 + entry->code_size; |
| 64 | padding_count = PADDING_8ALIGNED(rec_totalsize); |
| 65 | |
| 66 | verbprintf(debug, "record0: name=%s, vma=%llx, code_size=%i, " |
| 67 | "padding_count=%llu, life_start=%lli, life_end=%lli\n", entry->symbol_name, |
| 68 | entry->vma, entry->code_size, (unsigned long long)padding_count, entry->life_start, |
| 69 | entry->life_end); |
| 70 | /* If end == NULL, the dump does not include code, and this sanity |
| 71 | * check is skipped. |
| 72 | */ |
| 73 | if (end && (ptr + entry->code_size + padding_count != end)) { |
| 74 | verbprintf(debug, "record total size mismatch\n"); |
| 75 | rc = OP_JIT_CONV_FAIL; |
| 76 | } |
| 77 | return rc; |
| 78 | } |
| 79 | |
| 80 | |
| 81 | /* |
| 82 | * parse a code unload record. Search for existing record with this code |
| 83 | * address and fill life_end field with the timestamp. linear search not very |
| 84 | * efficient. FIXME: inefficient |
| 85 | */ |
| 86 | static void parse_code_unload(void const * ptr, unsigned long long end_time) |
| 87 | { |
| 88 | struct jr_code_unload const * rec = ptr; |
| 89 | struct jitentry * entry; |
| 90 | |
| 91 | verbprintf(debug,"record1: vma=%llx, life_end=%lli\n", |
| 92 | rec->vma, rec->timestamp); |
| 93 | /** |
| 94 | * Normally we won't get a jr_code_unload with a zero time stamp or |
| 95 | * a zero code address. The code address is directly provided by the JVMTI. |
| 96 | * The documentation of JVMTI does not say anything about the address value if |
| 97 | * it could be zero or not. Therefore it is only a sanity check at the moment. |
| 98 | */ |
| 99 | if (rec->timestamp > 0 && rec->vma != 0) { |
| 100 | for (entry = jitentry_list; entry; entry = entry->next) { |
| 101 | if (entry->vma == rec->vma && |
| 102 | entry->life_end == end_time) { |
| 103 | entry->life_end = rec->timestamp; |
| 104 | verbprintf(debug,"matching record found\n"); |
| 105 | break; |
| 106 | } |
| 107 | } |
| 108 | } |
| 109 | } |
| 110 | |
| 111 | |
| 112 | /* |
| 113 | * There is no real parsing here, we just record a pointer to the data, |
| 114 | * we will interpret on the fly the record when building the bfd file. |
| 115 | */ |
| 116 | static void parse_code_debug_info(void const * ptr, void const * end, |
| 117 | unsigned long long end_time) |
| 118 | { |
| 119 | struct jr_code_debug_info const * rec = ptr; |
| 120 | struct jitentry_debug_line * debug_line = |
| 121 | xmalloc(sizeof(struct jitentry_debug_line)); |
| 122 | |
| 123 | debug_line->data = rec; |
| 124 | debug_line->end = end; |
| 125 | debug_line->life_start = rec->timestamp; |
| 126 | debug_line->life_end = end_time; |
| 127 | |
| 128 | debug_line->next = jitentry_debug_line_list; |
| 129 | jitentry_debug_line_list = debug_line; |
| 130 | } |
| 131 | |
| 132 | |
| 133 | /* parse all entries in the jit dump file and build jitentry_list. |
| 134 | * the code needs to check always whether there is enough |
| 135 | * to read remaining. this is because the file may be written to |
| 136 | * concurrently. */ |
| 137 | static int parse_entries(void const * ptr, void const * end, |
| 138 | unsigned long long end_time) |
| 139 | { |
| 140 | int rc = OP_JIT_CONV_OK; |
| 141 | struct jr_prefix const * rec = ptr; |
| 142 | |
| 143 | while ((void *)rec + sizeof(struct jr_prefix) < end) { |
| 144 | if (((void *) rec + rec->total_size) > end) { |
| 145 | verbprintf(debug, "record past end of file\n"); |
| 146 | rc = OP_JIT_CONV_FAIL; |
| 147 | break; |
| 148 | } |
| 149 | |
| 150 | switch (rec->id) { |
| 151 | case JIT_CODE_LOAD: |
| 152 | if (parse_code_load(rec, rec->total_size, end_time)) { |
| 153 | rc = OP_JIT_CONV_FAIL; |
| 154 | break; |
| 155 | } |
| 156 | break; |
| 157 | |
| 158 | case JIT_CODE_UNLOAD: |
| 159 | parse_code_unload(rec, end_time); |
| 160 | break; |
| 161 | |
| 162 | // end of VM live time, no action |
| 163 | case JIT_CODE_CLOSE: |
| 164 | break; |
| 165 | |
| 166 | case JIT_CODE_DEBUG_INFO: |
| 167 | if (rec->total_size == 0) { |
| 168 | /* op_write_debug_line_info() ensures to write records with |
| 169 | * totalsize > 0. |
| 170 | */ |
| 171 | rc = OP_JIT_CONV_FAIL; |
| 172 | break; |
| 173 | } |
| 174 | |
| 175 | parse_code_debug_info(rec, end, end_time); |
| 176 | break; |
| 177 | |
| 178 | default: |
| 179 | verbprintf(debug, "unknown record type\n"); |
| 180 | rc = OP_JIT_CONV_FAIL; |
| 181 | break; |
| 182 | } |
| 183 | |
| 184 | /* advance to next record (incl. possible padding bytes) */ |
| 185 | rec = (void *)rec + rec->total_size; |
| 186 | } |
| 187 | |
| 188 | return rc; |
| 189 | } |
| 190 | |
| 191 | |
| 192 | /* parse the jit dump header information |
| 193 | * The ptr arg is the address of the pointer to the mmapped |
| 194 | * file, which we modify below. |
| 195 | */ |
| 196 | static int parse_header(char const ** ptr, char const * end) |
| 197 | { |
| 198 | int rc = OP_JIT_CONV_OK; |
| 199 | struct jitheader const * header; |
| 200 | |
| 201 | if (*ptr + sizeof(struct jitheader) >= end) { |
| 202 | verbprintf(debug, |
| 203 | "opjitconv: EOF in jitdump file, no header\n"); |
| 204 | rc = OP_JIT_CONV_FAIL; |
| 205 | goto out; |
| 206 | } |
| 207 | header = (struct jitheader *)*ptr; |
| 208 | if (header->magic != JITHEADER_MAGIC) { |
| 209 | verbprintf(debug, "opjitconv: Wrong jitdump file magic\n"); |
| 210 | rc = OP_JIT_CONV_FAIL; |
| 211 | goto out; |
| 212 | } |
| 213 | if (header->version != JITHEADER_VERSION) { |
| 214 | verbprintf(debug, "opjitconv: Wrong jitdump file version\n"); |
| 215 | rc = OP_JIT_CONV_FAIL; |
| 216 | goto out; |
| 217 | } |
| 218 | if (*ptr + header->totalsize > end) { |
| 219 | verbprintf(debug, "opjitconv: EOF in jitdump file, not enough " |
| 220 | "data for header\n"); |
| 221 | rc = OP_JIT_CONV_FAIL; |
| 222 | goto out; |
| 223 | } |
| 224 | dump_bfd_arch = header->bfd_arch; |
| 225 | dump_bfd_mach = header->bfd_mach; |
| 226 | dump_bfd_target_name = header->bfd_target; |
| 227 | verbprintf(debug, "header: bfd-arch=%i, bfd-mach=%i," |
| 228 | " bfd_target_name=%s\n", dump_bfd_arch, dump_bfd_mach, |
| 229 | dump_bfd_target_name); |
| 230 | *ptr = *ptr + header->totalsize; |
| 231 | out: |
| 232 | return rc; |
| 233 | } |
| 234 | |
| 235 | |
| 236 | /* Read in the memory mapped jitdump file. |
| 237 | * Build up jitentry structure and set global variables. |
| 238 | */ |
| 239 | int parse_all(void const * start, void const * end, |
| 240 | unsigned long long end_time) |
| 241 | { |
| 242 | char const * ptr = start; |
| 243 | if (!parse_header(&ptr, end)) |
| 244 | return parse_entries(ptr, end, end_time); |
| 245 | else |
| 246 | return OP_JIT_CONV_FAIL; |
| 247 | } |