ARM 架构 dump_stack 实现分析(3.0 printk %pS选项实现)

上篇提到了函数:

void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame)
{
#ifdef CONFIG_KALLSYMS
        //记住%pS是关键  printk 与 普通printf最大的不同
        //惭愧啊,现在才知道此选项
	printk("symbol<%08lx>] (%pS) from [<%08lx>] (%pS)\n", where, (void *)where, from, (void *)from);
#else
	printk("Function entered at [<%08lx>] from [<%08lx>]\n", where, from);
#endif
	if (in_exception_text(where))
		dump_mem("", "Exception stack", frame + 4, frame + 4 + sizeof(struct pt_regs));
}

实际打印的却类似下面的语句:

symbol<bf00c0c4>] (handler_pre+0x0/0x19c [kk]) from [<c063d174>] (kprobe_handler+0x194/0x234)

明明只是传入了一个PC指针而已,却可以打印出函数名字及偏移量。

查看了源码,发现是printk的功劳。

参考: kernel/lib/vsprintf
              kernel/kernel/printk

回顾下printk实现:
printk
   -->vprintk
     -->vsnprintf (格式话,及做更多功能)

* This function follows C99 vsnprintf, but has some extensions:
 * %pS output the name of a text symbol with offset    关键在这两个格式化选项
 * %ps output the name of a text symbol without offset
 * %pF output the name of a function pointer with its offset
 * %pf output the name of a function pointer without its offset
 * %pB output the name of a backtrace symbol with its offset
 * %pR output the address range in a struct resource with decoded flags
 * %pr output the address range in a struct resource with raw flags
 * %pM output a 6-byte MAC address with colons
 * %pm output a 6-byte MAC address without colons
 * %pI4 print an IPv4 address without leading zeros
 * %pi4 print an IPv4 address with leading zeros
 * %pI6 print an IPv6 address with colons
 * %pi6 print an IPv6 address without colons
 * %pI6c print an IPv6 address as specified by RFC 5952
 * %pU[bBlL] print a UUID/GUID in big or little endian using lower or upper
 *   case.
 * %n is ignored

int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
{
  ...
    case FORMAT_TYPE_PTR:
                        //格式化为函数名+偏移量
   str = pointer(fmt+1, str, end, va_arg(args, void *),
          spec);
                        //传入的必须是函数指针或地址 (text段)
   while (isalnum(*fmt))
    fmt++;
   break;
}

char *pointer(const char *fmt, char *buf, char *end, void *ptr,
	      struct printf_spec spec)
{
        ......
	switch (*fmt) {
	case 'S':
	case 's':
	case 'B':
		return symbol_string(buf, end, ptr, spec, *fmt);
}
char *symbol_string(char *buf, char *end, void *ptr,
		    struct printf_spec spec, char ext)
{
	unsigned long value = (unsigned long) ptr;
        // 函数名不能超过此长度,否则可能数组越界,导致奇怪问题的产生
	char sym[KSYM_SYMBOL_LEN];
        //真正去查找函数名的实现
	kallsyms_lookup(value, NULL, NULL, NULL, sym);
	return string(buf, end, sym, spec);
}

参考: kernel/kernel/kallsyms.c
呵呵,有时间好好看看kallsyms_lookup的实现

 

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值