응답하지 않는 앱 디버그하기

다른 언어: English Español Português 中文

라인 브레이크포인트를 설정하는 방법, 값 로깅, 또는 식 평가 등을 가르치는 디버거 튜토리얼이 많이 있습니다. 이러한 지식만으로도 애플리케이션 디버깅에 많은 도구를 제공하지만, 실제 시나리오는 약간 까다롭고 더 고급 방법이 필요할 수 있습니다.

이번 글에서는 프로젝트에 대한 사전 지식이 많지 않아도 UI 동작 중지를 일으키는 코드를 찾는 방법과 즉시 결함이 있는 코드를 수정하는 방법을 배워보겠습니다.

문제

따라가려면 먼저 이 저장소를 복제하십시오: https://github.com/flounder4130/debugger-example

어떤 작업을 수행하면 복잡한 애플리케이션에서 멈춤이 발생하는 상황을 가정해 봅시다. 버그를 재현하는 방법을 알고 있지만 문제는 이 기능을 담당하는 코드 부분을 모른다는 것입니다.

샘플 애플리케이션의 UI에는 여러 작업을 수행하기 위한 많은 버튼이 있습니다

우리 예제 앱에서는 Button N을 클릭하면 멈춤이 발생합니다. 그러나 이 작업을 담당하는 코드를 찾는 것은 그렇게 쉽지 않습니다:

프로젝트 전체에서 Button N을 검색해도 결과가 없습니다 프로젝트 전체에서 Button N을 검색해도 결과가 없습니다

디버거를 사용하여 이를 어떻게 찾을 수 있는지 살펴봅시다.

메서드 브레이크포인트

메서드 브레이크포인트의 장점은 클래스의 전체 계층 구조에 사용할 수 있다는 것입니다. 이것이 어떻게 우리에게 유용할까요?

예제 프로젝트를 보면 모든 작업 클래스가 Action 인터페이스에서 파생되었고, 이 인터페이스는 하나의 메서드인 perform() 만을 가지고 있다는 것을 알 수 있습니다.

에디터 자리표에서 메서드 브레이크포인트 아이콘 에디터 자리표에서 메서드 브레이크포인트 아이콘

이 인터페이스 메서드에 메서드 브레이크포인트를 설정하면 파생된 메서드 중 하나가 호출될 때마다 애플리케이션을 중지합니다. 메서드 브레이크포인트를 설정하려면 메서드를 선언하는 라인을 클릭하세요.

디버거 세션을 시작하고 Button N을 클릭하십시오. 애플리케이션은 ActionImpl14 에서 중지됩니다. 이제 이 버튼에 해당하는 코드가 어디에 위치하는지 알게 되었습니다.

애플리케이션이 Action 인터페이스를 구현하는 클래스에서 중지되었습니다 애플리케이션이 Action 인터페이스를 구현하는 클래스에서 중지되었습니다

이 글에서는 버그를 찾는 것에 집중하고 있지만, 이 기법은 큰 코드베이스에서 어떻게 작동하는지 이해하고자 할 때 많은 시간을 절약할 수 있습니다.

애플리케이션 일시 중지

메서드 브레이크포인트를 이용하는 접근법은 잘 작동하지만, 이는 우리가 부모 인터페이스에 대해 어떤 것을 알고 있다는 가정에 기초하고 있습니다. 이 가정이 틀리거나 다른 이유로 이 접근법을 사용할 수 없다면 어떻게 할까요?

우리는 심지어 브레이크포인트 없이도 이것을 할 수 있습니다. Button N을 클릭하고, 애플리케이션이 멈춤 상태일 때 IntelliJ IDEA로 돌아갑니다. 메인 메뉴에서 실행 | 디버그 액션 | 프로그램 일시 중지 (Run | Debugging Actions | Pause Program)을 선택합니다.

메인 스레드의 콜 스택은 현재 무엇을 하고 있는지 보여줍니다 메인 스레드의 콜 스택은 현재 무엇을 하고 있는지 보여줍니다

애플리케이션은 일시 중지되어, 우리는 스레드 및 변수 (Threads & Variables) 탭에서 스레드의 현재 상태를 검사할 수 있습니다. 이를 통해 애플리케이션의 현재 작업 상황을 이해할 수 있습니다. 애플리케이션이 멈춰 있기 때문에, 멈춤을 일으키는 메서드를 파악하고 호출 사이트를 추적할 수 있습니다.

이 접근 방식은 곧 언급하게 될 더 전통적인 스레드 덤프보다 일부 이점이 있습니다. 예를 들어, 이는 편리한 형태로 변수에 대한 정보를 제공하고 프로그램의 진행을 제어할 수 있게 해줍니다.

스레드 덤프

마지막으로, 디버거의 기능은 아니지만, 스레드 덤프를 사용할 수 있습니다. 이것은 디버거를 사용 중인지 여부와 관계없이 사용할 수 있습니다.

Button N을 클릭하십시오. 애플리케이션이 멈춰 있는 동안 IntelliJ IDEA로 돌아갑니다. 메인 메뉴에서 실행 | 디버그 액션 | 스레드 덤프 받기 (Run | Debugging Actions | Get Thread Dump)를 선택합니다.

왼쪽에서 사용 가능한 스레드를 스캔하면 AWT-EventQueue에서 문제가 발생하는 위치를 볼 수 있습니다.

IntelliJ IDEA의 스레드 덤프 뷰어 IntelliJ IDEA의 스레드 덤프 뷰어

스레드 덤프의 단점은 그것들이 만들어진 시점의 프로그램 상태에 대한 스냅샷만 제공한다는 것입니다. 스레드 덤프를 사용하여 변수를 탐색하거나 프로그램의 실행을 제어할 수 없습니다.

우리 예제에서는 스레드 덤프에 의존할 필요가 없습니다. 그러나 디버그 에이전트 없이 시작된 애플리케이션을 디버깅하려고 할 때와 같이 다른 경우에 유용할 수 있기 때문에 이 기법을 언급하고 싶었습니다.

문제 이해하기

디버깅 기법에 관계없이 우리는 ActionImpl14 에 도달합니다. 이 클래스에서는 누군가가 작업을 별도의 스레드에서 수행하려고 했지만 Thread.start() Thread.run() , 호출 코드와 같은 스레드에서 코드를 실행하게 만들었습니다.

IntelliJ IDEA의 정적 분석기는 설계 시간에 우리에게 이에 대해 경고조차 합니다:

IntelliJ IDEA의 정적 분석은 Thread.run()에 대한 의심스러운 호출에 대해 경고합니다. IntelliJ IDEA의 정적 분석은 Thread.run()에 대한 의심스러운 호출에 대해 경고합니다.

무거운 작업(또는 이 경우에는 무거운 수면)을 수행하는 메서드가 UI 스레드에서 호출되어 메서드가 완료될 때까지 그것을 차단합니다. 그래서 우리가 Button N을 클릭한 후에는 일정 시간 동안 UI에서 어떤 것도 할 수 없습니다.

핫스왑(HotSwap)

버그의 원인을 발견했으니 이 문제를 해결해 봅시다.

우리는 프로그램을 중지시키고 코드를 다시 컴파일한 다음 다시 실행할 수 있습니다. 그러나 작은 변경 사항 때문에 전체 애플리케이션을 다시 배포하는 것은 항상 편리하지는 않습니다.

스마트하게 해결해 봅시다. 먼저 제안된 퀵 수정을 사용하여 코드를 수정합니다:

컨텍스트 메뉴 (Alt-Enter)는 의심스러운 코드를 수정하는 옵션을 제공합니다. 컨텍스트 메뉴 (Alt-Enter)는 의심스러운 코드를 수정하는 옵션을 제공합니다.

코드가 실행 준비가 되면 실행 | 디버그 액션 | 변경된 클래스 다시 로드 (Run | Debugging Actions | Reload Changed Classes)를 클릭합니다. 새로운 코드가 VM에 전달되었다는 것을 확인하는 풍선이 나타납니다.

업데이트된 클래스가 런타임에 도달했다는 것을 확인하는 풍선 업데이트된 클래스가 런타임에 도달했다는 것을 확인하는 풍선

애플리케이션으로 돌아가서 확인해 봅시다. Button N을 클릭해도 앱은 더 이상 멈추지 않습니다.

Tip icon

HotSwap의 제한을 명심하세요. 확장된 HotSwap 기능에 관심이 있다면, DCEVM이나 JRebel 같은 고급 도구를 살펴보는 것이 좋을 수 있습니다.

요약

추론과 디버거의 몇 가지 기능을 사용하여 프로젝트에서 UI 동작 중지를 일으키는 코드를 찾을 수 있었습니다. 그런 다음, 코드를 수정하는 데 다시 컴파일과 재배포에 시간을 낭비하지 않고, 실제 프로젝트에서는 길게 걸릴 수 있는 과정을 거치지 않았습니다.

이러한 기법이 유용하다고 생각되어 말씀해 주시면 좋겠습니다!

더 자세한 내용을 기대해 주세요!

all posts ->