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