2012-04-29 6 views
9

소스 코드를 수정하지 않고도 어떤 함수 (예 : func100)가 호출 될 때 어떤 함수가 호출되고 어떤 매개 변수를 추적 할 수 있습니까? 출력을 다음과 같이하고 싶습니다.C에서 함수 호출을 추적하는 방법?

enter func100(p1001=xxx,p1002=xxx) 
     enter func110(p1101=xxx,p1102=xxx) 
     exit func110(p1101=xxx,p1102=xxx) 
     enter func120(p1201=xxx,p1202=xxx,p1203=xxx) 
       enter func121(p1211=xxx) 
       exit func121(p1211=xxx) 
     exit func120(p1201=xxx,p1202=xxx,p1203=xxx) 
exit func100(p1001=xxx,p1002=xxx) 

이 것이 가능합니까? 또는 소스 코드를 최소한으로 수정 한 솔루션은 무엇입니까?

+1

디버거를 사용하십시오. 또는 fprintf 로깅을 파일에 호출 할 수 있습니다. 그러나 마지막 옵션은 소스 코드를 수정하고 싶지 않으므로 좋지 않을 수 있습니다. – Lefteris

+0

아마도 콜 그래프를 가져 오는 프로파일 러일까요? –

+1

당신은 그런 것을 찾고 있습니까? http://stackoverflow.com/questions/311840/tool-to-trace-local-function-calls-in-linux – delannoyk

답변

4

Linux를 사용 중이라면 callgrind이 도움이 될 수 있습니다. 기본적으로 찾고있는 통계를 수집하므로 원시 데이터에 액세스 할 수있는 방법을 제공 할 수 있습니다.

+0

나는 callgrind 설명서를 살펴 봤지만 함수를 입력/종료 할 때 매개 변수의 값을 수집하는 방법을 언급하지 않았습니다. – Andrew

0

아파치 기초가 호스팅하는 프로젝트 인 log4cxx를 살펴볼 수 있습니다. log4j, 자바 변종이 감도를 설정할 수 있다는 것을 알고 있으며, 프로그램에서 수행 된 모든 일을 추적 할 수 있습니다. 어쩌면 C++ 변형이 동일 할 수도 있지만 여러 가지 대안이 있습니다. 즉, aspect 지향 C++ 컴파일러가 있으며 모든 함수에서 aspect를 정의하고 변수를 잡아서 인쇄 할 수 있습니다. 또 다른 대안은 디버거를 사용하는 것입니다.

은 요약하면 : 당신이 gcc를 사용하는 경우 디버거, log4cxx 또는 AOP는

+0

AFAIK는 [log4cxx] (http://logging.apache.org/log4cxx/)를 활용하기 위해 "LOG4CXX_DEBUG (barlogger, Exiting func100")와 같은 코드 스 니펫을 추가하여 소스 코드를 수정해야합니다. " – Andrew

+0

아, 그걸 몰랐어. 그래, 그 전화 갈기는 다음 최고의 솔루션처럼 보이는 –

13

, 당신은 -finstrument-functions 컴파일 플래그를 사용할 수 있습니다. 함수가 출입 할 때마다 __cyg_profile_func_enter__cyg_profile_func_exit의 두 함수를 호출하는 코드를 추가합니다.

원하는 기능을 구현하려면이 기능을 구현해야합니다. 플래그를 사용하지 않고 컴파일하거나 attribute((no_instrument_function))으로 컴파일하여 자신을 호출하지 않도록하십시오.

함수의 두 번째 매개 변수는 호출 사이트에 대한 포인터 (즉 호출 함수 내의 반환 주소)가됩니다. %p으로 인쇄 할 수는 있지만 사용하기가 다소 어려울 것입니다. nm을 사용하여이 주소가 포함 된 실제 기능을 파악할 수 있습니다.

이 방법으로 함수 매개 변수를 가져올 수 없습니다.

+1

너무 나쁜 주소입니다. – reader

+0

다음은 [예제 구현]입니다 (https://balau82.wordpress.com/2010/10/06/trace-and-profile-function-call-with-gcc/). – JohnMudd

+0

havent는 아직 이것을 시도 할 기회가 있었지만 강력하게 들립니다. 나는 모든 로그 주소를 기능 이름 –

12

GNU C 라이브러리를 사용하면 backtrace 모듈을 사용할 수 있습니다.

#include <stdio.h> 
#include <execinfo.h> 
#include <stdlib.h> 


void handler(char *caller) { 
    void *array[10]; 
    size_t size; 
    printf("Stack Trace Start for %s\n",caller); 
    size = backtrace(array, 10); 
    backtrace_symbols_fd(array, size, 2); 
    printf("Stack Trace End\n"); 
} 

void car() { 
    handler("car()"); 
    printf("Continue Execution"); 
} 
void baz() {car(); } 

void bar() { baz(); } 
void foo() { bar(); } 


int main(int argc, char **argv) { 
    foo(); 
} 

당신이 핸들러 함수를 작성할 수 있습니다 당신은

Stack Trace Start for car() 
./Test(handler+0x2d)[0x80486f1] 
./Test(car+0x12)[0x804872e] 
./Test(baz+0xb)[0x8048747] 
./Test(bar+0xb)[0x8048754] 
./Test(foo+0xb)[0x8048761] 
./Test(main+0xb)[0x804876e] 
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xe7)[0x126e37] 
./Test[0x8048631] 
Stack Trace End 
Continue Execution in car 

와 유사한 출력이 표시됩니다

gcc -g -rdynamic Test1.c -o Test 

기호

를로드 -g -rdynamic 컴파일러 옵션을 사용하여 컴파일 : 여기에 대한 예입니다 원하는 시간에 프로그램의 어느 곳에서나 전화 할 수 있습니다. 필요에 따라 array 크기를 늘리는 것을 잊지 마십시오.

3

디버거를 사용하여 관련된 동작으로 중단 점을 설정하십시오. 예를 들어, gdb에서 추적 할 각 함수의 시작과 끝 부분에 중단 점을 설정할 수 있습니다. 당신이 (디버거에서) 프로그램을 실행할 때

printf("Enter func100(p1001=%d, p1002=%d)", p1001, p1002) 

다음에,이 관련 매개 변수와 함께 당신의 각 명령의 텍스트를 인쇄 할 수 있습니다 : 당신은 같은 명령을 실행하는 중단 점, 각을 제공 할 수 있습니다 .

relevant documentation for gdb을 살펴보십시오.

+0

그것은 매력처럼 작동합니다. 유일한 문제는 각 함수의 끝에 중단 점을 설정하는 방법을 모르겠다는 것입니다. 함수의 시작 부분에 중단 점을 넣기 위해 break 명령과 함수 이름을 내 명령 파일에 넣었습니다. 함수의 끝에서 중단 점을 위해, 나는 이렇게 할 수 없다. 줄 번호 뒤에 break 명령을 사용할 수 있습니까? 중단 점이 상주하는 소스 파일을 지정하는 방법은 무엇입니까? – Andrew

+0

특정 행 번호를 사용할 수 있습니다. 또한, 대부분의 점잖은 IDE를 사용하면 간단하게 마진을 클릭하여 중단 점을 설정하는 것이 간단합니다. 당신은 어떤 도구를 사용하고 있는지 말하지 않았으므로 구체적인 조언을하기가 어렵습니다. – Caleb

+0

GDB를 사용합니다.그래서 --command 옵션을 사용하여 gdb에 전달 될 명령 파일을 준비해야합니다. 또는 Eclipse CDT가 gdb 명령 파일을 생성하는 데 도움이 될 수 있습니까? 나는 확실히 명령 파일을 필요로하고 약간 비틀기. – Andrew

0

때로는 많은 함수 호출을 추적해야합니다. 외부 라이브러리에 대해서도 컨트롤이 없거나 수정하고 싶지 않습니다.

언젠가 전에 나는 gdb의 정규 표현식 중단 점 (일반 것들은 괜찮습니다)을 결합 할 수 있다는 것을 깨달았습니다. 그런 다음 중단 점이 발생할 때마다 실행될 일련의 명령을 실행하십시오. 참조 : 예를 들어 http://www.ofb.net/gnu/gdb/gdb_35.html

, 당신은 "MPI_"접두어로 시작하는 모든 기능을 추적하려는 경우, 당신은 할 수 있습니다 :

는 는
(gdb) rbreak MPI_ 
[...] 
(gdb) command 1-XX 
(gdb) silent 
(gdb) bt 1 
(gdb) echo \n\n 
(gdb) continue 
(gdb) end 

자동 명령은 GDB 메시지를 숨기는 데 사용되는 중단 점을 발견 때 . 나는 보통 두 줄의 빈 줄을 출력하므로 읽기가 더 쉽다. 프로그램이 실행을 시작하면, GDB는 N 맨 위의 역 추적 레벨을 인쇄합니다 (GDB)

을 실행

그런 다음, 당신은 단지 프로그램을 실행합니다. 주어진 중단 점의 지역 변수를 인쇄

#0 0x000000000040dc60 in [email protected]() 


#0 PMPI_Initialized (flag=0x7fffffffba78) at ../../src/mpi/init/initialized.c:46 


#0 0x000000000040d9b0 in [email protected]() 


#0 PMPI_Init_thread (argc=0x7fffffffbe78, argv=0x7fffffffbde0, required=3, provided=0x7fffffffba74) at ../../src/mpi/init/initthread.c:946 


#0 0x000000000040e390 in [email protected]() 


#0 PMPI_Comm_rank (comm=1140850688, rank=0x7fffffffba7c) at ../../src/mpi/comm/comm_rank.c:53 


#0 0x000000000040e050 in [email protected]() 


#0 PMPI_Type_create_struct (count=3, array_of_blocklengths=0x7fffffffba90, array_of_displacements=0x7fffffffbab0, array_of_types=0x7fffffffba80, newtype=0x69de20) at ../../src/mpi/datatype/type_create_struct.c:116 


#0 0x000000000040e2a0 in [email protected]() 


#0 PMPI_Type_commit (datatype=0x69de20) at ../../src/mpi/datatype/type_commit.c:75 

더 자세한 정보를 원하는 경우

, 그냥 commandend 년 사이에 명령을 삽입하는 것도 가능하다.

보너스 팁 :이 모든 것을 .gdbinit 파일에 추가하고 실행 파일을 파일로 파이프하십시오.