碧玉妆成一树高
万条垂下绿丝绦
前言
如果是想做profile,可以使用libunwind
获得C++的堆栈信息。
libunwind
这玩意说实话,资料特别特别少,特别是相关接口使用的例子,我只找到了一个,然后其他人全部抄的这一个,而且他还是获取当前程序堆栈的,也就是必须在被profile
的程序中调用libunwind
接口才能获取到堆栈信息。。。然后还不是中文的,国内关于libunwind
的资料真的少
说实话,我觉得这样和现在的需求不符,我们需要是获取其他进程堆栈信息。
然后就只能啃libunwind
的文档了,发现了两个接口:
unw_init_local(unw_cursor_t *c, unw_context_t *ctxt)
unw_init_remote(unw_cursor_t *c, unw_addr_space_t as, void *arg)
很显然,第一个是在当前程序堆栈为unw_cursor_t
指针进行初始化,第二个是利用“远程”程序堆栈为unw_cursor_t
指针进行初始化,区别就在于第三个参数了,所以第三个参数一定带有其他进程的信息。
来看看文档中关于arg
参数的介绍:
The
arg
void-pointer tells the address space exactly what entity should be unwound. For example, ifunw_local_addr_space
is passed inas
, thenarg
needs to be a pointer to a context structure containing the machine-state of the initial stack frame. However, other address-spaces may instead expect a process-id, a thread-id, or a pointer to an arbitrary structure which identifies the stack-frame chain to be unwound. In other words, the interpretation ofarg
is entirely dependent on the address-space in use;libunwind
never interprets the argument in any way on its own.
翻译一下的话,意思就是arg
是用来告知“是哪里的堆栈信息应该被解开”,举个例子,如果unw_local_addr_space
是通过as
来告知是哪里的堆栈信息要解开,那么arg
需要被一个指针来指向一个存有机器状态的空间。然而如果是其他的程序的堆栈空间,则更期望arg
指向了一个存有进程id,线程id或者其他堆栈信息的空间。换句话说,需要用户自己来指定这些信息。
说的云里雾里的,可能是自己英语水平太渣了,大概就是arg
中需要存有目标进程的信息,目标进程就是要被“解堆栈”的进程,后面那个解释不解释我也不懂了就。
后面在github上直接搜unw_init_remote
这个关键词,找到了别人的调用方法。
调用格式是这样的:
void rctx = _UPT_create(pid); // pid就是目标进程id
unw_init_remote(&cursor, addr_space, rctx)
那怎么绑定进程就解决了。
主要接口介绍
这里只讲这里需要用到的接口,其他的可以自行参考libunwind文档。
-
获得一块被展开的空间:
unw_addr_space_t unw_create_addr_space(unw_accessors_t *ap, int byteorder)
-
获得当前机器状态的上下文:
int unw_getcontext(unw_context_t *ucp);
-
跟踪目标进程,可选参数很多,这里用的attach和detach:
long int ptrace (enum __ptrace_request __request, ...)
-
根据进程ID得到一个进程信息的
void *
指针,对应之前说的那个arg
:void *_UPT_create (pid_t)
-
对进程堆栈展开,并将信息赋给一个cursor,也就是之前介绍的:
int unw_init_remote(unw_cursor_t