/** * \file executer/user/mcinspect.c * License details are found in the file LICENSE. * * \brief * A DWARF based inspection tool for McKernel. * * \author Balazs Gerofi \par * Copyright (C) 2019 RIKEN */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void usage(char **argv) { printf("Usage: %s \n", basename(argv[0])); printf("Inspect internal state of McKernel.\n"); printf("\n"); printf("Mandatory arguments to long options are mandatory for short options too.\n"); printf(" --help Display this help message.\n"); printf(" --kernel PATH Path to kernel image.\n"); printf(" --ps List processes running on LWK.\n"); printf(" --vtop Dump page tables.\n"); printf(" -v, --va ADDR Dump page tables for ADDR only.\n"); printf(" -p, --pid PID Use process PID for vtop.\n"); printf(" --debug Enable debug mode.\n"); printf("\n"); printf("Examples: \n"); printf(" %s --kernel=smp-x86/kernel/mckernel.img --ps\n", basename(argv[0])); printf(" %s --kernel=smp-x86/kernel/mckernel.img --vtop --pid 100 --va 0x3fffff800000\n", basename(argv[0])); } int debug; int mcfd; /* * BFD based symbol table */ bfd *symbfd = NULL; asymbol **symtab = NULL; ssize_t nsyms; #define NOSYMBOL (-1UL) unsigned long int lookup_bfd_symbol(char *name) { int i; for (i = 0; i < nsyms; ++i) { if (!strcmp(symtab[i]->name, name)) { return (symtab[i]->section->vma + symtab[i]->value); } } return NOSYMBOL; } int init_bfd_symbols(char *fname) { ssize_t needs; bfd_boolean ok; symbfd = bfd_openr(fname, NULL); if (!symbfd) { bfd_perror("bfd_openr"); return -1; } ok = bfd_check_format(symbfd, bfd_object); if (!ok) { bfd_perror("bfd_check_format"); return -1; } needs = bfd_get_symtab_upper_bound(symbfd); if (needs < 0) { bfd_perror("bfd_get_symtab_upper_bound"); return -1; } if (!needs) { printf("no symbols\n"); return -1; } symtab = malloc(needs); if (!symtab) { perror("malloc"); return -1; } nsyms = bfd_canonicalize_symtab(symbfd, symtab); if (nsyms < 0) { bfd_perror("bfd_canonicalize_symtab"); return -1; } return 0; } /* * Walk DWARF tree and call func with arg for each Die */ int dwarf_walk_tree(Dwarf_Debug dbg, int (*func)(Dwarf_Debug dbg, Dwarf_Die die, void *arg), void *arg) { Dwarf_Bool is_info; Dwarf_Unsigned cu_length; Dwarf_Half cu_version; Dwarf_Off cu_abbrev_offset; Dwarf_Half cu_pointer_size; Dwarf_Half cu_offset_size; Dwarf_Half cu_extension_size; Dwarf_Sig8 type_signature; Dwarf_Unsigned type_offset; Dwarf_Unsigned cu_next_offset; Dwarf_Error err; Dwarf_Die unit; Dwarf_Die die; Dwarf_Half header_cu_type; int rc; /* Iterate compile and type units */ for (is_info = 0; is_info < 2; ++is_info) { rc = dwarf_next_cu_header_d(dbg, is_info, &cu_length, &cu_version, &cu_abbrev_offset, &cu_pointer_size, &cu_offset_size, &cu_extension_size, &type_signature, &type_offset, &cu_next_offset, &header_cu_type, &err); while (rc != DW_DLV_NO_ENTRY) { char *name = NULL; const char *tag_name; Dwarf_Half tag; Dwarf_Die next; if (rc != DW_DLV_OK) { fprintf(stderr, "error: dwarf_next_cu_header_c: %d %s\n", rc, dwarf_errmsg(err)); return -1; } rc = dwarf_siblingof_b(dbg, NULL, is_info, &unit, &err); if (rc != DW_DLV_OK) { fprintf(stderr, "error: dwarf_siblingof_b failed: %d %s\n", rc, dwarf_errmsg(err)); return -1; } if (debug) { rc = dwarf_diename(unit, &name, &err); if (rc == DW_DLV_NO_ENTRY) { name = NULL; } else if (rc != DW_DLV_OK) { fprintf(stderr, "error: dwarf_diename error: %d %s\n", rc, dwarf_errmsg(err)); return -1; } rc = dwarf_tag(unit, &tag, &err); if (rc != DW_DLV_OK) { fprintf(stderr, "dwarf_tag error: %d %s\n", rc, dwarf_errmsg(err)); return -1; } rc = dwarf_get_TAG_name(tag, &tag_name); if (rc != DW_DLV_OK) { fprintf(stderr, "dwarf_get_TAG_name error: %d\n", rc); return -1; } printf("%p <%d> %s: %s\n", unit, tag, tag_name, name ? name : ""); } /* Iterate entries in this unit */ rc = dwarf_child(unit, &die, &err); if (rc == DW_DLV_ERROR) { fprintf(stderr, "dwarf_child error: %d %s\n", rc, dwarf_errmsg(err)); return -1; } while (die) { if (debug) { char *name = NULL; const char *tag_name; Dwarf_Half tag; rc = dwarf_diename(die, &name, &err); if (rc == DW_DLV_NO_ENTRY) { name = NULL; } else if (rc != DW_DLV_OK) { fprintf(stderr, "error: dwarf_diename error: %d %s\n", rc, dwarf_errmsg(err)); return -1; } rc = dwarf_tag(die, &tag, &err); if (rc != DW_DLV_OK) { fprintf(stderr, "dwarf_tag error: %d %s\n", rc, dwarf_errmsg(err)); return -1; } rc = dwarf_get_TAG_name(tag, &tag_name); if (rc != DW_DLV_OK) { fprintf(stderr, "dwarf_get_TAG_name error: %d\n", rc); return -1; } printf(" %p <%d> %s: %s\n", die, tag, tag_name, name ? name : ""); } if (func) { rc = func(dbg, die, arg); /* Stop when DW_DLV_OK reached */ if (rc == DW_DLV_OK) { return 0; } } rc = dwarf_siblingof_b(dbg, die, is_info, &next, &err); dwarf_dealloc(dbg, die, DW_DLA_DIE); if (name) dwarf_dealloc(dbg, name, DW_DLA_STRING); if (rc != DW_DLV_OK) break; die = next; } rc = dwarf_next_cu_header_d(dbg, is_info, &cu_length, &cu_version, &cu_abbrev_offset, &cu_pointer_size, &cu_offset_size, &cu_extension_size, &type_signature, &type_offset, &cu_next_offset, &header_cu_type, &err); } } return -1; } int dwarf_get_size(Dwarf_Debug dbg, Dwarf_Die die, unsigned long *psize, Dwarf_Error *perr) { Dwarf_Attribute attr; Dwarf_Unsigned size; Dwarf_Half form; int rc; rc = dwarf_attr(die, DW_AT_byte_size, &attr, perr); if (rc != DW_DLV_OK) { return rc; } rc = dwarf_whatform(attr, &form, perr); if (rc != DW_DLV_OK) { fprintf(stderr, "%s: error: getting whatform: %s\n", __func__, dwarf_errmsg(*perr)); return rc; } if (form == DW_FORM_data1 || form == DW_FORM_data2 || form == DW_FORM_data2 || form == DW_FORM_data4 || form == DW_FORM_data8 || form == DW_FORM_udata) { dwarf_formudata(attr, &size, 0); } else if (form == DW_FORM_sdata) { Dwarf_Signed ssize; dwarf_formsdata(attr, &ssize, 0); if (ssize < 0) { fprintf(stderr, "%s: unsupported negative size\n", __func__); return DW_DLV_ERROR; } size = (Dwarf_Unsigned) ssize; } else { Dwarf_Loc_Head_c loclist_head = 0; Dwarf_Unsigned lcount = 0; Dwarf_Locdesc_c locdesc_entry = 0; Dwarf_Small op; Dwarf_Unsigned opd1, opd2, opd3; Dwarf_Unsigned offsetforbranch; int lres; lres = dwarf_get_loclist_c(attr, &loclist_head, &lcount, perr); if (lres != DW_DLV_OK) { fprintf(stderr, "%s: unsupported member size\n", __func__); return DW_DLV_ERROR; } if (lcount != 1) { fprintf(stderr, "%s: unsupported location expression\n", __func__); dwarf_loc_head_c_dealloc(loclist_head); return DW_DLV_ERROR; } lres = dwarf_get_locdesc_entry_d(loclist_head, 0, 0, 0, 0, 0, 0, 0, 0, &locdesc_entry, 0, 0, 0, perr); if (lres != DW_DLV_OK) { fprintf(stderr, "%s: unsupported location expression\n", __func__); dwarf_loc_head_c_dealloc(loclist_head); return DW_DLV_ERROR; } lres = dwarf_get_location_op_value_d(locdesc_entry, 0, &op, &opd1, &opd2, &opd3, NULL, NULL, NULL, &offsetforbranch, perr); if (lres != DW_DLV_OK) { fprintf(stderr, "%s: unsupported location expression\n", __func__); dwarf_loc_head_c_dealloc(loclist_head); return DW_DLV_ERROR; } if (op != DW_OP_plus_uconst) { fprintf(stderr, "%s: unsupported location expression\n", __func__); dwarf_loc_head_c_dealloc(loclist_head); return DW_DLV_ERROR; } size = opd1; dwarf_loc_head_c_dealloc(loclist_head); } dwarf_dealloc(dbg, attr, DW_DLA_ATTR); *psize = (unsigned long)size; return DW_DLV_OK; } /* * Find the size of a type. */ struct dwarf_size_arg { char *name; unsigned long *sizep; }; int dwarf_size(Dwarf_Debug dbg, Dwarf_Die die, void *arg) { struct dwarf_size_arg *ds = (struct dwarf_size_arg *)arg; Dwarf_Half tag; Dwarf_Error err; char *name = NULL; int rc; unsigned long size; rc = dwarf_tag(die, &tag, &err); if (rc != DW_DLV_OK) { fprintf(stderr, "%s: error: dwarf_tag: %d %s\n", __func__, rc, dwarf_errmsg(err)); goto out; } rc = dwarf_diename(die, &name, &err); if (rc == DW_DLV_NO_ENTRY) { name = NULL; } else if (rc != DW_DLV_OK) { fprintf(stderr, "%s: error: dwarf_diename: %d %s\n", __func__, rc, dwarf_errmsg(err)); goto out; } if (!name || strcasecmp(name, ds->name)) { rc = DW_DLV_NO_ENTRY; goto out; } rc = dwarf_get_size(dbg, die, &size, &err); if (rc == DW_DLV_NO_ENTRY) { goto out; } else if (rc != DW_DLV_OK) { fprintf(stderr, "%s: error: getting size: %s\n", __func__, dwarf_errmsg(err)); goto out; } if (debug) { fprintf(stdout, "size of type \"%s\": %lu\n", ds->name, size); } *ds->sizep = size; rc = DW_DLV_OK; out: if (name) dwarf_dealloc(dbg, name, DW_DLA_STRING); return rc; } #define DWARF_GET_SIZE(__name__) \ ({ \ unsigned long size; \ int rc; \ struct dwarf_size_arg ds = { \ .name = #__name__, \ .sizep = &size, \ }; \ rc = dwarf_walk_tree(dbg, dwarf_size, &ds); \ if (rc != DW_DLV_OK) { \ fprintf(stderr, "%s: error: finding size of %s\n", \ __func__, ds.name); \ exit(1); \ } \ size; \ }) int dwarf_get_offset(Dwarf_Debug dbg, Dwarf_Die die, unsigned long *poffset, Dwarf_Error *perr) { Dwarf_Attribute attr; Dwarf_Unsigned offset; Dwarf_Half form; int rc; rc = dwarf_attr(die, DW_AT_data_member_location, &attr, perr); if (rc != DW_DLV_OK) { return rc; } rc = dwarf_whatform(attr, &form, perr); if (rc != DW_DLV_OK) { fprintf(stderr, "%s: error: getting whatform: %s\n", __func__, dwarf_errmsg(*perr)); return rc; } if (form == DW_FORM_data1 || form == DW_FORM_data2 || form == DW_FORM_data2 || form == DW_FORM_data4 || form == DW_FORM_data8 || form == DW_FORM_udata) { dwarf_formudata(attr, &offset, 0); } else if (form == DW_FORM_sdata) { Dwarf_Signed soffset; dwarf_formsdata(attr, &soffset, 0); if (soffset < 0) { fprintf(stderr, "%s: unsupported negative offset\n", __func__); return DW_DLV_ERROR; } offset = (Dwarf_Unsigned) soffset; } else { Dwarf_Loc_Head_c loclist_head = 0; Dwarf_Unsigned lcount = 0; Dwarf_Locdesc_c locdesc_entry = 0; Dwarf_Small op; Dwarf_Unsigned opd1, opd2, opd3; Dwarf_Unsigned offsetforbranch; int lres; lres = dwarf_get_loclist_c(attr, &loclist_head, &lcount, perr); if (lres != DW_DLV_OK) { fprintf(stderr, "%s: unsupported member offset\n", __func__); return DW_DLV_ERROR; } if (lcount != 1) { fprintf(stderr, "%s: unsupported location expression\n", __func__); dwarf_loc_head_c_dealloc(loclist_head); return DW_DLV_ERROR; } lres = dwarf_get_locdesc_entry_d(loclist_head, 0, 0, 0, 0, 0, 0, 0, 0, &locdesc_entry, 0, 0, 0, perr); if (lres != DW_DLV_OK) { fprintf(stderr, "%s: unsupported location expression\n", __func__); dwarf_loc_head_c_dealloc(loclist_head); return DW_DLV_ERROR; } lres = dwarf_get_location_op_value_d(locdesc_entry, 0, &op, &opd1, &opd2, &opd3, NULL, NULL, NULL, &offsetforbranch, perr); if (lres != DW_DLV_OK) { fprintf(stderr, "%s: unsupported location expression\n", __func__); dwarf_loc_head_c_dealloc(loclist_head); return DW_DLV_ERROR; } if (op != DW_OP_plus_uconst) { fprintf(stderr, "%s: unsupported location expression\n", __func__); dwarf_loc_head_c_dealloc(loclist_head); return DW_DLV_ERROR; } offset = opd1; dwarf_loc_head_c_dealloc(loclist_head); } dwarf_dealloc(dbg, attr, DW_DLA_ATTR); *poffset = (unsigned long)offset; return DW_DLV_OK; } /* * Find the offset of a field in a struct. */ struct dwarf_struct_field_offset_arg { char *struct_name; char *field_name; unsigned long *offp; }; int dwarf_struct_field_offset(Dwarf_Debug dbg, Dwarf_Die die, void *arg) { struct dwarf_struct_field_offset_arg *dsfo = (struct dwarf_struct_field_offset_arg *)arg; Dwarf_Half tag; Dwarf_Error err; Dwarf_Die child, next; char *name = NULL; int rc; unsigned long offset; int found = 0; rc = dwarf_tag(die, &tag, &err); if (rc != DW_DLV_OK) { fprintf(stderr, "%s: error: dwarf_tag: %d %s\n", __func__, rc, dwarf_errmsg(err)); goto out; } rc = dwarf_diename(die, &name, &err); if (rc == DW_DLV_NO_ENTRY) { name = NULL; } else if (rc != DW_DLV_OK) { fprintf(stderr, "%s: error: dwarf_diename: %d %s\n", __func__, rc, dwarf_errmsg(err)); goto out; } if (tag != DW_TAG_structure_type || !name || strcasecmp(name, dsfo->struct_name)) { rc = DW_DLV_NO_ENTRY; goto out; } rc = dwarf_child(die, &child, &err); if (rc == DW_DLV_ERROR) { fprintf(stderr, "%s: dwarf_child error: %d %s\n", __func__, rc, dwarf_errmsg(err)); rc = DW_DLV_NO_ENTRY; goto out; } while (child) { rc = dwarf_diename(child, &name, &err); if (rc == DW_DLV_NO_ENTRY) { name = NULL; } else if (rc != DW_DLV_OK) { fprintf(stderr, "%s: error: dwarf_diename: %d %s\n", __func__, rc, dwarf_errmsg(err)); goto out; } rc = dwarf_tag(child, &tag, &err); if (rc != DW_DLV_OK) { fprintf(stderr, "%s: error: dwarf_tag: %d %s\n", __func__, rc, dwarf_errmsg(err)); goto out; } if (tag != DW_TAG_member || !name || strcasecmp(name, dsfo->field_name)) { goto next_child; } rc = dwarf_get_offset(dbg, child, &offset, &err); if (rc == DW_DLV_NO_ENTRY) { offset = 0; } else if (rc != DW_DLV_OK) { fprintf(stderr, "%s: error: getting dwarf attr offset: %s\n", __func__, dwarf_errmsg(err)); goto out; } if (debug) { fprintf(stdout, "offset of field \"%s\" in struct \"%s\": %lu\n", dsfo->field_name, dsfo->struct_name, offset); } *dsfo->offp = offset; dwarf_dealloc(dbg, child, DW_DLA_DIE); found = 1; break; next_child: rc = dwarf_siblingof_b(dbg, child, 1, &next, &err); dwarf_dealloc(dbg, child, DW_DLA_DIE); if (rc != DW_DLV_OK) { fprintf(stderr, "%s: error: dwarf_siblingof_b: %d %s\n", __func__, rc, dwarf_errmsg(err)); rc = DW_DLV_NO_ENTRY; goto out; } child = next; } if (found) { rc = DW_DLV_OK; } else { rc = DW_DLV_NO_ENTRY; } out: if (name) dwarf_dealloc(dbg, name, DW_DLA_STRING); return rc; } #define DWARF_GET_OFFSET_IN_STRUCT(__struct_name__, __field_name__) \ ({ \ unsigned long offset; \ int rc; \ struct dwarf_struct_field_offset_arg dsfo = { \ .struct_name = #__struct_name__, \ .field_name = #__field_name__, \ .offp = &offset, \ }; \ rc = dwarf_walk_tree(dbg, dwarf_struct_field_offset, &dsfo); \ if (rc != DW_DLV_OK) { \ fprintf(stderr, "%s: error: finding %s in struct %s\n", \ __func__, dsfo.field_name, dsfo.struct_name); \ exit(1); \ } \ offset; \ }) /* * Find the address of a global variable. */ int dwarf_get_address(Dwarf_Debug dbg, Dwarf_Die die, unsigned long *paddr, Dwarf_Error *perr) { Dwarf_Unsigned addr; Dwarf_Half form; Dwarf_Half directform = 0; int rc, i; int found = 0; Dwarf_Signed atcnt = 0; Dwarf_Attribute *atlist = 0; #if 0 Dwarf_Attribute attr; rc = dwarf_attr(die, DW_AT_location, &attr, perr); if (rc != DW_DLV_OK) { fprintf(stderr, "%s: warning: no location attr: %s\n", __func__, dwarf_errmsg(*perr)); return rc; } #endif rc = dwarf_attrlist(die, &atlist, &atcnt, perr); if (rc == DW_DLV_ERROR) { fprintf(stderr, "%s: error: getting attrlist: %s\n", __func__, dwarf_errmsg(*perr)); return rc; } else if (rc == DW_DLV_NO_ENTRY) { /* indicates there are no attrs. It is not an error. */ return rc; } for (i = 0; i < atcnt; i++) { Dwarf_Half attr_i; Dwarf_Attribute attr; rc = dwarf_whatattr(atlist[i], &attr_i, perr); if (rc != DW_DLV_OK) { fprintf(stderr, "%s: error: getting attr: %s\n", __func__, dwarf_errmsg(*perr)); goto dealloc_out; } attr = atlist[i]; if (attr_i != DW_AT_location) { continue; } printf("%s: DW_AT_location\n", __func__); rc = dwarf_whatform(attr, &form, perr); if (rc != DW_DLV_OK) { fprintf(stderr, "%s: error: getting whatform: %s\n", __func__, dwarf_errmsg(*perr)); goto dealloc_out; } dwarf_whatform_direct(attr, &directform, perr); if (form == DW_FORM_block1 || form == DW_FORM_block2 || form == DW_FORM_block4 || form == DW_FORM_block || form == DW_FORM_data4 || form == DW_FORM_data8 || form == DW_FORM_sec_offset) { Dwarf_Loc_Head_c loclist_head = 0; Dwarf_Unsigned lcount = 0; Dwarf_Locdesc_c locdesc_entry = 0; Dwarf_Small op; Dwarf_Unsigned opd1, opd2, opd3; Dwarf_Unsigned offsetforbranch; int lres; lres = dwarf_get_loclist_c(attr, &loclist_head, &lcount, perr); if (lres != DW_DLV_OK) { fprintf(stderr, "%s: dwarf_get_loclist_c: %s\n", __func__, dwarf_errmsg(*perr)); rc = DW_DLV_ERROR; goto dealloc_out; } if (lcount != 1) { fprintf(stderr, "%s: unsupported addr expression\n", __func__); dwarf_loc_head_c_dealloc(loclist_head); rc = DW_DLV_ERROR; goto dealloc_out; } lres = dwarf_get_locdesc_entry_d(loclist_head, 0, 0, 0, 0, 0, 0, 0, 0, &locdesc_entry, 0, 0, 0, perr); if (lres != DW_DLV_OK) { fprintf(stderr, "%s: unsupported addr expression\n", __func__); dwarf_loc_head_c_dealloc(loclist_head); rc = DW_DLV_ERROR; goto dealloc_out; } lres = dwarf_get_location_op_value_d(locdesc_entry, 0, &op, &opd1, &opd2, &opd3, NULL, NULL, NULL, &offsetforbranch, perr); if (lres != DW_DLV_OK) { fprintf(stderr, "%s: unsupported addr expression\n", __func__); dwarf_loc_head_c_dealloc(loclist_head); rc = DW_DLV_ERROR; goto dealloc_out; } if (op != DW_OP_addr) { fprintf(stderr, "%s: unsupported addr expression\n", __func__); dwarf_loc_head_c_dealloc(loclist_head); rc = DW_DLV_ERROR; goto dealloc_out; } addr = opd1; dwarf_loc_head_c_dealloc(loclist_head); } else if (form == DW_FORM_exprloc) { Dwarf_Half address_size = 0; Dwarf_Ptr x = 0; Dwarf_Unsigned tempud = 0; Dwarf_Loc_Head_c loclist_head = 0; Dwarf_Unsigned lcount = 0; Dwarf_Locdesc_c locdesc_entry = 0; Dwarf_Small op; Dwarf_Unsigned opd1, opd2, opd3; Dwarf_Unsigned offsetforbranch; int lres; Dwarf_Half version; Dwarf_Half offset_size; rc = dwarf_formexprloc(attr, &tempud, &x, perr); if (rc != DW_DLV_OK) { fprintf(stderr, "%s: dwarf_formexprloc(): %s\n", __func__, dwarf_errmsg(*perr)); goto dealloc_out; } rc = dwarf_get_die_address_size(die, &address_size, perr); if (rc != DW_DLV_OK) { fprintf(stderr, "%s: dwarf_get_die_address_size: %s\n", __func__, dwarf_errmsg(*perr)); goto dealloc_out; } rc = dwarf_get_version_of_die(die, &version, &offset_size); if (rc != DW_DLV_OK) { fprintf(stderr, "%s: dwarf_get_version_of_die: %s\n", __func__, dwarf_errmsg(*perr)); goto dealloc_out; } rc = dwarf_loclist_from_expr_c(dbg, x, tempud, address_size, offset_size, version, &loclist_head, &lcount, perr); if (rc != DW_DLV_OK) { fprintf(stderr, "%s: dwarf_loclist_from_expr_c: %s\n", __func__, dwarf_errmsg(*perr)); goto dealloc_out; } if (lcount != 1) { fprintf(stderr, "%s: unsupported addr expression\n", __func__); dwarf_loc_head_c_dealloc(loclist_head); rc = DW_DLV_ERROR; goto dealloc_out; } lres = dwarf_get_locdesc_entry_d(loclist_head, 0, 0, 0, 0, 0, 0, 0, 0, &locdesc_entry, 0, 0, 0, perr); if (lres != DW_DLV_OK) { fprintf(stderr, "%s: unsupported addr expression\n", __func__); dwarf_loc_head_c_dealloc(loclist_head); rc = DW_DLV_ERROR; goto dealloc_out; } lres = dwarf_get_location_op_value_d(locdesc_entry, 0, &op, &opd1, &opd2, &opd3, NULL, NULL, NULL, &offsetforbranch, perr); if (lres != DW_DLV_OK) { fprintf(stderr, "%s: unsupported addr expression\n", __func__); dwarf_loc_head_c_dealloc(loclist_head); rc = DW_DLV_ERROR; goto dealloc_out; } if (op != DW_OP_addr) { fprintf(stderr, "%s: unsupported addr expression\n", __func__); dwarf_loc_head_c_dealloc(loclist_head); rc = DW_DLV_ERROR; goto dealloc_out; } addr = opd1; dwarf_loc_head_c_dealloc(loclist_head); } else { fprintf(stderr, "%s: unsupported form type?\n", __func__); goto dealloc_out; } *paddr = (unsigned long)addr; if (debug) { printf("%s: addr: 0x%lx\n", __func__, (unsigned long)addr); } found = 1; break; } dealloc_out: for (i = 0; i < atcnt; i++) { dwarf_dealloc(dbg, atlist[i], DW_DLA_ATTR); } dwarf_dealloc(dbg, atlist, DW_DLA_LIST); if (found) { rc = DW_DLV_OK; } else { rc = DW_DLV_NO_ENTRY; } return rc; } struct dwarf_global_var_addr_arg { char *variable; unsigned long *addrp; }; int dwarf_global_var_addr(Dwarf_Debug dbg, Dwarf_Die die, void *arg) { struct dwarf_global_var_addr_arg *gva = (struct dwarf_global_var_addr_arg *)arg; Dwarf_Half tag; Dwarf_Error err; char *name = NULL; unsigned long addr; int rc; rc = dwarf_tag(die, &tag, &err); if (rc != DW_DLV_OK) { fprintf(stderr, "%s: error: dwarf_tag: %d %s\n", __func__, rc, dwarf_errmsg(err)); goto out; } rc = dwarf_diename(die, &name, &err); if (rc == DW_DLV_NO_ENTRY) { name = NULL; } else if (rc != DW_DLV_OK) { fprintf(stderr, "%s: error: dwarf_diename: %d %s\n", __func__, rc, dwarf_errmsg(err)); goto out; } if (tag != DW_TAG_variable || !name || strcasecmp(name, gva->variable)) { rc = DW_DLV_NO_ENTRY; goto out; } printf("%s: inspecting %s\n", __func__, name); rc = dwarf_get_address(dbg, die, &addr, &err); if (rc == DW_DLV_NO_ENTRY) { printf("%s: inspecting %s -> DW_DLV_NO_ENTRY for addr?\n", __func__, name); goto out; } else if (rc != DW_DLV_OK) { fprintf(stderr, "%s: error: getting dwarf addr location: %s\n", __func__, dwarf_errmsg(err)); goto out; } if (debug) { fprintf(stdout, "%s: found %s @ 0x%lx\n", __func__, name, addr); } *gva->addrp = addr; rc = DW_DLV_OK; out: if (name) dwarf_dealloc(dbg, name, DW_DLA_STRING); return rc; } #define DWARF_GET_VARIABLE_ADDRESS(__variable__) \ ({ \ unsigned long addr; \ int rc; \ struct dwarf_global_var_addr_arg gva = { \ .variable = #__variable__, \ .addrp = &addr, \ }; \ addr = lookup_bfd_symbol(gva.variable); \ if (addr == NOSYMBOL) { \ rc = dwarf_walk_tree(dbg, dwarf_global_var_addr, &gva); \ if (rc != DW_DLV_OK) { \ fprintf(stderr, "%s: error: finding addr of %s\n", \ __func__, gva.variable); \ exit(1); \ } \ } \ addr; \ }) /* IHK kernel inspection I/F */ #define IHK_OS_READ_KADDR 0x112a39 #define IHK_OS_READ_KADDR_VIRT 0 #define IHK_OS_READ_KADDR_PHYS 1 struct ihk_os_read_kaddr_desc { unsigned long kaddr; unsigned long len; void *ubuf; int flags; }; void ihk_read_kernel(unsigned long addr, unsigned long len, void *buf, int flags) { struct ihk_os_read_kaddr_desc desc; desc.kaddr = addr; desc.len = len; desc.ubuf = buf; desc.flags = flags; if (ioctl(mcfd, IHK_OS_READ_KADDR, &desc) != 0) { fprintf(stderr, "%s: error: accessing kernel addr 0x%lx\n", __func__, addr); exit(1); } } #define ihk_read_val(addr, pval) \ ihk_read_kernel(addr, sizeof(*pval), (void *)pval, \ IHK_OS_READ_KADDR_VIRT) #define ihk_read_val_phys(addr, pval) \ ihk_read_kernel(addr, sizeof(*pval), (void *)pval, \ IHK_OS_READ_KADDR_PHYS) #define get_pointer_symbol_val(__variable__, pval) \ ({ \ unsigned long addr; \ addr = DWARF_GET_VARIABLE_ADDRESS(__variable__); \ ihk_read_kernel(addr, sizeof(*pval), (void *)pval, \ IHK_OS_READ_KADDR_VIRT); \ }) #define PS_RUNNING 0x1 #define PS_INTERRUPTIBLE 0x2 #define PS_UNINTERRUPTIBLE 0x4 #define PS_ZOMBIE 0x8 #define PS_EXITED 0x10 #define PS_STOPPED 0x20 /* * Globals */ int nr_cpus; unsigned long clv; unsigned long clv_size; unsigned long clv_runq_offset; unsigned long clv_idle_offset; unsigned long clv_current_offset; unsigned long thread_tid_offset; unsigned long thread_sched_list_offset; unsigned long thread_proc_offset; unsigned long thread_status_offset; unsigned long process_pid_offset; unsigned long process_vm_offset; unsigned long process_saved_cmdline_offset; unsigned long process_saved_cmdline_len_offset; unsigned long vm_address_space_offset; unsigned long address_space_page_table_offset; void init_globals(Dwarf_Debug dbg) { unsigned long num_processors_addr; num_processors_addr = DWARF_GET_VARIABLE_ADDRESS(mck_num_processors); ihk_read_val(num_processors_addr, &num_processors_addr); ihk_read_val(num_processors_addr, &nr_cpus); ihk_read_val(DWARF_GET_VARIABLE_ADDRESS(clv), &clv); clv_size = DWARF_GET_SIZE(cpu_local_var); clv_runq_offset = DWARF_GET_OFFSET_IN_STRUCT(cpu_local_var, runq); clv_idle_offset = DWARF_GET_OFFSET_IN_STRUCT(cpu_local_var, idle); clv_current_offset = DWARF_GET_OFFSET_IN_STRUCT(cpu_local_var, current); thread_tid_offset = DWARF_GET_OFFSET_IN_STRUCT(thread, tid); thread_proc_offset = DWARF_GET_OFFSET_IN_STRUCT(thread, proc); thread_status_offset = DWARF_GET_OFFSET_IN_STRUCT(thread, status); thread_sched_list_offset = DWARF_GET_OFFSET_IN_STRUCT(thread, sched_list); process_pid_offset = DWARF_GET_OFFSET_IN_STRUCT(process, pid); process_saved_cmdline_offset = DWARF_GET_OFFSET_IN_STRUCT(process, saved_cmdline); process_saved_cmdline_len_offset = DWARF_GET_OFFSET_IN_STRUCT(process, saved_cmdline_len); process_vm_offset = DWARF_GET_OFFSET_IN_STRUCT(process, vm); vm_address_space_offset = DWARF_GET_OFFSET_IN_STRUCT(process_vm, address_space); address_space_page_table_offset = DWARF_GET_OFFSET_IN_STRUCT(address_space, page_table); } void print_thread(int cpu, unsigned long thread, unsigned long idle, int active) { int tid; int pid; int status; unsigned long proc; char *comm = "(unknown)"; char *cmd_line = NULL; long cmd_line_len; long cmd_line_addr; ihk_read_val(thread + thread_tid_offset, &tid); ihk_read_val(thread + thread_proc_offset, &proc); ihk_read_val(thread + thread_status_offset, &status); ihk_read_val(proc + process_pid_offset, &pid); ihk_read_val(proc + process_saved_cmdline_len_offset, &cmd_line_len); if (thread == idle) { comm = "(idle)"; } if (cmd_line_len) { cmd_line = malloc(cmd_line_len + 1); if (!cmd_line) { fprintf(stderr, "%s: error: allocating cmdline\n", __func__); exit(1); } memset(cmd_line, 0, cmd_line_len + 1); ihk_read_val(proc + process_saved_cmdline_offset, &cmd_line_addr); ihk_read_kernel(cmd_line_addr, cmd_line_len, cmd_line, IHK_OS_READ_KADDR_VIRT); comm = basename(cmd_line); } printf("%3d %s%6d %6d 0x%16lx %2s %s\n", cpu, active ? ">" : " ", tid, pid, thread, //"DS", status == PS_RUNNING ? "R" : status == PS_INTERRUPTIBLE ? "IN" : status == PS_UNINTERRUPTIBLE ? "UN" : status == PS_ZOMBIE ? "Z" : status == PS_EXITED ? "E" : status == PS_STOPPED ? "S" : "U", comm); if (cmd_line) free(cmd_line); } int mcps(Dwarf_Debug dbg) { int cpu; printf("%3s %s%6s %6s %18s %2s %s\n", "CPU", " ", "TID", "PID", "Thread", "ST", "exe"); printf("-----------------------------------------------\n"); /* Iterate CPUs */ for (cpu = 0; cpu < nr_cpus; ++cpu) { unsigned long per_cpu; unsigned long runq; unsigned long thread; unsigned long thread_sched_list; unsigned long idle; unsigned long current; per_cpu = clv + (clv_size * cpu); runq = per_cpu + clv_runq_offset; idle = per_cpu + clv_idle_offset; ihk_read_val(per_cpu + clv_current_offset, ¤t); ihk_read_val(per_cpu + clv_runq_offset, &thread_sched_list); print_thread(cpu, current, idle, 1); /* Iterate threads */ for (; thread_sched_list != runq; ihk_read_val(thread_sched_list, &thread_sched_list)) { thread = thread_sched_list - thread_sched_list_offset; if (thread == current) continue; print_thread(cpu, thread, idle, 0); } if (current != idle) { print_thread(cpu, idle, idle, 0); } } return 0; } int find_proc(Dwarf_Debug dbg, int pid, unsigned long *rproc) { int cpu; /* Iterate CPUs */ for (cpu = 0; cpu < nr_cpus; ++cpu) { unsigned long per_cpu; unsigned long runq; unsigned long thread; unsigned long thread_sched_list; int ipid; per_cpu = clv + (clv_size * cpu); runq = per_cpu + clv_runq_offset; ihk_read_val(per_cpu + clv_runq_offset, &thread_sched_list); /* Iterate threads */ for (; thread_sched_list != runq; ihk_read_val(thread_sched_list, &thread_sched_list)) { unsigned long proc; thread = thread_sched_list - thread_sched_list_offset; ihk_read_val(thread + thread_proc_offset, &proc); ihk_read_val(proc + process_pid_offset, &ipid); if (pid == ipid) { *rproc = proc; return 0; } } } return -1; } #if 0 void do_pte_walk_single(Dwarf_Debug dbg, unsigned long pt, int level, unsigned long va) { unsigned long pte; int idx = va >> ptl_shift(level); ihk_read_val(pt + idx * sizeof(pte), &pte); if (pte_is_type_page(pte, level)) { } if (level > 1) { pt = do_pte_walk_single(); } } int print_pte_single(Dwarf_Debug dbg, unsigned long pt, unsigned long va) { int level = PGTABLE_LEVELS; printf("VA: 0x%lx -> \n", va); do_pte_walk_single(dbg, pt, PGTABLE_LEVELS, va); } #endif int mcvtop(Dwarf_Debug dbg, int pid, unsigned long vtop_addr) { unsigned long proc = 0; unsigned long init_pt; unsigned long vm, ap, pt = 0; if (pid != 0) { if (find_proc(dbg, pid, &proc) < 0) { fprintf(stderr, "%s: error: finding PID %d\n", __func__, pid); return -1; } } get_pointer_symbol_val(swapper_page_table, &init_pt); printf("%s: init_pt: 0x%lx\n", __func__, init_pt); if (proc) { ihk_read_val(proc + process_vm_offset, &vm); ihk_read_val(vm + vm_address_space_offset, &ap); ihk_read_val(ap + address_space_page_table_offset, &pt); } return 0; } int help; int ps; int vtop; int pid; unsigned long vtop_addr; struct option mcinspect_options[] = { { .name = "kernel", .has_arg = required_argument, .flag = NULL, .val = 'k', }, { .name = "ps", .has_arg = no_argument, .flag = &ps, .val = 1, }, { .name = "help", .has_arg = no_argument, .flag = &help, .val = 1, }, { .name = "debug", .has_arg = no_argument, .flag = &debug, .val = 1, }, { .name = "vtop", .has_arg = no_argument, .flag = &vtop, .val = 1, }, { .name = "va", .has_arg = required_argument, .flag = NULL, .val = 'v', }, { .name = "pid", .has_arg = required_argument, .flag = NULL, .val = 'p', }, /* end */ { NULL, 0, NULL, 0, }, }; int main(int argc, char **argv) { Dwarf_Debug dbg = 0; int dwarffd = -1; int rc = DW_DLV_ERROR; char *kernel_path = NULL; Dwarf_Error error; Dwarf_Handler errhand = 0; Dwarf_Ptr errarg = 0; int opt; debug = 0; mcfd = -1; help = 0; ps = 0; vtop = 0; vtop_addr = -1UL; pid = 0; while ((opt = getopt_long(argc, argv, "+k:v:p:", mcinspect_options, NULL)) != -1) { switch (opt) { case 'k': kernel_path = optarg; break; case 'v': vtop_addr = strtoul(optarg, 0, 16); if (vtop_addr == 0 || errno == EINVAL || errno == ERANGE) { fprintf(stderr, "error: invalid VA? (expected format: 0xXXXX)\n\n"); usage(argv); exit(1); } break; case 'p': pid = atoi(optarg); break; } } if (help) { usage(argv); exit(0); } if (!kernel_path) { fprintf(stderr, "error: you must specify the kernel image\n\n"); usage(argv); exit(1); } if (!ps && !vtop) { printf("PID: %d\n", pid); usage(argv); exit(1); } if (init_bfd_symbols(kernel_path) < 0) { fprintf(stderr, "error: accessing ELF image %s\n", kernel_path); exit(1); } mcfd = open("/dev/mcos0", O_RDONLY); if (mcfd < 0) { fprintf(stderr, "error: opening IHK OS device file\n"); exit(1); } dwarffd = open(kernel_path, O_RDONLY); if (dwarffd < 0) { fprintf(stderr, "error: opening %s\n", kernel_path); exit(1); } rc = dwarf_init_b(dwarffd, DW_DLA_WEAK, errhand, errarg, &dbg, &error); if (rc != DW_DLV_OK) { fprintf(stderr, "error: accessing DWARF information\n"); exit(1); } init_globals(dbg); if (ps) mcps(dbg); if (vtop) { mcvtop(dbg, pid, vtop_addr); } dwarf_finish(dbg); close(dwarffd); close(mcfd); return 0; }