| #ifndef _DWARVES_H_ |
| #define _DWARVES_H_ 1 |
| /* |
| SPDX-License-Identifier: GPL-2.0-only |
| |
| Copyright (C) 2006 Mandriva Conectiva S.A. |
| Copyright (C) 2006..2019 Arnaldo Carvalho de Melo <acme@redhat.com> |
| */ |
| |
| |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <obstack.h> |
| #include <dwarf.h> |
| #include <elfutils/libdwfl.h> |
| #include <sys/types.h> |
| |
| #include "dutil.h" |
| #include "list.h" |
| #include "rbtree.h" |
| #include "pahole_strings.h" |
| |
| struct cu; |
| |
| enum load_steal_kind { |
| LSK__KEEPIT, |
| LSK__DELETE, |
| LSK__STOP_LOADING, |
| }; |
| |
| /* |
| * BTF combines all the types into one big CU using btf_dedup(), so for something |
| * like a allyesconfig vmlinux kernel we can get over 65535 types. |
| */ |
| typedef uint32_t type_id_t; |
| |
| struct conf_fprintf; |
| |
| /** struct conf_load - load configuration |
| * @extra_dbg_info - keep original debugging format extra info |
| * (e.g. DWARF's decl_{line,file}, id, etc) |
| * @fixup_silly_bitfields - Fixup silly things such as "int foo:32;" |
| * @get_addr_info - wheter to load DW_AT_location and other addr info |
| */ |
| struct conf_load { |
| enum load_steal_kind (*steal)(struct cu *cu, |
| struct conf_load *conf); |
| void *cookie; |
| char *format_path; |
| bool extra_dbg_info; |
| bool fixup_silly_bitfields; |
| bool get_addr_info; |
| struct conf_fprintf *conf_fprintf; |
| }; |
| |
| /** struct conf_fprintf - hints to the __fprintf routines |
| * |
| * @count - Just like 'dd', stop pretty printing input after 'count' records |
| * @skip - Just like 'dd', skip 'count' records when pretty printing input |
| * @seek_bytes - Number of bytes to seek, if stdin only from start, when we have --pretty FILE, then from the end as well with negative numbers, |
| * may be of the form $header.MEMBER_NAME when using with --header. |
| * @size_bytes - Number of bytes to read, similar to seek_bytes, and when both are in place, first seek seek_bytes then read size_bytes |
| * @range - data structure field in --header to determine --seek_bytes and --size_bytes, must have 'offset' and 'size' fields |
| * @flat_arrays - a->foo[10][2] becomes a->foo[20] |
| * @classes_as_structs - class f becomes struct f, CTF doesn't have a "class" |
| * @cachelinep - pointer to current cacheline, so that when expanding types we keep track of it, |
| * needs to be "global", i.e. not set at each recursion. |
| * @suppress_force_paddings: This makes sense only if the debugging format has struct alignment information, |
| * So allow for it to be disabled and disable it automatically for things like BTF, |
| * that don't have such info. |
| */ |
| struct conf_fprintf { |
| const char *prefix; |
| const char *suffix; |
| int32_t type_spacing; |
| int32_t name_spacing; |
| uint32_t base_offset; |
| uint32_t count; |
| uint32_t *cachelinep; |
| const char *seek_bytes; |
| const char *size_bytes; |
| const char *header_type; |
| const char *range; |
| uint32_t skip; |
| uint8_t indent; |
| uint8_t expand_types:1; |
| uint8_t expand_pointers:1; |
| uint8_t rel_offset:1; |
| uint8_t emit_stats:1; |
| uint8_t suppress_comments:1; |
| uint8_t has_alignment_info:1; |
| uint8_t suppress_aligned_attribute:1; |
| uint8_t suppress_offset_comment:1; |
| uint8_t suppress_force_paddings:1; |
| uint8_t suppress_packed:1; |
| uint8_t show_decl_info:1; |
| uint8_t show_only_data_members:1; |
| uint8_t no_semicolon:1; |
| uint8_t show_first_biggest_size_base_type_member:1; |
| uint8_t flat_arrays:1; |
| uint8_t first_member:1; |
| uint8_t last_member:1; |
| uint8_t union_member:1; |
| uint8_t no_parm_names:1; |
| uint8_t classes_as_structs:1; |
| uint8_t hex_fmt:1; |
| uint8_t strip_inline:1; |
| }; |
| |
| struct cus { |
| uint32_t nr_entries; |
| struct list_head cus; |
| }; |
| |
| struct cus *cus__new(void); |
| void cus__delete(struct cus *cus); |
| |
| int cus__load_file(struct cus *cus, struct conf_load *conf, |
| const char *filename); |
| int cus__load_files(struct cus *cus, struct conf_load *conf, |
| char *filenames[]); |
| int cus__fprintf_load_files_err(struct cus *cus, const char *tool, |
| char *argv[], int err, FILE *output); |
| int cus__load_dir(struct cus *cus, struct conf_load *conf, |
| const char *dirname, const char *filename_mask, |
| const int recursive); |
| void cus__add(struct cus *cus, struct cu *cu); |
| void cus__print_error_msg(const char *progname, const struct cus *cus, |
| const char *filename, const int err); |
| struct cu *cus__find_cu_by_name(const struct cus *cus, const char *name); |
| struct tag *cus__find_struct_by_name(const struct cus *cus, struct cu **cu, |
| const char *name, const int include_decls, |
| type_id_t *id); |
| struct tag *cus__find_struct_or_union_by_name(const struct cus *cus, struct cu **cu, |
| const char *name, const int include_decls, type_id_t *id); |
| struct tag *cu__find_type_by_name(const struct cu *cu, const char *name, const int include_decls, type_id_t *idp); |
| struct tag *cus__find_type_by_name(const struct cus *cus, struct cu **cu, const char *name, |
| const int include_decls, type_id_t *id); |
| struct function *cus__find_function_at_addr(const struct cus *cus, |
| uint64_t addr, struct cu **cu); |
| void cus__for_each_cu(struct cus *cus, int (*iterator)(struct cu *cu, void *cookie), |
| void *cookie, |
| struct cu *(*filter)(struct cu *cu)); |
| |
| struct ptr_table { |
| void **entries; |
| uint32_t nr_entries; |
| uint32_t allocated_entries; |
| }; |
| |
| struct function; |
| struct tag; |
| struct cu; |
| struct variable; |
| |
| /* Same as DW_LANG, so that we don't have to include dwarf.h in CTF */ |
| enum dwarf_languages { |
| LANG_C89 = 0x01, /* ISO C:1989 */ |
| LANG_C = 0x02, /* C */ |
| LANG_Ada83 = 0x03, /* ISO Ada:1983 */ |
| LANG_C_plus_plus = 0x04, /* ISO C++:1998 */ |
| LANG_Cobol74 = 0x05, /* ISO Cobol:1974 */ |
| LANG_Cobol85 = 0x06, /* ISO Cobol:1985 */ |
| LANG_Fortran77 = 0x07, /* ISO FORTRAN 77 */ |
| LANG_Fortran90 = 0x08, /* ISO Fortran 90 */ |
| LANG_Pascal83 = 0x09, /* ISO Pascal:1983 */ |
| LANG_Modula2 = 0x0a, /* ISO Modula-2:1996 */ |
| LANG_Java = 0x0b, /* Java */ |
| LANG_C99 = 0x0c, /* ISO C:1999 */ |
| LANG_Ada95 = 0x0d, /* ISO Ada:1995 */ |
| LANG_Fortran95 = 0x0e, /* ISO Fortran 95 */ |
| LANG_PL1 = 0x0f, /* ISO PL/1:1976 */ |
| LANG_Objc = 0x10, /* Objective-C */ |
| LANG_ObjC_plus_plus = 0x11, /* Objective-C++ */ |
| LANG_UPC = 0x12, /* Unified Parallel C */ |
| LANG_D = 0x13, /* D */ |
| }; |
| |
| /** struct debug_fmt_ops - specific to the underlying debug file format |
| * |
| * @function__name - will be called by function__name(), giving a chance to |
| * formats such as CTF to get this from some other place |
| * than the global strings table. CTF does this by storing |
| * GElf_Sym->st_name in function->name, and by using |
| * function->name as an index into the .strtab ELF section. |
| * @variable__name - will be called by variable__name(), see @function_name |
| * cu__delete - called at cu__delete(), to give a chance to formats such as |
| * CTF to keep the .strstab ELF section available till the cu is |
| * deleted. See @function__name |
| */ |
| struct debug_fmt_ops { |
| const char *name; |
| int (*init)(void); |
| void (*exit)(void); |
| int (*load_file)(struct cus *cus, |
| struct conf_load *conf, |
| const char *filename); |
| const char *(*tag__decl_file)(const struct tag *tag, |
| const struct cu *cu); |
| uint32_t (*tag__decl_line)(const struct tag *tag, |
| const struct cu *cu); |
| unsigned long long (*tag__orig_id)(const struct tag *tag, |
| const struct cu *cu); |
| void (*tag__free_orig_info)(struct tag *tag, |
| struct cu *cu); |
| const char *(*function__name)(struct function *tag, |
| const struct cu *cu); |
| const char *(*variable__name)(const struct variable *var, |
| const struct cu *cu); |
| const char *(*strings__ptr)(const struct cu *cu, strings_t s); |
| void (*cu__delete)(struct cu *cu); |
| bool has_alignment_info; |
| }; |
| |
| extern struct debug_fmt_ops *dwarves__active_loader; |
| |
| struct cu { |
| struct list_head node; |
| struct list_head tags; |
| struct list_head tool_list; /* To be used by tools such as ctracer */ |
| struct ptr_table types_table; |
| struct ptr_table functions_table; |
| struct ptr_table tags_table; |
| struct rb_root functions; |
| char *name; |
| char *filename; |
| void *priv; |
| struct obstack obstack; |
| struct debug_fmt_ops *dfops; |
| Elf *elf; |
| Dwfl_Module *dwfl; |
| uint32_t cached_symtab_nr_entries; |
| uint8_t addr_size; |
| uint8_t extra_dbg_info:1; |
| uint8_t has_addr_info:1; |
| uint8_t uses_global_strings:1; |
| uint8_t little_endian:1; |
| uint16_t language; |
| unsigned long nr_inline_expansions; |
| size_t size_inline_expansions; |
| uint32_t nr_functions_changed; |
| uint32_t nr_structures_changed; |
| size_t max_len_changed_item; |
| size_t function_bytes_added; |
| size_t function_bytes_removed; |
| int build_id_len; |
| unsigned char build_id[0]; |
| }; |
| |
| struct cu *cu__new(const char *name, uint8_t addr_size, |
| const unsigned char *build_id, int build_id_len, |
| const char *filename); |
| void cu__delete(struct cu *cu); |
| |
| const char *cu__string(const struct cu *cu, strings_t s); |
| |
| static inline int cu__cache_symtab(struct cu *cu) |
| { |
| int err = dwfl_module_getsymtab(cu->dwfl); |
| if (err > 0) |
| cu->cached_symtab_nr_entries = dwfl_module_getsymtab(cu->dwfl); |
| return err; |
| } |
| |
| static inline __pure bool cu__is_c_plus_plus(const struct cu *cu) |
| { |
| return cu->language == LANG_C_plus_plus; |
| } |
| |
| /** |
| * cu__for_each_cached_symtab_entry - iterate thru the cached symtab entries |
| * @cu: struct cu instance |
| * @id: uint32_t tag id |
| * @pos: struct GElf_Sym iterator |
| * @name: char pointer where the symbol_name will be stored |
| */ |
| #define cu__for_each_cached_symtab_entry(cu, id, pos, name) \ |
| for (id = 1, \ |
| name = dwfl_module_getsym(cu->dwfl, id, &sym, NULL); \ |
| id < cu->cached_symtab_nr_entries; \ |
| ++id, name = dwfl_module_getsym(cu->dwfl, id, &sym, NULL)) |
| |
| /** |
| * cu__for_each_type - iterate thru all the type tags |
| * @cu: struct cu instance to iterate |
| * @id: type_id_t id |
| * @pos: struct tag iterator |
| * |
| * See cu__table_nullify_type_entry and users for the reason for |
| * the NULL test (hint: CTF Unknown types) |
| */ |
| #define cu__for_each_type(cu, id, pos) \ |
| for (id = 1; id < cu->types_table.nr_entries; ++id) \ |
| if (!(pos = cu->types_table.entries[id])) \ |
| continue; \ |
| else |
| |
| /** |
| * cu__for_each_struct - iterate thru all the struct tags |
| * @cu: struct cu instance to iterate |
| * @pos: struct class iterator |
| * @id: type_id_t id |
| */ |
| #define cu__for_each_struct(cu, id, pos) \ |
| for (id = 1; id < cu->types_table.nr_entries; ++id) \ |
| if (!(pos = tag__class(cu->types_table.entries[id])) || \ |
| !tag__is_struct(class__tag(pos))) \ |
| continue; \ |
| else |
| |
| /** |
| * cu__for_each_struct_or_union - iterate thru all the struct and union tags |
| * @cu: struct cu instance to iterate |
| * @pos: struct class iterator |
| * @id: type_id_t tag id |
| */ |
| #define cu__for_each_struct_or_union(cu, id, pos) \ |
| for (id = 1; id < cu->types_table.nr_entries; ++id) \ |
| if (!(pos = tag__class(cu->types_table.entries[id])) || \ |
| !(tag__is_struct(class__tag(pos)) || \ |
| tag__is_union(class__tag(pos)))) \ |
| continue; \ |
| else |
| |
| /** |
| * cu__for_each_function - iterate thru all the function tags |
| * @cu: struct cu instance to iterate |
| * @pos: struct function iterator |
| * @id: uint32_t tag id |
| */ |
| #define cu__for_each_function(cu, id, pos) \ |
| for (id = 0; id < cu->functions_table.nr_entries; ++id) \ |
| if (!(pos = tag__function(cu->functions_table.entries[id]))) \ |
| continue; \ |
| else |
| |
| /** |
| * cu__for_each_variable - iterate thru all the global variable tags |
| * @cu: struct cu instance to iterate |
| * @pos: struct tag iterator |
| * @id: uint32_t tag id |
| */ |
| #define cu__for_each_variable(cu, id, pos) \ |
| for (id = 0; id < cu->tags_table.nr_entries; ++id) \ |
| if (!(pos = cu->tags_table.entries[id]) || \ |
| !tag__is_variable(pos)) \ |
| continue; \ |
| else |
| |
| int cu__add_tag(struct cu *cu, struct tag *tag, uint32_t *id); |
| int cu__add_tag_with_id(struct cu *cu, struct tag *tag, uint32_t id); |
| int cu__table_add_tag(struct cu *cu, struct tag *tag, uint32_t *id); |
| int cu__table_add_tag_with_id(struct cu *cu, struct tag *tag, uint32_t id); |
| int cu__table_nullify_type_entry(struct cu *cu, uint32_t id); |
| struct tag *cu__find_base_type_by_name(const struct cu *cu, const char *name, |
| type_id_t *id); |
| struct tag *cu__find_base_type_by_sname_and_size(const struct cu *cu, |
| strings_t name, |
| uint16_t bit_size, |
| type_id_t *idp); |
| struct tag *cu__find_enumeration_by_name(const struct cu *cu, const char *name, type_id_t *idp); |
| struct tag *cu__find_enumeration_by_sname_and_size(const struct cu *cu, |
| strings_t sname, |
| uint16_t bit_size, |
| type_id_t *idp); |
| struct tag *cu__find_first_typedef_of_type(const struct cu *cu, |
| const type_id_t type); |
| struct tag *cu__find_function_by_name(const struct cu *cu, const char *name); |
| struct tag *cu__find_struct_by_sname(const struct cu *cu, strings_t sname, |
| const int include_decls, type_id_t *idp); |
| struct function *cu__find_function_at_addr(const struct cu *cu, |
| uint64_t addr); |
| struct tag *cu__function(const struct cu *cu, const uint32_t id); |
| struct tag *cu__tag(const struct cu *cu, const uint32_t id); |
| struct tag *cu__type(const struct cu *cu, const type_id_t id); |
| struct tag *cu__find_struct_by_name(const struct cu *cu, const char *name, |
| const int include_decls, type_id_t *id); |
| struct tag *cu__find_struct_or_union_by_name(const struct cu *cu, const char *name, |
| const int include_decls, type_id_t *id); |
| bool cu__same_build_id(const struct cu *cu, const struct cu *other); |
| void cu__account_inline_expansions(struct cu *cu); |
| int cu__for_all_tags(struct cu *cu, |
| int (*iterator)(struct tag *tag, |
| struct cu *cu, void *cookie), |
| void *cookie); |
| |
| /** struct tag - basic representation of a debug info element |
| * @priv - extra data, for instance, DWARF offset, id, decl_{file,line} |
| * @top_level - |
| */ |
| struct tag { |
| struct list_head node; |
| type_id_t type; |
| uint16_t tag; |
| bool visited; |
| bool top_level; |
| uint16_t recursivity_level; |
| void *priv; |
| }; |
| |
| // To use with things like type->type_enum == perf_event_type+perf_user_event_type |
| struct tag_cu { |
| struct tag *tag; |
| struct cu *cu; |
| }; |
| |
| void tag__delete(struct tag *tag, struct cu *cu); |
| |
| static inline int tag__is_enumeration(const struct tag *tag) |
| { |
| return tag->tag == DW_TAG_enumeration_type; |
| } |
| |
| static inline int tag__is_namespace(const struct tag *tag) |
| { |
| return tag->tag == DW_TAG_namespace; |
| } |
| |
| static inline int tag__is_struct(const struct tag *tag) |
| { |
| return tag->tag == DW_TAG_structure_type || |
| tag->tag == DW_TAG_interface_type || |
| tag->tag == DW_TAG_class_type; |
| } |
| |
| static inline int tag__is_typedef(const struct tag *tag) |
| { |
| return tag->tag == DW_TAG_typedef; |
| } |
| |
| static inline int tag__is_rvalue_reference_type(const struct tag *tag) |
| { |
| return tag->tag == DW_TAG_rvalue_reference_type; |
| } |
| |
| static inline int tag__is_union(const struct tag *tag) |
| { |
| return tag->tag == DW_TAG_union_type; |
| } |
| |
| static inline int tag__is_const(const struct tag *tag) |
| { |
| return tag->tag == DW_TAG_const_type; |
| } |
| |
| static inline int tag__is_pointer(const struct tag *tag) |
| { |
| return tag->tag == DW_TAG_pointer_type; |
| } |
| |
| static inline int tag__is_pointer_to(const struct tag *tag, type_id_t type) |
| { |
| return tag__is_pointer(tag) && tag->type == type; |
| } |
| |
| static inline bool tag__is_variable(const struct tag *tag) |
| { |
| return tag->tag == DW_TAG_variable; |
| } |
| |
| static inline bool tag__is_volatile(const struct tag *tag) |
| { |
| return tag->tag == DW_TAG_volatile_type; |
| } |
| |
| static inline bool tag__is_restrict(const struct tag *tag) |
| { |
| return tag->tag == DW_TAG_restrict_type; |
| } |
| |
| static inline int tag__is_modifier(const struct tag *tag) |
| { |
| return tag__is_const(tag) || |
| tag__is_volatile(tag) || |
| tag__is_restrict(tag); |
| } |
| |
| static inline bool tag__has_namespace(const struct tag *tag) |
| { |
| return tag__is_struct(tag) || |
| tag__is_union(tag) || |
| tag__is_namespace(tag) || |
| tag__is_enumeration(tag); |
| } |
| |
| /** |
| * tag__is_tag_type - is this tag derived from the 'type' class? |
| * @tag - tag queried |
| */ |
| static inline int tag__is_type(const struct tag *tag) |
| { |
| return tag__is_union(tag) || |
| tag__is_struct(tag) || |
| tag__is_typedef(tag) || |
| tag__is_rvalue_reference_type(tag) || |
| tag__is_enumeration(tag); |
| } |
| |
| /** |
| * tag__is_tag_type - is this one of the possible types for a tag? |
| * @tag - tag queried |
| */ |
| static inline int tag__is_tag_type(const struct tag *tag) |
| { |
| return tag__is_type(tag) || |
| tag->tag == DW_TAG_array_type || |
| tag->tag == DW_TAG_string_type || |
| tag->tag == DW_TAG_base_type || |
| tag->tag == DW_TAG_const_type || |
| tag->tag == DW_TAG_pointer_type || |
| tag->tag == DW_TAG_rvalue_reference_type || |
| tag->tag == DW_TAG_ptr_to_member_type || |
| tag->tag == DW_TAG_reference_type || |
| tag->tag == DW_TAG_restrict_type || |
| tag->tag == DW_TAG_subroutine_type || |
| tag->tag == DW_TAG_unspecified_type || |
| tag->tag == DW_TAG_volatile_type; |
| } |
| |
| static inline const char *tag__decl_file(const struct tag *tag, |
| const struct cu *cu) |
| { |
| if (cu->dfops && cu->dfops->tag__decl_file) |
| return cu->dfops->tag__decl_file(tag, cu); |
| return NULL; |
| } |
| |
| static inline uint32_t tag__decl_line(const struct tag *tag, |
| const struct cu *cu) |
| { |
| if (cu->dfops && cu->dfops->tag__decl_line) |
| return cu->dfops->tag__decl_line(tag, cu); |
| return 0; |
| } |
| |
| static inline unsigned long long tag__orig_id(const struct tag *tag, |
| const struct cu *cu) |
| { |
| if (cu->dfops && cu->dfops->tag__orig_id) |
| return cu->dfops->tag__orig_id(tag, cu); |
| return 0; |
| } |
| |
| static inline void tag__free_orig_info(struct tag *tag, struct cu *cu) |
| { |
| if (cu->dfops && cu->dfops->tag__free_orig_info) |
| cu->dfops->tag__free_orig_info(tag, cu); |
| } |
| |
| size_t tag__fprintf_decl_info(const struct tag *tag, |
| const struct cu *cu, FILE *fp); |
| size_t tag__fprintf(struct tag *tag, const struct cu *cu, |
| const struct conf_fprintf *conf, FILE *fp); |
| |
| const char *tag__name(const struct tag *tag, const struct cu *cu, |
| char *bf, size_t len, const struct conf_fprintf *conf); |
| void tag__not_found_die(const char *file, int line, const char *func); |
| |
| #define tag__assert_search_result(tag) \ |
| do { if (!tag) tag__not_found_die(__FILE__,\ |
| __LINE__, __func__); } while (0) |
| |
| size_t tag__size(const struct tag *tag, const struct cu *cu); |
| size_t tag__nr_cachelines(const struct tag *tag, const struct cu *cu); |
| struct tag *tag__follow_typedef(const struct tag *tag, const struct cu *cu); |
| struct tag *tag__strip_typedefs_and_modifiers(const struct tag *tag, const struct cu *cu); |
| |
| size_t __tag__id_not_found_fprintf(FILE *fp, type_id_t id, |
| const char *fn, int line); |
| #define tag__id_not_found_fprintf(fp, id) \ |
| __tag__id_not_found_fprintf(fp, id, __func__, __LINE__) |
| |
| int __tag__has_type_loop(const struct tag *tag, const struct tag *type, |
| char *bf, size_t len, FILE *fp, |
| const char *fn, int line); |
| #define tag__has_type_loop(tag, type, bf, len, fp) \ |
| __tag__has_type_loop(tag, type, bf, len, fp, __func__, __LINE__) |
| |
| struct ptr_to_member_type { |
| struct tag tag; |
| type_id_t containing_type; |
| }; |
| |
| static inline struct ptr_to_member_type * |
| tag__ptr_to_member_type(const struct tag *tag) |
| { |
| return (struct ptr_to_member_type *)tag; |
| } |
| |
| /** struct namespace - base class for enums, structs, unions, typedefs, etc |
| * |
| * @sname - for clones, for instance, where we can't always add a new string |
| * @tags - class_member, enumerators, etc |
| * @shared_tags: if this bit is set, don't free the entries in @tags |
| */ |
| struct namespace { |
| struct tag tag; |
| strings_t name; |
| uint16_t nr_tags; |
| uint8_t shared_tags; |
| char * sname; |
| struct list_head tags; |
| }; |
| |
| static inline struct namespace *tag__namespace(const struct tag *tag) |
| { |
| return (struct namespace *)tag; |
| } |
| |
| void namespace__delete(struct namespace *nspace, struct cu *cu); |
| |
| /** |
| * namespace__for_each_tag - iterate thru all the tags |
| * @nspace: struct namespace instance to iterate |
| * @pos: struct tag iterator |
| */ |
| #define namespace__for_each_tag(nspace, pos) \ |
| list_for_each_entry(pos, &(nspace)->tags, node) |
| |
| /** |
| * namespace__for_each_tag_safe_reverse - safely iterate thru all the tags, in reverse order |
| * @nspace: struct namespace instance to iterate |
| * @pos: struct tag iterator |
| * @n: struct class_member temp iterator |
| */ |
| #define namespace__for_each_tag_safe_reverse(nspace, pos, n) \ |
| list_for_each_entry_safe_reverse(pos, n, &(nspace)->tags, node) |
| |
| void namespace__add_tag(struct namespace *nspace, struct tag *tag); |
| |
| struct ip_tag { |
| struct tag tag; |
| uint64_t addr; |
| }; |
| |
| struct inline_expansion { |
| struct ip_tag ip; |
| size_t size; |
| uint64_t high_pc; |
| }; |
| |
| static inline struct inline_expansion * |
| tag__inline_expansion(const struct tag *tag) |
| { |
| return (struct inline_expansion *)tag; |
| } |
| |
| struct label { |
| struct ip_tag ip; |
| strings_t name; |
| }; |
| |
| static inline struct label *tag__label(const struct tag *tag) |
| { |
| return (struct label *)tag; |
| } |
| |
| static inline const char *label__name(const struct label *label, |
| const struct cu *cu) |
| { |
| return cu__string(cu, label->name); |
| } |
| |
| enum vscope { |
| VSCOPE_UNKNOWN, |
| VSCOPE_LOCAL, |
| VSCOPE_GLOBAL, |
| VSCOPE_REGISTER, |
| VSCOPE_OPTIMIZED |
| } __attribute__((packed)); |
| |
| struct location { |
| Dwarf_Op *expr; |
| size_t exprlen; |
| }; |
| |
| struct variable { |
| struct ip_tag ip; |
| strings_t name; |
| uint8_t external:1; |
| uint8_t declaration:1; |
| enum vscope scope; |
| struct location location; |
| struct hlist_node tool_hnode; |
| struct variable *spec; |
| }; |
| |
| static inline struct variable *tag__variable(const struct tag *tag) |
| { |
| return (struct variable *)tag; |
| } |
| |
| enum vscope variable__scope(const struct variable *var); |
| const char *variable__scope_str(const struct variable *var); |
| |
| const char *variable__name(const struct variable *var, const struct cu *cu); |
| |
| const char *variable__type_name(const struct variable *var, |
| const struct cu *cu, char *bf, size_t len); |
| |
| struct lexblock { |
| struct ip_tag ip; |
| struct list_head tags; |
| uint32_t size; |
| uint16_t nr_inline_expansions; |
| uint16_t nr_labels; |
| uint16_t nr_variables; |
| uint16_t nr_lexblocks; |
| uint32_t size_inline_expansions; |
| }; |
| |
| static inline struct lexblock *tag__lexblock(const struct tag *tag) |
| { |
| return (struct lexblock *)tag; |
| } |
| |
| void lexblock__delete(struct lexblock *lexblock, struct cu *cu); |
| |
| struct function; |
| |
| void lexblock__add_inline_expansion(struct lexblock *lexblock, |
| struct inline_expansion *exp); |
| void lexblock__add_label(struct lexblock *lexblock, struct label *label); |
| void lexblock__add_lexblock(struct lexblock *lexblock, struct lexblock *child); |
| void lexblock__add_tag(struct lexblock *lexblock, struct tag *tag); |
| void lexblock__add_variable(struct lexblock *lexblock, struct variable *var); |
| size_t lexblock__fprintf(const struct lexblock *lexblock, const struct cu *cu, |
| struct function *function, uint16_t indent, |
| const struct conf_fprintf *conf, FILE *fp); |
| |
| struct parameter { |
| struct tag tag; |
| strings_t name; |
| }; |
| |
| static inline struct parameter *tag__parameter(const struct tag *tag) |
| { |
| return (struct parameter *)tag; |
| } |
| |
| static inline const char *parameter__name(const struct parameter *parm, |
| const struct cu *cu) |
| { |
| return cu__string(cu, parm->name); |
| } |
| |
| /* |
| * tag.tag can be DW_TAG_subprogram_type or DW_TAG_subroutine_type. |
| */ |
| struct ftype { |
| struct tag tag; |
| struct list_head parms; |
| uint16_t nr_parms; |
| uint8_t unspec_parms; /* just one bit is needed */ |
| }; |
| |
| static inline struct ftype *tag__ftype(const struct tag *tag) |
| { |
| return (struct ftype *)tag; |
| } |
| |
| void ftype__delete(struct ftype *ftype, struct cu *cu); |
| |
| /** |
| * ftype__for_each_parameter - iterate thru all the parameters |
| * @ftype: struct ftype instance to iterate |
| * @pos: struct parameter iterator |
| */ |
| #define ftype__for_each_parameter(ftype, pos) \ |
| list_for_each_entry(pos, &(ftype)->parms, tag.node) |
| |
| /** |
| * ftype__for_each_parameter_safe - safely iterate thru all the parameters |
| * @ftype: struct ftype instance to iterate |
| * @pos: struct parameter iterator |
| * @n: struct parameter temp iterator |
| */ |
| #define ftype__for_each_parameter_safe(ftype, pos, n) \ |
| list_for_each_entry_safe(pos, n, &(ftype)->parms, tag.node) |
| |
| /** |
| * ftype__for_each_parameter_safe_reverse - safely iterate thru all the parameters, in reverse order |
| * @ftype: struct ftype instance to iterate |
| * @pos: struct parameter iterator |
| * @n: struct parameter temp iterator |
| */ |
| #define ftype__for_each_parameter_safe_reverse(ftype, pos, n) \ |
| list_for_each_entry_safe_reverse(pos, n, &(ftype)->parms, tag.node) |
| |
| void ftype__add_parameter(struct ftype *ftype, struct parameter *parm); |
| size_t ftype__fprintf(const struct ftype *ftype, const struct cu *cu, |
| const char *name, const int inlined, |
| const int is_pointer, const int type_spacing, bool is_prototype, |
| const struct conf_fprintf *conf, FILE *fp); |
| size_t ftype__fprintf_parms(const struct ftype *ftype, |
| const struct cu *cu, int indent, |
| const struct conf_fprintf *conf, FILE *fp); |
| int ftype__has_parm_of_type(const struct ftype *ftype, const type_id_t target, |
| const struct cu *cu); |
| |
| struct function { |
| struct ftype proto; |
| struct lexblock lexblock; |
| struct rb_node rb_node; |
| strings_t name; |
| strings_t linkage_name; |
| uint32_t cu_total_size_inline_expansions; |
| uint16_t cu_total_nr_inline_expansions; |
| uint8_t inlined:2; |
| uint8_t abstract_origin:1; |
| uint8_t external:1; |
| uint8_t accessibility:2; /* DW_ACCESS_{public,protected,private} */ |
| uint8_t virtuality:2; /* DW_VIRTUALITY_{none,virtual,pure_virtual} */ |
| uint8_t declaration:1; |
| uint8_t btf:1; |
| int32_t vtable_entry; |
| struct list_head vtable_node; |
| /* fields used by tools */ |
| union { |
| struct list_head tool_node; |
| struct hlist_node tool_hnode; |
| }; |
| void *priv; |
| }; |
| |
| static inline struct function *tag__function(const struct tag *tag) |
| { |
| return (struct function *)tag; |
| } |
| |
| static inline struct tag *function__tag(const struct function *func) |
| { |
| return (struct tag *)func; |
| } |
| |
| void function__delete(struct function *func, struct cu *cu); |
| |
| static __pure inline int tag__is_function(const struct tag *tag) |
| { |
| return tag->tag == DW_TAG_subprogram; |
| } |
| |
| /** |
| * function__for_each_parameter - iterate thru all the parameters |
| * @func: struct function instance to iterate |
| * @pos: struct parameter iterator |
| */ |
| #define function__for_each_parameter(func, cu, pos) \ |
| ftype__for_each_parameter(func->btf ? tag__ftype(cu__type(cu, func->proto.tag.type)) : &func->proto, pos) |
| |
| const char *function__name(struct function *func, const struct cu *cu); |
| |
| static inline const char *function__linkage_name(const struct function *func, |
| const struct cu *cu) |
| { |
| return cu__string(cu, func->linkage_name); |
| } |
| |
| size_t function__fprintf_stats(const struct tag *tag_func, |
| const struct cu *cu, |
| const struct conf_fprintf *conf, |
| FILE *fp); |
| const char *function__prototype(const struct function *func, |
| const struct cu *cu, char *bf, size_t len); |
| |
| static __pure inline uint64_t function__addr(const struct function *func) |
| { |
| return func->lexblock.ip.addr; |
| } |
| |
| static __pure inline uint32_t function__size(const struct function *func) |
| { |
| return func->lexblock.size; |
| } |
| |
| static inline int function__declared_inline(const struct function *func) |
| { |
| return (func->inlined == DW_INL_declared_inlined || |
| func->inlined == DW_INL_declared_not_inlined); |
| } |
| |
| static inline int function__inlined(const struct function *func) |
| { |
| return (func->inlined == DW_INL_inlined || |
| func->inlined == DW_INL_declared_inlined); |
| } |
| |
| /* struct class_member - struct, union, class member |
| * |
| * @bit_offset - offset in bits from the start of the struct |
| * @bit_size - cached bit size, can be smaller than the integral type if in a bitfield |
| * @byte_offset - offset in bytes from the start of the struct |
| * @byte_size - cached byte size, integral type byte size for bitfields |
| * @bitfield_offset - offset in the current bitfield |
| * @bitfield_size - size in the current bitfield |
| * @bit_hole - If there is a bit hole before the next one (or the end of the struct) |
| * @bitfield_end - Is this the last entry in a bitfield? |
| * @alignment - DW_AT_alignement, zero if not present, gcc emits since circa 7.3.1 |
| * @accessibility - DW_ACCESS_{public,protected,private} |
| * @virtuality - DW_VIRTUALITY_{none,virtual,pure_virtual} |
| * @hole - If there is a hole before the next one (or the end of the struct) |
| */ |
| struct class_member { |
| struct tag tag; |
| strings_t name; |
| uint32_t bit_offset; |
| uint32_t bit_size; |
| uint32_t byte_offset; |
| size_t byte_size; |
| int8_t bitfield_offset; |
| uint8_t bitfield_size; |
| uint8_t bit_hole; |
| uint8_t bitfield_end:1; |
| uint64_t const_value; |
| uint32_t alignment; |
| uint8_t visited:1; |
| uint8_t is_static:1; |
| uint8_t accessibility:2; |
| uint8_t virtuality:2; |
| uint16_t hole; |
| }; |
| |
| void class_member__delete(struct class_member *member, struct cu *cu); |
| |
| static inline struct class_member *tag__class_member(const struct tag *tag) |
| { |
| return (struct class_member *)tag; |
| } |
| |
| static inline const char *class_member__name(const struct class_member *member, |
| const struct cu *cu) |
| { |
| return cu__string(cu, member->name); |
| } |
| |
| static __pure inline int tag__is_class_member(const struct tag *tag) |
| { |
| return tag->tag == DW_TAG_member; |
| } |
| |
| int tag__is_base_type(const struct tag *tag, const struct cu *cu); |
| bool tag__is_array(const struct tag *tag, const struct cu *cu); |
| |
| struct class_member_filter; |
| |
| struct tag_cu_node { |
| struct list_head node; |
| struct tag_cu tc; |
| }; |
| |
| /** |
| * struct type - base type for enumerations, structs and unions |
| * |
| * @nnr_members: number of non static DW_TAG_member entries |
| * @nr_static_members: number of static DW_TAG_member entries |
| * @nr_tags: number of tags |
| * @alignment: DW_AT_alignement, zero if not present, gcc emits since circa 7.3.1 |
| * @natural_alignment: For inferring __packed__, normally the widest scalar in it, recursively |
| * @sizeof_member: Use this to find the size of the record |
| * @type_member: Use this to select a member from where to get an id on an enum to find a type |
| * to cast for, needs to be used with the upcoming type_enum. |
| * @type_enum: enumeration(s) to use together with type_member to find a type to cast |
| * @member_prefix: the common prefix for all members, say in an enum, this should be calculated on demand |
| * @member_prefix_len: the lenght of the common prefix for all members |
| */ |
| struct type { |
| struct namespace namespace; |
| struct list_head node; |
| uint32_t size; |
| int32_t size_diff; |
| uint16_t nr_static_members; |
| uint16_t nr_members; |
| uint32_t alignment; |
| struct class_member *sizeof_member; |
| struct class_member *type_member; |
| struct class_member_filter *filter; |
| struct list_head type_enum; |
| char *member_prefix; |
| uint16_t member_prefix_len; |
| uint16_t max_tag_name_len; |
| uint16_t natural_alignment; |
| bool packed_attributes_inferred; |
| uint8_t declaration; /* only one bit used */ |
| uint8_t definition_emitted:1; |
| uint8_t fwd_decl_emitted:1; |
| uint8_t resized:1; |
| }; |
| |
| void __type__init(struct type *type); |
| |
| static inline struct class *type__class(const struct type *type) |
| { |
| return (struct class *)type; |
| } |
| |
| static inline struct tag *type__tag(const struct type *type) |
| { |
| return (struct tag *)type; |
| } |
| |
| void type__delete(struct type *type, struct cu *cu); |
| |
| /** |
| * type__for_each_tag - iterate thru all the tags |
| * @type: struct type instance to iterate |
| * @pos: struct tag iterator |
| */ |
| #define type__for_each_tag(type, pos) \ |
| list_for_each_entry(pos, &(type)->namespace.tags, node) |
| |
| /** |
| * type__for_each_enumerator - iterate thru the enumerator entries |
| * @type: struct type instance to iterate |
| * @pos: struct enumerator iterator |
| */ |
| #define type__for_each_enumerator(type, pos) \ |
| struct list_head *__type__for_each_enumerator_head = \ |
| (type)->namespace.shared_tags ? \ |
| (type)->namespace.tags.next : \ |
| &(type)->namespace.tags; \ |
| list_for_each_entry(pos, __type__for_each_enumerator_head, tag.node) |
| |
| /** |
| * type__for_each_enumerator_safe_reverse - safely iterate thru the enumerator entries, in reverse order |
| * @type: struct type instance to iterate |
| * @pos: struct enumerator iterator |
| * @n: struct enumerator temp iterator |
| */ |
| #define type__for_each_enumerator_safe_reverse(type, pos, n) \ |
| if ((type)->namespace.shared_tags) /* Do nothing */ ; else \ |
| list_for_each_entry_safe_reverse(pos, n, &(type)->namespace.tags, tag.node) |
| |
| /** |
| * type__for_each_member - iterate thru the entries that use space |
| * (data members and inheritance entries) |
| * @type: struct type instance to iterate |
| * @pos: struct class_member iterator |
| */ |
| #define type__for_each_member(type, pos) \ |
| list_for_each_entry(pos, &(type)->namespace.tags, tag.node) \ |
| if (!(pos->tag.tag == DW_TAG_member || \ |
| pos->tag.tag == DW_TAG_inheritance)) \ |
| continue; \ |
| else |
| |
| /** |
| * type__for_each_data_member - iterate thru the data member entries |
| * @type: struct type instance to iterate |
| * @pos: struct class_member iterator |
| */ |
| #define type__for_each_data_member(type, pos) \ |
| list_for_each_entry(pos, &(type)->namespace.tags, tag.node) \ |
| if (pos->tag.tag != DW_TAG_member) \ |
| continue; \ |
| else |
| |
| /** |
| * type__for_each_member_safe - safely iterate thru the entries that use space |
| * (data members and inheritance entries) |
| * @type: struct type instance to iterate |
| * @pos: struct class_member iterator |
| * @n: struct class_member temp iterator |
| */ |
| #define type__for_each_member_safe(type, pos, n) \ |
| list_for_each_entry_safe(pos, n, &(type)->namespace.tags, tag.node) \ |
| if (pos->tag.tag != DW_TAG_member) \ |
| continue; \ |
| else |
| |
| /** |
| * type__for_each_data_member_safe - safely iterate thru the data member entries |
| * @type: struct type instance to iterate |
| * @pos: struct class_member iterator |
| * @n: struct class_member temp iterator |
| */ |
| #define type__for_each_data_member_safe(type, pos, n) \ |
| list_for_each_entry_safe(pos, n, &(type)->namespace.tags, tag.node) \ |
| if (pos->tag.tag != DW_TAG_member) \ |
| continue; \ |
| else |
| |
| /** |
| * type__for_each_tag_safe_reverse - safely iterate thru all tags in a type, in reverse order |
| * @type: struct type instance to iterate |
| * @pos: struct class_member iterator |
| * @n: struct class_member temp iterator |
| */ |
| #define type__for_each_tag_safe_reverse(type, pos, n) \ |
| list_for_each_entry_safe_reverse(pos, n, &(type)->namespace.tags, tag.node) |
| |
| void type__add_member(struct type *type, struct class_member *member); |
| struct class_member * |
| type__find_first_biggest_size_base_type_member(struct type *type, |
| const struct cu *cu); |
| |
| struct class_member *type__find_member_by_name(const struct type *type, |
| const struct cu *cu, |
| const char *name); |
| uint32_t type__nr_members_of_type(const struct type *type, const type_id_t oftype); |
| struct class_member *type__last_member(struct type *type); |
| |
| void enumeration__calc_prefix(struct type *type, const struct cu *cu); |
| const char *enumeration__prefix(struct type *type, const struct cu *cu); |
| uint16_t enumeration__prefix_len(struct type *type, const struct cu *cu); |
| int enumeration__max_entry_name_len(struct type *type, const struct cu *cu); |
| |
| void enumerations__calc_prefix(struct list_head *enumerations); |
| |
| size_t typedef__fprintf(const struct tag *tag_type, const struct cu *cu, |
| const struct conf_fprintf *conf, FILE *fp); |
| |
| static inline struct type *tag__type(const struct tag *tag) |
| { |
| return (struct type *)tag; |
| } |
| |
| struct class { |
| struct type type; |
| struct list_head vtable; |
| uint16_t nr_vtable_entries; |
| uint8_t nr_holes; |
| uint8_t nr_bit_holes; |
| uint16_t pre_hole; |
| uint16_t padding; |
| uint8_t pre_bit_hole; |
| uint8_t bit_padding; |
| bool holes_searched; |
| bool is_packed; |
| void *priv; |
| }; |
| |
| static inline struct class *tag__class(const struct tag *tag) |
| { |
| return (struct class *)tag; |
| } |
| |
| static inline struct tag *class__tag(const struct class *cls) |
| { |
| return (struct tag *)cls; |
| } |
| |
| struct class *class__clone(const struct class *from, |
| const char *new_class_name, struct cu *cu); |
| void class__delete(struct class *cls, struct cu *cu); |
| |
| static inline struct list_head *class__tags(struct class *cls) |
| { |
| return &cls->type.namespace.tags; |
| } |
| |
| static __pure inline const char *namespace__name(const struct namespace *nspace, |
| const struct cu *cu) |
| { |
| return nspace->sname ?: cu__string(cu, nspace->name); |
| } |
| |
| static __pure inline const char *type__name(const struct type *type, |
| const struct cu *cu) |
| { |
| return namespace__name(&type->namespace, cu); |
| } |
| |
| static __pure inline const char *class__name(struct class *cls, |
| const struct cu *cu) |
| { |
| return type__name(&cls->type, cu); |
| } |
| |
| static inline int class__is_struct(const struct class *cls) |
| { |
| return tag__is_struct(&cls->type.namespace.tag); |
| } |
| |
| void class__find_holes(struct class *cls); |
| int class__has_hole_ge(const struct class *cls, const uint16_t size); |
| |
| bool class__infer_packed_attributes(struct class *cls, const struct cu *cu); |
| |
| void union__infer_packed_attributes(struct type *type, const struct cu *cu); |
| |
| void type__check_structs_at_unnatural_alignments(struct type *type, const struct cu *cu); |
| |
| size_t class__fprintf(struct class *cls, const struct cu *cu, FILE *fp); |
| |
| void class__add_vtable_entry(struct class *cls, struct function *vtable_entry); |
| static inline struct class_member * |
| class__find_member_by_name(const struct class *cls, |
| const struct cu *cu, const char *name) |
| { |
| return type__find_member_by_name(&cls->type, cu, name); |
| } |
| |
| static inline uint16_t class__nr_members(const struct class *cls) |
| { |
| return cls->type.nr_members; |
| } |
| |
| static inline uint32_t class__size(const struct class *cls) |
| { |
| return cls->type.size; |
| } |
| |
| static inline int class__is_declaration(const struct class *cls) |
| { |
| return cls->type.declaration; |
| } |
| |
| const struct class_member *class__find_bit_hole(const struct class *cls, |
| const struct class_member *trailer, |
| const uint16_t bit_hole_size); |
| |
| #define class__for_each_member_from(cls, from, pos) \ |
| pos = list_prepare_entry(from, class__tags(cls), tag.node); \ |
| list_for_each_entry_from(pos, class__tags(cls), tag.node) \ |
| if (!tag__is_class_member(&pos->tag)) \ |
| continue; \ |
| else |
| |
| #define class__for_each_member_safe_from(cls, from, pos, tmp) \ |
| pos = list_prepare_entry(from, class__tags(cls), tag.node); \ |
| list_for_each_entry_safe_from(pos, tmp, class__tags(cls), tag.node) \ |
| if (!tag__is_class_member(&pos->tag)) \ |
| continue; \ |
| else |
| |
| #define class__for_each_member_continue(cls, from, pos) \ |
| pos = list_prepare_entry(from, class__tags(cls), tag.node); \ |
| list_for_each_entry_continue(pos, class__tags(cls), tag.node) \ |
| if (!tag__is_class_member(&pos->tag)) \ |
| continue; \ |
| else |
| |
| #define class__for_each_member_reverse(cls, member) \ |
| list_for_each_entry_reverse(member, class__tags(cls), tag.node) \ |
| if (member->tag.tag != DW_TAG_member) \ |
| continue; \ |
| else |
| |
| enum base_type_float_type { |
| BT_FP_SINGLE = 1, |
| BT_FP_DOUBLE, |
| BT_FP_CMPLX, |
| BT_FP_CMPLX_DBL, |
| BT_FP_CMPLX_LDBL, |
| BT_FP_LDBL, |
| BT_FP_INTVL, |
| BT_FP_INTVL_DBL, |
| BT_FP_INTVL_LDBL, |
| BT_FP_IMGRY, |
| BT_FP_IMGRY_DBL, |
| BT_FP_IMGRY_LDBL |
| }; |
| |
| struct base_type { |
| struct tag tag; |
| strings_t name; |
| uint16_t bit_size; |
| uint8_t name_has_encoding:1; |
| uint8_t is_signed:1; |
| uint8_t is_bool:1; |
| uint8_t is_varargs:1; |
| uint8_t float_type:4; |
| }; |
| |
| static inline struct base_type *tag__base_type(const struct tag *tag) |
| { |
| return (struct base_type *)tag; |
| } |
| |
| static inline uint16_t base_type__size(const struct tag *tag) |
| { |
| return tag__base_type(tag)->bit_size / 8; |
| } |
| |
| const char *base_type__name(const struct base_type *btype, const struct cu *cu, |
| char *bf, size_t len); |
| |
| void base_type_name_to_size_table__init(struct strings *strings); |
| size_t base_type__name_to_size(struct base_type *btype, struct cu *cu); |
| |
| struct array_type { |
| struct tag tag; |
| uint32_t *nr_entries; |
| uint8_t dimensions; |
| bool is_vector; |
| }; |
| |
| static inline struct array_type *tag__array_type(const struct tag *tag) |
| { |
| return (struct array_type *)tag; |
| } |
| |
| struct string_type { |
| struct tag tag; |
| uint32_t nr_entries; |
| }; |
| |
| static inline struct string_type *tag__string_type(const struct tag *tag) |
| { |
| return (struct string_type *)tag; |
| } |
| |
| struct enumerator { |
| struct tag tag; |
| strings_t name; |
| uint32_t value; |
| struct tag_cu type_enum; // To cache the type_enum searches |
| }; |
| |
| static inline const char *enumerator__name(const struct enumerator *enumerator, |
| const struct cu *cu) |
| { |
| return cu__string(cu, enumerator->name); |
| } |
| |
| void enumeration__delete(struct type *type, struct cu *cu); |
| void enumeration__add(struct type *type, struct enumerator *enumerator); |
| size_t enumeration__fprintf(const struct tag *tag_enum, const struct cu *cu, |
| const struct conf_fprintf *conf, FILE *fp); |
| |
| int dwarves__init(uint16_t user_cacheline_size); |
| void dwarves__exit(void); |
| |
| const char *dwarf_tag_name(const uint32_t tag); |
| |
| struct argp_state; |
| |
| void dwarves_print_version(FILE *fp, struct argp_state *state); |
| void dwarves_print_numeric_version(FILE *fp); |
| |
| extern bool print_numeric_version;; |
| |
| extern bool no_bitfield_type_recode; |
| |
| extern const char tabs[]; |
| |
| #endif /* _DWARVES_H_ */ |