• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
parse_code_load(void const * ptr_arg,int size,unsigned long long end_time)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  */
parse_code_unload(void const * ptr,unsigned long long end_time)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  */
parse_code_debug_info(void const * ptr,void const * end,unsigned long long end_time)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. */
parse_entries(void const * ptr,void const * end,unsigned long long end_time)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  */
parse_header(char const ** ptr,char const * end)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 */
parse_all(void const * start,void const * end,unsigned long long end_time)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