Configuring BreakPoint

TRACE32
이동: 둘러보기, 검색

  BreakPoint

BreakPoint는 타깃 CPU가 특정 상태일 때 멈추도록 하여 그 때의 시스템 상태를 확인해 볼 수 있게 하는 유용한 기능입니다. BreakPoint는 크게 Hardware BreakPoint(이하 Onchip BreakPoint)와 Software BreakPoint로 나뉩니다.

Onchip BreakPoint

칩 내부의 Hardware인 debug logic을 이용하여 BreakPoint를 설정하는 것으로 Program의 Code나 Data에 상관없이 설정이 가능하며 주소가 할당된 어느 영역이든 설정이 가능합니다. 그러나 CPU에 내장된 debug logic을 사용하므로 개수가 제한적입니다. 보통 Cortex Series ARM의 경우 2개에서 12개까지 가능합니다. 가끔은 CPU의 Core Register에 BreakPoint 설정이 가능한지를 문의하는 경우가 있습니다만 대부분의 CPU에서는 Core Register에 BreakPoint 설정은 불가 합니다.

 

Software BreakPoint

원래 메모리에 있던 명령어를 Backup한 후 HLT/BKPT 명령어와 같은 Trap Code를 삽입하는 Software 적인 구현 방법을 사용하는 것으로 구현 특성상 초기화된 RAM의 코드영역에만 설정 가능합니다. Software 적인 구현 특성상 Code 삽입 방식을 사용하므로 무한대의 개수만큼 설정이 가능합니다.

 

ü  Program(or code) BreakPoint

프로그램 코드에 설정하는 BreakPoint로 Onchip/Software 방식 모두 설정이 가능합니다.

ü  Data BreakPoint:

프로그램의 코드가 실행되면서 읽고 쓰는 Data에 설정하는 BreakPoint로서 오직 Hardware (Onchip debug) logic을 통해서만 설정가능합니다. 왜냐하면 CPU core의 Read/Write bus cycle을 감지할 수 있는 방법은 오직 Hardware trigger 만이 가능하기 때문입니다.

 

 

 

   BreakPoint 설정정책 선택

아래 기본 설정과 같이 BreakPoint는 TRACE32가 자동으로 알아서 선택하여 설정하므로 특별한 경우를 제외하고는 염두할 필요가 없습니다. 다만 임의로 특정 BreakPoint Mechanism을 설정하여 사용하고자 할 경우

메뉴>>Break>>Implementation 클릭하여 정책을 바꿀 수 있습니다.

 

 

 

   Data BreakPoint 설정

     특정 주소에 access(read/Write/Read or write)

본 chapter에서는 특정 주소의 Data에 BreakPoint 설정 방법을 학습합니다. CPU는 현재 동작하고 있는 Memory Management mode를 기준으로 Break Event가 Trigger 됩니다. 즉 MMU가 켜져 있는 경우 Virtual을 기준으로 설정된 어드레스에 Trigger되며 MMU가 꺼져 있는 경우 Physical Address를 기준으로 설정된 Address에 Trigger됩니다.

kernel 영역(SFR 포함) Data BreakPoint 설정 

Kernel 영역은 Common 영역으로 각 Process의 MMU mapping table이 바뀌어 로드되더라도 항상 동일 어드레스와 내용을 갖는 영역입니다. 따라서 현재 CPU가 바라보는 virtual address를 기준으로 별다른 고려사항 없이 BreakPoint를 설정하면 됩니다.

 

ü  설정하고자 하는 virtual address  

만약 설정할 Virtual address 0xEC4D3800에 어떤 Data를 Write할 때 Break가 걸리도록 설정하고자 한다면 TRACE32 Command line에서

 

Break.Set 0xEC4D3800 /Write

 

명령을 입력하거나

TRACE32 메뉴>>Break>>Set 클릭 후 아래와 같이 설정할 수 있습니다.

만약 위 어드레스에 특정 값이 써질 때 멈추고자 한다면 Data Field에 다음과 같이 설정할 수 있습니다.

 

ü  설정하고자 하는 physical address 알고 있을 때 

MMU를 사용하는 시스템에서 CPU는 Virual Address만을 보게되므로 Physical에 해당하는 Virtual Address를 얻은 후 BreakPoint를 설정해야 합니다.

만약 Physical 0x11400000에 Data BreakPoint를 설정하고 싶다면 해당 번지에 대한 Virtual Address을 얻는 방법은 두가지가 있습니다.

1)    첫 째는 TRACE32 Script함수 중에 MMU.LOGICAL(A:물리주소)이라는 함수가 있어 쉽게 얻어 낼수 있습니다. 다만 해당 함수를 사용하기 전에는 반드시 MMU.SCAN.PageTable 이나 MMU.SCAN.KernelPageTable 명령을 실행하여 해당 Physical에 대한 Virtual Address를 준비하도록 하여야 합니다. 간단히 다음과 같이 실행하시면 원하는 곳에 BreakPoint를 설정할 수 있습니다.

 

MMU.SCAN.PageTable 또는

MMU.SCAN.KernelPageTable

Break.Set MMU.LOGICAL(A:0x11400000) /Onchip

 

위의 경우 Physical Address 0x11400000에 해당하는 Virtual Address에 BreakPoint를 설정합니다. ARMv8에 64bit Kernel을 사용하고 있다면 위와 동일하며 원하는 address를 적으면 되겠습니다.

 

2)    두 번째로는, TRACE32 Command Line에서

 

MMU.List.PageTable

 

을 입력하여 Table List를 보여주면 다음과 같이 그 Physical에 해당하는 Virtual Address 0xF0006000를 얻어 냅니다.

 

다음은 얻어낸 Virtual Address에 다음과 같이 TRACE32 Command Line을 이용하여 BreakPoint를 설정하거나

 

Break.Set 0xF0006000 /Write

 

TRACE32 메뉴>>Break>>Set 클릭 후 Break.Set 윈도우를 이용해 설정할 수 있습니다.

 

user 영역에 Data BreakPoint 설정 

user 영역의 경우 특별히 고정된 Hardware Address에 해당하는 Virtual Address를 직접 Access한다거나 하는 일이 없기 때문에 다른 고려 사항은 없습니다. 따라서 원하는 Virtual Address에 BreakPoint를 설정하면 되겠습니다. 다만 주의할 것은 아래 그림과 같이 CPU에서 바라보는 user 영영의 Virtual Address는 현재 Process 뿐 아니라 다른 Process도 동일한 Address를 가질 수 있기 때문에 설정해 놓은 BreakPoint Address에 다른 Process가 실행하다 Access하는 경우도 Break Event가 발생하여 멈추게 됩니다. 멈추었을 때 원하는 SpaceID를 갖지 않는 경우 간단히 Run 시키시면 되겠습니다.

 

만약 다른 Process가 실행하다 해당 Address를 Access하는 경우 멈추지 않도록 설정하고 싶은 경우에는 아래와 같이 TASK 옵션의 해당 Thread 선택 박스를 클릭해 디버깅하던 현재 Process나 Thread를 선택해 주면 되겠습니다. 아래 예의 경우 Address 0xBF001600에 데이터를 쓰거나 읽는 경우 Break Trigger를 거는 설정입니다. 다만 kthreadd Process나 Thread일 경우만 트리거하여 멈추겠다는 설정입니다.

 

 

참고할 점은 상기와 같이 설정한 경우 해당 Address를 어떤 Process나 Thread가 Access했는지 확인하기 위해 CPU를 잠깐 세우게 되는데 이에 따라 약간 느려질 수 있습니다. 즉 Realtime 동작이 아닌 경우가 되며 아래와 같이 TRACE32 상태바에서 갈색으로 S라는 표기해 개발자로 하여금 확인할 수 있게 해줍니다.

 

 

     특정 symbol 에 access(read and/or write) 시 BreakPoint 설정

BreakPoint 설정 시 symbol에 BreakPoint 설정하는 것은 Symbol이 이미 해당 Address를 포함하고 있기 때문에 Kernel이나 User 영역 상관없이 기존에 알고 있는 symbol을 이용해 BreakPoint를 설정하면 됩니다. 다만 User 영역의 경우 다른 Process가 해당 Address를 Access하는 경우도 Trigger될 수 있으므로 TASK 설정이 필요합니다.

1)    kernel 영역에 있는 특정 symbol에 BreakPoint설정하기

우선 Trigger 하고자 하는 변수를 Symbol윈도우를 이용해 먼저 찾습니다. 찾은 후 아래 그림과 같이 변수에 우클릭하면 PopUp에 Breakpoints라는 항목을 찾을 수 있으며 여기를 클릭하면 Data Access Cycle에 대한 설정을 할 수 있게 되는데 Read/Write나 Read 또는 Write중 원하는 조건을 선택하는 것으로 설정을 완료할 수 있습니다.

 

 보통 자주 확인하는 변수의 경우 Watch윈도우에 등록해 놓은 경우가 있습니다. 그런 경우 아래 그림과 같이 Watch윈도우에서 해당 변수를 우클릭하여 동일한 방법으로 BreakPoint를 등록할 수 있습니다.

 

2)    user 영역에 있는 특정 sybmol에 BreakPoint설정하기

역시 Kernel 영역에 있는 변수에 BreakPoint 설정하는 방법과 동일하나 다른 점이 있다면 User 영역에 설정하는 것이므로 해당 Process나 Thread일 때만 Trigger되도록 TASK 항목을 설정하여야 합니다. 아래 예는 vold라는 Process에서 fs_mgr_flags 변수에 값을 쓰거나 읽는 경우 Break Evnet를 발생시키도록 설정한 예입니다. 만약 TASK 항목을 설정하지 않으면 symbol이 가진 address가 Aceess되면 다른 Process라 할지라도 멈추게 됩니다.

 

 

참고할 점은 상기와 같이 설정한 경우 해당 Address를 어떤 Process나 Thread가 Access했는지 확인하기 위해 CPU를 잠깐 세우게 되는데 이에 따라 약간 느려질 수 있습니다. 즉 Realtime 동작이 아닌 경우가 되며 아래와 같이 TRACE32 상태바에서 갈색으로 S라는 표기를 해주며 개발자로 하여금 현재 상태를 확인할 수 있게 해줍니다.

 

     특정 주소나 symbol 의 값이 특정 값일 때

3.2.2와 같이 Kernel이나 User 영역의 특정 symbol에 BreakPoint를 설정하고 아래 그림처럼 Data 필드와 Size 필드를 설정하면 특정 변수에 특정 값이 Access된 경우 Break Trigger Event를 발생시킬 수 있습니다.

CPU core에 따라 Access 데이터 값을 모니터링 hardware가 없는 경우가 많으며, 이런 경우 Write Cycle에서 항상 멈추도록 설정하여 멈추면 해당 변수 값을 TRACE32가 Software적으로 확인하는 방법으로 구현을 하게됩니다. 이의 경우 Realtime 동작이 아닌 경우가 되며 TRACE32 상태바에서 갈색으로 S라는 표시를 해주게 됩니다

 

 

 

   Program(code) BreakPoint 설정

본장에서는 Linux Kernel이 동작하는 Target 위에서 특정 code에 BreakPoint 설정하는 방법을 학습합니다.

     Kernel의 특정 함수에 BreakPoint 설정하기

Kernel의 특정함수에 BreakPoint설정하는 방법은 특별이 고려할 것 없이 기존에 사용하던 방식으로 설정하면 되겠습니다.

1)    아이콘 중 Symbol() 버튼을 클릭하여 열거나 View 메뉴 >> Symbols >> Browse 항목을 클릭하여 Symbol 윈도우를 엽니다.

2)    Symbol 윈도우에서 설정하고자 하는 함수이름을 찾습니다.

3)    우클릭하여 PopUp 메뉴로부터 아래와 같이 선택하여 BreakPoint를 설정하거나 해당 함수를 더블클릭하여 Data.List를 연 후 해당함수에 더블 클릭하여 BreakPoint를 설정합니다. 설정하고 나면 윈도우 좌측에 갈색 수직바로 BreakPoint가 설정 되어 있음을 표기해 줍니다.

 

 

 

 

     특정 process의 Main 함수에 BreakPoint를 설정하여 멈추기

특정 process의 Main함수는 Process가 만들어지고 한번만 실행될 것입니다. 따라서 Main 함수에서 멈추도록 하기 위해서는 해당 Process가 만들어 지는 시점 후 Process Symbol을 로드하고 Main함수에 BreakPoint를 설정하여 실행하면 멈추게 될 것입니다. 이 과정은 상당히 복잡한 과정을 거쳐야 하지만 TRACE32의 Linux Awareness를 사용하게 되면 쉽게 가능합니다.

 

1)    해당 Process가 실행되기 전 Target을 Stop시킨 후 Linux 메뉴 >> Process Debugging >> Debug Process on Main... 을 클릭합니다.

 

2)    1)의 과정을 실행하게 되면 아래 디버깅 Process이름 입력 윈도우가 열립니다. 디버깅을 원하는 Process 이름 입력 후 OK 버튼을 클릭합니다.

 

3)    이후 해당 Process가 실행되면 자동으로 해당 Process의 Main함수에 BreakPoint를 설정해 멈추도록 합니다. 다음 그림은 Main함수에서 멈춘 상태를 보여주고 있으며 TASK.Process윈도우에 보면 현재 Process가 입력했던 Process인 것을 확인할 수 있습니다.

 

 

 

 

     특정 process의 함수에 BreakPoint 설정하기

Linux Platform에서는 대단히 많은 process들이 로드되어 실행되고 있습니다. 따라서 이 Process들에 대한 모든 symbol을 로드 해놓은 상태에서 디버깅하는 것은 장점보다는 단점들이 더 많은 상태가 될 것입니다.

TRACE32는 기본적으로 필요에 따라 symbol을 로드해 사용하는 정책을 가지며 다음과 같은 순서로 원하는 함수에 BreakPoint를 설정하시기 바랍니다.

1)    다음 그림과 같이 TASK.Process윈도우를 통해 해당 Process symbol을 로드합니다.

 

2)    1)을 실행하고 Symbol 윈도우()를 열고 Up 버튼()을 클릭해 상위로 올라가면 현재 어떤 ELF symbol 들이 로드되었는지 확인할 수 있습니다. 다음은 아래 symbol 윈도우에서 해당 ELF(netd>> )를 클릭해 들어가거나(해당 ELF에 대한 symbol들만 보임),  버튼을 클릭해 들어간 후 다시 한번 클릭하면 로드된 모든 ELF의 symbol들을 보여줍니다.

 

3)    설정하고자 하는 함수를 찾아 더블클릭하여 Data.List윈도우를 열고 원하는 소스라인에 더블클릭하여 BreakPoint를 설정합니다.

 

4)    해당 Process에서만 멈추고자 한다면 아래 그림과 같이 Break.List 창에서 우클릭후 Change를 클릭하

여 TASK 필드를 해당 Process로 설정해 줍니다.

 

 

 

 

     특정 process 내의 Library 함수에 BreakPoint 설정하기

우리는 필요에 따라 특정 Process에서 로드하여 사용하는 특정 Library의 함수를 디버깅하고자 하는 경우가 있습니다. 다음과 같은 순서로 우리는 쉽게 해당 함수에서 BreakPoint를 설정하여 디버깅 할 수 있습니다.

1)    특정 Process의 Library Symbol을 로드하기 위해 Linux메뉴 >> Display Process... 항목을 클릭(또는 Command line에서 TASK.Process 입력)하여 디버깅하고자 하는 Process를 찾고 더블클릭하여 해당 Process 정보를 엽니다.

 

2)    여러 정보 중 Code File 항목은 해당 Process가 사용하는 Resource들에 대한 정보를 담고 있는 부분으로 디버깅하고자 하는 Library의 파일 이름을 찾습니다. 해당 파일을 찾은 후 우클릭하고 PopUp 메뉴의 Load Library Symbols 항목을 클릭하면 자동으로 해당 Library Symbol을 로드하게 됩니다. 만약 해당 .so 파일을 찾지 못한 경우 File Browse 윈도우가 띄우게 되는데 해당 .so 파일을 찾아 지정하면 되겠습니다.

 

3)    2)를 실행하고 Symbol 윈도우()를 열고 Up 버튼()을 클릭해 상위로 올라가면 현재 어떤 ELF symbol 들이 로드되었는지 확인할 수 있습니다. 다음은 아래 symbol 윈도우에서 해당 ELF(netd >> )를 클릭해 들어가거나(해당 ELF에 대한 symbol들만 보임),  버튼을 클릭해 들어간 후 다시 한번 클릭하면 로드된 모든 ELF의 symbol들을 보여줍니다.

 

4)    Symbol 중 원하는 함수를 찾아 BreakPoint를 설정합니다. 해당 함수를 찾았다면 더블클릭하여그 함수의 Data.List 윈도우를 열고 원하는 소스라인에 더블 클릭하여 BreakPoint를 설정합니다. 설정이 완료 되었으며 Target을 Run하면 해당 함수가 실행될 경우 멈추게 될 것입니다.

 

5)    만약 해당 Process에서만 멈추고자 한다면 아래 그림과 같이 Break.List 창에서 우클릭한 후 Change를 클릭하고 TASK 필드에 해당 Process를 설정해 줍니다.

 

 

 

 

     특정 kernel module 이 loading 될 때 Init 함수에 BreakPoint 설정하여 멈추기

Module이 로딩될 때 symbol을 올린 후 Init 함수에 BreakPoint를 설정하여 멈추도록 하는 개념을 갖습니다. 이 과정을 수동으로 하는 것은 생각보다 상당히 복잡한 과정을 거쳐야 합니다. 하지만 TRACE32의 Linux Awareness를 사용하게 되면 쉽게 가능합니다.

 

1)    우선 모듈이 로딩되기전 Linux 메뉴 >> Module Debugging >> Debug Module on Init... 항목을 클릭합니다. 클릭하면 우측과 같은 디버깅할 모듈이름 입력 윈도우가 열립니다. 모듈이름을 입력하고 Ok 버튼을 클릭하면 Module이 로딩 될때까지 waiting 합니다.

 

 

2)    사용자에 의해 Module이 로딩된 후 TRACE32는 로딩된 것을 인식하고 해당 Module의 symbol을 자동으로 로드하게 될 것이며 모듈의 Init함수에 BreakPoint를 설정하고 다시 Run을 시키게 됩니다.

다음 그림은 위의 과정을 통해 해당 모듈의 Init 함수에서 멈춘 상태를 보여주고 있습니다.

 

     user_fault/kernel_fault/panic 등 의도하지 않은 exception 발생 대비 BreakPoint 설정

Linux Kernel은 Data Abort/Prepatch Abort/Undefined Exception을 임의의 용도로 사용하고 있습니다. 따라서 잘못된 코드 동작에 의한 excetption 발생에서 excetpion을 구분하여야 하며 이는 Kernel에서 제공하고 있습니다. 만약 의도하지 않은 Excetption 발생할 경우 특정 코드를 타도록 되어 있습니다.

따라서 의도하지 않은 Exception 발생 시 타는 특정 코드가 Debugging Point가 될 것이며 우리는 이곳에 BreakPoint를 설정하여 Trigger시 이 코드를 보는 작업을 하게 됩니다.

Linux의 경우 TRACE32에서는 좀더 간편한 Exception Debugging을 지원하기 위해 SegV.cmm이라는 파일을 제공하고 있는데 바로 의도하지 않은 Exception발생시 실행되는 코드에 자동으로 BreakPoint를 설정해 주고 BreakPoint Trigger시 자동으로 Exception위치를 추적해 주는 기능을 담당해 줍니다.

ARMv8을 Debugging 시 본기능을 사용하기 위해서는 반드시 iTSP version 3.8.5이상을 추천합니다.

 

1)    Exception 발생이 확인되었다면 system을 다시 시작하고 해당 Exception발생 전 아래 그림과 같이 SegV.cmm을 실행해 주는 ”Segment Violation” 버튼을 클릭하여 의도하지 않은 Exception발생시 실행되는 코드에 BreakPoint를 설정합니다.

SegV.cmm을 실행하면 die()/__do_user_fault()/__do_kernel_fault()/panic() 함수에 BreakPoint를 자동으로 설정하게 됩니다.

 

2)    다음은 Target을 실행시켜 Exception을 재현합니다. Exception이 재현되면 아래와 같이 의도하지 않은 Exeption발생 시 실행되는 Kernel 함수에서 멈추게 됩니다.

 

3)    다음으로는 Exception이 발생한 위치를 확인하기 위해 다시 ”Segment Violation” 버튼()을 클릭합니다. 그러면 아래 그림과 같이 Exeption이 발생했던 위치를 추적해 직전 상태를 보여 줍니다. 아래 원인을 살펴보면  Exception의 원인은 ldr r0,[r12]이며 R12가 가리키는 번지인 0번지(MMU에 Mapping되지 않은)로부터 데이터를 읽어 R0에 옮기는 작업을 진행하다 발생한 것임을 알 수 있습니다.

 

다음은 ARMv8용linux Kernel64에서의 해당 기능을 사용한 예입니다. 어떤 원인에 의해 “Kernel Fault”에서 멈춘 것을 확인할 수 있습니다. 이 상태에서 다시 ”Segment Violation” 버튼()을 클릭하시면

다음 그림과 같이 Fault를 발생시킨 코드 위치를 추적하여 찾아 오게 됩니다.

 

 

 

 

   Hyervisor(EL2)/Secure/NonSecure Breakpoint 설정

Processor 동작 모드에 따라 Breakpoint 설정이 가능합니다(2015년 1월~ 버전). 아래 그림은 같은 Address이지만 Secure와 NonScure에 따라 다른 BreakPoint가 설정되어 있는 것을 볼 수 있습니다.

또한 동일 Address이지만 Source code도 다른 code가 보이는 것을 볼 수 있습니다. 즉 동작 Mode에 따라 동일 Address에 다른 Application이 동작할 수 있고 Debugger는 이런 환경에서 구분하여 Debugging할 수 있느 환경을 제공해 주어야 합니다.

 

     동작 Mode(or Exception Level)에 따른 BreakPoint 설정

기본적으로 Breakpoint를 설정하면 동작모드에 상관없이 설정됩니다. 그러나 어떤 경우에는 특정 동작 Mode에서만 유효하도록 설정할 수 있습니다. 특히 Secure OS를 Debugging 한다거나 Hypervisor Mode debugging을 효율적으로 하고자 할 경우 필요하게 될 것입니다.

사용방법은

1)    SYStem.Option.ZoneSPACES ON       ; 동작 Mode를 구분하여 BreakPoint 관리

2)    의도하는 동작 모드에 대해 BreakPoint를 설정

Ex) Break.Set H:0x30E000                ; Hypervisor Mode(EL2) SoftBreakpoint(RAM 공간인 경우)

    Break.Set N:0x202000                ; NonSecure World(EL1&EL0) SoftBreakpoint(RAM 공간인 경우)

    Break.Set Z:0x202004                ; Secure World(EL1&EL0) SoftBreakpoint(RAM 공간인 경우)

    Break.Set H:0x30E000 /Onchip     ; Hypervisor(EL2) Mode Onchip Breakpoint

    Break.Set N:0x202000 /Onchip    ; NonSecure(EL1&EL0) World Onchip Breakpoint

    Break.Set Z:0x202004 /Onchip     ; Secure World(EL1&EL0) Onchip Breakpoint

    Break.Set Z:0x300014 /Write       ; Secure World(EL1&EL0) data write breakpoint

 

     동작 Mode(or Exception Level)에 따라 구분하여 Symbol 다운로드 하기

기본적으로 Symbol을 다운로드하면 동작 Mode(or EL)에 상관없이 Symbol을 로드 합니다. 따라서 Linux와 Secure OS가 동시에 동작하는 Application과 같은 개발환경에서 두 동작 Mode를 동시에 Debugging하기 원한다면 매우 불편한 상황을 접하게 될 것입니다. 즉, 어드레스는 같지만 Secure/Non-Secure World에 따라 다른 코드가 위치될 수 있기 때문에 필요시마다 해당 동작모드에 대한 Symbol을 로드하여 사용해야 할 것입니다. 따라서 TRACE32는 동작모드에 따라 동일 어드레스라 할지라도 다른 Symbol을 다운로드하는 기능이 있어 이러한 번거러움을 해결해 줍니다.(2014년 11월~ 버전)

방법은 다음과 같습니다.

 

1)    SYStem.Option.ZoneSPACES ON       ; 동작 Mode를 구분하여 Symbol 관리

2)    동작 Mode(Exception Level)에 따라 Symbol 로드

Ex) Data.LOAD.Elf  HyperAppFileName.elf  H:  /nocode /noclear          ; @HyperVisor(or EL2)

      Data.LOAD.Elf  SecureAppFileName.elf  Z:  /nocode /noclear         ; @Secure World

      Data.LOAD.Elf  NonSecAppFileName.elf  N:  /nocode /noclear       ; @NonSecure World