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 */
26static 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 */
86static 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 */
116static 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. */
137static 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 */
196static 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;
231out:
232	return rc;
233}
234
235
236/* Read in the memory mapped jitdump file.
237 * Build up jitentry structure and set global variables.
238*/
239int 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