/**
* @file parse_dump.c
* parse a jit dump file
*
* @remark Copyright 2007 OProfile authors
* @remark Read the file COPYING
*
* @author Jens Wilke
* @Modifications Maynard Johnson
* @Modifications Philippe Elie
* @Modifications Daniel Hansel
*
* Copyright IBM Corporation 2007
*
*/
#include "opjitconv.h"
#include "jitdump.h"
#include "opd_printf.h"
#include "op_libiberty.h"
#include <string.h>
#include <stdio.h>
/* parse a code load record and add the entry to the jitentry list */
static int parse_code_load(void const * ptr_arg, int size,
unsigned long long end_time)
{
struct jitentry * entry;
int rc = OP_JIT_CONV_OK;
char const * ptr = ptr_arg;
struct jr_code_load const * rec = ptr_arg;
char const * end;
size_t padding_count, rec_totalsize;
end = rec->code_addr ? ptr + size : NULL;
entry = xcalloc(1, sizeof(struct jitentry));
// jitentry constructor
entry->next = NULL;
ptr += sizeof(*rec);
/* symbol_name can be malloced so we cast away the constness. */
entry->symbol_name = (char *)ptr;
entry->sym_name_malloced = 0;
ptr += strlen(ptr) + 1;
entry->code = rec->code_addr ? ptr : NULL;
entry->vma = rec->vma;
entry->code_size = rec->code_size;
entry->section = NULL;
entry->life_start = rec->timestamp;
// if nothing else is known the symbol lives till the end of the
// sampling run, this value may be overwritten by an unload record1
// later
entry->life_end = end_time;
// build list
entry->next = jitentry_list;
jitentry_list = entry;
/* padding bytes are calculated over the complete record
* (i.e. header + symbol name + code)
*/
rec_totalsize = sizeof(*rec) + strlen(entry->symbol_name) + 1 + entry->code_size;
padding_count = PADDING_8ALIGNED(rec_totalsize);
verbprintf(debug, "record0: name=%s, vma=%llx, code_size=%i, "
"padding_count=%llu, life_start=%lli, life_end=%lli\n", entry->symbol_name,
entry->vma, entry->code_size, (unsigned long long)padding_count, entry->life_start,
entry->life_end);
/* If end == NULL, the dump does not include code, and this sanity
* check is skipped.
*/
if (end && (ptr + entry->code_size + padding_count != end)) {
verbprintf(debug, "record total size mismatch\n");
rc = OP_JIT_CONV_FAIL;
}
return rc;
}
/*
* parse a code unload record. Search for existing record with this code
* address and fill life_end field with the timestamp. linear search not very
* efficient. FIXME: inefficient
*/
static void parse_code_unload(void const * ptr, unsigned long long end_time)
{
struct jr_code_unload const * rec = ptr;
struct jitentry * entry;
verbprintf(debug,"record1: vma=%llx, life_end=%lli\n",
rec->vma, rec->timestamp);
/**
* Normally we won't get a jr_code_unload with a zero time stamp or
* a zero code address. The code address is directly provided by the JVMTI.
* The documentation of JVMTI does not say anything about the address value if
* it could be zero or not. Therefore it is only a sanity check at the moment.
*/
if (rec->timestamp > 0 && rec->vma != 0) {
for (entry = jitentry_list; entry; entry = entry->next) {
if (entry->vma == rec->vma &&
entry->life_end == end_time) {
entry->life_end = rec->timestamp;
verbprintf(debug,"matching record found\n");
break;
}
}
}
}
/*
* There is no real parsing here, we just record a pointer to the data,
* we will interpret on the fly the record when building the bfd file.
*/
static void parse_code_debug_info(void const * ptr, void const * end,
unsigned long long end_time)
{
struct jr_code_debug_info const * rec = ptr;
struct jitentry_debug_line * debug_line =
xmalloc(sizeof(struct jitentry_debug_line));
debug_line->data = rec;
debug_line->end = end;
debug_line->life_start = rec->timestamp;
debug_line->life_end = end_time;
debug_line->next = jitentry_debug_line_list;
jitentry_debug_line_list = debug_line;
}
/* parse all entries in the jit dump file and build jitentry_list.
* the code needs to check always whether there is enough
* to read remaining. this is because the file may be written to
* concurrently. */
static int parse_entries(void const * ptr, void const * end,
unsigned long long end_time)
{
int rc = OP_JIT_CONV_OK;
struct jr_prefix const * rec = ptr;
while ((void *)rec + sizeof(struct jr_prefix) < end) {
if (((void *) rec + rec->total_size) > end) {
verbprintf(debug, "record past end of file\n");
rc = OP_JIT_CONV_FAIL;
break;
}
switch (rec->id) {
case JIT_CODE_LOAD:
if (parse_code_load(rec, rec->total_size, end_time)) {
rc = OP_JIT_CONV_FAIL;
break;
}
break;
case JIT_CODE_UNLOAD:
parse_code_unload(rec, end_time);
break;
// end of VM live time, no action
case JIT_CODE_CLOSE:
break;
case JIT_CODE_DEBUG_INFO:
if (rec->total_size == 0) {
/* op_write_debug_line_info() ensures to write records with
* totalsize > 0.
*/
rc = OP_JIT_CONV_FAIL;
break;
}
parse_code_debug_info(rec, end, end_time);
break;
default:
verbprintf(debug, "unknown record type\n");
rc = OP_JIT_CONV_FAIL;
break;
}
/* advance to next record (incl. possible padding bytes) */
rec = (void *)rec + rec->total_size;
}
return rc;
}
/* parse the jit dump header information
* The ptr arg is the address of the pointer to the mmapped
* file, which we modify below.
*/
static int parse_header(char const ** ptr, char const * end)
{
int rc = OP_JIT_CONV_OK;
struct jitheader const * header;
if (*ptr + sizeof(struct jitheader) >= end) {
verbprintf(debug,
"opjitconv: EOF in jitdump file, no header\n");
rc = OP_JIT_CONV_FAIL;
goto out;
}
header = (struct jitheader *)*ptr;
if (header->magic != JITHEADER_MAGIC) {
verbprintf(debug, "opjitconv: Wrong jitdump file magic\n");
rc = OP_JIT_CONV_FAIL;
goto out;
}
if (header->version != JITHEADER_VERSION) {
verbprintf(debug, "opjitconv: Wrong jitdump file version\n");
rc = OP_JIT_CONV_FAIL;
goto out;
}
if (*ptr + header->totalsize > end) {
verbprintf(debug, "opjitconv: EOF in jitdump file, not enough "
"data for header\n");
rc = OP_JIT_CONV_FAIL;
goto out;
}
dump_bfd_arch = header->bfd_arch;
dump_bfd_mach = header->bfd_mach;
dump_bfd_target_name = header->bfd_target;
verbprintf(debug, "header: bfd-arch=%i, bfd-mach=%i,"
" bfd_target_name=%s\n", dump_bfd_arch, dump_bfd_mach,
dump_bfd_target_name);
*ptr = *ptr + header->totalsize;
out:
return rc;
}
/* Read in the memory mapped jitdump file.
* Build up jitentry structure and set global variables.
*/
int parse_all(void const * start, void const * end,
unsigned long long end_time)
{
char const * ptr = start;
if (!parse_header(&ptr, end))
return parse_entries(ptr, end, end_time);
else
return OP_JIT_CONV_FAIL;
}