1223-TIL

그로밋·2023년 12월 26일
0

krafton jungle

목록 보기
51/58

Trouble Shooting

issue 1

annonymous 관련 코드 짠 후 돌려보니 돌리자마자 바로 커널 패닉🫠

밑에서 세번째 줄에 filesys/fsutil.c:160 in fsutil_get(): db: open failed

왜 저기서 터지는지 모르겠다.

debugging 해보고 싶은데 launch.json에 문제가 있다면서 안돌아간다. (내일 팀원한테 어떻게 하는지 물어봐야지)

할 수 있는 걸 해보자.

콜스택을 백트레이스 해봤다.

try #1

동료가 vm_get_frame()에서 frame에 NULL을 넣어줬던거를 말록으로 바꿨더니 문제가 해결됐다고 해서 나도 해보았다.

before

after

fsutil_get()에서 터지는 문제는 넘어가고 is_thread()에서 터지는 현상이 나타났다.

issue 2


try #1

현재 내 코드에 따르면 vm_claim_page가 성공하면 user_stack과는 상관없이 무조건 true 리턴하는 형식이라 setup_stack의 success와 vm_claim_page의 success를 분리시켜야 할 것 같다고 했다.

아래와 같이 바꿔봤다.

before

after

try #2

비록 결과는 똑같이 is_thread에서 커널 패닉이 나지만.. 유의미한 코드 수정이였다.

주석친 부분이 page_fault() 에서 project 2 작업할 때 넣었던 코드인데 이게 project 3에서 문제가 될 수 있을 것 같아서 주석처리를 해보았다.

static void
page_fault (struct intr_frame *f) {
	bool not_present;  /* True: not-present page, false: writing r/o page. */
	bool write;        /* True: access was write, false: access was read. */
	bool user;         /* True: access by user, false: access by kernel. */
	void *fault_addr;  /* Fault address. */

	/* Obtain faulting address, the virtual address that was
	   accessed to cause the fault.  It may point to code or to
	   data.  It is not necessarily the address of the instruction
	   that caused the fault (that's f->rip). */

	fault_addr = (void *) rcr2();

	/* Turn interrupts back on (they were only off so that we could
	   be assured of reading CR2 before it changed). */
	intr_enable ();


	/* Determine cause. */
	not_present = (f->error_code & PF_P) == 0;
	write = (f->error_code & PF_W) != 0;
	user = (f->error_code & PF_U) != 0;

	// int PHYS_BASE = 0x80040000;
	// int VIRTUAL_BASE = 0x00000000;
	// // printf("page fault at %p\n", fault_addr);
	// /* Handle kernel-mode page faults. */
	// if (!user) {
	// 	// printf("not user\n");
	// 	if (is_kernel_vaddr(fault_addr)) {
	// 		// printf("is kernel vaddr\n");
	// 		if (fault_addr >= PHYS_BASE) {
	// 			// printf("fault addr >= phys base\n");
	// 			exit(-1);
	// 		}
	// 		else {
	// 			// printf("fault addr < phys base\n");
	// 		}

	// 	}

	// 	else {
	// 		// printf("not kernel vaddr\n");
	// 		exit(-1);
	// 	}
	// }

	// /* Handle user-mode page faults. */
	// else {
	// 	// printf("user\n");
	// 	if (is_kernel_vaddr(fault_addr)) {
	// 		// printf("is kernel vaddr\n");
	// 		exit(-1);
	// 	}
	// 	else {
	// 		// printf("not kernel vaddr\n");
	// 		if (fault_addr <= VIRTUAL_BASE) {
	// 			// printf("fault addr <= virtual base\n");
	// 			exit(-1);
	// 		} else {
	// 			// printf("fault addr > virtual base\n");
	// 		}
	// 	}
	// }

#ifdef VM
	/* For project 3 and later. */
	if (vm_try_handle_fault (f, fault_addr, user, write, not_present))
		return;
#endif

	/* Count page faults. */
	page_fault_cnt++;

	/* If the fault is true fault, show info and exit. */
	printf ("Page fault at %p: %s error %s page in %s context.\n",
			fault_addr,
			not_present ? "not present" : "rights violation",
			write ? "writing" : "reading",
			user ? "user" : "kernel");
	kill (f);
}

오! 주석처리 하니 일단 is_thread()에서 터지는 문제는 넘어가서 해당 코드는 삭제해두었다.

issue 3

이제는 page_fault발생하고 나서 exception.c의 kill()에서 문제가 터진다.

Kernel PANIC at ../../userprog/exception.c:97 in kill(): Kernel bug - unexpected interrupt in kernel

try #1

  • va를 매개변수로 받아서 그걸로 page find 해야하는데 여기서 넘어오는 va가 값이 page단위가 아니라 페이지 중간 값일 수 있어서 페이지 단위로 라운드 해줘야 찾을 수 있기 때문에 pg_round_down()함수로 주소를 맞춰줬다.

  • page에 palloc을 해주는 이유를 모르겠어서 뺐다.

  • page에 NULL을 넣어주고 시작했는데 NULL을 넣어줄 필요성을 못느껴 뺐다.

before

after

결과는 에러가 해결되지 않고 똑같은 모습이였다.

에러를 다시 보았다. exception.c의 에러가 나는 곳으로 이동하니 이러한 주석이 쓰여있다.

Kernel's code segment, which indicates a kernel bug.
Kernel code shouldn't throw exceptions. (Page faults
may cause kernel exceptions--but they shouldn't arrive
here.) Panic the kernel to make the point.
커널 코드는 예외를 던져서는 안된다. 페이지 폴트는 커널 익셉션을 야기할 수 있다. 하지만 여기에 와서는 안된다.

try #2

하..vm_alloc_page_with_initializer()의 함수를 찬찬히 뜯어보다가 if문 괄호가 닫히기 전에 err: return false;를 하고있는 모습을 보고 띠용했다. 보는 순간 직감한다. 이것이 이 문제의 근원이라는 것을...
돌려보니 역시나 커널패닉이 드디어 해결되었다.

before

after

result

그렇지만 테스트케이스는 FAIL 이었다.

issue 4

  • spt_find_page()에서 hash_find()하고 free 안해주고 있어서 free 해줬다

try #1

  • spt_insert_page() 에서 insert하기 전에 넣으려 하는 가상주소가 존재하지 않는지 검사하는 코드가 없어서 추가하고 succ 를 리턴하는 형식으로 바꿨다.

원래 return hash_insert(&spt, &page->hash_elem) == NULL ? true : false; 이렇게 리턴해줬다가 위처럼 바꿨더니 다시 커널 패닉이 exception.c kill()에서 난다🫠 동료에게 물어봤더니 spt에 존재하는지 체크 예외처리를 해주지 않아도 괜찮다고 해서 다시 돌려놨다.

a few fixes #1

음 이건 해당 에러를 고치기 위해서가 아니라 코드를 보다 고쳐 놓아야 할듯 해서 고쳐놓는다.

syscall.c 의 check_address()에서 lazy loading 으로 바뀌었기 때문에 pml4에서 보이지 않더라도 정상적인 상황일 수 있기 떄문에 예외처리를 살짝 수정해 주어야 한다.

void check_address(void *addr) {
    struct thread *t = thread_current();
    
	/*  이전 코드
	if (!is_user_vaddr(addr) || addr == NULL || pml4_get_page(t->pml4 , addr)
    == NULL) { 	exit(-1);
    }
	*/

    if (!is_user_vaddr(addr) || addr == NULL) {
        exit(-1);
    }

    if (pml4_get_page(t->pml4, addr) == NULL) {
        // pml4에 없고, spt에 있으면 정상적인 lazy loading 상황 -> 에러 아님!
        if (!spt_find_page(&t->spt, addr)) {
            exit(-1);
        }
    }
}

a few fixes #2

spt_insert_page(spt, p); 이렇게 해주고 있었다.
hashmap에 넣어줘야 하는게 맞기에 find하는 함수들과 insert하는 함수들의 spt를 &spt->spt_hashmap 으로 수정.

이쯤에서 다시 원인분석

현재 상황은 테케를 돌리면 이렇게 뜨고,

  • 지금 나는 exec도 못들어가고 있는 상황.

  • init.c 의 main 함수를 돌고 나서 child process인 test가 돌아가는 건데 나는 main함수도 못돌고 있다.

  • run_actions 에서 죽음.

  • 여기서 죽음

롸?

profile
Work as though your strength were limitless. <S. Bernhardt>

0개의 댓글