210121(목) 해결
https://askubuntu.com/questions/893922/ubuntu-16-04-gives-x-error-of-failed-request-badvalue-integer-parameter-out-o
위의 글에도 나와있듯이 나와 비슷한 상황인 사람이 있어서 살펴봤는데 cpu문제인 것 같다고 나와있어서 VM을 다시 받아야 하나 걱정했다. 하지만 저건 42에서 준 .ova 파일이여서 제발 그 문제는 아니라고 믿고 주석처리를 하면서 어디서 부터 오류가 걸렸는지 살펴보았다.
우선 .rt 파일의 내용을 읽어서 원하는 곳에 저장하고 있지 않았다ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ
이렇게 하면 되지 않을까? 라는 뇌피셜로 코드를 짜면서 돌려서 그런지 역시 허술한 면이 백만개다.
그래서 우선 진정하고 주석처리로 어디서 부터 잘못됐는지 살펴보았는데 파싱하는 부분에서 이상하게 받아들이고 있었다.
새벽까지 붙들고 해서 정신이 나가 있었는지 int랑 char를 잘못 써놔서 제대로 값이 저장이 안되어 있던 것이다! 이런 멍청이!
암튼 다행히
이렇게 R 600 400 이라고 적은 a.rt 파일을 읽어서 값을 저장했다.
그 다음 문제에 봉착했다. 이리저리 다른 자잘한 문제를 고친 후 창을 띄웠는데 다행히 창이 띄워졌다. 하지만 ESC를 누르는 순간 이러한 seg fault가 떴다.
이번 문제는 키 버튼을 조종하는 mlx_hook 문제라는 것을 바로 알고 확인해봤는데 왠지 모르겠지만, mlx_clear_window(...)
와 mlx_destroy(...)
함수 두개로 인해 생기는 버그였다. 그래서 왜 이러한 일이 일어날까 고민해보았다.
생각보다 쉬운 버그로 넘겨주는 타입이
t_MW *mw_data;
mlx_hook(mw_data->win, KeyPress, KeyPressMask, ft_key_press, &mw_data);
이거 였는데 &mw_data
로 넘겨줬으니 받았을 때 t_MW **mw_data
가 됐어야했는데 t_MW *mw_data
로 받고 있었다. 즉시 &mw_data
에서 mw_data
로 바꾸니 ESC를 눌러도 seg fault가 뜨지 않고, 원하는 해상도에 맞게 창이 뜨는 것을 확인 할 수 있었다!
Multiple definition of 함수 오류로 같은 헤더파일을 두번 include를 해서 생기는 오류라고 한다.
그래서 헤더파일을 살펴봤는데 별 이상이 없었다. 다른 곳에서 두 번 inlcude를 하는 것이 아니라서 대체 뭔가 하고 봤는데 makefile에서 같은 .c
파일을 2번 적어놨던 것이다! 이런이런...
여튼 바로 makefile에서 하나를 빼고 돌린 결과 잘 make되었다.
평면과 구체까지 만든 상태에서 메모리 릭 체크를 해서 얼마나 누수가 되는지 알아보자.
makefile에서 fsanitize=address를 이용하여 알아보았다!
171,120,874 바이트가 누수....? 다음에 알아보자.
진짜 망했다. 누수 체크를 하지 않고 만든게 죄지! 만드는게 문제가 아니라 관리하는게 문제라는 걸 다시 한번 깨달았다.
심지어 저 누수 바이트도 줄인거다 하핳하하하 처음엔 300,000,000대의 바이트가 누수되어 있었는데 코드를 해치지 않는 선에서 free()
한게 저정도였다... 시무룩...
그래서 오늘은 원래 사각형을 만들려고 했지만 어짜피 해야하는 메모리 누수 관리 오늘 끝내자는 생각으로 계속 줄여나갔다.
하하하 5시간 넘게 해서 드디어
330바이트까지 줄였다!! 근데 나머지를 어떻게 해야할지 모르겠다.. 계속 누수를 잡아봐야겠다.
참고
https://dojang.io/mod/page/view.php?id=511
https://dojang.io/mod/page/view.php?id=463
제일 이해가 안갔던 메모리 누수였다.
get_next_line에서 누수라니. '대체 왜? VM이 날 엿먹이려고 일부러 이런 건가?'라는 생각이 제일 먼저 들었다. 하지만 메모리 누수가 떳다는 것은 분명 문제가 있을 터. 그 문제를 고민해보았다.
그런데 잘 생각해보니 나는 동적할당을 받은 애들을 포인터 증가 연산으로 돌려가면서 .rt의 데이터를 저장하는 형태의 코드로 만들었다. 어라? 그럼 메모리 해제할 때 대체 어디를 가리키고 해제하는거지...?
free()함수를 사용할 때 그냥 변수명을 넣으면 해제되는게 아닌가? 하고 바로 visual studio를 깔고 메모리 누수 검사를 해보았다.
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__ )
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
char *p = (char *)malloc(sizeof(char) *10);
scanf("%s", p);
p++;
free(p);
return (0);
}
그결과,
해제가 안된다! 홀리몰리~ 🤣😂
그래서 자료를 찾아보다가 위의 참고에 나온 사이트에서 왜 그런지 알게 되었다. 사실 이미 알고 있었는데 자세히 생각도 안해보았고, 당연히 get_next_line은 메모리 누수가 안될 줄 알았다.
동적 메모리를 할당받은 포인터는 ++, -- 연산자로 포인터 연산을 하게 되면 포인터에 저장된 메모리 주소 자체가 바뀌게 되서 free 함수에서 메모리 주소가 바뀐 포인터로 메모리 해제를 하면 에러가 발생한다. 그래서 free가 반복문에서 안됐었다는 것을 깨달았다. (어쩐지 free가 안된다더니...)
그래서 처음 주소값을 저장한 후에 그 주소값을 해제해보자라는 생각으로 실험을 해보았다.
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__ )
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
char *p = (char *)malloc(sizeof(char) *10);
char *s;
scanf("%s", p);
s = p;
p++;
free(s);
return (0);
}
어머 세상에나, 메모리 누수가 안일어난다. 그러면 이것을 바로 miniRT코드에 적용시켰다.
get_next_line로 라인별로 받아와서 parsing하기로 했으니 parsing전에 line_p = line
이런 식으로 할당받은 메모리 첫 주소값을 가지고 있게 하고 line은 증가연산으로 .rt의 내용을 저장한다.
그리고 끝나면 free(line_p)
로 해제해본다. 그러고 나니
이제 그 많고 많은 메모리 누수에서 16바이트까지 잡았다.
이제 리스트와 리스트 안에 content로 동적할당 받아있는 정보를 해제할 차례이다.
int lst_free(t_list **head)
{
t_list *prev;
t_list *rm;
prev = NULL;
rm = *head;
while (rm)
{
prev = rm;
rm = rm->next;
free(prev);
}
*head = NULL;
return (0);
}
int cam_free(t_list *lst_cam)
{
t_camera *cam;
while (lst_cam->next)
{
cam = (t_camera *)(lst_cam->content);
free(cam);
lst_cam = lst_cam->next;
}
lst_free(&lst_cam);
return (0);
}
int li_free(t_list *lst_li)
{
t_light *li;
while (lst_li->next)
{
li = (t_light *)(lst_li->content);
free(li);
lst_li = lst_li->next;
}
lst_free(&lst_li);
return (0);
}
int sp_free(t_list *lst_sp)
{
t_sphere *sp;
while (lst_sp->next)
{
sp = (t_sphere *)(lst_sp->content);
free(sp);
lst_sp = lst_sp->next;
}
lst_free(&lst_sp);
return (0);
}
int pl_free(t_list *lst_pl)
{
t_plane *pl;
while (lst_pl->next)
{
pl = (t_plane *)(lst_pl->content);
free(pl);
lst_pl = lst_pl->next;
}
lst_free(&lst_pl);
return (0);
}
int rt_free(t_rt_info *info)
{
cam_free(info->camera);
li_free(info->light);
sp_free(info->sphere);
pl_free(info->plane);
return (0);
}
int lst_free(t_list **head)
는 연결 리스트들을 해제하는 함수이고, 각각의 free함수들은 content에 동적할당으로 있는 것들을 해제하는 함수이다.
그리고 이렇게 해제를 하니까 아래와 같이 깨끗하게 메모리 누수를 정리한 모습이다.
앞으로 만들 때 무조건 메모리 누수부터 잡으면서 해나가야겠다.