it-swarm.com.de

Tool zur Verfolgung lokaler Funktionsaufrufe unter Linux

Ich suche ein Tool wie ltrace oder strace , das lokal definierte Funktionen in einer ausführbaren Datei verfolgen kann. ltrace verfolgt nur dynamische Bibliotheksaufrufe und strace verfolgt nur Systemaufrufe. Zum Beispiel mit dem folgenden C-Programm:

#include <stdio.h>

int triple ( int x )
{
  return 3 * x;
}

int main (void)
{
  printf("%d\n", triple(10));
  return 0;
}

Wenn Sie das Programm mit ltrace ausführen, wird der Aufruf von printf angezeigt, da dies eine Standardbibliotheksfunktion ist (eine dynamische Bibliothek auf meinem System), und strace wird das gesamte System anzeigen Aufrufe vom Startcode, den Systemaufrufen, die zum Implementieren von printf verwendet werden, und dem Shutdown-Code, aber ich möchte etwas, das mir zeigt, dass die Funktion triple aufgerufen wurde. Angenommen, die lokalen Funktionen wurden nicht von einem optimierenden Compiler eingebunden und die Binärdatei wurde nicht entfernt (Symbole entfernt), gibt es ein Tool, das dies tun kann?

Bearbeiten

Ein paar Klarstellungen:

  • Es ist in Ordnung, wenn das Tool auch Trace-Informationen für nicht lokale Funktionen bereitstellt.
  • Ich möchte die Programme nicht mit Unterstützung für bestimmte Tools neu kompilieren müssen, die Symbolinformationen in der ausführbaren Datei sollten ausreichen.
  • Ich wäre wirklich nett, wenn ich das Tool verwenden könnte, um vorhandene Prozesse so zu verknüpfen, wie ich es mit ltrace/strace kann.
58
Robert Gamble

Angenommen, Sie möchten nur für bestimmte Funktionen benachrichtigt werden, können Sie dies folgendermaßen tun:

kompilieren Sie mit Debug-Informationen (da Sie bereits Symbolinformationen haben, haben Sie wahrscheinlich auch genug Debugs in)

gegeben

#include <iostream>

int fac(int n) {
    if(n == 0)
        return 1;
    return n * fac(n-1);
}

int main()
{
    for(int i=0;i<4;i++)
        std::cout << fac(i) << std::endl;
}

Verwenden Sie gdb, um Folgendes zu verfolgen:

[[email protected] cpp]$ g++ -g3 test.cpp
[[email protected] cpp]$ gdb ./a.out
(gdb) b fac
Breakpoint 1 at 0x804866a: file test.cpp, line 4.
(gdb) commands 1
Type commands for when breakpoint 1 is hit, one per line.
End with a line saying just "end".
>silent
>bt 1
>c
>end
(gdb) run
Starting program: /home/js/cpp/a.out
#0  fac (n=0) at test.cpp:4
1
#0  fac (n=1) at test.cpp:4
#0  fac (n=0) at test.cpp:4
1
#0  fac (n=2) at test.cpp:4
#0  fac (n=1) at test.cpp:4
#0  fac (n=0) at test.cpp:4
2
#0  fac (n=3) at test.cpp:4
#0  fac (n=2) at test.cpp:4
#0  fac (n=1) at test.cpp:4
#0  fac (n=0) at test.cpp:4
6

Program exited normally.
(gdb)

Hier ist, was ich mache, um alle Adressen der Funktion zu sammeln:

tmp=$(mktemp)
readelf -s ./a.out | gawk '
{ 
  if($4 == "FUNC" && $2 != 0) { 
    print "# code for " $NF; 
    print "b *0x" $2; 
    print "commands"; 
    print "silent"; 
    print "bt 1"; 
    print "c"; 
    print "end"; 
    print ""; 
  } 
}' > $tmp; 
gdb --command=$tmp ./a.out; 
rm -f $tmp

Beachten Sie, dass Sie nicht nur das aktuelle Bild drucken müssen (bt 1), können Sie alles tun, was Sie wollen, den Wert eines globalen Befehls drucken, einen Shell-Befehl ausführen oder etwas mailen, wenn er das fatal_bomb_exploded function :) Leider gibt gcc zwischendurch einige "Current Language changed" -Nachrichten aus. Aber das ist leicht zu übersehen. Keine große Sache.

System Tap kann auf einer modernen Linux-Box (Fedora 10, RHEL 5 usw.) verwendet werden.

Laden Sie zuerst das Skript para-callgraph.stp herunter.

Dann renne:

$ Sudo stap para-callgraph.stp 'process("/bin/ls").function("*")' -c /bin/ls
0    ls(12631):->main argc=0x1 argv=0x7fff1ec3b038
276  ls(12631): ->human_options spec=0x0 opts=0x61a28c block_size=0x61a290
365  ls(12631): <-human_options return=0x0
496  ls(12631): ->clone_quoting_options o=0x0
657  ls(12631):  ->xmemdup p=0x61a600 s=0x28
815  ls(12631):   ->xmalloc n=0x28
908  ls(12631):   <-xmalloc return=0x1efe540
950  ls(12631):  <-xmemdup return=0x1efe540
990  ls(12631): <-clone_quoting_options return=0x1efe540
1030 ls(12631): ->get_quoting_style o=0x1efe540

Siehe auch: Systemtap- und Oprofile-Updates beachten

20
callgiraffe

Verwenden von probes (seit Linux 3.5)

Angenommen, Sie wollten alle Funktionen in ~/Desktop/datalog-2.2/datalog Verfolgen, wenn Sie sie mit den Parametern -l ~/Desktop/datalog-2.2/add.lua ~/Desktop/datalog-2.2/test.dl Aufrufen.

  1. cd /usr/src/linux-`uname -r`/tools/perf
  2. for i in `./perf probe -F -x ~/Desktop/datalog-2.2/datalog`; do Sudo ./perf probe -x ~/Desktop/datalog-2.2/datalog $i; done
  3. Sudo ./perf record -agR $(for j in $(Sudo ./perf probe -l | cut -d' ' -f3); do echo "-e $j"; done) ~/Desktop/datalog-2.2/datalog -l ~/Desktop/datalog-2.2/add.lua ~/Desktop/datalog-2.2/test.dl
  4. Sudo ./perf report -G

list of functions in datalog binarycall tree when selecting dl_pushlstring, showing how main called loadfile called dl_load called program called rule which called literal which in turn called other functions that ended up calling dl_pushlstring, scan (parent: program, that is, the third scan from the top) which called dl_pushstring and so on

11
Janus Troelsen

Angenommen, Sie können den Code, den Sie verfolgen möchten, mit der gcc-Option -finstrument-functions Neu kompilieren (keine Quellenänderung erforderlich), dann können Sie etrace verwenden, um das Funktionsaufrufdiagramm abzurufen.

So sieht die Ausgabe aus:

\-- 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

Unter Solaris kann Truss (Strace-Äquivalent) die zu verfolgende Bibliothek filtern. Ich war überrascht, als ich entdeckte, dass Strace keine solche Fähigkeit besitzt.

9
philant
$ Sudo yum install frysk
$ ftrace -sym:'*' -- ./a.out

Mehr: ftrace.1

4
callgiraffe

Wenn Sie diese Funktion in eine externe Bibliothek externalisieren, sollten Sie auch sehen können, wie sie aufgerufen wird (mit ltrace).

Der Grund dafür ist, dass sich ltrace zwischen Ihrer App und der Bibliothek befindet und den Aufruf nicht abfangen kann, wenn der gesamte Code mit der einen Datei internalisiert ist.

dh: ltrace xterm

spuckt Zeug aus X-Bibliotheken, und X ist kaum ein System.

Außerhalb davon ist die einzige Möglichkeit, dies zu tun, das Abfangen zur Kompilierungszeit über prof-Flags oder Debug-Symbole.

Ich habe gerade diese App überfahren, die interessant aussieht:

http://www.gnu.org/software/cflow/

Aber ich glaube nicht, dass du das willst.

2
Kent Fredric

Es gibt ein Shell-Skript zur Automatisierung von Tracing-Funktionsaufrufen mit gdb. Es kann jedoch keine Verbindung zum laufenden Prozess herstellen.

blog.superadditive.com/2007/12/01/call-graphs-using-the-gnu-project-debugger/

Kopie der Seite - http://web.archive.org/web/20090317091725/http://blog.superadditive.com/2007/12/01/call-graphs-using-the-gnu-project -debugger /

Kopie des Tools - callgraph.tar.gz

http://web.archive.org/web/20090317091725/http://superadditive.com/software/callgraph.tar.gz

Es gibt alle Funktionen aus dem Programm aus und generiert eine gdb-Befehlsdatei mit Haltepunkten für jede Funktion. An jedem Haltepunkt werden "Backtrace 2" und "Continue" ausgeführt.

Dieses Skript ist auf großen Projekten ziemlich langsam (~ Tausende von Funktionen), daher füge ich einen Filter in die Funktionsliste ein (via egrep). Es war sehr einfach und ich benutze dieses Skript fast jeden Tag.

2
osgx

Wenn die Funktionen nicht inline sind, haben Sie möglicherweise sogar Glück mit objdump -d <program>.

Nehmen wir als Beispiel eine Beute zu Beginn der main -Routine von GCC 4.3.2:

$ objdump `which gcc` -d | grep '\(call\|main\)' 

08053270 <main>:
8053270:    8d 4c 24 04             lea    0x4(%esp),%ecx
--
8053299:    89 1c 24                mov    %ebx,(%esp)
805329c:    e8 8f 60 ff ff          call   8049330 <[email protected]>
80532a1:    8d 04 03                lea    (%ebx,%eax,1),%eax
--
80532cf:    89 04 24                mov    %eax,(%esp)
80532d2:    e8 b9 c9 00 00          call   805fc90 <xmalloc_set_program_name>
80532d7:    8b 5d 9c                mov    0xffffff9c(%ebp),%ebx
--
80532e4:    89 04 24                mov    %eax,(%esp)
80532e7:    e8 b4 a7 00 00          call   805daa0 <expandargv>
80532ec:    8b 55 9c                mov    0xffffff9c(%ebp),%edx
--
8053302:    89 0c 24                mov    %ecx,(%esp)
8053305:    e8 d6 2a 00 00          call   8055de0 <Prune_options>
805330a:    e8 71 ac 00 00          call   805df80 <unlock_std_streams>
805330f:    e8 4c 2f 00 00          call   8056260 <gcc_init_libintl>
8053314:    c7 44 24 04 01 00 00    movl   $0x1,0x4(%esp)
--
805331c:    c7 04 24 02 00 00 00    movl   $0x2,(%esp)
8053323:    e8 78 5e ff ff          call   80491a0 <[email protected]>
8053328:    83 e8 01                sub    $0x1,%eax

Es ist ein bisschen mühsam, durch den gesamten Assembler zu waten, aber Sie können alle möglichen Aufrufe einer bestimmten Funktion sehen. Es ist nicht so einfach zu bedienen wie gprof oder einige der anderen genannten Dienstprogramme, bietet jedoch verschiedene Vorteile:

  • Im Allgemeinen müssen Sie eine Anwendung nicht neu kompilieren, um sie zu verwenden
  • Es werden alle möglichen Funktionsaufrufe angezeigt, während in etwa gprof nur die ausgeführten Funktionsaufrufe angezeigt werden.
2
Tom

Siehe Traces, ein Tracing-Framework für Linux C/C++ - Anwendungen: https://github.com/baruch/traces#readme

Sie müssen Ihren Code mit dem Instrument neu kompilieren, erhalten jedoch eine Liste aller Funktionen, ihrer Parameter und Rückgabewerte. Es gibt eine interaktive Funktion, mit der große Datenmengen einfach durchsucht werden können.

1
Greythorn

Gprof könnte das sein, was du willst

1

KcacheGrind

https://kcachegrind.github.io/html/Home.html

Testprogramm:

int f2(int i) { return i + 2; }
int f1(int i) { return f2(2) + i + 1; }
int f0(int i) { return f1(1) + f2(2); }
int pointed(int i) { return i; }
int not_called(int i) { return 0; }

int main(int argc, char **argv) {
    int (*f)(int);
    f0(1);
    f1(1);
    f = pointed;
    if (argc == 1)
        f(1);
    if (argc == 2)
        not_called(1);
    return 0;
}

Verwendung:

Sudo apt-get install -y kcachegrind valgrind

# Compile the program as usual, no special flags.
gcc -ggdb3 -O0 -o main -std=c99 main.c

# Generate a callgrind.out.<PID> file.
valgrind --tool=callgrind ./main

# Open a GUI tool to visualize callgrind data.
kcachegrind callgrind.out.1234

Sie befinden sich nun in einem großartigen GUI-Programm, das viele interessante Leistungsdaten enthält.

Wählen Sie unten rechts die Registerkarte "Anrufdiagramm". Dies zeigt ein interaktives Anrufdiagramm, das mit Leistungsmesswerten in anderen Fenstern korreliert, wenn Sie auf die Funktionen klicken.

Um das Diagramm zu exportieren, klicken Sie mit der rechten Maustaste darauf und wählen Sie "Diagramm exportieren". Das exportierte PNG sieht folgendermaßen aus:

Daraus können wir folgendes ersehen:

  • der Wurzelknoten ist _start, der der eigentliche ELF-Einstiegspunkt ist und das Glibc-Initialisierungs-Boilerplate enthält
  • f0, f1 und f2 werden wie erwartet voneinander aufgerufen
  • pointed wird auch angezeigt, obwohl wir es mit einem Funktionszeiger aufgerufen haben. Es wäre möglicherweise nicht aufgerufen worden, wenn wir ein Befehlszeilenargument übergeben hätten.
  • not_called wird nicht angezeigt, weil es im Lauf nicht aufgerufen wurde, weil wir kein zusätzliches Befehlszeilenargument übergeben haben.

Das Coole an valgrind ist, dass keine speziellen Kompilierungsoptionen erforderlich sind.

Daher können Sie es auch verwenden, wenn Sie nicht den Quellcode, sondern nur die ausführbare Datei haben.

valgrind schafft dies, indem Sie Ihren Code über eine "virtuelle Maschine" ausführen.

Getestet unter Ubuntu 18.04.

ANMERKUNG: Dies ist kein auf dem Linux-Kernel basierendes Ftrace, sondern ein Tool, das ich kürzlich entwickelt habe, um die lokale Funktionsverfolgung durchzuführen und den Ablauf zu steuern. Linux ELF x86_64/x86_32 werden öffentlich unterstützt.

https://github.com/leviathansecurity/ftrace

0
elfmaster

Hoffentlich gibt Ihnen das Callgrind- oder Cachegrind-Werkzeug für Valgrind die Informationen, die Sie suchen.

0
activout.se