임베디드 레시피

Egloos | Log-in





Stack Size는 어떻게 잡는가

Stack의 Size는 어떻게 정하게 되는 건가요? 그냥 막무가내로 Stack의 Size를 정할 수는 없겠지요. 보통 실무에서 넉넉하게 정해주는 Stack 측정 방법이 있는데 별거 아니에요. 복습 하는 기분으로 함수가 호출 될 때 Stack에 값을 어떻게 쌓았었는지 다시 한번 보는 건 어떨까요. 우선, 아래와 같은 Code가 있다고 치시고요, 현재 PC값이 0x18122C9E인 경우이지요.
 
 
addr/line__|code_____|label____|mnemonic________________|comment
 ST:18122C90|4010                and     r0,r2
 ST:18122C92|40C8                lsr     r0,r1
 ST:18122C94|40CB                lsr     r3,r1
 ST:18122C96|1A18                sub     r0,r3,r0
 ST:18122C98|2801                cmp     r0,#0x1
 ST:18122C9A|D103                bne     0x18122CA4
            |           {
        1552|             gc_sm (UP_EV);
 ST:18122C9C|2002                mov     r0,#0x2
 ST:18122C9E|FE6BF7FF____________bl______0x18122978_______;_gc_sm
 ST:18122CA2|E002                b       0x18122CAA
            |         }
            |     else
            |     {
        1556|             gc_sm (STAY_EV);
 ST:18122CA4|2001                mov     r0,#0x1
 ST:18122CA6|FE67F7FF            bl      0x18122978       ; gc_sm
            |     }
            |    #endif /* hieonn #if 0 */
            |  }
            |
            |
            |  /* Check the page status it should be marked erased. */
 
 
일단, 위 상태에서의 Context를 보면 R0~R15의 값은 아래와 같습니다요.
 

N _  R0          2  R8          0  SP> 0000030D
Z _  R1          6  R9          0  -1C 0000030F
C C  R2   FFFFFFC0  R10         0  -18 00002842
V _  R3         14  R11         0  -14 01D63EE0
I _  R4   01D64160  R12  01803AD0  -10 00000000
F _  R5   01D63EE0  R13  01803AD0  -0C 0000030D
T T  R6   01D64120  R14  18122C5D  -08 00000004
J _  R7   18B5E7D4  PC   18122C9E  -04 1812434D
usr  SPSR           CPSR 20000030  FP> 00900C0C
Q _                                +04 00000004


이때, Stack Pointer (R13)이 가리키고 있는 곳을 한번 볼까요? 어디에 무엇을 가리키고 있는지..


___address__|________0________4________8________C_0123456789ABCDEF
 SD:01803A70| 00000000 00000000 00000000 00000000 ................
 SD:01803A80| 00000000 00000000 00000000 00000000 ................
 SD:01803A90| 00000000 00000000 00000000 00000000 ................
 SD:01803AA0| 00000000 00000000 00000000 00000000 ................
 SD:01803AB0| 00000000 00000000 00000000 00000000 ................
 SD:01803AC0| 00000000 00000000 00000000 00000000 ................
 SD:01803AD0|>0000030D 0000030F 00002842 01D63EE0 ........B(...>..
 SD:01803AE0| 00000000 0000030D 00000004 1812434D ............MC..
 SD:01803AF0| 00900C0C 00000004 03000004 0000030C ................
 SD:01803B00| 0000030E 00000000 00000000 00000000 ................
 SD:01803B10| 00000004 00000001 30730000 00000000 ..........s0....
 SD:01803B20| 00000200 00000036 00000000 00000000 ....6...........
 SD:01803B30| 01D6371C 00901FE8 00901F68 0000081C .7......h.......
 SD:01803B40| 01D6381C 01D6379C FFFFFFF1 0000030E .8...7..........
 SD:01803B50| 01D6379C 01D6371C 00000A73 01DA6B00 .7...7..s....k..
 SD:01803B60| 00000000 18014B03 000004C4 01DA6B00 .....K.......k..
 SD:01803B70| 00000A73 180136FD 0000030E 01D63EE0 s....6.......>..
 SD:01803B80| 00000025 01D76B00 00000004 01D638E4 %....k.......8..
 

오호라, 0x1803AD0을 가르키고 있었는데, 0x30D라는 값이 들어 있네요. 자자, 이제부터 함수가 호출되면 어떤 일이 벌어지는 지 다시 한번 슬슬 들여다 보시지요. 우선 PC값이 0x18122C9E였고요, 그 곳의 opcode는 bl 0x18122978 이었지요. 0x18122978에는 무엇이 있는지 한번 살펴 볼까요? void gc_sm(..) 함수의 시작 지점인 push {r14} 지점을 가르키고 있지요. 그곳으로 PC는 Jump를 하고, gc_sm()함수가 호출 되는 겁니다. 물론 돌아 갈 주소인 0x18122CA2을 의미하는 0x18122CA3 (Thumb mode니까요)이 자리 잡겠지요? 자, 한 번 살펴 봅시다.


_addr/line__|code_____|label____|mnemonic________________|comment
            |
            |void gc_sm (curr_gc_event_type curr_event)
        1452|{
 ST:18122978|B500      gc_sm:    push    {r14}
            |      curr_gc_state next_status;
            |      curr_gc_state old_status;
            |
        1456|      old_status = curr_status;  /* save the current status */
 ST:1812297A|4B6E________________ldr_____r3,0x18122B34
 ST:1812297C|7819                ldrb    r1,[r3]
            |
        1458|      if (curr_status == INIT_ST)
 ST:1812297E|2900                cmp     r1,#0x0
 ST:18122980|D101                bne     0x18122986
        1459|          curr_status = STAY_ST;
 ST:18122982|2101                mov     r1,#0x1
 ST:18122984|7019                strb    r1,[r3]
            |
        1461|      switch (curr_status)
 ST:18122986|7819                ldrb    r1,[r3]
 ST:18122988|2901                cmp     r1,#0x1
 ST:1812298A|D005                beq     0x18122998
 ST:1812298C|2902                cmp     r1,#0x2
 ST:1812298E|D101                bne     0x18122994


OK 그러면 push {r14}를 하고 나면 무슨 일이 벌어지는 지 Context를 한 번 살펴 보아야 하겠지요. 그 때의 Context를 한번 보시죠. R14의 0x1822CA3은 gc_sm()함수를 실행하고 나서 돌아가야 할 이겠고요, 그리고 push를 한번 했으니까, R13-0x4를 하시어, 0x1803ACC를 가르키고 있을테고, 0x1803ACC에는 R14 값인 0x18122CA3가 들어 있겠네요? 진짜 그런 가 한번 보시죠.


N _  R0          2  R8          0  SP> 18122CA3
Z _  R1          6  R9          0  FP> 0000030D
C C  R2   FFFFFFC0  R10         0  +04 0000030F
V _  R3         14  R11         0  +08 00002842
I _  R4   01D64160  R12  01803AD0  +0C 01D63EE0
F _  R5   01D63EE0  R13  01803ACC  +10 00000000
T T  R6   01D64120  R14  18122CA3  +14 0000030D
J _  R7   18B5E7D4  PC   1812297A  +18 00000004
usr  SPSR           CPSR 20000030  +1C 1812434D
Q _                                +20 00900C0C
 

___address__|________0________4________8________C_0123456789ABCDEF
 SD:01803A70| 00000000 00000000 00000000 00000000 ................
 SD:01803A80| 00000000 00000000 00000000 00000000 ................
 SD:01803A90| 00000000 00000000 00000000 00000000 ................
 SD:01803AA0| 00000000 00000000 00000000 00000000 ................
 SD:01803AB0| 00000000 00000000 00000000 00000000 ................
 SD:01803AC0| 00000000 00000000 00000000>18122CA3 ................
 SD:01803AD0| 0000030D 0000030F 00002842 01D63EE0 ........B(...>..
 SD:01803AE0| 00000000 0000030D 00000004 1812434D ............MC..
 SD:01803AF0| 00900C0C 00000004 03000004 0000030C ................
 SD:01803B00| 0000030E 00000000 00000000 00000000 ................
 SD:01803B10| 00000004 00000001 30730000 00000000 ..........s0....
 SD:01803B20| 00000200 00000036 00000000 00000000 ....6...........
 SD:01803B30| 01D6371C 00901FE8 00901F68 0000081C .7......h.......
 SD:01803B40| 01D6381C 01D6379C FFFFFFF1 0000030E .8...7..........
 SD:01803B50| 01D6379C 01D6371C 00000A73 01DA6B00 .7...7..s....k..
 SD:01803B60| 00000000 18014B03 000004C4 01DA6B00 .....K.......k..
 SD:01803B70| 00000A73 180136FD 0000030E 01D63EE0 s....6.......>..
 SD:01803B80| 00000025 01D76B00 00000004 01D638E4 %....k.......8..


꺄오~ 정말 그르네요. Stack에 돌아 갈 주소인 0x18122CA3를 쌓고서, 일을 시작하네요. 어쨌거나, gc_sm(..) 이 생명을 다하면, 당연히 0x18122CA2로 돌아 오겠지요. 그 때는 당연히 pop {pc}를 이용해서, Stack에 들어 있던, R14 값을 PC에 넣고서 Stack Pointer를 +0x4 하고 빠져 나오겠지요. 한 번 보시죠.

 

_addr/line__|code_____|label____|mnemonic________________|comment
            |           {
        1552|             gc_sm (UP_EV);
 ST:18122C9C|2002                mov     r0,#0x2
 ST:18122C9E|FE6BF7FF            bl      0x18122978       ; gc_sm
 ST:18122CA2|E002________________b_______0x18122CAA
            |         }
            |     else
            |     {
        1556|             gc_sm (STAY_EV);
 ST:18122CA4|2001                mov     r0,#0x1
 ST:18122CA6|FE67F7FF            bl      0x18122978       ; gc_sm
            |     }
            |    #endif /* hieonn #if 0 */
            |  }
            |
            |
            |  /* Check the page status it should be marked erased. */
 ST:18122CAA|6A68                ldr     r0,[r5,#0x24]
 ST:18122CAC|9901                ldr     r1,[r13,#0x4]
 ST:18122CAE|6843                ldr     r3,[r0,#0x4]
 ST:18122CB0|2200                mov     r2,#0x0
 ST:18122CB2|4798                blx     r3
 

N _  R0          2  R8          0  SP> 0000030D
Z _  R1       010E  R9          0  -1C 0000030F
C _  R2   18B5E7D4  R10         0  -18 00002842
V _  R3   18B5E7D4  R11         0  -14 01D63EE0
I _  R4   01D64160  R12  01803AD0  -10 00000000
F _  R5   01D63EE0  R13  01803AD0  -0C 0000030D
T T  R6   01D64120  R14  18122995  -08 00000004
J _  R7   18B5E7D4  PC   18122CA2  -04 1812434D
usr  SPSR           CPSR       30  FP> 00900C0C
Q _                                +04 00000004
 

___address__|________0________4________8________C_0123456789ABCDEF
 SD:01803A70| 00000000 00000000 00000000 00000000 ................
 SD:01803A80| 00000000 00000000 00000000 00000000 ................
 SD:01803A90| 00000000 00000000 00000000 00000000 ................
 SD:01803AA0| 00000000 00000000 00000000 00000000 ................
 SD:01803AB0| 00000000 00000000 00000000 00000000 ................
 SD:01803AC0| 00000000 00000000 00000000 18122CA3 ................
 SD:01803AD0|>0000030D 0000030F 00002842 01D63EE0 ........B(...>..
 SD:01803AE0| 00000000 0000030D 00000004 1812434D ............MC..
 SD:01803AF0| 00900C0C 00000004 03000004 0000030C ................
 SD:01803B00| 0000030E 00000000 00000000 00000000 ................
 SD:01803B10| 00000004 00000001 30730000 00000000 ..........s0....
 SD:01803B20| 00000200 00000036 00000000 00000000 ....6...........
 SD:01803B30| 01D6371C 00901FE8 00901F68 0000081C .7......h.......
 SD:01803B40| 01D6381C 01D6379C FFFFFFF1 0000030E .8...7..........
 SD:01803B50| 01D6379C 01D6371C 00000A73 01DA6B00 .7...7..s....k..
 SD:01803B60| 00000000 18014B03 000004C4 01DA6B00 .....K.......k..
 

요요욧! 정말 그렇지요? 0x18122CA2로 돌아온 순간, R13 (SP)는 0x1803AD0을 가르키고 있네요~ 와하~ 복습 한 번 잘했다~ 라고만 할 게 아니고요, 여기에서 배울 점이 한 가지 있어요. 뭔가 하니, 원래 0으로 채워져 있던 순백의 Stack 영역에 push와 pop을 한 번 씩 했는데, push 했던 자리에 다시 0x0으로 지워지는게 아니라, 그냥 SP만 옮기고 그 자리에 있던 값은 Clear 되지 않네요! 오호라. 이런 거라면, 우리가 관심을 가지고 있는 특정 Stack에서 가장 많이 사용된 양은 0x0가 아닌 주소가 바로 Stack을 최대로 많이 사용 했을 때의 값이 겠지요? 그렇습니다~ 바로 그거지요~

여러가지 시나리오에서 System을 마구 사용해 보고서, Stack의 값을 주르륵 모니터링 해 보면 최대로 많이 사용 되었을 때의 Stack의 크기를 알 수 있는거에요. 일단 줄 수 있는 만큼 크게 Stack을 준 후에 Test를 해서, 적당한 Stack 크기를 잡게 되는데요, 보통 여러가지 시나리오를 통해서 Stack이 최대 얼마나 사용되는지 확인하고요, 그 최대 크기를 전체 Stack의 크기의 3/4으로 잡아서 System을 만들면 십중 팔구, Stack이 모자라서 Overflow가 나거나 하는 일은 없답니다. 별 거 아니죠? 예를 들어 Stack의 start 번지가 0x1000이고, 가장 많이 push를 해서 쌓았을 때의 주소가 0x400이라면,  0x2000-0x1400 = 0xC00이므로, 0xC00이 전체 Stack Size의 3/4을 차지하게 하려면 0xC00 X 4/3 = 0x1000이므로, 0x1000부터 ~ 0x2000 까지를 Stack으로 잡으면 문제 없다는 얘기죠.


 이런 식으로 Stack을 잡으면 아주~ 널널하고, 안전하게 잡은 거나 다름 없어요. 만약에 Memory 크기가 여의치 않으면 Stack의 최대치가 나오도록 많은 복합 적인 Test를 거친 후에, Stack의 최대치에서 약간의 마진을 둔 정도로 만들 때도 있어요. 하지만, Stack Overflow가 나면 그 책임은 누가 지겠사옵니까! 신이시어.

by 히언 | 2009/07/31 00:09 | System비네팅 | 트랙백 | 핑백(1)

Linked at 친절한 임베디드 시스템 개발자.. at 2009/09/02 20:17

... ack 뒤지기 신공 ⓖ Stack 동작의 비밀과 실제 메모리 덤프 ⓗ Stack Size는 어떻게 잡는가 ⓘ 함수 포인터와 실행주소 변경 ⓙ Linked ... more

◀ 이전 페이지          다음 페이지 ▶