Home | History | Annotate | Download | only in opjitconv
      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 }
    248