2002 lines · 51.0 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); enum type_state_kind {	TSR_KIND_INVALID = 0,	TSR_KIND_TYPE,	TSR_KIND_PERCPU_BASE,	TSR_KIND_CONST,	TSR_KIND_POINTER,	TSR_KIND_CANARY,}; #define pr_debug_dtp(fmt, ...)					\do {								\	if (debug_type_profile)					\		pr_info(fmt, ##__VA_ARGS__);			\	else							\		pr_debug3(fmt, ##__VA_ARGS__);			\} while (0) static 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;	}} /* * Type information in a register, valid when @ok is true. * The @caller_saved registers are invalidated after a function call. */struct type_state_reg {	Dwarf_Die type;	u32 imm_value;	bool ok;	bool caller_saved;	u8 kind;}; /* Type information in a stack location, dynamically allocated */struct type_state_stack {	struct list_head list;	Dwarf_Die type;	int offset;	int size;	bool compound;	u8 kind;}; /* FIXME: This should be arch-dependent */#define TYPE_STATE_MAX_REGS  16 /* * State table to maintain type info in each register and stack location. * It'll be updated when new variable is allocated or type info is moved * to a new location (register or stack).  As it'd be used with the * shortest path of basic blocks, it only maintains a single table. */struct type_state {	/* state of general purpose registers */	struct type_state_reg regs[TYPE_STATE_MAX_REGS];	/* state of stack location */	struct list_head stack_vars;	/* return value register */	int ret_reg;	/* stack pointer register */	int stack_reg;}; static 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;	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_aggregate_size(&member_type, &size) < 0)		size = 0; 	if (!dwarf_attr_integrate(die, DW_AT_data_member_location, &attr))		loc = 0;	else		dwarf_formudata(&attr, &loc); 	member->type_name = strbuf_detach(&sb, NULL);	/* member->var_name can be NULL */	if (dwarf_diename(die))		member->var_name = strdup(dwarf_diename(die));	member->size = size;	member->offset = loc + parent->offset;	INIT_LIST_HEAD(&member->children);	list_add_tail(&member->node, &parent->children); 	tag = dwarf_tag(&member_type);	switch (tag) {	case DW_TAG_structure_type:	case DW_TAG_union_type:		die_find_child(&member_type, __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);	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;} /* The type info will be saved in @type_die */static int 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 is_pointer = true; 	if (reg == DWARF_REG_PC)		is_pointer = false;	else if (reg == dloc->fbreg || is_fbreg)		is_pointer = false;	else if (arch__is(dloc->arch, "x86") && reg == X86_REG_SP)		is_pointer = false; 	/* Get the type of the variable */	if (die_get_real_type(var_die, type_die) == NULL) {		pr_debug_dtp("variable has no type\n");		ann_data_stat.no_typeinfo++;		return -1;	} 	/*	 * Usually it expects a pointer type for a memory access.	 * Convert to a real type it points to.  But global variables	 * and local variables are accessed directly without a pointer.	 */	if (is_pointer) {		if ((dwarf_tag(type_die) != DW_TAG_pointer_type &&		     dwarf_tag(type_die) != DW_TAG_array_type) ||		    die_get_real_type(type_die, type_die) == NULL) {			pr_debug_dtp("no pointer or no type\n");			ann_data_stat.no_typeinfo++;			return -1;		}	} 	/* Get the size of the actual type */	if (dwarf_aggregate_size(type_die, &size) < 0) {		pr_debug_dtp("type size is unknown\n");		ann_data_stat.invalid_size++;		return -1;	} 	/* Minimal sanity check */	if ((unsigned)offset >= size) {		pr_debug_dtp("offset: %d is bigger than size: %"PRIu64"\n",			     offset, size);		ann_data_stat.bad_offset++;		return -1;	} 	return 0;} static struct type_state_stack *find_stack_state(struct type_state *state,						 int offset){	struct type_state_stack *stack; 	list_for_each_entry(stack, &state->stack_vars, list) {		if (offset == stack->offset)			return stack; 		if (stack->compound && stack->offset < offset &&		    offset < stack->offset + stack->size)			return stack;	}	return NULL;} static void set_stack_state(struct type_state_stack *stack, int offset, u8 kind,			    Dwarf_Die *type_die){	int tag;	Dwarf_Word size; 	if (dwarf_aggregate_size(type_die, &size) < 0)		size = 0; 	tag = dwarf_tag(type_die); 	stack->type = *type_die;	stack->size = size;	stack->offset = offset;	stack->kind = kind; 	switch (tag) {	case DW_TAG_structure_type:	case DW_TAG_union_type:		stack->compound = (kind != TSR_KIND_POINTER);		break;	default:		stack->compound = false;		break;	}} static struct type_state_stack *findnew_stack_state(struct type_state *state,						    int offset, u8 kind,						    Dwarf_Die *type_die){	struct type_state_stack *stack = find_stack_state(state, offset); 	if (stack) {		set_stack_state(stack, offset, kind, type_die);		return stack;	} 	stack = malloc(sizeof(*stack));	if (stack) {		set_stack_state(stack, offset, kind, type_die);

Lines 1–500 of 2002.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t