동적 분석 방법
여기에서는 몇 가지 동적 분석 방법을 설명합니다.
동적 메서드는 실제로 프로그램을 실행하여 호출 그래프를 결정합니다.
동적 메서드의 반대는 프로그램을 실행하지 않고 소스에서만 확인하려고하는 정적 메서드입니다.
동적 방법의 장점 :
- 함수 포인터와 가상 C ++ 호출을 포착합니다. 이들은 사소하지 않은 소프트웨어에 많이 있습니다.
동적 방법의 단점 :
- 프로그램을 실행해야합니다. 속도가 느리거나없는 설정 (예 : 크로스 컴파일)이 필요할 수 있습니다.
- 실제로 호출 된 함수 만 표시됩니다. 예를 들어 일부 함수는 명령 줄 인수에 따라 호출되거나 호출되지 않을 수 있습니다.
KcacheGrind
https://kcachegrind.github.io/html/Home.html
테스트 프로그램 :
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;
}
용법:
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
이제 흥미로운 성능 데이터가 많이 포함 된 멋진 GUI 프로그램에 남아 있습니다.
오른쪽 하단에서 '콜 그래프'탭을 선택합니다. 여기에는 함수를 클릭 할 때 다른 창에있는 성능 메트릭과 관련된 대화 형 호출 그래프가 표시됩니다.
그래프를 내보내려면 마우스 오른쪽 버튼을 클릭하고 "그래프 내보내기"를 선택합니다. 내 보낸 PNG는 다음과 같습니다.
그로부터 우리는 다음을 볼 수 있습니다.
- 루트 노드는
_start
실제 ELF 진입 점이며 glibc 초기화 상용구를 포함합니다.
f0
, f1
그리고 f2
서로 예상대로라고
pointed
함수 포인터로 호출 했음에도 불구하고 표시됩니다. 명령 줄 인수를 전달했다면 호출되지 않았을 수 있습니다.
not_called
추가 명령 줄 인수를 전달하지 않았기 때문에 실행 중에 호출되지 않았기 때문에 표시되지 않습니다.
멋진 점은 valgrind
특별한 컴파일 옵션이 필요하지 않다는 것입니다.
따라서 소스 코드가 없어도 실행 파일 만 사용할 수 있습니다.
valgrind
경량 "가상 머신"을 통해 코드를 실행하여이를 관리합니다. 이로 인해 기본 실행에 비해 실행 속도가 매우 느립니다.
그래프에서 볼 수 있듯이 각 함수 호출에 대한 타이밍 정보도 얻을 수 있으며, 이는 호출 그래프를 보는 것뿐만 아니라이 설정의 원래 사용 사례 인 프로그램을 프로파일 링하는 데 사용할 수 있습니다. 프로파일 링 방법 Linux에서 실행되는 C ++ 코드?
Ubuntu 18.04에서 테스트되었습니다.
gcc -finstrument-functions
+ etrace
https://github.com/elcritch/etrace
-finstrument-functions
콜백을 추가 하고 etrace는 ELF 파일을 구문 분석하고 모든 콜백을 구현합니다.
하지만 안타깝게도 작동하지 못했습니다. 왜`-finstrument-functions`가 작동하지 않습니까?
청구 된 출력은 다음 형식입니다.
\-- 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
특정 하드웨어 추적 지원 외에 가장 효율적인 방법 일 수 있지만 코드를 다시 컴파일해야한다는 단점이 있습니다.