使用etrace

时间:2016-04-27 16:54:48

标签: c++ c perl macros profiling

背景

我有一个很大的模拟工具,我需要了解它的逻辑行为。为了做到这一点,如果我有一个函数调用的时间顺序,我将得到的大部分帮助,用于最小的工作示例。

我在网上找到了很多工具,例如CygProfileretrace。我找到了一个解决方案,我开始遵循使用调试器“步入”的最疯狂的解决方案,我变得非常痛苦。如果你有一个小程序但不是一个完整的模拟工具,这是一个很好的选择。

问题:

我面临的一个问题是上述解决方案最初用于C,并且在编译时会生成静态文件(*.o)。另一方面,模拟工具生成共享库(.so)。我对较低级别的东西知之甚少,所以当我尝试链接它们时,我似乎失败了。

我专门查看了etrace documentation,并说:

  

要了解如何修改ptrace.c以使用动态库,请查看   example2目录。那里的消息来源也创造了一个独立的   可执行文件,但PTRACE_REFERENCE_FUNCTION宏定义如下   它将用于动态库。

如果您查看回购,exampleexample2文件夹中的文件没有区别。只有.h中有一个额外的example2文件。

另一方面,如果你看src/ptrace.c那就说:

  

在动态库上使用ptrace时,必须设置     PTRACE_REFERENCE_FUNCTION宏是一个函数的名称     图书馆。加载时此函数的地址将是第一个     行输出到跟踪文件并允许转换     其符号名称的其他入口和出口指针。你可以设置     宏PTRACE_INCLUDE,需要任何#include指令     该函数可以访问此源文件。

下面有评论代码:

/* 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与动态库结合使用?

我是否需要#include任何文件?

  

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

如何将通过makefile构建的C ++代码链接到ptrace.c

最后的注意事项:如果有人因我的无知而承担责任并为我的问题提供逐步解决方案,我将不胜感激。

更新1:

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

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

默认提供的perl script时出现以下错误
Hexadecimal number > 0xffffffff non-portable"

这可能会改变这个问题的性质,在这一点上更多地转向与perl相关的问题。

如果这个问题得到解决,我希望etrace可以处理一个复杂的项目,我会提供详细信息

更新2:

我接受了@Harry的建议,我相信这对大多数项目都有效。但是在我的情况下,我从perl脚本中获得以下内容:

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

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

由于autegenerated makefile我使用LD_PRELOAD加载etrace.so的共享库,我得到如下:

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

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

#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

并使用Crumble_buy用于#defineetrace.h用于#include

1 个答案:

答案 0 :(得分:4)

修复Perl脚本

  

十六进制数&gt; 0xffffffff非便携式&#34;

这是来自hex的警告,因为它检测到可能的非便携值(某些> 32位)。

在脚本的最顶部,添加:

use bigint qw/hex oct/;

当编写这个工具时,我怀疑人们在32位机器上。您可以使用带有标记-m32的32位编译程序,但如果您更改了上面提到的perl脚本,则您不需要。

请注意,如果您使用的是Mac,则无法使用mknod在脚本中使用的方式来创建管道;你需要使用没有参数的mkfifo

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

执行此操作
../src/etrace.pl crumble
# Switch to a different terminal
./crumble

我在Mac和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的情况下,这将是以下内容:

#include "crumble.h"
#define PTRACE_REFERENCE_FUNCTION Crumble_buy

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

CFLAGS = -g -finstrument-functions -I.

请注意添加包含-I.。标题中的符号地址(在我们的例子中,Crumble_buy)用于计算目标文件和实际地址之间的偏移量;这允许程序计算正确的地址以找到符号。

如果你查看nm的输出,你会得到如下内容:

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

左边的地址是相对的,也就是说,在运行时,这些地址实际上是变化的。 etrace.pl程序将这些存储在这样的哈希中:

$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上。在运行时,这些地址不正确,但它们的相对偏移量是。如果可以计算偏移量,可以调整运行时获得的地址以查找实际符号。执行此操作的代码如下:

 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,如果已定义,则输出该函数的地址作为第一件事。然后计算偏移量,对于所有后续地址,按此数量调整它们,在散列中查找正确的符号。