2013-05-16 5 views
0

큰 프로젝트에서 몇 달 동안 tcmalloc을 사용했으며 지금까지는 메모리 누출을 추적하고 제거 할 수있는 HeapProfiling 기능에 대해 매우 만족한다고 말해야합니다. 그들.tcmalloc에서 예기치 않은 동작이 발생했습니다.

지난 몇 주 동안 우리는 응용 프로그램에서 임의의 충돌을 경험했지만 무작위 충돌의 원인을 찾을 수 없었습니다. 매우 특별한 상황에서 응용 프로그램이 충돌했을 때 응용 프로그램 스레드 중 하나에 대해 완전히 손상된 스택을 발견했습니다. 여러 번 대신 스레드가 tcmalloc :: PageHeap :: AllocLarge()에 갇혀 있다는 것을 알았지 만, tcmalloc의 디버그 기호가 링크되어 있지 않아 문제가 무엇인지 이해할 수 없었습니다.

조사 일주일이 지난 오늘, 나는 가장 간단한 것을 시도해 보았습니다. 사용을 피하기 위해 연결에서 tcmalloc을 제거하여 무슨 일이 일어 났는지 보았습니다. 음 ... 드디어 문제가 무엇인지 발견하고, 문제가되는 코드는 다음과 매우 같습니다

void AllocatingFunction() 
    { 
     Object object_on_stack; 
     ProcessObject(&object_on_stack); 

    } 

    void ProcessObject(Object* object) 
    { 
     ... 
     // Do Whatever 
     ... 
     delete object; 
    } 

응용 프로그램이 여전히 충돌하지만 결국 내가 있었다 개체에 대한 삭제를 호출 것을보고 libc의 사용 스택에 할당됩니다.

여전히 알아낼 수없는 것은 tcmalloc이 매우 위험한 (완전히 잘못된 것은 아니지만) 객체 할당 해제 및 AllocatingFunction이 끝날 때 object_on_stack이 범위를 벗어날 때의 이중 할당 해제와 상관없이 응용 프로그램을 계속 실행하는 이유입니다. 사실은 기분 나쁜 코드가 근본적인 가증함을 암시하지 않고 반복적으로 호출 될 수 있다는 것입니다.

나는 메모리 할당 해제가 제대로 사용되지 않을 때 "정의되지 않은 동작"중 하나라는 것을 알고 있지만 놀랍게도 "표준"libc와 tcmalloc 사이에는 이와 같은 다른 동작이 있습니다.

tcmalloc이 응용 프로그램을 계속 실행하는 이유에 대해 통찰력에 대한 설명이 있습니까? 사전 :

에서

덕분에

+0

그들은 "정의되지 않은 동작"을 선택하여 * 크래시 *하지 않았습니다. 나는 이것에 의지 할 것이냐? * 지옥 안돼. – WhozCraig

답변

2

매우 위험 (그렇지 않으면 완전히 잘못된) 객체 해제

아니라, 내가 여기에 동의, 그것은 가 완전히입니다 좋은 하루를 되세요 틀렸어. UB를 호출 한 이후로 아무 일도 일어나지 않을거야.

tallococ 코드가 deallocation에서 acutally 수행하는 작업과 해당 위치에서 스택 주변의 (가비지) 데이터를 사용하는 방법에 따라 달라집니다.

나는 그런 경우에도 tcmalloc 충돌을 보았고 glibc는 무한 루프로 들어갔다. 당신이 보는 것은 우연의 일치입니다.

+0

음 ... 나는 프로그래머가 성취하려고하는 어떤 종류의 더러운 트릭을 결코 알지 못하기 때문에 감히 "완전히 잘못"이라고 부르지 않았다. 어쩌면 세계 곳곳에 그런 일을해야한다고 주장하는 사람들이있을 것입니다. 필자의 경우에는 완전히 틀렸고 문제가되는 코드를 수정하면 모든 것이 수정되었습니다. 여전히 7 일간 무의미한 조사를 통해 저와 다른 사람들을 데려갔습니다 .- ' – BaroneAshura

+0

@BaroneAshura : 그런 경우조차도 개인적으로 완전히 잘못이라고 생각합니다. Btw. valgrind를 통해 코드를 실행하면 그 사실을 알 수 있습니다. – PlasmaHH

+0

이것은 몇 개월 전에 메모리 누수에 대한 사냥을 할 때 선택 사항이었습니다 ... 불행히도 우리는 valgrind 환경에서 응용 프로그램을 성공적으로 실행할 수 없었습니다. (어쨌든, 나는 "완전히 잘못"이라고 생각합니다. 나는 누군가가 나를 "전혀 틀린"것이 아니라고 말하고 싶지 않았다. P – BaroneAshura

0

첫 번째로, 귀하의 경우에는 이중 free가 없습니다. object_on_stack이 범위를 벗어날 때 free 호출이 없으면 스택 포인터가 줄어 듭니다 (또는 스택이 작아 질수록 증가합니다).

둘째, 삭제하는 동안 TcMalloc은 스택의 주소가 프로그램 힙에 속하지 않음을 인식 할 수 있어야합니다. 충돌 invalid_free_fn하는

const PageID p = reinterpret_cast<uintptr_t>(ptr) >> kPageShift; 
Span* span = NULL; 
size_t cl = Static::pageheap()->GetSizeClassIfCached(p); 

if (cl == 0) { 
    span = Static::pageheap()->GetDescriptor(p); 
    if (!span) { 
     // span can be NULL because the pointer passed in is invalid 
     // (not something returned by malloc or friends), or because the 
     // pointer was allocated with some other allocator besides 
     // tcmalloc. The latter can happen if tcmalloc is linked in via 
     // a dynamic library, but is not listed last on the link line. 
     // In that case, libraries after it on the link line will 
     // allocate with libc malloc, but free with tcmalloc's free. 
     (*invalid_free_fn)(ptr); // Decide how to handle the bad free request 
     return; 
    } 

전화 : 여기 free(ptr) 구현의 일부입니다.

관련 문제