当前位置: 首页 > wzjs >正文

中国做美国酒店的网站成都seo达人

中国做美国酒店的网站,成都seo达人,深圳做网站的网,视频网站顶部效果怎么做的一、背景 在之前的博客 内核逻辑里抓取用户栈的几种方法-CSDN博客 里,介绍了使用内核逻辑进行用户栈的函数地址的抓取逻辑,但是并没有涉及如何解析出函数符号的逻辑。 就如perf工具一样,它也是分为两个步骤,一个步骤是内核态抓取…

一、背景

在之前的博客 内核逻辑里抓取用户栈的几种方法-CSDN博客 里,介绍了使用内核逻辑进行用户栈的函数地址的抓取逻辑,但是并没有涉及如何解析出函数符号的逻辑。

就如perf工具一样,它也是分为两个步骤,一个步骤是内核态抓取函数地址的PC调用链,第二个步骤则是在用户态逻辑里根据抓取的PC转换成人可阅读的函数符号名字。

当前如果不去考虑解析的性能的话,那么只要拿到elf的路径及offset(具体参考之前的博客 内核逻辑里抓取用户栈的几种方法-CSDN博客),然后通过addr2line就可以解析出用户栈的调用链符号,但是这样解析的话,重复的进行进程创建及退出,性能是不够好的。这篇博客里,我们就讲如何高效地进行用户栈的调用链符号的解析。

二、高性能解析的核心思想

2.1 复用elf的解析结果,预解析常用的so

该高性能解析的核心思想就是启动一个用来解析elf和进程的maps表的应用服务,该应用服务可以以守护进程方式长期运行,被解析请求触发来执行。

由于该应用服务是常驻的,所以就避免了反复启动进程重复解析一样的elf文件导致性能损失。该应用服务会把已经解析elf文件得到的信息进行记录,下一次需要解析相同的elf文件时复用之前解析的结果,同样的,下一次需要解析相同的进程时,也可以复用之前的/proc/<pid>/maps的解析结果。

对于elf文件,一般来说,它是不变的。但是要注意,对于/proc/<pid>/maps里的内容,它是可能变化的,如程序使用dlopen/dlclose这种动态加载和解加载so库的函数,就会触发maps的变更,在实现时需要考虑这样的情况。

另外,为了进一步提升性能,我们可以在该进程启动后,先把一些常用的so库先解析出来,这样在真正接到要解析的请求时由于之前已经解析过了,就可以减少第一次解析某个so的elf文件的耗时。

2.2 使用map容器的upper_bound函数快速找到对应的符号及偏移

假设我们已经解析了某个elf的文件,拿到了函数表,我们如何能快速找到对应的符号呢?

我们可以使用map容器的upper_bound接口来实现这样的快速查找,并且map容器的下标得用地址区间的end,而不能用地址区间的start。

这算是一个小算法,但是也有一定的细节,不能用lower_bound,也不能用start作为key,否则会出现解析不符合预期及解析错误的情况。

这里面主要考虑的就是地址区间通常来说是指[start, end)这么一个区间,也就是start是大于等于,而end是小于。

三、完整的解析步骤

这里说的完整的解析步骤是假设了已经拿到了用户栈调用链的PC的情况下的。至于如何抓取用户栈PC,在之前的博客里 内核逻辑里抓取用户栈的几种方法-CSDN博客 给出了通过内核态逻辑抓取的方法。

我们分析,如果根据进程的pid及进程的PC地址的va,得到对应的函数符号和offset。

3.1 先根据进程的pid获取到进程的elf和maps信息的管理对象

每个进程都对应有一个管理对象,来管理进程的相关与解析函数符号逻辑有关的信息,最主要就是/proc/<pid>/maps,其他信息则是用于辅助输出的内容,比如cmdline内容,这些辅助内容的输出可以帮助定位是具体哪个进程,因为有时候进程名是一样的(/proc/<pid>/comm),但是cmdline是不一样的,可以看出一些细节信息。

关于/proc/<pid>/maps的解析,参考如下逻辑:

if (unlikely(!READ_PROC_MAPS(i_processid, [&](char *i_obuf, int i_obufsize) -> bool {unsigned long start;unsigned long end;char permissions[5]; // 读、写、执行、共享unsigned long offset;char pathname[HARDLINK_MAXBYTE];int ret = sscanf(i_obuf, "%lx-%lx %4s %lx %*x:%*x %*u %s",&start, &end, permissions, &offset,pathname);if (ret == 5 && permissions[2] == 'x') {...
#if (DEBUG_LOG == 1)printf("range: %lx-%lx, permission: %s, offset: %lx, hlink: %s\n",start, end, permissions, offset, pathname);
#endifreturn true;}else {return false;}}))) {...break;}

上面的逻辑里使用了lambda表达式,可以简化逻辑。

3.2 根据elf路径进行增量解析

所谓“增量”解析,也就是指已经解析过的elf文件不再重复解析,因为我们已经保存下来之前解析出来的结果了。

我们可以用一个map来保存已经解析过的内容,key表示elf绝对路径。

如果之前没有解析过相关的elf,则使用objdump -t来进行解析,要注意,务必使用objdump -t来解析,因为objdump -t可以解析出弱符号和static的局部符号,而objdump -T则解析不出这些符号。

objdump的命令如下:

"objdump -t %s | grep -E '\\.text'"

上面的%s替换成elf的绝对路径。

3.3 解析vdso及vsyscall的符号

不管哪个平台,一般都有vdso的符号,但是vsyscall则不同的平台不一样,x86上是有的。

有关vdso和vsyscall的基础介绍和相关内核逻辑和glibc逻辑的相关细节见之前的博客 vdso概念及原理,vdso_fault缺页异常,vdso符号的获取-CSDN博客 和 vdso内核与glibc配合的相关逻辑分析-CSDN博客。

3.3.1 vdso符号表的获取

vdso的符号表的获取,我们是通过dd命令从系统上一般都存在的systemd进程里捞取取出相关的so文件内容,并通过objdump进行解析。

通过dd命令捞取vdso.so文件的命令如下:

if (unlikely(!PROC1MAPS_GREP_VDSO([&](char *i_obuf, int i_obufsize) -> bool {unsigned long vdso_va_begin;unsigned long vdso_va_end;if (sscanf(i_obuf, "%lx%*c%lx", &vdso_va_begin, &vdso_va_end) == 2) {...
#if (DEBUG_LOG == 1)printf("vdso_va_begin:0x%lx, vdso_va_end:0x%lx, size:0x%lx\n", vdso_va_begin, vdso_va_end, vdso_va_end - vdso_va_begin);
#endifchar systemcmd[256];snprintf(systemcmd, 256, "dd if=/proc/1/mem of=" TEMP_VDSO_SO_FILE " skip=%lu ibs=1 count=%lu\n", vdso_va_begin, vdso_va_end - vdso_va_begin);system(systemcmd);return true;}return false;}))) {// 出错了return psyminfo;}

上面PROC1MAPS_GREP_VDSO则是执行如下的命令:

"cat /proc/1/maps | grep -E '\\[vdso\\]'"

然后通过sscanf解析出vdso.so的va的begin和end,然后通过dd命令去dump,dump到一个临时文件中。

然后再通过如下的objdump命令进行解析:

"objdump -T %s | grep -E '\\.text'"

上面的%s则是vdso.so的临时文件的路径。

3.3.2 vsyscall符号表的获取

vsyscall的符号表则是直接根据对应平台的内核里的相关符号的内容情况,手动进行组装。有关vsyscall的符号信息如何查看,参考之前的博客 vdso概念及原理,vdso_fault缺页异常,vdso符号的获取-CSDN博客 里的 4.2 一节。

下面的是大致的拼凑逻辑:

..* ...() {..* psyminfo;...// vsyscall目前只用考虑x86场景,x86的vsyscall的情况是固定的,就三个符号...unsigned long start = 0;unsigned long span = 1024;psysrange = ...psysrange->start = start;psysrange->span = span;strscpy(psysrange->sym, "gettimeofday", SYM_MAXBYTE);
#if (DEBUG_LOG == 1)printf("start:0x%llx, span:0x%llx, typestr:%s, symname:%s \n",psysrange->start, psysrange->span, HLINK_VSYSCALL, psysrange->sym);
#endifstart += 1024;...psysrange->start = start;psysrange->span = span;strscpy(psysrange->sym, "time", SYM_MAXBYTE);
#if (DEBUG_LOG == 1)printf("start:0x%llx, span:0x%llx, typestr:%s, symname:%s \n",psysrange->start, psysrange->span, HLINK_VSYSCALL, psysrange->sym);
#endifstart += 1024;...psysrange->start = start;psysrange->span = span;strscpy(psysrange->sym, "getcpu", SYM_MAXBYTE);
#if (DEBUG_LOG == 1)printf("start:0x%llx, span:0x%llx, typestr:%s, symname:%s \n",psysrange->start, psysrange->span, HLINK_VSYSCALL, psysrange->sym);
#endifreturn psyminfo;}

3.4 通过upper_bound来查找对应的符号及offset

有关为什么用upper_bound在上面 2.2 里做了简要说明。

大致的代码如下:

..* ...(u64 i_addr) {auto it = xx.upper_bound(i_addr);//printf("count[%d]\n", xx.size());if (it != xx.end()) {if (it->second->start <= i_addr && i_addr < it->second->start + it->second->span) {return it->second;}}return NULL;}

3.5 成果展示

我们使用perf来抓取进程pid是1的systemd这个进程的用户态符号,然后,再用该程序进行解析,看解析出的内容是否一致。

用perf record -g -p 1之后,用perf script得到的如下图的一处采样:

我们使用解析程序,输入pid和va,进行解析,可以看到解析出一样的函数符号名和offset。

http://www.dtcms.com/wzjs/402439.html

相关文章:

  • 菏泽网架公司湖南seo网站策划
  • 做网站备案需要哪些材料爱站网 关键词挖掘工具站
  • 中山专业做网站郑州官网网站推广优化
  • 工厂做网站有用吗seo是什么意思为什么要做seo
  • 赤峰网站策划seo网站推广培训
  • b2b行业网站综合影响力排名云优化seo软件
  • 滨海网站建设公司品牌营销策略四种类型
  • 医疗网站制作网站建设的流程是什么
  • 济宁专业网站建设提高工作效率心得体会
  • 网站头条怎么做成都网络推广运营公司
  • 海口专业网站建设地址东莞seo培训
  • 泰安人才市场最新招聘信息上海seo搜索优化
  • 网站ppt怎么做营销策略4p
  • 一个网站绑定两个域名网上营销的平台有哪些
  • 代刷网站系统怎么做今日新闻最新头条10条摘抄
  • 做网站时的注册权起到什么作用品牌宣传的推广
  • 西安未央区网站建设广州推广引流公司
  • 在线黑科技网站优化网站界面的工具
  • 闵行虹桥网站建设seo技术培训宁波
  • 手工制作小钱包郑州网站seo外包
  • 如何向google提交网站最好的免费建站网站
  • 网站开发 改进京津冀协同发展
  • 建设企业网站方案引擎搜索器
  • 论文旅游网站建设seo网站推广批发
  • 中江门户网站最新舆情信息网
  • 在电脑新建网站站点职业培训网络平台
  • 腾讯快速建站平台桂林网页
  • 白云网站开发南宁网站建设
  • 莞城网页设计系统优化大师
  • 网站运营专员具体每天怎么做手机网站建设公司