우선, 이것이 절대적인 답을 가진 완벽한 Q & A 스타일의 질문이 아니라는 것을 알고 있지만 더 잘 작동하는 표현을 생각할 수는 없습니다. 나는 이것에 대한 절대적인 해결책이 없다고 생각하며 이것이 Stack Overflow 대신 여기에 게시하는 이유 중 하나입니다.
지난 달 동안 나는 더 현대적이고 확장하기 쉽고 수정하기 위해 상당히 오래된 서버 코드 (mmorpg)를 다시 작성했습니다. 나는 네트워크 부분으로 시작하여 나를 위해 물건을 처리하기 위해 타사 라이브러리 (libevent)를 구현했습니다. 모든 리팩토링 및 코드 변경으로 어딘가에 메모리 손상이 발생했으며 어디에서 발생하는지 찾기 위해 고심하고 있습니다.
일부 봇을 시뮬레이션하기 위해 기본 봇을 구현할 때도 더 이상 충돌이 발생하지 않습니다 (일부 문제를 일으키는 libevent 문제를 수정했습니다).
나는 지금까지 시도했다 :
지옥에서 벗어나기-실제로 충돌 할 때까지는 잘못된 쓰기가 없습니다 (생산에서 1 + 하루가 걸리거나 1 시간이 걸릴 수 있습니다). 기회? (주소 범위를 "확산"하는 방법이 있습니까?)
코드 분석 도구, 즉 적용 범위 및 cppcheck 그들이 코드에서 지적하고 혐의와 우연한 사례를 지적하는 동안 심각한 것은 없었습니다.
undodb를 통해 gdb와 충돌 할 때까지 프로세스를 기록 한 다음 거꾸로 작동합니다. 이 / sounds /는 할 수 있어야하지만 자동 완성 기능을 사용하여 gdb를 충돌 시키거나 가능한 많은 분기가 있기 때문에 손실되는 내부 libevent 구조로 이어집니다 (하나의 손상으로 인해 다른 것이 발생하므로 에). 포인터가 원래 할당 된 위치 / 할당 된 위치를 볼 수 있다면 대부분의 분기 문제를 제거 할 수 있다면 좋을 것 같습니다. 나는 undodb로 valgrind를 실행할 수 없으며 정상적인 gdb 레코드는 사용할 수 없을 정도로 느립니다 (valgrind와 결합하여 작동하는 경우).
코드 검토! 나 자신에 의해 (완전히) 친구들이 내 코드를 살펴 보도록했지만, 그것이 충분하다는 것은 의심 스럽다. 나는 코드 검토 / 디버깅을하기 위해 개발자를 고용하려고 생각했지만 너무 많은 돈을 넣을 여유가 없으며 작은 일을 할 사람을 찾을 곳을 모를 것입니다. 그가 문제를 찾지 못하거나 자격을 갖춘 사람이 없다면 돈을 들이지 않아도됩니다.
나는 또한주의해야한다 : 나는 보통 일관된 역 추적을 얻는다. 크래시가 발생하는 곳은 몇 가지가 있는데, 대부분 소켓 클래스가 어떻게 든 손상되는 것과 관련이 있습니다. 소켓이 아닌 것을 가리키는 잘못된 포인터이거나 소켓 클래스 자체가 횡설수설로 덮어 씌워집니다 (부분적으로?). 가장 많이 사용되는 부분 중 하나이기 때문에 가장 많이 충돌하는 것으로 생각되지만 사용되는 첫 번째 손상된 메모리입니다.
이 문제는 모두 거의 2 개월 동안 (바람직한 취미 프로젝트로) 바빠서 내가 심술 I은 IRL이되어 포기하는 것에 대해 정말 실망하고 있습니다. 나는 그 문제를 찾기 위해 내가 무엇을해야한다고 생각할 수 없다.
내가 놓친 유용한 기술이 있습니까? 어떻게 처리합니까? (이것에 대한 정보가 많지 않기 때문에 일반적이지 않을 수 있습니다 .. 또는 나는 정말 장님입니까?)
편집하다:
중요한 경우 일부 사양 :
gcc 4.7을 통한 C ++ (11) 사용 (데비안 Wheezy에서 제공하는 버전)
코드베이스는 약 150k 라인입니다
david.pfx post에 대한 응답으로 편집 : (느린 응답에 대해 죄송합니다)
패턴을 찾기 위해 충돌에 대한주의 깊은 기록을 유지하고 있습니까?
예, 여전히 최근에 발생한 충돌에 대한 덤프가 있습니다.
몇 곳이 정말 비슷합니까? 어떤 방법으로?
글쎄, 가장 최신 버전 (코드를 추가 / 제거하거나 관련 구조를 변경할 때마다 변경되는 것처럼 보입니다)은 항상 항목 타이머 방법에 잡힐 것입니다. 기본적으로 항목은 특정 시간이 지난 후 만료되며 업데이트 된 정보를 클라이언트에 보냅니다. 유효하지 않은 소켓 포인터는 플레이어 클래스에 있습니다 (주로 말할 수있는 한 유효합니다). 또한 정상적인 종료 후 명시 적으로 파괴되지 않은 모든 정적 클래스 ( __run_exit_handlers
백 트레이스에서)를 파괴하는 정리 단계에서 많은 충돌이 발생합니다 . 대부분 std::map
하나의 클래스를 포함 하며, 그것이 첫 번째로 올 것이라고 추측합니다.
손상된 데이터는 어떻게 생겼습니까? 제로? 아스키? 패턴?
나는 아직 패턴을 찾지 못했습니다. 부패가 시작된 곳을 모르기 때문에 말하기가 어렵습니다.
힙 관련입니까?
그것은 전적으로 힙 관련입니다 (gcc의 스택 가드를 활성화했으며 아무것도 잡지 못했습니다).
부패 이후에 부패가 발생합니까
free()
?
당신은 그것에 대해 조금 더 자세히 설명해야 할 것입니다. 이미 자유로 워진 물건에 대한 포인터를 가지고 있다는 의미입니까? 일단 객체가 파괴되면 모든 참조를 null로 설정합니다. 그것은 valgrind에 나타나야하지만 그렇지 않았습니다.
네트워크 트래픽 (버퍼 크기, 복구주기)에 특별한 것이 있습니까?
네트워크 트래픽은 원시 데이터로 구성됩니다. 따라서 (u) intX_t 또는 압축 (패딩을 제거하기 위해) char 배열은보다 복잡한 것들을 위해 구조화됩니다. 각 패킷에는 예상 크기에 대해 유효성이 검사되는 id와 패킷 크기 자체로 구성된 헤더가 있습니다. 그것들은 크기가 몇 Mb 인 가장 큰 (내부 '부팅'패킷, 시작시 한 번 발생) 10-60 바이트 정도입니다.
많은 생산 주장. 피해가 전파되기 전에 조기에 예상대로 충돌합니다.
한때 std::map
부패 와 관련된 충돌이 있었으며 , 각 엔터티에는 "보기"맵이 있습니다. 각 엔터티는이를 볼 수 있으며 그 반대도 마찬가지입니다. 나는 앞뒤에 200 바이트 버퍼를 추가하고 0x33으로 채우고 각 액세스 전에 확인했습니다. 부패가 마술처럼 사라졌고, 나는 무언가를 움직여서 다른 것을 부패시켰다.
전략적 로깅을 통해 이전에 발생한 상황을 정확하게 알 수 있습니다. 답변에 가까워 질수록 로깅에 추가하십시오.
그것은 .. 연장합니다.
필사적으로 상태를 저장하고 자동 다시 시작할 수 있습니까? 그렇게하는 몇 가지 프로덕션 소프트웨어를 생각할 수 있습니다.
나는 다소 그렇게한다. 이 소프트웨어는 기본 "캐시"프로세스와 캐시에 액세스하여 물건을 가져오고 저장하는 다른 작업자 프로세스로 구성됩니다. 따라서 충돌 당별로 많은 진전을 잃지 않고 여전히 모든 사용자를 연결 해제하는 등 솔루션이 아닙니다.
동시성 : 스레딩, 경쟁 조건 등
"비동기"쿼리를 수행하는 mysql 스레드가 있습니다. 이는 모두 손대지 않고 모든 잠금 기능을 통해 데이터베이스 클래스와 정보를 공유합니다.
인터럽트
30 초 동안주기를 완료하지 않으면 중단되는 중단 타이머를 막을 수 있습니다.이 코드는 안전해야합니다.
if (!tics) {
abort();
} else
tics = 0;
틱은 volatile int tics = 0;
사이클이 완료 될 때마다 증가합니다. 오래된 코드도 있습니다.
이벤트 / 콜백 / 예외 : 상태 또는 스택 손상이 예기치 않게
많은 콜백 (비동기 네트워크 I / O, 타이머)이 사용되고 있지만 나쁜 일을해서는 안됩니다.
비정상적인 데이터 : 비정상적인 입력 데이터 / 타이밍 / 상태
나는 그와 관련된 몇 가지 우연한 사례가있었습니다. 패킷이 처리되는 동안 소켓을 연결 해제하면 nullptr 등에 액세스 할 수 있었지만 클래스 자체에 완료된 후에 모든 참조가 정리되므로 지금까지 쉽게 파악할 수있었습니다. (파괴 자체는 매 사이클마다 파괴 된 객체를 모두 삭제하는 루프에 의해 처리됩니다)
비동기 외부 프로세스에 대한 종속성
정교하게 관리? 위에서 언급 한 캐시 프로세스와 다소 차이가 있습니다. 내 머리 꼭대기에서 상상할 수있는 유일한 것은 충분히 빨리 끝내지 않고 가비지 데이터를 사용하는 것입니다.하지만 네트워크를 사용하고 있기 때문에 그렇지 않습니다. 동일한 패킷 모델.
/analyze
) 및 Apple의 Malloc 및 Scribble 가드도 추가하십시오. 컴파일러 경고는 진단적이고 시간이 지남에 따라 더 좋아지기 때문에 가능한 한 많은 표준을 사용하여 가능한 많은 컴파일러를 사용해야합니다. 은 총알이 없으며 하나의 크기가 모든 것에 맞지는 않습니다. 툴과 컴파일러를 많이 사용할수록 각 툴의 장단점이 있으므로 적용 범위가 더 넓어집니다.