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