| /** |
| * @file parse_dump.c |
| * parse a jit dump file |
| * |
| * @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 "jitdump.h" |
| #include "opd_printf.h" |
| #include "op_libiberty.h" |
| |
| #include <string.h> |
| #include <stdio.h> |
| |
| /* parse a code load record and add the entry to the jitentry list */ |
| static int parse_code_load(void const * ptr_arg, int size, |
| unsigned long long end_time) |
| { |
| struct jitentry * entry; |
| int rc = OP_JIT_CONV_OK; |
| char const * ptr = ptr_arg; |
| struct jr_code_load const * rec = ptr_arg; |
| char const * end; |
| size_t padding_count, rec_totalsize; |
| end = rec->code_addr ? ptr + size : NULL; |
| |
| entry = xcalloc(1, sizeof(struct jitentry)); |
| |
| // jitentry constructor |
| entry->next = NULL; |
| ptr += sizeof(*rec); |
| /* symbol_name can be malloced so we cast away the constness. */ |
| entry->symbol_name = (char *)ptr; |
| entry->sym_name_malloced = 0; |
| ptr += strlen(ptr) + 1; |
| entry->code = rec->code_addr ? ptr : NULL; |
| entry->vma = rec->vma; |
| entry->code_size = rec->code_size; |
| entry->section = NULL; |
| entry->life_start = rec->timestamp; |
| // if nothing else is known the symbol lives till the end of the |
| // sampling run, this value may be overwritten by an unload record1 |
| // later |
| entry->life_end = end_time; |
| |
| // build list |
| entry->next = jitentry_list; |
| jitentry_list = entry; |
| |
| /* padding bytes are calculated over the complete record |
| * (i.e. header + symbol name + code) |
| */ |
| rec_totalsize = sizeof(*rec) + strlen(entry->symbol_name) + 1 + entry->code_size; |
| padding_count = PADDING_8ALIGNED(rec_totalsize); |
| |
| verbprintf(debug, "record0: name=%s, vma=%llx, code_size=%i, " |
| "padding_count=%llu, life_start=%lli, life_end=%lli\n", entry->symbol_name, |
| entry->vma, entry->code_size, (unsigned long long)padding_count, entry->life_start, |
| entry->life_end); |
| /* If end == NULL, the dump does not include code, and this sanity |
| * check is skipped. |
| */ |
| if (end && (ptr + entry->code_size + padding_count != end)) { |
| verbprintf(debug, "record total size mismatch\n"); |
| rc = OP_JIT_CONV_FAIL; |
| } |
| return rc; |
| } |
| |
| |
| /* |
| * parse a code unload record. Search for existing record with this code |
| * address and fill life_end field with the timestamp. linear search not very |
| * efficient. FIXME: inefficient |
| */ |
| static void parse_code_unload(void const * ptr, unsigned long long end_time) |
| { |
| struct jr_code_unload const * rec = ptr; |
| struct jitentry * entry; |
| |
| verbprintf(debug,"record1: vma=%llx, life_end=%lli\n", |
| rec->vma, rec->timestamp); |
| /** |
| * Normally we won't get a jr_code_unload with a zero time stamp or |
| * a zero code address. The code address is directly provided by the JVMTI. |
| * The documentation of JVMTI does not say anything about the address value if |
| * it could be zero or not. Therefore it is only a sanity check at the moment. |
| */ |
| if (rec->timestamp > 0 && rec->vma != 0) { |
| for (entry = jitentry_list; entry; entry = entry->next) { |
| if (entry->vma == rec->vma && |
| entry->life_end == end_time) { |
| entry->life_end = rec->timestamp; |
| verbprintf(debug,"matching record found\n"); |
| break; |
| } |
| } |
| } |
| } |
| |
| |
| /* |
| * There is no real parsing here, we just record a pointer to the data, |
| * we will interpret on the fly the record when building the bfd file. |
| */ |
| static void parse_code_debug_info(void const * ptr, void const * end, |
| unsigned long long end_time) |
| { |
| struct jr_code_debug_info const * rec = ptr; |
| struct jitentry_debug_line * debug_line = |
| xmalloc(sizeof(struct jitentry_debug_line)); |
| |
| debug_line->data = rec; |
| debug_line->end = end; |
| debug_line->life_start = rec->timestamp; |
| debug_line->life_end = end_time; |
| |
| debug_line->next = jitentry_debug_line_list; |
| jitentry_debug_line_list = debug_line; |
| } |
| |
| |
| /* parse all entries in the jit dump file and build jitentry_list. |
| * the code needs to check always whether there is enough |
| * to read remaining. this is because the file may be written to |
| * concurrently. */ |
| static int parse_entries(void const * ptr, void const * end, |
| unsigned long long end_time) |
| { |
| int rc = OP_JIT_CONV_OK; |
| struct jr_prefix const * rec = ptr; |
| |
| while ((void *)rec + sizeof(struct jr_prefix) < end) { |
| if (((void *) rec + rec->total_size) > end) { |
| verbprintf(debug, "record past end of file\n"); |
| rc = OP_JIT_CONV_FAIL; |
| break; |
| } |
| |
| switch (rec->id) { |
| case JIT_CODE_LOAD: |
| if (parse_code_load(rec, rec->total_size, end_time)) { |
| rc = OP_JIT_CONV_FAIL; |
| break; |
| } |
| break; |
| |
| case JIT_CODE_UNLOAD: |
| parse_code_unload(rec, end_time); |
| break; |
| |
| // end of VM live time, no action |
| case JIT_CODE_CLOSE: |
| break; |
| |
| case JIT_CODE_DEBUG_INFO: |
| if (rec->total_size == 0) { |
| /* op_write_debug_line_info() ensures to write records with |
| * totalsize > 0. |
| */ |
| rc = OP_JIT_CONV_FAIL; |
| break; |
| } |
| |
| parse_code_debug_info(rec, end, end_time); |
| break; |
| |
| default: |
| verbprintf(debug, "unknown record type\n"); |
| rc = OP_JIT_CONV_FAIL; |
| break; |
| } |
| |
| /* advance to next record (incl. possible padding bytes) */ |
| rec = (void *)rec + rec->total_size; |
| } |
| |
| return rc; |
| } |
| |
| |
| /* parse the jit dump header information |
| * The ptr arg is the address of the pointer to the mmapped |
| * file, which we modify below. |
| */ |
| static int parse_header(char const ** ptr, char const * end) |
| { |
| int rc = OP_JIT_CONV_OK; |
| struct jitheader const * header; |
| |
| if (*ptr + sizeof(struct jitheader) >= end) { |
| verbprintf(debug, |
| "opjitconv: EOF in jitdump file, no header\n"); |
| rc = OP_JIT_CONV_FAIL; |
| goto out; |
| } |
| header = (struct jitheader *)*ptr; |
| if (header->magic != JITHEADER_MAGIC) { |
| verbprintf(debug, "opjitconv: Wrong jitdump file magic\n"); |
| rc = OP_JIT_CONV_FAIL; |
| goto out; |
| } |
| if (header->version != JITHEADER_VERSION) { |
| verbprintf(debug, "opjitconv: Wrong jitdump file version\n"); |
| rc = OP_JIT_CONV_FAIL; |
| goto out; |
| } |
| if (*ptr + header->totalsize > end) { |
| verbprintf(debug, "opjitconv: EOF in jitdump file, not enough " |
| "data for header\n"); |
| rc = OP_JIT_CONV_FAIL; |
| goto out; |
| } |
| dump_bfd_arch = header->bfd_arch; |
| dump_bfd_mach = header->bfd_mach; |
| dump_bfd_target_name = header->bfd_target; |
| verbprintf(debug, "header: bfd-arch=%i, bfd-mach=%i," |
| " bfd_target_name=%s\n", dump_bfd_arch, dump_bfd_mach, |
| dump_bfd_target_name); |
| *ptr = *ptr + header->totalsize; |
| out: |
| return rc; |
| } |
| |
| |
| /* Read in the memory mapped jitdump file. |
| * Build up jitentry structure and set global variables. |
| */ |
| int parse_all(void const * start, void const * end, |
| unsigned long long end_time) |
| { |
| char const * ptr = start; |
| if (!parse_header(&ptr, end)) |
| return parse_entries(ptr, end, end_time); |
| else |
| return OP_JIT_CONV_FAIL; |
| } |