2010-12-22 1 views
3

을 호출 할 때마다 컴파일러에서 추가 한 코드의 일부에서 예외가 발생했습니다. 그것은 표준 C++ 새로운입니다.이 힙에서 일부 메모리를 가져 와서 클래스의 생성자를 호출해야합니다.new (heap)을 호출 한 후 예기치 않은 어셈블러 코드에 의해 예외가 throw되었습니다.

우리는 SH4 프로세서에서 GCC 2.95 (또는 확실하지 않은 2.96)로 VxWorks 5.5.1을 실행하고 있습니다. SNiFF + 4.1 패치 1 내에 컴파일되었습니다.

C++ 코드는 다음과 같습니다.

CBlocksFile* pBlockFile = new CBlocksFile(szHeaderFile, szDataFile); 

그리고 생성 된 어셈블러 코드는 가/삭제/종료 새로운에 대한 호출 후 처리를 던져 있습니다. 이 패턴은 에 대한 모든 호출에 적용되는 것으로 보입니다.

// call to "new" 
c4d6000 d14d  mov.l  @(0x134,pc),r1 (= 0x0c06c1e0 = ___builtin_new) 
c4d6002 410b  jsr  @r1 
c4d6004 e414  (mov  #20,r4) 
... 
// compiler generates throw path address 
c4d6022 d246  mov.l  @(0x118,pc),r2 (= 0x0c4d6034) 
... 
// and pushes it to the stack 
c4d602c 1121  mov.l  r2,@(4,r1) 
... 
c4d6030 a002  bra  +4  (==> 0x0c4d6038 : GOOD_PATH) 
... 
// throw path (there is no visible jump to this address) 
c4d6034 a088  bra  +272  (==> 0x0c4d6148 : THROW_PATH) 
... 
GOOD_PATH: 
... 
// call to constructor 
c4d6058 d139  mov.l  @(0xe4,pc),r1 (= 0x0c4d1730 T ___Q211CBlocksFilePCcT1bUcl) 
... 
c4d6060 410b  jsr  @r1 
... 
// normal path 
return 

THROW_PATH: 
... 
// same pattern again, compiler generates terminate path address 
c4d6164 d22f  mov.l  @(0xbc,pc),r2 (= 0x0c4d6172) 
... 
// and pushes it to the stack 
c4d616a 1121  mov.l  r2,@(4,r1) 
... 
c4d616e a002  bra  +4  (==> 0x0c4d6176 : NO_TERMINATE) 
... 
c4d6172 a039  bra  +114  (==> 0x0c4d61e8 : TERMINATE_A) 
... 
NO_TERMINATE: 
... 
// delete handling 
if (??) 
c4d617c 2118  tst  r1,r1 
c4d617e 8d04  bt/s  +8  (==> 0x0c4d618a) 
... 
{ 
    delete ?? 
... 
c4d6184 d128  mov.l  @(0xa0,pc),r1 (= 0x0c06be20 = ___builtin_delete) 
c4d6186 410b  jsr  @r1 
... 
} 
c4d618a 9044  mov.w  @(0x88,pc),r0 (= 0x0000028c) 
c4d618c 02ee  mov.l  @(r0,r14),r2 
c4d618e 5121  mov.l  @(4,r2),r1 
c4d6190 6112  mov.l  @r1,r1 
c4d6192 1211  mov.l  r1,@(4,r2) 
c4d6194 d125  mov.l  @(0x94,pc),r1 (= 0x0c06a880 = ___sjthrow) 
c4d6196 410b  jsr  @r1 

이 코드는 무엇에 유용합니까? throw가 새로운 함수 내에 있지 않기 때문에 메모리가없는 것처럼 보이지 않습니다.

왜 던지기가 호출됩니까? 그리고 누구로부터? 스택에 코드 주소를 두는이 패턴이 두 번 있습니다.이 패턴은 나중에 실행에 사용될 수 있습니다.

감사

+0

멀티 스레드 코드입니까? 새로운 throw 전에 코드 메모리 사용량을 확인하십시오. – DumbCoder

+0

단일 스레드 코드입니다. 나를 위해, 그것은 새 것처럼 보이지 않는 것 같습니다,이 경우에는 호출 스택에 __builtin_new (그리고 ___sjthrow에 대한 호출이 표시된 스택 코드와 동일한 스택 레벨에 있음)가 표시되기를 기대하기 때문입니다. 또는 생성자에서 throw 된 예외를 처리하는 동안 다시 throw 될 수 있습니다. 내년에 메모리 사용량을 확인하겠습니다. – Martin

답변

2

는 추측은 생성자가 발생하는 경우, 오브젝트의 메모리를 해제해야한다고 C++의 요구 사항이기 때문에이 패턴이 적용되는 것입니다. 따라서 생성자 내에서 예외가 발생하면 새로 할당 된 객체가 삭제됩니다.

항상 개체 생성자로부터 무언가를 던져서이 경로의 실행이 유용한 지 여부를 확인하는 데 필요한 경로를 확인할 수 있습니다.

+0

좋은 지적입니다. 예외를 다시 던지면 다음으로 찾아 낼 수 있습니다. 이 경우에는 생성자에 대한 호출이나 호출 스택의 new에 대한 호출이 표시되지 않습니다. 내년에 그걸 확인해 볼게. 감사 – Martin

0

문제가 해결 된 후 내 자신의 질문에 신속하게 답변하고 싶습니다.

새로운 실패 처리기를 추가하여 호출되는지 확인했습니다. 그리고 언급 된 예외가 던져지기 직전에 호출되었습니다. 그래서 그것은 '메모리 부족 상태'로 바뀌었고 몇 시간 내에 메모리 누수를 발견했습니다.

메모리 부족 조건 이후에 예외가 발생할 경우 VxWorks에서 스택 추적에 '새'함수 호출이 표시되지 않는 것으로 보입니다.

DumpCoder와 villintehaspam 덕분에 그들은 나를 올바른 방향으로 생각하게 만들었습니다.