valgrind를 사용하여 프로그램에서 메모리 누수를 찾으려면 어떻게합니까?
누군가 나를 도와주고 절차를 수행하는 단계를 설명해 주시겠습니까?
우분투 10.04를 사용하고 있으며 프로그램이 a.c
있습니다. 도와주세요.
valgrind를 사용하여 프로그램에서 메모리 누수를 찾으려면 어떻게합니까?
누군가 나를 도와주고 절차를 수행하는 단계를 설명해 주시겠습니까?
우분투 10.04를 사용하고 있으며 프로그램이 a.c
있습니다. 도와주세요.
답변:
OP를 모욕하지 말고이 질문을 받고 Linux 를 처음 접하는 사람들을 위해 Valgrind를 설치 해야합니다.
sudo apt install valgrind # Ubuntu, Debian, etc.
sudo yum install valgrind # RHEL, CentOS, Fedora, etc.
Valgrind의는 C / C ++ 코드를 쉽게 사용할 수 있지만 (참조 제대로 구성된 경우에도 다른 언어에 사용할 수있는 이 파이썬을).
Valgrind를 실행하려면 실행 파일을 매개 변수와 함께 인수로 프로그램에 전달하십시오.
valgrind --leak-check=full \
--show-leak-kinds=all \
--track-origins=yes \
--verbose \
--log-file=valgrind-out.txt \
./executable exampleParam1
간단히 말해서 플래그는 다음과 같습니다.
--leak-check=full
: "각 개별 누출이 자세히 표시됩니다"--show-leak-kinds=all
: "전체"보고서에 "확정, 간접, 가능, 도달 가능한"누출 유형을 모두 표시하십시오.--track-origins=yes
: 속도보다 유용한 출력을 선호합니다. 초기화되지 않은 값의 출처를 추적하여 메모리 오류에 매우 유용 할 수 있습니다. Valgrind가 너무 느리면 끄는 것을 고려하십시오.--verbose
: 프로그램의 비정상적인 동작에 대해 알려줄 수 있습니다. 더 자세한 정보를 얻으려면 반복하십시오.--log-file
: 파일에 씁니다. 출력이 터미널 공간을 초과 할 때 유용합니다.마지막으로 다음과 같은 Valgrind 보고서를 보려고합니다.
HEAP SUMMARY:
in use at exit: 0 bytes in 0 blocks
total heap usage: 636 allocs, 636 frees, 25,393 bytes allocated
All heap blocks were freed -- no leaks are possible
ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
따라서 메모리 누수가 있고 Valgrind는 의미있는 말을하지 않습니다. 아마도 다음과 같은 것이 있습니다.
5 bytes in 1 blocks are definitely lost in loss record 1 of 1
at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
by 0x40053E: main (in /home/Peri461/Documents/executable)
내가 쓴 C 코드도 살펴 보자.
#include <stdlib.h>
int main() {
char* string = malloc(5 * sizeof(char)); //LEAK: not freed!
return 0;
}
글쎄, 5 바이트가 손실되었습니다. 어떻게이 일이 일어 났어요? 오류 보고서는 말한다
main
와 malloc
. 더 큰 프로그램에서는 사냥하기가 매우 까다로울 수 있습니다. 실행 파일이 컴파일 된 방식 때문입니다 . 실제로 무엇이 잘못되었는지에 대한 세부적인 정보를 얻을 수 있습니다. 디버그 플래그로 프로그램을 다시 컴파일하십시오 ( gcc
여기에서 사용하고 있습니다).
gcc -o executable -std=c11 -Wall main.c # suppose it was this at first
gcc -o executable -std=c11 -Wall -ggdb3 main.c # add -ggdb3 to it
이제이 디버그 빌드를 통해 Valgrind 는 누출 된 메모리를 할당하는 정확한 코드 줄을 가리 킵니다 . (문구는 중요하다 : 당신의 누출이 어디 있는지 정확히하지 않을 수도 있습니다,하지만 무슨 일이 유출있어 추적 당신이 찾는 데 도움이됩니다. 여기서 .)
5 bytes in 1 blocks are definitely lost in loss record 1 of 1
at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
by 0x40053E: main (main.c:4)
IndexOutOfBoundsException
유형 문제입니다.때로는 닫는 괄호를 입력하지 않았 음을 발견 한 IDE와 마찬가지로 누출 / 오류가 서로 연결될 수 있습니다. 한 가지 문제를 해결하면 다른 문제를 해결할 수 있으므로 좋은 범인이되는 문제를 찾아 다음 아이디어 중 일부를 적용하십시오.
gdb
아마도)을 따르고 전제 조건 / 후 조건 오류를 찾으십시오. 할당 된 메모리 수명에 중점을 두면서 프로그램 실행을 추적하는 것이 좋습니다.60 bytes in 1 blocks are definitely lost in loss record 1 of 1
at 0x4C2BB78: realloc (vg_replace_malloc.c:785)
by 0x4005E4: resizeArray (main.c:12)
by 0x40062E: main (main.c:19)
그리고 코드 :
#include <stdlib.h>
#include <stdint.h>
struct _List {
int32_t* data;
int32_t length;
};
typedef struct _List List;
List* resizeArray(List* array) {
int32_t* dPtr = array->data;
dPtr = realloc(dPtr, 15 * sizeof(int32_t)); //doesn't update array->data
return array;
}
int main() {
List* array = calloc(1, sizeof(List));
array->data = calloc(10, sizeof(int32_t));
array = resizeArray(array);
free(array->data);
free(array);
return 0;
}
교수 조교로서 나는이 실수를 자주 보았다. 학생은 지역 변수를 사용하고 원래 포인터를 업데이트하는 것을 잊습니다. 여기서 오류 realloc
는 실제로 할당 된 메모리를 다른 곳으로 이동하고 포인터의 위치를 변경할 수 있다는 것을 알 수 있습니다. 그런 다음 어레이가 어디로 옮겼는지 알려주지 resizeArray
않습니다
array->data
.
1 errors in context 1 of 1:
Invalid write of size 1
at 0x4005CA: main (main.c:10)
Address 0x51f905a is 0 bytes after a block of size 26 alloc'd
at 0x4C2B975: calloc (vg_replace_malloc.c:711)
by 0x400593: main (main.c:5)
그리고 코드 :
#include <stdlib.h>
#include <stdint.h>
int main() {
char* alphabet = calloc(26, sizeof(char));
for(uint8_t i = 0; i < 26; i++) {
*(alphabet + i) = 'A' + i;
}
*(alphabet + 26) = '\0'; //null-terminate the string?
free(alphabet);
return 0;
}
Valgrind는 위의 주석 처리 된 코드 라인을 나타냅니다. 크기 26의 배열은 색인화되어 [0,25] *(alphabet + 26)
유효하지 않은 쓰기입니다. 범위를 벗어났습니다. 유효하지 않은 쓰기는 일대일 오류의 일반적인 결과입니다. 할당 작업의 왼쪽을보십시오.
1 errors in context 1 of 1:
Invalid read of size 1
at 0x400602: main (main.c:9)
Address 0x51f90ba is 0 bytes after a block of size 26 alloc'd
at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
by 0x4005E1: main (main.c:6)
그리고 코드 :
#include <stdlib.h>
#include <stdint.h>
int main() {
char* destination = calloc(27, sizeof(char));
char* source = malloc(26 * sizeof(char));
for(uint8_t i = 0; i < 27; i++) {
*(destination + i) = *(source + i); //Look at the last iteration.
}
free(destination);
free(source);
return 0;
}
Valgrind는 위의 주석 처리 된 줄을 가리 킵니다. 마지막 반복을 살펴보십시오
*(destination + 26) = *(source + 26);
. 그러나 *(source + 26)
유효하지 않은 쓰기와 유사하게 다시 범위를 벗어났습니다. 유효하지 않은 읽기는 일대일 오류의 일반적인 결과이기도합니다. 할당 작업의 오른쪽을보십시오.
누출이 언제 발생하는지 어떻게 알 수 있습니까? 다른 사람의 코드를 사용할 때 누수를 어떻게 찾습니까? 나는 내 것이 아닌 누출을 발견했다. 내가 뭔가를해야합니까? 모두 합법적 인 질문입니다. 첫째, 2 가지 클래스의 일반적인 만남을 보여주는 실제 사례 2 개.
#include <jansson.h>
#include <stdio.h>
int main() {
char* string = "{ \"key\": \"value\" }";
json_error_t error;
json_t* root = json_loads(string, 0, &error); //obtaining a pointer
json_t* value = json_object_get(root, "key"); //obtaining a pointer
printf("\"%s\" is the value field.\n", json_string_value(value)); //use value
json_decref(value); //Do I free this pointer?
json_decref(root); //What about this one? Does the order matter?
return 0;
}
이것은 간단한 프로그램입니다. JSON 문자열을 읽고 구문 분석합니다. 만드는 과정에서 라이브러리 호출을 사용하여 구문 분석을 수행합니다. JSON은 자체적으로 중첩 된 구조를 포함 할 수 있으므로 Jansson은 필요한 할당을 동적으로 수행합니다. 그러나 이것이 decref
우리가 모든 기능에서 우리에게 주어진 메모리를 "사용"하거나 의미하지는 않습니다 . 사실, 위에서 작성한이 코드는 "잘못된 읽기"와 "잘못된 쓰기"를 모두 발생시킵니다. 에 대한 decref
라인을 꺼내면 이러한 오류가 사라집니다 value
.
왜? 이 변수 value
는 Jansson API에서 "빌려온 참조"로 간주됩니다. Jansson은 메모리를 추적하므로 decref
JSON 구조를 서로 독립적으로 유지 해야합니다 . 레슨 :
문서를 읽으십시오 . 정말. 때로는 이해하기 어렵지만 왜 이런 일이 발생하는지 알려줍니다. 대신
이 메모리 오류에 대한 기존 질문 이 있습니다.
#include "SDL2/SDL.h"
int main(int argc, char* argv[]) {
if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) != 0) {
SDL_Log("Unable to initialize SDL: %s", SDL_GetError());
return 1;
}
SDL_Quit();
return 0;
}
이 코드에 어떤 문제가 있습니까? 그것은 ~ 212 KiB의 메모리를 지속적으로 누출시킵니다. 그것에 대해 잠시 생각하십시오. 우리는 SDL을 켜고 끈다. 대답? 아무 문제가 없다.
처음에는 기괴한 소리가납니다 . 진실은 그래픽이 지저분하고 때로는 표준 라이브러리의 일부로 누출을 받아 들여야한다는 것입니다. 여기서 교훈 : 모든 메모리 누수를 멈출 필요는 없습니다 . 누수 에 대해서는 아무것도 할 수없는 알려진 문제이기 때문에 누수 를 억제 해야하는 경우가 있습니다 . (이것은 당신의 누출을 무시하는 나의 허락이 아닙니다!)
누출이 언제 발생하는지 어떻게 알 수 있습니까?
그것은. (어쨌든 99 % 확실)
다른 사람의 코드를 사용할 때 누수를 어떻게 찾습니까?
다른 사람이 이미 찾았을 가능성이 있습니다. 구글을보십시오! 그래도 실패하면 위에서 설명한 기술을 사용하십시오. 이것이 실패하고 대부분 API 호출과 자신의 스택 추적이 거의 없다면 다음 질문을 참조하십시오.
나는 내 것이 아닌 누출을 발견했다. 내가 뭔가를해야합니까?
예! 대부분의 API에는 버그와 문제를보고하는 방법이 있습니다. 그것을 써! 프로젝트에서 사용중인 도구를 돌려주십시오!
이렇게 오래있어 주셔서 감사합니다. 이 답변에 도달하는 광범위한 사람들을 돌 보려고 노력하면서 무언가를 배웠기를 바랍니다. C의 메모리 할당자가 어떻게 작동합니까? 실제로 메모리 누수와 메모리 오류는 무엇입니까? 그것들은 segfault와 어떻게 다릅니 까? Valgrind는 어떻게 작동합니까? 당신이 이것들 중 하나를 가지고 있다면, 당신의 호기심을 먹이십시오 :
memcheck
도구는 기본적으로 활성화되어 있습니다?
memcheck
기본 도구 임을 알려줍니다 .--tool=<toolname> [default: memcheck]
이 시도:
valgrind --leak-check=full -v ./your_program
valgrind가 설치되어 있으면 프로그램을 통해 무엇이 잘못되었는지 알려줍니다. 누출이 발견 될 수있는 대략적인 장소와 포인터를 제공 할 수 있습니다. segfault'ing하는 경우을 통해 실행하십시오 gdb
.
your_program
== 실행 파일 이름 또는 응용 프로그램을 실행하는 데 사용하는 명령