diff options
Diffstat (limited to 'extension_eppic.c')
-rw-r--r-- | extension_eppic.c | 468 |
1 files changed, 468 insertions, 0 deletions
diff --git a/extension_eppic.c b/extension_eppic.c new file mode 100644 index 0000000..45bc032 --- /dev/null +++ b/extension_eppic.c @@ -0,0 +1,468 @@ +/* + * extension_eppic.c + * + * Created by: Aravinda Prasad <aravinda@linux.vnet.ibm.com> + * + * Copyright (C) 2012, 2013 IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <fcntl.h> +#include <dwarf.h> + +#include "makedumpfile.h" +#include "extension_eppic.h" + +static int apigetctype(int, char *, type_t *); + +/* + * Most of the functions included in this file performs similar + * functionality as in the applications/crash/eppic.c file part of + * eppic, but uses DWARF instead of gdb. Few of the functions are + * reused directly which are acknowledged in the comment before the + * function. + */ + +/* + * This is the call back function called when a new eppic macro is + * loaded. This will execute the loaded eppic macro. + * + * "fname" is considered as the entry point of an eppic macro only if + * the following functions are defined: + * + * fname_help() + * fname_usage() + * + * These functions have no relevance in makedumpfile context as + * makedumpfile automatically executes the eppic macro by calling the + * entry point and user will not have any option to execute the usage + * or help functions. However they are required to identify the entry + * points in the eppic macro. + */ +void +reg_callback(char *name, int load) +{ + char fname[MAX_SYMNAMELEN]; + + /* Nothing to process for unload request */ + if (!load) + return; + + snprintf(fname, sizeof(fname), "%s_help", name); + if (eppic_chkfname(fname, 0)) { + snprintf(fname, sizeof(fname), "%s_usage", name); + if (eppic_chkfname(fname, 0)) + eppic_cmd(name, NULL, 0); + } + return; +} + +/* + * This function is a copy of eppic_setupidx() function in + * applications/crash/eppic.c file from eppic source code + * repository. + * + * set idx value to actual array indexes from specified size + */ +static void +eppic_setupidx(TYPE_S *t, int ref, int nidx, int *idxlst) +{ + /* put the idxlst in index size format */ + if (nidx) { + int i; + for (i = 0; i < nidx - 1; i++) { + /* kludge for array dimensions of [1] */ + if (idxlst[i + 1] == 0) + idxlst[i + 1] = 1; + idxlst[i] = idxlst[i] / idxlst[i + 1]; + } + + /* divide by element size for last element bound */ + if (ref) + idxlst[i] /= eppic_defbsize(); + else + idxlst[i] /= eppic_type_getsize(t); + eppic_type_setidxlst(t, idxlst); + } +} + +/* + * Call back functions for eppic to query the dump image + */ + +static int +apigetmem(ull iaddr, void *p, int nbytes) +{ + return READMEM(VADDR, iaddr, p, nbytes); +} + +static int +apiputmem(ull iaddr, void *p, int nbytes) +{ + return 1; +} + +/* + * Drill down the type of the member and update eppic with information + * about the member + */ +static char * +drilldown(ull offset, type_t *t) +{ + int type_flag, len = 0, t_len = 0, nidx = 0; + int fctflg = 0, ref = 0, *idxlst = 0; + ull die_off = offset, t_die_off; + char *tstr = NULL, *tstr_dup = NULL; + + while (GET_DIE_ATTR_TYPE(die_off, &type_flag, &t_die_off)) { + switch (type_flag) { + /* typedef inserts a level of reference to the actual type */ + case DW_TAG_pointer_type: + ref++; + die_off = t_die_off; + /* + * This could be a void *, in which case the drill + * down stops here + */ + if (!GET_DIE_ATTR_TYPE(die_off, &type_flag, + &t_die_off)) { + /* make it a char* */ + eppic_parsetype("char", t, ref); + return eppic_strdup(""); + } + break; + /* Handle pointer to function */ + case DW_TAG_subroutine_type: + fctflg = 1; + die_off = t_die_off; + break; + /* Handle arrays */ + case DW_TAG_array_type: + if (!idxlst) { + idxlst = eppic_calloc(sizeof(int) * \ + (MAX_ARRAY_DIMENSION + 1)); + if (!idxlst) { + ERRMSG("Out of memory\n"); + return NULL; + } + } + if (nidx >= MAX_ARRAY_DIMENSION) { + ERRMSG("Too many array indexes. Max=%d\n", + MAX_ARRAY_DIMENSION); + return NULL; + } + + /* handle multi-dimensional array */ + len = GET_DIE_LENGTH(die_off, FALSE); + t_len = GET_DIE_LENGTH(t_die_off, FALSE); + if (len > 0 && t_len > 0) + idxlst[nidx++] = len / t_len; + die_off = t_die_off; + break; + /* Handle typedef */ + case DW_TAG_typedef: + die_off = t_die_off; + break; + case DW_TAG_base_type: + eppic_parsetype(tstr = GET_DIE_NAME(t_die_off), t, 0); + goto out; + case DW_TAG_union_type: + eppic_type_mkunion(t); + goto label; + case DW_TAG_enumeration_type: + eppic_type_mkenum(t); + goto label; + case DW_TAG_structure_type: + eppic_type_mkstruct(t); +label: + eppic_type_setsize(t, GET_DIE_LENGTH(t_die_off, TRUE)); + eppic_type_setidx(t, (ull)t_die_off); + tstr = GET_DIE_NAME(t_die_off); + /* Drill down further */ + if (tstr) + apigetctype(V_STRUCT, tstr, t); + die_off = 0; + break; + /* Unknown TAG ? */ + default: + die_off = t_die_off; + break; + } + } + +out: + eppic_setupidx(t, ref, nidx, idxlst); + if (fctflg) + eppic_type_setfct(t, 1); + eppic_pushref(t, ref + (nidx ? 1 : 0)); + tstr_dup = (tstr) ? eppic_strdup(tstr) : eppic_strdup(""); + /* Free the memory allocated by makedumpfile. */ + free(tstr); + return tstr_dup; +} + +/* + * Get the type, size and position information for a member of a structure. + */ +static char * +apimember(char *mname, ull idx, type_t *tm, member_t *m, ull *last_index) +{ + int index, nfields = -1, size; + int nbits = 0, fbits = 0; + long offset; + ull m_die, die_off = idx; + char *name = NULL; + + nfields = GET_DIE_NFIELDS_ALL(die_off); + /* + * GET_DIE_NFIELDS() returns < 0 if the die is not structure type + * or union type + */ + if (nfields <= 0) + return NULL; + + /* if we're being asked the next member in a getfirst/getnext + * sequence + */ + if (mname && !mname[0] && last_index && (*last_index)) + index = *last_index; + else + index = 0; + + while (index < nfields) { + size = GET_DIE_MEMBER_ALL(die_off, index, &offset, &name, + &nbits, &fbits, &m_die); + + if (size < 0) + return NULL; + + if (!mname || !mname[0] || !strcmp(mname, name)) { + eppic_member_ssize(m, size); + if (name) { + eppic_member_sname(m, name); + /* + * Free the memory allocated by makedumpfile. + */ + free(name); + } + else + eppic_member_sname(m, ""); + eppic_member_soffset(m, offset); + eppic_member_snbits(m, nbits); + eppic_member_sfbit(m, fbits); + *last_index = index + 1; + return drilldown(m_die, tm); + } + index++; + } + return NULL; +} + +static int +apigetctype(int ctype, char *name, type_t *tout) +{ + long size = 0; + unsigned long long die = 0; + + switch (ctype) { + case V_TYPEDEF: + size = GET_DOMAIN_ALL(name, DWARF_INFO_GET_DOMAIN_TYPEDEF, + &die); + break; + case V_STRUCT: + size = GET_DOMAIN_ALL(name, DWARF_INFO_GET_DOMAIN_STRUCT, &die); + break; + case V_UNION: + size = GET_DOMAIN_ALL(name, DWARF_INFO_GET_DOMAIN_UNION, &die); + break; + /* TODO + * Implement for all the domains + */ + } + + if (size <= 0 || !die) + return 0; + + /* populate */ + eppic_type_settype(tout, ctype); + eppic_type_setsize(tout, size); + eppic_type_setidx(tout, (ull)(unsigned long)die); + eppic_pushref(tout, 0); + return 1; +} + +static char * +apigetrtype(ull idx, type_t *t) +{ + return drilldown(idx, t); +} + +static int +apialignment(ull idx) +{ + return 0; +} + +int +apigetval(char *name, ull *val, VALUE_S *value) +{ + ull ptr = 0; + + ptr = GET_SYMBOL_ADDR_ALL(name); + + if (!ptr) + return 0; + + *val = ptr; + + if (!value) + return 1; + + /* Support for fully typed symbol access */ + ull type; + TYPE_S *stype; + + type = GET_DIE_OFFSET(name); + stype = eppic_gettype(value); + + apigetrtype(type, stype); + + eppic_pushref(stype, 1); + eppic_setmemaddr(value, *val); + eppic_do_deref(1, value, value); + + *val = eppic_getval(value); + + if (!eppic_typeislocal(stype) && eppic_type_getidx(stype) > 100) { + char *tname = GET_DIE_NAME(eppic_type_getidx(stype)); + if (tname) { + eppic_chktype(stype, tname); + /* Free the memory allocated by makedumpfile. */ + free(tname); + } + } + return 1; +} + +static enum_t * +apigetenum(char *name) +{ + return 0; +} + +static def_t * +apigetdefs(void) +{ + return 0; +} + +static uint8_t +apigetuint8(void *ptr) +{ + uint8_t val; + if (!READMEM(VADDR, (unsigned long)ptr, (char *)&val, sizeof(val))) + return (uint8_t) -1; + return val; +} + +static uint16_t +apigetuint16(void *ptr) +{ + uint16_t val; + if (!READMEM(VADDR, (unsigned long)ptr, (char *)&val, sizeof(val))) + return (uint16_t) -1; + return val; +} + +static uint32_t +apigetuint32(void *ptr) +{ + uint32_t val; + if (!READMEM(VADDR, (unsigned long)ptr, (char *)&val, sizeof(val))) + return (uint32_t) -1; + return val; +} + +static uint64_t +apigetuint64(void *ptr) +{ + uint64_t val; + if (!READMEM(VADDR, (unsigned long)ptr, (char *)&val, sizeof(val))) + return (uint64_t) -1; + return val; +} + +static char * +apifindsym(char *p) +{ + return NULL; +} + +apiops icops = { + apigetmem, + apiputmem, + apimember, + apigetctype, + apigetrtype, + apialignment, + apigetval, + apigetenum, + apigetdefs, + apigetuint8, + apigetuint16, + apigetuint32, + apigetuint64, + apifindsym +}; + +/* Extensions to built-in functions */ +VALUE_S * +eppic_memset(VALUE_S *vaddr, VALUE_S *vch, VALUE_S *vlen) +{ + ull addr = eppic_getval(vaddr); + int len = eppic_getval(vlen); + int ch = eppic_getval(vch); + + /* + * Set the value at address from iaddr till iaddr + nbytes + * to the value specified in variable ch + */ + UPDATE_FILTER_INFO_RAW(addr, ch, len); + return eppic_makebtype(1); +} + + +/* Initialize eppic */ +int +eppic_init(void *fun_ptr) +{ + cb = (struct call_back *)fun_ptr; + + if (eppic_open() >= 0) { + + /* Register call back functions */ + eppic_apiset(&icops, 3, sizeof(long), 0); + + /* set the new function callback */ + eppic_setcallback(reg_callback); + + /* Extend built-in functions to include memset */ + eppic_builtin("int memset(char *, int, int)", + (bf_t *)eppic_memset); + + return 0; + } + return 1; +} + |