1828 lines · 45.9 KB · C
RawDownload
/* SPDX-License-Identifier: GPL-2.0 *//* * Convert sample address to data type using DWARF debug info. * * Written by Namhyung Kim <namhyung@kernel.org> */ #include <stdio.h>#include <stdlib.h>#include <inttypes.h>#include <linux/zalloc.h> #include "annotate.h"#include "annotate-data.h"#include "debuginfo.h"#include "debug.h"#include "dso.h"#include "dwarf-regs.h"#include "evsel.h"#include "evlist.h"#include "map.h"#include "map_symbol.h"#include "sort.h"#include "strbuf.h"#include "symbol.h"#include "symbol_conf.h"#include "thread.h" /* register number of the stack pointer */#define X86_REG_SP 7 static void delete_var_types(struct die_var_type *var_types); #define pr_debug_dtp(fmt, ...)					\do {								\	if (debug_type_profile)					\		pr_info(fmt, ##__VA_ARGS__);			\	else							\		pr_debug3(fmt, ##__VA_ARGS__);			\} while (0) void pr_debug_type_name(Dwarf_Die *die, enum type_state_kind kind){	struct strbuf sb;	char *str;	Dwarf_Word size = 0; 	if (!debug_type_profile && verbose < 3)		return; 	switch (kind) {	case TSR_KIND_INVALID:		pr_info("\n");		return;	case TSR_KIND_PERCPU_BASE:		pr_info(" percpu base\n");		return;	case TSR_KIND_CONST:		pr_info(" constant\n");		return;	case TSR_KIND_POINTER:		pr_info(" pointer");		/* it also prints the type info */		break;	case TSR_KIND_CANARY:		pr_info(" stack canary\n");		return;	case TSR_KIND_TYPE:	default:		break;	} 	dwarf_aggregate_size(die, &size); 	strbuf_init(&sb, 32);	die_get_typename_from_type(die, &sb);	str = strbuf_detach(&sb, NULL);	pr_info(" type='%s' size=%#lx (die:%#lx)\n",		str, (long)size, (long)dwarf_dieoffset(die));	free(str);} static void pr_debug_location(Dwarf_Die *die, u64 pc, int reg){	ptrdiff_t off = 0;	Dwarf_Attribute attr;	Dwarf_Addr base, start, end;	Dwarf_Op *ops;	size_t nops; 	if (!debug_type_profile && verbose < 3)		return; 	if (dwarf_attr(die, DW_AT_location, &attr) == NULL)		return; 	while ((off = dwarf_getlocations(&attr, off, &base, &start, &end, &ops, &nops)) > 0) {		if (reg != DWARF_REG_PC && end <= pc)			continue;		if (reg != DWARF_REG_PC && start > pc)			break; 		pr_info(" variable location: ");		switch (ops->atom) {		case DW_OP_reg0 ...DW_OP_reg31:			pr_info("reg%d\n", ops->atom - DW_OP_reg0);			break;		case DW_OP_breg0 ...DW_OP_breg31:			pr_info("base=reg%d, offset=%#lx\n",				ops->atom - DW_OP_breg0, (long)ops->number);			break;		case DW_OP_regx:			pr_info("reg%ld\n", (long)ops->number);			break;		case DW_OP_bregx:			pr_info("base=reg%ld, offset=%#lx\n",				(long)ops->number, (long)ops->number2);			break;		case DW_OP_fbreg:			pr_info("use frame base, offset=%#lx\n", (long)ops->number);			break;		case DW_OP_addr:			pr_info("address=%#lx\n", (long)ops->number);			break;		default:			pr_info("unknown: code=%#x, number=%#lx\n",				ops->atom, (long)ops->number);			break;		}		break;	}} static void pr_debug_scope(Dwarf_Die *scope_die){	int tag; 	if (!debug_type_profile && verbose < 3)		return; 	pr_info("(die:%lx) ", (long)dwarf_dieoffset(scope_die)); 	tag = dwarf_tag(scope_die);	if (tag == DW_TAG_subprogram)		pr_info("[function] %s\n", dwarf_diename(scope_die));	else if (tag == DW_TAG_inlined_subroutine)		pr_info("[inlined] %s\n", dwarf_diename(scope_die));	else if (tag == DW_TAG_lexical_block)		pr_info("[block]\n");	else		pr_info("[unknown] tag=%x\n", tag);} bool has_reg_type(struct type_state *state, int reg){	return (unsigned)reg < ARRAY_SIZE(state->regs);} static void init_type_state(struct type_state *state, struct arch *arch){	memset(state, 0, sizeof(*state));	INIT_LIST_HEAD(&state->stack_vars); 	if (arch__is(arch, "x86")) {		state->regs[0].caller_saved = true;		state->regs[1].caller_saved = true;		state->regs[2].caller_saved = true;		state->regs[4].caller_saved = true;		state->regs[5].caller_saved = true;		state->regs[8].caller_saved = true;		state->regs[9].caller_saved = true;		state->regs[10].caller_saved = true;		state->regs[11].caller_saved = true;		state->ret_reg = 0;		state->stack_reg = X86_REG_SP;	}} static void exit_type_state(struct type_state *state){	struct type_state_stack *stack, *tmp; 	list_for_each_entry_safe(stack, tmp, &state->stack_vars, list) {		list_del(&stack->list);		free(stack);	}} /* * Compare type name and size to maintain them in a tree. * I'm not sure if DWARF would have information of a single type in many * different places (compilation units).  If not, it could compare the * offset of the type entry in the .debug_info section. */static int data_type_cmp(const void *_key, const struct rb_node *node){	const struct annotated_data_type *key = _key;	struct annotated_data_type *type; 	type = rb_entry(node, struct annotated_data_type, node); 	if (key->self.size != type->self.size)		return key->self.size - type->self.size;	return strcmp(key->self.type_name, type->self.type_name);} static bool data_type_less(struct rb_node *node_a, const struct rb_node *node_b){	struct annotated_data_type *a, *b; 	a = rb_entry(node_a, struct annotated_data_type, node);	b = rb_entry(node_b, struct annotated_data_type, node); 	if (a->self.size != b->self.size)		return a->self.size < b->self.size;	return strcmp(a->self.type_name, b->self.type_name) < 0;} /* Recursively add new members for struct/union */static int __add_member_cb(Dwarf_Die *die, void *arg){	struct annotated_member *parent = arg;	struct annotated_member *member;	Dwarf_Die member_type, die_mem;	Dwarf_Word size, loc, bit_size = 0;	Dwarf_Attribute attr;	struct strbuf sb;	int tag; 	if (dwarf_tag(die) != DW_TAG_member)		return DIE_FIND_CB_SIBLING; 	member = zalloc(sizeof(*member));	if (member == NULL)		return DIE_FIND_CB_END; 	strbuf_init(&sb, 32);	die_get_typename(die, &sb); 	__die_get_real_type(die, &member_type);	if (dwarf_tag(&member_type) == DW_TAG_typedef)		die_get_real_type(&member_type, &die_mem);	else		die_mem = member_type; 	if (dwarf_aggregate_size(&die_mem, &size) < 0)		size = 0; 	if (dwarf_attr_integrate(die, DW_AT_data_member_location, &attr))		dwarf_formudata(&attr, &loc);	else {		/* bitfield member */		if (dwarf_attr_integrate(die, DW_AT_data_bit_offset, &attr) &&		    dwarf_formudata(&attr, &loc) == 0)			loc /= 8;		else			loc = 0; 		if (dwarf_attr_integrate(die, DW_AT_bit_size, &attr) &&		    dwarf_formudata(&attr, &bit_size) == 0)			size = (bit_size + 7) / 8;	} 	member->type_name = strbuf_detach(&sb, NULL);	/* member->var_name can be NULL */	if (dwarf_diename(die)) {		if (bit_size) {			if (asprintf(&member->var_name, "%s:%ld",				     dwarf_diename(die), (long)bit_size) < 0)				member->var_name = NULL;		} else {			member->var_name = strdup(dwarf_diename(die));		} 		if (member->var_name == NULL) {			free(member);			return DIE_FIND_CB_END;		}	}	member->size = size;	member->offset = loc + parent->offset;	INIT_LIST_HEAD(&member->children);	list_add_tail(&member->node, &parent->children); 	tag = dwarf_tag(&die_mem);	switch (tag) {	case DW_TAG_structure_type:	case DW_TAG_union_type:		die_find_child(&die_mem, __add_member_cb, member, &die_mem);		break;	default:		break;	}	return DIE_FIND_CB_SIBLING;} static void add_member_types(struct annotated_data_type *parent, Dwarf_Die *type){	Dwarf_Die die_mem; 	die_find_child(type, __add_member_cb, &parent->self, &die_mem);} static void delete_members(struct annotated_member *member){	struct annotated_member *child, *tmp; 	list_for_each_entry_safe(child, tmp, &member->children, node) {		list_del(&child->node);		delete_members(child);		zfree(&child->type_name);		zfree(&child->var_name);		free(child);	}} static struct annotated_data_type *dso__findnew_data_type(struct dso *dso,							  Dwarf_Die *type_die){	struct annotated_data_type *result = NULL;	struct annotated_data_type key;	struct rb_node *node;	struct strbuf sb;	char *type_name;	Dwarf_Word size; 	strbuf_init(&sb, 32);	if (die_get_typename_from_type(type_die, &sb) < 0)		strbuf_add(&sb, "(unknown type)", 14);	type_name = strbuf_detach(&sb, NULL); 	if (dwarf_tag(type_die) == DW_TAG_typedef)		die_get_real_type(type_die, type_die); 	dwarf_aggregate_size(type_die, &size); 	/* Check existing nodes in dso->data_types tree */	key.self.type_name = type_name;	key.self.size = size;	node = rb_find(&key, dso__data_types(dso), data_type_cmp);	if (node) {		result = rb_entry(node, struct annotated_data_type, node);		free(type_name);		return result;	} 	/* If not, add a new one */	result = zalloc(sizeof(*result));	if (result == NULL) {		free(type_name);		return NULL;	} 	result->self.type_name = type_name;	result->self.size = size;	INIT_LIST_HEAD(&result->self.children); 	if (symbol_conf.annotate_data_member)		add_member_types(result, type_die); 	rb_add(&result->node, dso__data_types(dso), data_type_less);	return result;} static bool find_cu_die(struct debuginfo *di, u64 pc, Dwarf_Die *cu_die){	Dwarf_Off off, next_off;	size_t header_size; 	if (dwarf_addrdie(di->dbg, pc, cu_die) != NULL)		return cu_die; 	/*	 * There are some kernels don't have full aranges and contain only a few	 * aranges entries.  Fallback to iterate all CU entries in .debug_info	 * in case it's missing.	 */	off = 0;	while (dwarf_nextcu(di->dbg, off, &next_off, &header_size,			    NULL, NULL, NULL) == 0) {		if (dwarf_offdie(di->dbg, off + header_size, cu_die) &&		    dwarf_haspc(cu_die, pc))			return true; 		off = next_off;	}	return false;} enum type_match_result {	PERF_TMR_UNKNOWN = 0,	PERF_TMR_OK,	PERF_TMR_NO_TYPE,	PERF_TMR_NO_POINTER,	PERF_TMR_NO_SIZE,	PERF_TMR_BAD_OFFSET,	PERF_TMR_BAIL_OUT,}; static const char *match_result_str(enum type_match_result tmr){	switch (tmr) {	case PERF_TMR_OK:		return "Good!";	case PERF_TMR_NO_TYPE:		return "no type information";	case PERF_TMR_NO_POINTER:		return "no/void pointer";	case PERF_TMR_NO_SIZE:		return "type size is unknown";	case PERF_TMR_BAD_OFFSET:		return "offset bigger than size";	case PERF_TMR_UNKNOWN:	case PERF_TMR_BAIL_OUT:	default:		return "invalid state";	}} static bool is_pointer_type(Dwarf_Die *type_die){	int tag = dwarf_tag(type_die); 	return tag == DW_TAG_pointer_type || tag == DW_TAG_array_type;} static bool is_compound_type(Dwarf_Die *type_die){	int tag = dwarf_tag(type_die); 	return tag == DW_TAG_structure_type || tag == DW_TAG_union_type;} /* returns if Type B has better information than Type A */static bool is_better_type(Dwarf_Die *type_a, Dwarf_Die *type_b){	Dwarf_Word size_a, size_b;	Dwarf_Die die_a, die_b; 	/* pointer type is preferred */	if (is_pointer_type(type_a) != is_pointer_type(type_b))		return is_pointer_type(type_b); 	if (is_pointer_type(type_b)) {		/*		 * We want to compare the target type, but 'void *' can fail to		 * get the target type.		 */		if (die_get_real_type(type_a, &die_a) == NULL)			return true;		if (die_get_real_type(type_b, &die_b) == NULL)			return false; 		type_a = &die_a;		type_b = &die_b;	} 	/* bigger type is preferred */	if (dwarf_aggregate_size(type_a, &size_a) < 0 ||	    dwarf_aggregate_size(type_b, &size_b) < 0)		return false; 	if (size_a != size_b)		return size_a < size_b; 	/* struct or union is preferred */	if (is_compound_type(type_a) != is_compound_type(type_b))		return is_compound_type(type_b); 	/* typedef is preferred */	if (dwarf_tag(type_b) == DW_TAG_typedef)		return true; 	return false;} /* The type info will be saved in @type_die */static enum type_match_result check_variable(struct data_loc_info *dloc,					     Dwarf_Die *var_die,					     Dwarf_Die *type_die, int reg,					     int offset, bool is_fbreg){	Dwarf_Word size;	bool needs_pointer = true;	Dwarf_Die sized_type; 	if (reg == DWARF_REG_PC)		needs_pointer = false;	else if (reg == dloc->fbreg || is_fbreg)		needs_pointer = false;	else if (arch__is(dloc->arch, "x86") && reg == X86_REG_SP)		needs_pointer = false; 	/* Get the type of the variable */	if (__die_get_real_type(var_die, type_die) == NULL)		return PERF_TMR_NO_TYPE; 	/*	 * Usually it expects a pointer type for a memory access.	 * Convert to a real type it points to.  But global variables

Lines 1–500 of 1828.