⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 unwind.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
#include "unwind_decoder.c"/* Unwind scripts. */static inline unw_hash_index_thash (unsigned long ip){#	define magic	0x9e3779b97f4a7c16	/* based on (sqrt(5)/2-1)*2^64 */	return (ip >> 4)*magic >> (64 - UNW_LOG_HASH_SIZE);}static inline longcache_match (struct unw_script *script, unsigned long ip, unsigned long pr){	read_lock(&script->lock);	if (ip == script->ip && ((pr ^ script->pr_val) & script->pr_mask) == 0)		/* keep the read lock... */		return 1;	read_unlock(&script->lock);	return 0;}static inline struct unw_script *script_lookup (struct unw_frame_info *info){	struct unw_script *script = unw.cache + info->hint;	unsigned short index;	unsigned long ip, pr;	STAT(++unw.stat.cache.lookups);	ip = info->ip;	pr = info->pr;	if (cache_match(script, ip, pr)) {		STAT(++unw.stat.cache.hinted_hits);		return script;	}	index = unw.hash[hash(ip)];	if (index >= UNW_CACHE_SIZE)		return 0;	script = unw.cache + index;	while (1) {		if (cache_match(script, ip, pr)) {			/* update hint; no locking required as single-word writes are atomic */			STAT(++unw.stat.cache.normal_hits);			unw.cache[info->prev_script].hint = script - unw.cache;			return script;		}		if (script->coll_chain >= UNW_HASH_SIZE)			return 0;		script = unw.cache + script->coll_chain;		STAT(++unw.stat.cache.collision_chain_traversals);	}}/* * On returning, a write lock for the SCRIPT is still being held. */static inline struct unw_script *script_new (unsigned long ip){	struct unw_script *script, *prev, *tmp;	unw_hash_index_t index;	unsigned long flags;	unsigned short head;	STAT(++unw.stat.script.news);	/*	 * Can't (easily) use cmpxchg() here because of ABA problem	 * that is intrinsic in cmpxchg()...	 */	spin_lock_irqsave(&unw.lock, flags);	{		head = unw.lru_head;		script = unw.cache + head;		unw.lru_head = script->lru_chain;	}	spin_unlock(&unw.lock);	/*	 * XXX We'll deadlock here if we interrupt a thread that is	 * holding a read lock on script->lock.  A try_write_lock()	 * might be mighty handy here...  Alternatively, we could	 * disable interrupts whenever we hold a read-lock, but that	 * seems silly.	 */	write_lock(&script->lock);	spin_lock(&unw.lock);	{		/* re-insert script at the tail of the LRU chain: */		unw.cache[unw.lru_tail].lru_chain = head;		unw.lru_tail = head;		/* remove the old script from the hash table (if it's there): */		if (script->ip) {			index = hash(script->ip);			tmp = unw.cache + unw.hash[index];			prev = 0;			while (1) {				if (tmp == script) {					if (prev)						prev->coll_chain = tmp->coll_chain;					else						unw.hash[index] = tmp->coll_chain;					break;				} else					prev = tmp;				if (tmp->coll_chain >= UNW_CACHE_SIZE)				/* old script wasn't in the hash-table */					break;				tmp = unw.cache + tmp->coll_chain;			}		}		/* enter new script in the hash table */		index = hash(ip);		script->coll_chain = unw.hash[index];		unw.hash[index] = script - unw.cache;		script->ip = ip;	/* set new IP while we're holding the locks */		STAT(if (script->coll_chain < UNW_CACHE_SIZE) ++unw.stat.script.collisions);	}	spin_unlock_irqrestore(&unw.lock, flags);	script->flags = 0;	script->hint = 0;	script->count = 0;	return script;}static voidscript_finalize (struct unw_script *script, struct unw_state_record *sr){	script->pr_mask = sr->pr_mask;	script->pr_val = sr->pr_val;	/*	 * We could down-grade our write-lock on script->lock here but	 * the rwlock API doesn't offer atomic lock downgrading, so	 * we'll just keep the write-lock and release it later when	 * we're done using the script.	 */}static inline voidscript_emit (struct unw_script *script, struct unw_insn insn){	if (script->count >= UNW_MAX_SCRIPT_LEN) {		dprintk("unwind: script exceeds maximum size of %u instructions!\n",			UNW_MAX_SCRIPT_LEN);		return;	}	script->insn[script->count++] = insn;}static inline voidemit_nat_info (struct unw_state_record *sr, int i, struct unw_script *script){	struct unw_reg_info *r = sr->curr.reg + i;	enum unw_insn_opcode opc;	struct unw_insn insn;	unsigned long val = 0;	switch (r->where) {	      case UNW_WHERE_GR:		if (r->val >= 32) {			/* register got spilled to a stacked register */			opc = UNW_INSN_SETNAT_TYPE;			val = UNW_NAT_REGSTK;		} else			/* register got spilled to a scratch register */			opc = UNW_INSN_SETNAT_MEMSTK;		break;	      case UNW_WHERE_FR:		opc = UNW_INSN_SETNAT_TYPE;		val = UNW_NAT_VAL;		break;	      case UNW_WHERE_BR:		opc = UNW_INSN_SETNAT_TYPE;		val = UNW_NAT_NONE;		break;	      case UNW_WHERE_PSPREL:	      case UNW_WHERE_SPREL:		opc = UNW_INSN_SETNAT_MEMSTK;		break;	      default:		dprintk("unwind: don't know how to emit nat info for where = %u\n", r->where);		return;	}	insn.opc = opc;	insn.dst = unw.preg_index[i];	insn.val = val;	script_emit(script, insn);}static voidcompile_reg (struct unw_state_record *sr, int i, struct unw_script *script){	struct unw_reg_info *r = sr->curr.reg + i;	enum unw_insn_opcode opc;	unsigned long val, rval;	struct unw_insn insn;	long need_nat_info;	if (r->where == UNW_WHERE_NONE || r->when >= sr->when_target)		return;	opc = UNW_INSN_MOVE;	val = rval = r->val;	need_nat_info = (i >= UNW_REG_R4 && i <= UNW_REG_R7);	switch (r->where) {	      case UNW_WHERE_GR:		if (rval >= 32) {			opc = UNW_INSN_MOVE_STACKED;			val = rval - 32;		} else if (rval >= 4 && rval <= 7) {			if (need_nat_info) {				opc = UNW_INSN_MOVE2;				need_nat_info = 0;			}			val = unw.preg_index[UNW_REG_R4 + (rval - 4)];		} else {			opc = UNW_INSN_ADD_SP;			val = -sizeof(struct pt_regs) + pt_regs_off(rval);		}		break;	      case UNW_WHERE_FR:		if (rval <= 5)			val = unw.preg_index[UNW_REG_F2  + (rval -  1)];		else if (rval >= 16 && rval <= 31)			val = unw.preg_index[UNW_REG_F16 + (rval - 16)];		else {			opc = UNW_INSN_ADD_SP;			val = -sizeof(struct pt_regs);			if (rval <= 9)				val += struct_offset(struct pt_regs, f6) + 16*(rval - 6);			else				dprintk("unwind: kernel may not touch f%lu\n", rval);		}		break;	      case UNW_WHERE_BR:		if (rval >= 1 && rval <= 5)			val = unw.preg_index[UNW_REG_B1 + (rval - 1)];		else {			opc = UNW_INSN_ADD_SP;			val = -sizeof(struct pt_regs);			if (rval == 0)				val += struct_offset(struct pt_regs, b0);			else if (rval == 6)				val += struct_offset(struct pt_regs, b6);			else				val += struct_offset(struct pt_regs, b7);		}		break;	      case UNW_WHERE_SPREL:		opc = UNW_INSN_ADD_SP;		break;	      case UNW_WHERE_PSPREL:		opc = UNW_INSN_ADD_PSP;		break;	      default:		dprintk("unwind: register %u has unexpected `where' value of %u\n", i, r->where);		break;	}	insn.opc = opc;	insn.dst = unw.preg_index[i];	insn.val = val;	script_emit(script, insn);	if (need_nat_info)		emit_nat_info(sr, i, script);	if (i == UNW_REG_PSP) {		/*		 * info->psp must contain the _value_ of the previous		 * sp, not it's save location.  We get this by		 * dereferencing the value we just stored in		 * info->psp:		 */		insn.opc = UNW_INSN_LOAD;		insn.dst = insn.val = unw.preg_index[UNW_REG_PSP];		script_emit(script, insn);	}}static inline struct unw_table_entry *lookup (struct unw_table *table, unsigned long rel_ip){	struct unw_table_entry *e = 0;	unsigned long lo, hi, mid;	/* do a binary search for right entry: */	for (lo = 0, hi = table->length; lo < hi; ) {		mid = (lo + hi) / 2;		e = &table->array[mid];		if (rel_ip < e->start_offset)			hi = mid;		else if (rel_ip >= e->end_offset)			lo = mid + 1;		else			break;	}	return e;}/* * Build an unwind script that unwinds from state OLD_STATE to the * entrypoint of the function that called OLD_STATE. */static inline struct unw_script *build_script (struct unw_frame_info *info){	struct unw_reg_state *rs, *next;	struct unw_table_entry *e = 0;	struct unw_script *script = 0;	unsigned long ip = info->ip;	struct unw_state_record sr;	struct unw_table *table;	struct unw_reg_info *r;	struct unw_insn insn;	u8 *dp, *desc_end;	u64 hdr;	int i;	STAT(unsigned long start, parse_start;)	STAT(++unw.stat.script.builds; start = ia64_get_itc());	/* build state record */	memset(&sr, 0, sizeof(sr));	for (r = sr.curr.reg; r < sr.curr.reg + UNW_NUM_REGS; ++r)		r->when = UNW_WHEN_NEVER;	sr.pr_val = info->pr;	script = script_new(ip);	if (!script) {		dprintk("unwind: failed to create unwind script\n");		STAT(unw.stat.script.build_time += ia64_get_itc() - start);		return 0;	}	unw.cache[info->prev_script].hint = script - unw.cache;	/* search the kernels and the modules' unwind tables for IP: */	STAT(parse_start = ia64_get_itc());	for (table = unw.tables; table; table = table->next) {		if (ip >= table->start && ip < table->end) {			e = lookup(table, ip - table->segment_base);			break;		}	}	if (!e) {		/* no info, return default unwinder (leaf proc, no mem stack, no saved regs)  */		dprintk("unwind: no unwind info for ip=0x%lx (prev ip=0x%lx)\n", ip,			unw.cache[info->prev_script].ip);		sr.curr.reg[UNW_REG_RP].where = UNW_WHERE_BR;		sr.curr.reg[UNW_REG_RP].when = -1;		sr.curr.reg[UNW_REG_RP].val = 0;		compile_reg(&sr, UNW_REG_RP, script);		script_finalize(script, &sr);		STAT(unw.stat.script.parse_time += ia64_get_itc() - parse_start);		STAT(unw.stat.script.build_time += ia64_get_itc() - start);		return script;	}	sr.when_target = (3*((ip & ~0xfUL) - (table->segment_base + e->start_offset))/16			  + (ip & 0xfUL));	hdr = *(u64 *) (table->segment_base + e->info_offset);	dp =   (u8 *)  (table->segment_base + e->info_offset + 8);	desc_end = dp + 8*UNW_LENGTH(hdr);	while (!sr.done && dp < desc_end)		dp = unw_decode(dp, sr.in_body, &sr);	if (sr.when_target > sr.epilogue_start) {		/*		 * sp has been restored and all values on the memory stack below		 * psp also have been restored.		 */		sr.curr.reg[UNW_REG_PSP].where = UNW_WHERE_NONE;		sr.curr.reg[UNW_REG_PSP].val = 0;		for (r = sr.curr.reg; r < sr.curr.reg + UNW_NUM_REGS; ++r)			if ((r->where == UNW_WHERE_PSPREL && r->val <= 0x10)			    || r->where == UNW_WHERE_SPREL)				r->where = UNW_WHERE_NONE;	}	script->flags = sr.flags;	/*	 * If RP did't get saved, generate entry for the return link	 * register.	 */	if (sr.curr.reg[UNW_REG_RP].when >= sr.when_target) {		sr.curr.reg[UNW_REG_RP].where = UNW_WHERE_BR;		sr.curr.reg[UNW_REG_RP].when = -1;		sr.curr.reg[UNW_REG_RP].val = sr.return_link_reg;	}#if UNW_DEBUG	printk("unwind: state record for func 0x%lx, t=%u:\n",	       table->segment_base + e->start_offset, sr.when_target);	for (r = sr.curr.reg; r < sr.curr.reg + UNW_NUM_REGS; ++r) {		if (r->where != UNW_WHERE_NONE || r->when != UNW_WHEN_NEVER) {			printk("  %s <- ", unw.preg_name[r - sr.curr.reg]);			switch (r->where) {			      case UNW_WHERE_GR:     printk("r%lu", r->val); break;			      case UNW_WHERE_FR:     printk("f%lu", r->val); break;			      case UNW_WHERE_BR:     printk("b%lu", r->val); break;			      case UNW_WHERE_SPREL:  printk("[sp+0x%lx]", r->val); break;			      case UNW_WHERE_PSPREL: printk("[psp+0x%lx]", r->val); break;			      case UNW_WHERE_NONE:				printk("%s+0x%lx", unw.preg_name[r - sr.curr.reg], r->val);				break; 			      default:		     printk("BADWHERE(%d)", r->where); break;			}			printk("\t\t%d\n", r->when);		}	}#endif	STAT(unw.stat.script.parse_time += ia64_get_itc() - parse_start);	/* translate state record into unwinder instructions: */	/*	 * First, set psp if we're dealing with a fixed-size frame;	 * subsequent instructions may depend on this value.	 */	if (sr.when_target > sr.curr.reg[UNW_REG_PSP].when	    && (sr.curr.reg[UNW_REG_PSP].where == UNW_WHERE_NONE)	    && sr.curr.reg[UNW_REG_PSP].val != 0) {		/* new psp is sp plus frame size */		insn.opc = UNW_INSN_ADD;		insn.dst = struct_offset(struct unw_frame_info, psp)/8;		insn.val = sr.curr.reg[UNW_REG_PSP].val;	/* frame size */		script_emit(script, insn);	}	/* determine where the primary UNaT is: */	if (sr.when_target < sr.curr.reg[UNW_REG_PRI_UNAT_GR].when)		i = UNW_REG_PRI_UNAT_MEM;	else if (sr.when_target < sr.curr.reg[UNW_REG_PRI_UNAT_MEM].when)		i = UNW_REG_PRI_UNAT_GR;	else if (sr.curr.reg[UNW_REG_PRI_UNAT_MEM].when > sr.curr.reg[UNW_REG_PRI_UNAT_GR].when)		i = UNW_REG_PRI_UNAT_MEM;	else		i = UNW_REG_PRI_UNAT_GR;	compile_reg(&sr, i, script);	for (i = UNW_REG_BSP; i < UNW_NUM_REGS; ++i)		compile_reg(&sr, i, script);	/* free labelled register states & stack: */	STAT(parse_start = ia64_get_itc());	for (rs = sr.reg_state_list; rs; rs = next) {		next = rs->next;		free_reg_state(rs);	}	while (sr.stack)		pop(&sr);	STAT(unw.stat.script.parse_time += ia64_get_itc() - parse_start);	script_finalize(script, &sr);	STAT(unw.stat.script.build_time += ia64_get_itc() - start);	return script;}/* * Apply the unwinding actions represented by OPS and update SR to * reflect the state that existed upon entry to the function that this * unwinder represents. */static inline voidrun_script (struct unw_script *script, struct unw_frame_info *state){	struct unw_insn *ip, *limit, next_insn;	unsigned long opc, dst, val, off;	unsigned long *s = (unsigned long *) state;	STAT(unsigned long start;)	STAT(++unw.stat.script.runs; start = ia64_get_itc());	state->flags = script->flags;	ip = script->insn;	limit = script->insn + script->count;	next_insn = *ip;	while (ip++ < limit) {		opc = next_insn.opc;		dst = next_insn.dst;		val = next_insn.val;		next_insn = *ip;	  redo:		switch (opc) {		      case UNW_INSN_ADD:			s[dst] += val;			break;		      case UNW_INSN_MOVE2:			if (!s[val])				goto lazy_init;			s[dst+1] = s[val+1];			s[dst] = s[val];			break;		      case UNW_INSN_MOVE:

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -