如何在动态库中使用etrace来按时间顺序跟踪C ++中的函数调用? [英] How to use etrace with a dynamic library for chronological traceing of function calls in C++?

查看:64
本文介绍了如何在动态库中使用etrace来按时间顺序跟踪C ++中的函数调用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景:

我有一个大的仿真工具,我需要了解它的逻辑行为.为了做到这一点,如果按时间顺序排列函数调用,我将获得最大的帮助,这是一个最小的工作示例.

I have one big simulation tool, and I need to understand its logical behavior. In order to do that, the most of help I would get if I have the chronological order of function calls, for a minimal working example.

我在线上找到了许多工具,例如 CygProfiler

I found many tools online, like CygProfiler and etrace. I became so miserable on finding a solution that I started to follow the craziest solution of using "step into" with the debugger. Which is a good option if you have a small program but not a complete simulation tool.

问题:

我面临的问题之一是上述解决方案最初是针对C的,并且在编译时会生成一个静态文件(*.o).另一方面,模拟工具会生成一个共享库(.so).我对低级内容不了解很多,所以尝试链接它们时似乎失败了.

One of the problems I face is that the above-mentioned solutions are originally meant for C and they generate a static file (*.o) when compiled. On the other hand the simulation tool generates a shared library (.so). I don't have much knowledge on lower level stuff so I seem to fail when I try linking them.

我专门查看了etrace 文档,它说:

I looked specifically at the etrace documentation, and it says:

要查看如何修改ptrace.c以使其与动态库一起使用,请查看 example2目录.那里的资源也创建了一个独立的 可执行文件,但PTRACE_REFERENCE_FUNCTION宏的定义与 这将是一个动态库.

To see how to modify ptrace.c to work with a dynamic library, look at the example2 directory. The sources there also create a stand-alone executable, but the PTRACE_REFERENCE_FUNCTION macro is defined just as it would be for a dynamic library.

如果查看存储库,则exampleexample2文件夹中的文件之间没有区别. example2中只有一个额外的.h文件.

If you look at the repo there is no difference between the files in example and example2 folders. Only there is an extra .h file in example2.

另一方面,如果您查看src/ptrace.c,则会显示:

On the other hand, if you look at src/ptrace.c there it says:

在动态库上使用ptrace时,必须设置 PTRACE_REFERENCE_FUNCTION宏将成为函数中的名称 图书馆.加载时此函数的地址将是第一个 行输出到跟踪文件,并将允许翻译 其他进入和退出指向其符号名称的指针.你可以设置 宏PTRACE_INCLUDE带有任何所需的#include指令 该功能可用于此源文件.

When using ptrace on a dynamic library, you must set the PTRACE_REFERENCE_FUNCTION macro to be the name of a function in the library. The address of this function when loaded will be the first line output to the trace file and will permit the translation of the other entry and exit pointers to their symbolic names. You may set the macro PTRACE_INCLUDE with any #include directives needed for that function to be accesible to this source file.

下面有一段注释的代码:

a little below there is the commented code:

/* When using ptrace on a dynamic library, the following must be defined:
#include "any files needed for PTRACE_REFERENCE_FUNCTION"
#define PTRACE_REFERENCE_FUNCTION functionName
`*/


问题:

本质上,问题是以下内容:如何在动态库中使用etrace?

In essence the question is the following: How to use etrace with a dynamic library?

我需要#include任何文件吗?

Do I need to #include any files?

要跟踪独立程序,无需#include任何 附加文件.只需将您的代码链接到ptrace.c并使用 -finstrument-functions选项作为gcc的编译选项.这应该做.

To trace a stand-alone program, there is no need to #include any additional file. Just link your code against ptrace.c and use the -finstrument-functions option as a compile option for gcc. This should do it.

如何链接通过makefile针对ptrace.c

How do I link a C++ code which is built via makefiles against ptrace.c

最后说明:如果有人忍受我的无知并为我的问题提供循序渐进的解决方案,我将不胜感激.

Final Note: I would appreciate if someone bears with my ignorance and provides a step-by-step solution to my question.

更新1:

我设法将与etrace相关的库添加到模拟工具中,并且它可以正常执行.

I managed to add the libraries related to etrace to the simulation tool, and it executes fine.

但是,(可能是因为脚本太旧,或者不适合与C ++一起使用),当使用

However, (probably because the scripts are too old, or are not meant for use with C++) I get the following error when using the perl script provided by default by etrace

Hexadecimal number > 0xffffffff non-portable"

这可能会改变此问题的性质,此刻将其更多地转变为与Perl相关的问题.

Probably this changes a bit the nature of this question, turning it more to a perl related issue at this point.

如果此问题得到解决,我希望etrace将在一个复杂的项目中工作,我会提供详细信息

If this problem is solved, I hope etrace will work with a complicated project and I will provide the details

更新2:

我采纳了@Harry的建议,并且我相信这在大多数项目中都会奏效.但是,就我而言,我从perl脚本中获得了以下信息:

I took the suggestions from @Harry, and I believe that would work in most projects. However in my case I get the following from the perl script:

Use of uninitialized value within %SYMBOLTABLE in list assignment at etrace2.pl line 99, <CALL_DATA> line 1.

\-- ???
|   \-- ???
\-- ???
|   \-- ???
|   |   \-- ???
\-- ???
|   \-- ???
\-- ???
|   \-- ???
\-- ???
|   \-- ???
\-- ???
|   \-- ???
\-- ???
|   \-- ???

由于生成的生成文件,我使用了 LD_PRELOAD 为etrace.so加载共享库,结果如下:

Due to autegenerated makefiles I used the LD_PRELOAD to load the shared library for etrace.so which I got as follows:

gcc -g -finstrument-functions -shared -fPIC ptrace.c -o etrace.so -I <path-to-etrace>

我在工具内部创建了虚拟etrace.h:

I created the dummy etrace.h inside the tool:

#ifndef __ETRACE_H_
#define __ETRACE_H_

#include <stdio.h>

void Crumble_buy(char * what, int quantity, char * unit);


void Crumble_buy(char * what, int quantity, char * unit)
{
    printf("buy %d %s of %s\n", quantity, unit, what);
}

#endif

,对于#define使用Crumble_buy,对于#include使用etrace.h.

and used Crumble_buy for the #define and the etrace.h for the #include.

推荐答案

修复Perl脚本

十六进制数> 0xffffffff不可移植"

Hexadecimal number > 0xffffffff non-portable"

这是来自hex的警告,因为它检测到可能是不可移植的值(大于32位).

This is a warning from hex because it's detecting a possibly non-portable value (something > 32bits).

在脚本的顶部,添加以下内容:

At the very top of the script, add this:

use bigint qw/hex oct/;

编写此工具时,我怀疑人们使用的是32位计算机.您可以使用带有标志-m32的32位编译程序,但是如果您如上所述更改了perl脚本,则无需这样做.

When this tool was written, I suspect the people were on 32-bit machines. You can compile the program using 32-bit with the flag -m32, but if you change the perl script as mentioned above you won't need to.

请注意,如果您使用的是Mac,则不能使用mknod脚本中创建管道的方式;您需要使用不带参数的mkfifo来代替.

Note, if you're on a Mac, you can't use mknod the way it's used in the script to create a pipe; you need to use mkfifo with no arguments instead.

在Linux上,添加上面的bigint修复程序有效.然后,您需要从同一目录运行这两个命令,我使用example2做到了这一点:

On Linux, adding the bigint fix above works. You then need to run both commands from the same directory, I did this using example2:

../src/etrace.pl crumble
# Switch to a different terminal
./crumble

我在Mac和Linux上都可以使用

and I get this on the Mac and Linux

\-- main
|   \-- Crumble_make_apple_crumble
|   |   \-- Crumble_buy_stuff
|   |   |   \-- Crumble_buy
|   |   |   \-- Crumble_buy
|   |   |   \-- Crumble_buy
|   |   |   \-- Crumble_buy
|   |   |   \-- Crumble_buy
|   |   \-- Crumble_prepare_apples
|   |   |   \-- Crumble_skin_and_dice
|   |   \-- Crumble_mix
|   |   \-- Crumble_finalize
|   |   |   \-- Crumble_put
|   |   |   \-- Crumble_put
|   |   \-- Crumble_cook
|   |   |   \-- Crumble_put
|   |   |   \-- Crumble_bake

关于动态库...

加载动态库时,目标文件中的地址不是运行时将使用的地址. etrace要做的是从您指定的标头中获取函数名称.例如,对于example2,将是以下内容:

About the Dynamic Library...

When you load a dynamic library, the address in the object file is not the address that will be used when running. What etrace does is take a function name from a header you specify. For example, in the case of example2, this would be the following:

#include "crumble.h"
#define PTRACE_REFERENCE_FUNCTION Crumble_buy

然后您将编辑makefile以确保可以找到头文件:

You would then edit the makefile to make sure that the header file can be found:

CFLAGS = -g -finstrument-functions -I.

请注意添加了include -I..标头中符号的地址(在本例中为Crumble_buy)用于计算目标文件与实际地址之间的偏移量;这样,程序就可以计算出正确的地址来找到符号.

Note the addition of the include -I.. The address of the symbol from the header (in our case, Crumble_buy) is used to calculate the offset between the object file and the actual address; this allows the program to calculate the correct address to find the symbol.

如果查看nm的输出,则会得到类似以下的内容:

If you look at the output of nm, you get something like the following:

0000000100000960 T _Crumble_bake
00000001000005b0 T _Crumble_buy
0000000100000640 T _Crumble_buy_stuff
00000001000009f0 T _Crumble_cook

左侧的地址是相对的,即在运行时,这些地址实际上发生了变化. etrace.pl程序将它们存储在这样的哈希中:

The addresses on the left are relative, that is, at runtime, these addresses actually change. The etrace.pl program is storing these in a hash like this:

$VAR1 = {
          '4294969696' => '_Crumble_bake',
          '4294969424' => '_Crumble_put',
          '4294970096' => '_main',
          '4294969264' => '_Crumble_mix',
          '4294970704' => '_gnu_ptrace_close',
          '4294967296' => '__mh_execute_header',
          '4294968752' => '_Crumble_buy',
          '4294968896' => '_Crumble_buy_stuff',
          '4294969952' => '_Crumble_make_apple_crumble',
          '4294969184' => '_Crumble_prepare_apples',
          '4294971512' => '___GNU_PTRACE_FILE__',
          '4294971504' => '_gnu_ptrace.first',
          '4294970208' => '_gnu_ptrace',
          '4294970656' => '___cyg_profile_func_exit',
          '4294970608' => '___cyg_profile_func_enter',
          '4294969552' => '_Crumble_finalize',
          '4294971508' => '_gnu_ptrace.active',
          '4294969840' => '_Crumble_cook',
          '4294969088' => '_Crumble_skin_and_dice',
          '4294970352' => '_gnu_ptrace_init'
        };

请注意下划线,因为这是在使用clang的Mac上.在运行时,这些地址不正确,但是它们的相对偏移量是正确的.如果可以算出偏移量是多少,则可以调整运行时获得的地址以找到实际的符号.执行此操作的代码如下:

Note the leading underscore because this is on a Mac using clang. At runtime, these addresses are not correct, but their relative offsets are. If you can work out what the offset is, you can adjust the addresses you get at runtime to find the actual symbol. The code that does this follows:

 if ($offsetLine =~ m/^$REFERENCE_OFFSET\s+($SYMBOL_NAME)\s+($HEX_NUMBER)$/) {
    # This is a dynamic library; need to calculate the load offset
    my $offsetSymbol  = "_$1";
    my $offsetAddress = hex $2; 

    my %offsetTable = reverse %SYMBOLTABLE;

    print Dumper(\%offsetTable);
    $baseAddress = $offsetTable{$offsetSymbol} - $offsetAddress;
    #print("offsetSymbol == $offsetSymbol\n");
    #print("offsetAddress == $offsetAddress\n");
    #print("baseoffsetAddress == $offsetAddress\n");
    $offsetLine = <CALL_DATA>;
  } else {
    # This is static
    $baseAddress = 0;
  }

这是#define PTRACE_REFERENCE_FUNCTION Crumble_buy行的作用. ptrace中的C代码正在使用该MACRO,并且如果已定义,则首先输出该函数的地址.然后,它计算偏移量,并为所有后续地址按此量进行调整,在哈希中查找正确的符号.

This is what the line #define PTRACE_REFERENCE_FUNCTION Crumble_buy is for. The C code in ptrace is using that MACRO, and if defined, outputting the address of that function as the first thing. It then calculates the offset, and for all subsequent addresses, adjusts them by this amount, looking up the correct symbol in the hash.

这篇关于如何在动态库中使用etrace来按时间顺序跟踪C ++中的函数调用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆