WPR/WPA 실무 ── 「PC 전체가 느리다」를 시스템 전체로 조사하는 입문
· Go Komura · Windows, 성능, WPR, WPA, ETW, 성능 조사, 불량 조사, Windows 개발
「새 앱을 설치한 뒤 PC 전체가 느려졌다고 하는데, 작업 관리자를 보면 CPU도 메모리도 여유가 있다」. 「기동에 3분이 걸리는 PC가 한 대 있다. 무엇이 문제인지 전혀 모르겠다」── 성능 상담은 정말로 이런 형태로 많이 들어옵니다. 공통점은 특정 프로세스를 봐도 답이 나오지 않는다는 것입니다.
프로세스 수준의 도구는 갖춰져 있습니다. 파일·레지스트리 접근은 Process Monitor로 볼 수 있고, .NET 앱의 CPU와 GC는 PerfView로 따라갈 수 있습니다. 그러나 「PC 전체가 느리다」나 「CPU가 놀고 있는데도 느리다」는 증상은, 어느 프로세스가 범인인지도 모르는 데서 시작합니다. 앱 A가 느린 이유는 백신 스캔일 수도, 다른 서비스가 디스크에 대량 쓰고 있기 때문일 수도, 여러 프로세스에 걸친 락 사슬일 수도 있습니다. 필요한 것은 프로세스 안이 아니라 OS 전체를 하나의 타임라인에 기록한 데이터입니다.
그것을 캡처하고 읽는 도구가 Windows Performance Recorder(WPR)와 Windows Performance Analyzer(WPA)입니다. WPR은 ETW(Event Tracing for Windows) 기반으로 OS 전체 활동을 기록하고, WPA는 그 기록을 그래프와 표로 분석합니다. 누가 어느 스택에서 CPU를 썼는지, 스레드가 누구를 기다렸는지, 어느 프로세스가 어느 파일에 디스크 I/O를 냈는지── 작업 관리자보다 한두 단계 아래의 사실이 타임스탬프와 함께 남습니다.
중소기업의 IT 담당자와 Windows 앱 개발자를 대상으로, WPR로 캡처하는 실무와 WPA를 읽는 법── 특히 「CPU가 높을 때」와 「CPU가 낮은데도 느릴 때」의 조사 차이를, 2026년 8월 시점의 1차 정보로 정리합니다.
1. 먼저 결론
- 「PC 전체가 느리다」조사의 첫 선택은, OS 전체 ETW 트레이스를 캡처하고 읽는 WPR/WPA입니다. 프로세스 수준 도구(작업 관리자, Procmon, PerfView)가 집어내지 못하는 문제는, 모든 프로세스와 커널을 하나의 타임라인에서 보면 따라갈 수 있습니다.12
- 캡처 도구 wpr.exe는 Windows 8.1 이후 OS에 포함됩니다. 추가 설치 없이 쓸 수 있습니다. GUI 판(WPRUI)과 분석 도구 WPA는 Windows ADK에 들어 있습니다.12
- 기본 절차는 세 줄입니다. 관리자 권한으로
wpr -start GeneralProfile -filemode→ 문제를 재현 →wpr -stop C:\temp\trace.etl. 이것만 기억하면 캡처를 시작할 수 있습니다.3 - 현장의 기준은 「고객 환경에서는 wpr.exe로만 캡처하고, 읽기는 자기 PC의 WPA」로 나누는 것입니다. 소프트웨어를 설치할 수 없는 서버에서도 캡처할 수 있습니다. 패킷 캡처의 「표준 도구로 잡고, Wireshark로 읽는다」와 같은 생각입니다.1
- WPA 읽기는 「CPU인가, 대기인가, I/O인가」로 분류하는 데서 시작합니다. CPU가 타고 있으면 CPU Usage (Sampled), CPU가 놀고 있는데도 느리면 CPU Usage (Precise)의 대기 분석, 디스크가 의심되면 Disk Usage── 처음부터 길이 갈립니다.45
- CPU Usage (Sampled)는 약 1밀리초마다의 샘플링으로 「어느 함수가 CPU를 썼는지」를 보여 줍니다. 작업 관리자의 「50%」를 프로세스 → 스레드 → 스택 → 함수로 분해할 수 있습니다.6
- CPU Usage (Precise)는 컨텍스트 스위치의 완전 기록이며, 「스레드가 누구를 기다렸는지」를 알려 줍니다. Waits(대기 시간), ReadyingProcess(누가 깨웠는지), ReadyThreadStack(깨운 쪽의 스택)을 따라가는 것이 이 글이 가장 전하고 싶은 기법입니다.47
- 스택을 읽으려면 심볼 설정이 필요합니다. WPA는 기본적으로 Microsoft 공개 심볼 서버를 참조합니다. 자기 앱의 함수 이름을 보려면 자기 PDB 경로를 추가합니다.8
- ETL 파일에는 프로세스 이름과 파일 경로 같은 시스템 내부 정보가 들어갑니다. 캡처는 필요한 최소로 두고, 사외로 나갈 때의 취급을 캡처 전에 정합니다.
2. 도구의 자리 ── WPR이 캡처하고, WPA가 읽는다
Windows Performance Toolkit(WPT)은 Windows ADK(Windows Assessment and Deployment Kit)에 포함된 성능 조사 도구 세트이며, 중심은 WPR과 WPA의 쌍입니다.2 역할은 분명히 나뉩니다.
- WPR(Windows Performance Recorder) = 캡처. ETW 프로바이더 묶음을 「프로필」이라는 단위로 모아 기록을 시작하고 멈추며 ETL 파일을 만듭니다. 명령줄 판 wpr.exe는 Windows 8.1 이후 OS에 포함되어 추가 설치가 없습니다. GUI 판(WPRUI.exe)은 ADK에 들어 있습니다.1
- WPA(Windows Performance Analyzer) = 분석. ETL 파일을 열어 그래프와 표로 분석합니다. ADK 설치가 필요합니다.2
즉 고객 환경에 둘 것은 없습니다. OS 표준 wpr.exe로 캡처하고, ETL 파일을 가져와 자기 PC의 WPA로 읽습니다── 패킷 캡처의 「pktmon으로 잡고, Wireshark로 읽는다」와 같은 분리가 성립합니다.
flowchart TB
accTitle: WPR로 캡처하고 WPA로 읽는 분리
accDescr: 고객 환경에서는 OS 표준 wpr.exe로 기록해 ETL 파일을 만들고, 가져와 ADK로 설치한 자기 PC의 WPA에서 분석한다
subgraph customer["고객 PC(추가 설치 없음)"]
wpr["wpr start → 재현 → stop"] --> etl["ETL 파일"]
end
subgraph office["자기 PC(ADK의 WPA)"]
wpa["그래프와 표를 분석"]
end
etl -->|"가져온다"| wpa
그림 1: 고객 환경에서는 OS 표준 wpr.exe로 기록해 ETL 파일을 만들고, 가져와 ADK로 설치한 자기 PC의 WPA에서 분석한다.
비슷한 도구와의 차이도 먼저 정리해 둡니다.
| Process Monitor | PerfView | WPR + WPA | |
|---|---|---|---|
| 답하는 질문 | 어느 프로세스가 어느 경로에 무엇을 했고 무슨 일이 났는가 | .NET 앱의 CPU, GC, 할당은 어떤가 | OS 전체에서 시간은 어디로 사라졌는가 |
| 범위 | 파일, 레지스트리, 프로세스 기동의 조작 로그 | 관리 코드가 우선 | 시스템 전체의 CPU, 대기, 디스크, 파일 I/O, 전원 등 |
| 맞는 증상 | 설정이 읽히지 않음, ACCESS DENIED | 자기 .NET 앱만의 느림이나 메모리 | PC 전체가 느림, CPU가 놀고 있는데도 느림, 범인 프로세스 불명 |
| 기사 | Procmon 실전 가이드 | PerfView 실전 입문 | 이 글 |
Procmon이 「무엇을 했는가」의 조작 로그이고 PerfView가 「.NET 안에서 무슨 일이 났는가」라면, WPA는 모든 프로세스에 걸쳐 「시간이 어디로 사라졌는지」를 감사하는 도구입니다. ETW 자체의 구조와 자기 앱에 ETW를 넣는 법은 「Windows 이벤트 로그·ETW 입문」에서 다룹니다. 자기 앱이 ETW 이벤트를 내면, 앱의 체크포인트가 같은 트레이스에 기록되어 맞추기가 훨씬 쉬워집니다. 다만 WPR은 선택한 기록 프로필이 켠 프로바이더의 이벤트만 기록합니다. GeneralProfile에는 자기 프로바이더가 들어 있지 않으므로, 섞으려면 자기 프로바이더를 켜는 커스텀 기록 프로필(.wprp)을 준비하고 wpr -start GeneralProfile -start MyApp.wprp!MyAppProfile처럼 .wprp 안의 프로필 이름을 !로 지정해 조합합니다.3
3. 캡처 실무(WPR) ── start, 재현, stop
관리자 터미널에서의 기본 절차입니다.
:: List of built-in profiles you can use
wpr -profiles
:: 1. Start capture (general-purpose profile, file mode)
wpr -start GeneralProfile -filemode
:: 2. Reproduce the issue (check capture status with wpr -status)
:: 3. Stop and save (you can attach a description of the problem).
:: Create the destination folder in advance (without it, -stop fails to save)
mkdir C:\temp 2>nul
wpr -stop C:\temp\slow-pc.etl "Reproduced the issue where the whole PC becomes slow while starting App X"
:: To abandon without saving
wpr -cancel
-start에 넘기는 것은 조사에 필요한 ETW 프로바이더를 묶은 프로필입니다.3 자주 쓰는 것만 기억하면 충분합니다.9
| 프로필 | 기록하는 것 | 쓸 때 |
|---|---|---|
GeneralProfile |
CPU 샘플, 컨텍스트 스위치, 디스크 I/O를 포함한 범용 세트 | 여기서 시작합니다. 무엇이 문제인지 모를 때의 첫 수 |
CPU |
상세 CPU 사용 | CPU가 타고 있음을 이미 알 때 |
DiskIO |
디스크 I/O 활동 | 디스크가 의심될 때 |
FileIO |
파일 I/O 활동 | 어느 파일에 접근하는지 따라가고 싶을 때 |
-start를 여러 개 나열하면 프로필을 동시에 지정할 수 있습니다(예: wpr -start GeneralProfile -start FileIO -filemode).3
flowchart TB
accTitle: WPR 캡처 흐름과 모드 선택
accDescr: 그 자리에서 재현할 수 있는 문제는 파일 모드로 짧고 확실하게 캡처하고, 시점을 모르는 문제는 기본 메모리 모드 링 버퍼로 기다린다. 부팅이나 로그온 중의 문제는 부트 트레이스를 쓴다. 어느 쪽이든 start / 재현 / stop 절차는 같다
q{"언제 일어나는가?"}
q -->|"그 자리"| file["파일 모드: 짧은 캡처"]
q -->|"시점 불명"| mem["메모리 모드: 대기(3.1)"]
q -->|"부팅 또는 로그온"| boot["부트 트레이스(8장)"]
file --> s1["start → 재현 → stop"]
mem --> s1
그림 2: 그 자리에서 재현할 수 있는 문제는 파일 모드로 짧고 확실하게 캡처하고, 시점을 모르는 문제는 기본 메모리 모드 링 버퍼로 기다린다. 부팅이나 로그온 중의 문제는 부트 트레이스를 쓴다. 어느 쪽이든 start / 재현 / stop 절차는 같다.
3.1. Memory 모드와 File 모드 ── 재현할 수 있는가, 기다리는가
WPR에는 기록처 모드가 둘 있고, 기본은 Memory 모드(메모리상의 링 버퍼)입니다. 오래된 이벤트부터 덮어쓰는 링 버퍼이므로, 시점을 모르는 장애를 기다리며 캡처를 켜 두고, 일어나면 멈추는 데 맞습니다. -filemode를 붙이면 File 모드로 바뀌고, 모두 연속 파일에 기록됩니다. 덮어쓰지 않으며, 상한은 디스크 여유뿐이고 파일은 한없이 커집니다.10
flowchart TB
accTitle: Memory 모드와 File 모드의 기록 방식
accDescr: Memory 모드는 메모리상의 링 버퍼에 기록하며 오래된 이벤트는 덮어쓰고 최근만 남으므로 대기하는 데 맞다. File 모드는 파일에 모두 남기지만 상한은 디스크 여유뿐이므로 짧고 확실한 재현에 맞다
ev["ETW 이벤트"] --> ring["Memory 모드: 링 버퍼"]
ev --> filem["File 모드: 파일을 키운다"]
ring -.-> use1["시점을 모를 때 기다린다"]
filem -.-> use2["짧고 확실하게 재현"]
그림 3: Memory 모드는 메모리상의 링 버퍼에 기록하며 오래된 이벤트는 덮어쓰고 최근만 남으므로 대기하는 데 맞다. File 모드는 파일에 모두 남기지만 상한은 디스크 여유뿐이므로 짧고 확실한 재현에 맞다.
선택 경험칙은 다음과 같습니다.
- 그 자리에서 재현 가능 → File 모드. 재현 직전에 시작하고 직후에 멈추며, 캡처를 수분 이내로 둡니다
- 시점 불명 → Memory 모드(기본)로 기다립니다. 일어나면 바로
wpr -stop - GeneralProfile 수분만으로도 ETL은 수백 MB에서 GB급이 됩니다. 너무 큰 파일은 WPA에서 분석할 수 없게 되므로, 「오래 캡처할수록 좋다」는 역효과입니다.1011
GUI로 캡처하려면 WPRUI를 시작해 프로필과 Logging mode를 고르고 Start/Save 하면 됩니다. 공식 How-to가 절차를 정리합니다.11 고객 현장 담당자에게 캡처를 부탁할 때는 위의 세 명령을 절차서에 그대로 넣을 수 있습니다.
4. WPA를 읽는 기본 ── 그래프, 표의 황금률, 시간 좁히기
캡처한 ETL을 WPA에서 열면, 왼쪽 Graph Explorer에 System Activity, Computation, Storage, Memory 같은 분류의 그래프 썸네일이 나열됩니다.12 보고 싶은 그래프를 오른쪽 Analysis 탭으로 끌면 위에 그래프, 아래에 표가 나타납니다. 먼저 받아들일 것은 세 가지입니다.
- 표의 황금률 ── 열 순서가 그룹을 정한다. WPA 표에는 금색과 파란색 세로 막대가 둘 있고, 금색 막대 왼쪽 열은 그 순서로 데이터를 계층화(그룹)하고, 파란색 막대 오른쪽 열은 집계입니다.13 Process → Stack으로 두면 프로세스별 스택 집계가 되고, Stack → Process로 두면 같은 스택을 쓰는 모든 프로세스의 집계가 됩니다── 열을 끌어 순서를 바꾸는 것 자체가 분석 조작입니다. 이 한 점을 이해하면 모든 WPA 표를 같은 방식으로 읽습니다.
flowchart TB
accTitle: 표의 황금률 ── 두 막대와 열의 역할
accDescr: 금색 막대 왼쪽 열은 그 순서로 데이터를 계층화하고, 금색과 파란색 사이는 표시 열이며, 파란색 막대 오른쪽 열은 집계이다. 열을 끌어 순서를 바꾸는 것 자체가 분석 조작이다
left["금색 왼쪽: 그룹화"] --> gold["금색 막대"]
gold --> mid["막대 사이: 표시"]
mid --> blue["파란색 막대"]
blue --> right["파란색 오른쪽: 집계"]
left -.-> op["열을 끌어 분석한다"]
그림 4: 금색 막대 왼쪽 열은 그 순서로 데이터를 계층화하고, 금색과 파란색 사이는 표시 열이며, 파란색 막대 오른쪽 열은 집계이다. 열을 끌어 순서를 바꾸는 것 자체가 분석 조작이다.
- 시간 범위를 좁힌다. 그래프에서 끌어 범위를 고른 뒤 오른쪽 클릭하고 “Zoom”하면, 집계가 그 구간만으로 바뀝니다. 성능 조사는 원칙적으로 항상 「문제가 일어나던 구간」만 봅니다(9장).
- 심볼을 설정한다. 스택을 함수 이름으로 읽으려면 메뉴에서 Trace > Load Symbols를 실행합니다.14 기본은 Microsoft 공개 심볼 서버(msdl.microsoft.com)를 참조하므로, 인터넷이 되면 Windows 자체의 스택은 해석됩니다. 자기 앱의 함수 이름을 보려면 Trace > Configure Symbol Paths에 앱 PDB 폴더를 추가합니다.8 PDB가 무엇인지, Release 빌드에서도 왜 반드시 남겨야 하는지는 「PDB(프로그램 데이터베이스)란 무엇인가?」에 정리되어 있습니다. .NET Framework NGen 네이티브 이미지에 대해 WPR은 캡처 때 NGen PDB(.ngenpdb)를 만들어 트레이스 옆 폴더에 두고, WPA는 자동으로 참조합니다.8 이것은 NGen 이미지 전용 메커니즘이며, 보통의 JIT .NET 앱 코드는 대상 밖입니다. JIT 코드 주소에서 함수 이름으로의 매핑은 CLR이 내는 JIT 이벤트로 해석되므로, .NET 앱을 조사할 때는 CLR 프로바이더(Microsoft-Windows-DotNETRuntime과 대응하는 Rundown)를 켜는 기록 프로필(.wprp)을 준비하고, 3장의 자기 프로바이더와 같이
wpr -start GeneralProfile -start MyDotNet.wprp!profile-name으로 조합해 CLR 이벤트가 트레이스에 들어가게 합니다(로컬 WPR이 제공하는 내장 프로필은wpr -profiles로 확인할 수 있습니다). 그 위에, 소스 줄 매핑용으로 빌드가 만든 PDB를 남겨 위의 심볼 경로에 추가합니다.
flowchart TB
accTitle: 스택을 함수 이름으로 읽기 위한 심볼 해석
accDescr: Trace Load Symbols를 실행하면 Windows 자체는 Microsoft 공개 심볼 서버에서, 자기 앱은 심볼 경로에 추가한 빌드 PDB에서 해석된다. NGen 이미지는 WPR이 만드는 NGen PDB를 쓰고, JIT .NET 코드는 트레이스의 CLR JIT 이벤트와 빌드 PDB로 해석된다
load["Trace > Load Symbols"] --> ms["Windows: 공개 심볼"]
load --> own["자기 앱: 빌드 PDB"]
ms -.-> ngen["NGen: WPR .ngenpdb"]
own -.-> jit["JIT: CLR 이벤트 + PDB"]
그림 5: Trace Load Symbols를 실행하면 Windows 자체는 Microsoft 공개 심볼 서버에서, 자기 앱은 심볼 경로에 추가한 빌드 PDB에서 해석된다. NGen 이미지는 WPR이 만드는 NGen PDB를 쓰고, JIT .NET 코드는 트레이스의 CLR JIT 이벤트와 빌드 PDB로 해석된다.
준비가 되면 다음 분기에서 들어갑니다. 그 구간에서 CPU가 높았는가, 낮았는가? 높으면 5장(Sampled), 낮은데도 느리면 6장(Precise).
flowchart TB
accTitle: 증상에서 WPA 그래프를 고르는 분기
accDescr: 문제 구간으로 줌한 뒤, CPU가 높으면 CPU Usage Sampled로, 낮은데도 느리면 단일 코어/단일 스레드 고정을 확인한 뒤 CPU Usage Precise의 대기 분석으로, 디스크가 의심되면 Disk Usage와 File IO로 간다
zoom["문제 구간으로 줌"] --> cpu{"그 구간의 CPU는?"}
cpu -->|"높음"| sampled["5장: Sampled"]
cpu -->|"낮은데도 느림"| core{"1코어 / 1스레드 고정?"}
core -->|"예"| sampled
core -->|"아니오"| precise["6장: Precise"]
cpu -->|"디스크 의심"| disk["7장: Disk / File I/O"]
그림 6: 문제 구간으로 줌한 뒤, CPU가 높으면 CPU Usage Sampled로, 낮은데도 느리면 단일 코어/단일 스레드 고정을 확인한 뒤 CPU Usage Precise의 대기 분석으로, 디스크가 의심되면 Disk Usage와 File IO로 간다.
5. CPU가 높을 때 ── CPU Usage (Sampled)로 「누가 어느 함수를 태우고 있는가」
CPU가 박혀 있으면 보는 것은 CPU Usage (Sampled)입니다. 모든 CPU에서 약 1밀리초마다 「지금 어느 프로세스의 어느 스택이 돌고 있는가」를 기록한 샘플링 데이터이며, 샘플 수의 비율이 곧 CPU 시간의 내역입니다.6
flowchart TB
accTitle: CPU Usage Sampled의 동작
accDescr: 약 1밀리초마다 모든 CPU에서 돌고 있는 스택을 기록하고, 집계한 샘플 비율이 CPU 시간의 내역이 된다. 프로세스에서 스레드, 스택, 함수로 읽는다. 샘플 사이에 끝나는 짧은 활동은 나타나지 않는다
tick["약 1ms마다의 인터럽트"] --> snap["돌고 있는 스택을 기록"]
snap --> agg["샘플 비율 = CPU 내역"]
agg --> drill["프로세스 → 스레드 → 스택"]
snap -.-> miss["샘플 사이 활동은 빠진다"]
그림 7: 약 1밀리초마다 모든 CPU에서 돌고 있는 스택을 기록하고, 집계한 샘플 비율이 CPU 시간의 내역이 된다. 프로세스에서 스레드, 스택, 함수로 읽는다. 샘플 사이에 끝나는 짧은 활동은 나타나지 않는다.
- Graph Explorer의 Computation에서 CPU Usage (Sampled)를 Analysis 탭에 두고 Utilization by Process, Stack 프리셋을 고릅니다.5
- Weight(또는 Count) 내림차순으로 프로세스를 봅니다. 작업 관리자에서 「50%」였던 것의 정체가 먼저 프로세스 수준에서 분명해집니다.
- 범인 프로세스의 Stack 열을 펼칩니다. 스택은 트리로 집계되며, 분기에서 숫자가 크게 떨어지지 않는 길을 따라가면 CPU를 태우는 함수에 도착합니다. 심볼이 해석되면 자기 코드의 어느 함수인지까지 일직선입니다.
- 트리 펼치기가 번거로우면 그래프 표시를 Flame으로 바꿉니다. 폭 = CPU 시간 점유율로 그려지므로, 어느 호출 경로가 지배적인지가 한눈에 보입니다. CPU Usage (Sampled)에는 Flame by Process, Stack 프리셋도 있습니다.13
주의가 하나 있습니다. 샘플링이므로 샘플 사이에 끝나는 짧은 활동은 나타나지 않습니다.6 「합쳐서 어디서 CPU가 쓰였는가」를 보는 도구이지, 호출당 정확한 시간을 재는 도구가 아님을 기억하십시오.
6. CPU가 낮은데도 느릴 때 ── CPU Usage (Precise)와 대기 분석
이 글의 핵심입니다. 다만 대기 분석으로 가기 전에 확인할 것이 있습니다. 「전체 CPU 사용률이 낮다」는 「CPU가 병목이 아니다」가 아닙니다. 16코어 PC에서 한 코어에 박힌 직렬 작업(UI 스레드 하나가 풀가동)은 전체로 약 6%로만 보입니다. 먼저 5장의 Sampled(또는 CPU Usage (Precise)의 Utilization by CPU)에서 특정 코어나 스레드에 고정이 없는지 확인하고, 없으면 이 장으로 옵니다── 작업은 돌지 못하는 것이 아니라 기다리고 있습니다. 무엇을 기다리는지 알려 주는 것이 CPU Usage (Precise)입니다.
Sampled가 샘플링이라면, Precise는 컨텍스트 스위치(스레드 전환)의 완전 기록입니다. 스레드가 대기에 들어가고, 누군가에게 깨어나고(Ready), CPU에 착지하는── 그 왕복이 한 행씩 남고, 다음 열을 읽을 수 있습니다.74
| 열 | 의미 |
|---|---|
| NewThreadStack | 그 스레드가 어느 스택에서 대기에 들어갔는지(= 멈출 때 무엇을 하고 있었는지) |
| Waits (us) | 얼마나 기다렸는지 |
| Ready (us) | 깨어난 뒤 CPU에 착지할 때까지 얼마나 기다리게 되었는지(CPU 경합) |
| ReadyingProcess / ReadyingThreadId | 그 스레드를 깨운(대기를 해제한) 프로세스와 스레드 |
| ReadyThreadStack | 깨운 쪽이 어느 스택에서 깨웠는지 |
flowchart TB
accTitle: 대기 한 왕복과 열의 대응
accDescr: 스레드는 NewThreadStack에 남는 스택에서 대기에 들어가 Waits 시간만큼 기다린다. 누군가가 깨우면 그 쪽이 ReadyingProcess와 ReadyThreadStack에 남고, CPU 경합의 Ready 시간을 기다린 뒤 다시 돈다
run1["실행 중"] -->|"대기 진입"| waitst["대기(Waits us)"]
waitst -->|"누군가가 깨움"| ready["Ready(CPU 경합)"]
ready -->|"CPU에 착지"| run2["다시 실행"]
waitst -.-> col["NewThreadStack / ReadyingProcess"]
그림 8: 스레드는 NewThreadStack에 남는 스택에서 대기에 들어가 Waits 시간만큼 기다린다. 누군가가 깨우면 그 쪽이 ReadyingProcess와 ReadyThreadStack에 남고, CPU 경합의 Ready 시간을 기다린 뒤 다시 돈다.
읽는 패턴은 다음과 같습니다.4
- Utilization by Process, Thread 프리셋을 적용하고 NewThreadStack과 ReadyThreadStack을 열에 추가합니다.
- 먼저 지연된 작업을 실행하고 있던 스레드(설명 UI 스레드, 해당 요청을 처리하던 스레드)를 특정합니다. 총 Waits 내림차순만 보면, 메시지 펌프나 타이머처럼 「일부러 계속 기다리는」 스레드가 위를 차지하므로 헷갈립니다. 대상 스레드를 찾은 뒤, CPU Usage (ms)가 크면 5장의 CPU 문제이고, Waits가 지배하면 대기 문제입니다.
- NewThreadStack을 펼쳐 멈출 때 무엇을 하고 있었는지를 봅니다.
WaitForSingleObject나EnterCriticalSection이면 락 대기,ReadFile같은 동기 I/O 안이면 I/O 대기, 소켓 수신 안이면 상대 응답 대기입니다. - 다음으로 누가 대기를 해제했는지를 봅니다. ReadyThreadStack을 펼치고 ReadyingProcess / ReadyingThreadId를 확인합니다. 커널의
KiTimerExpiration에서 깨어났으면 타이머(= 타임아웃까지 잠들었다)이고, I/O 완료 처리에서 깨어났으면 I/O 대기임이 확정됩니다.4 - 깨운 쪽이 다른 스레드나 다른 프로세스이면 그 스레드를 같은 절차로 조사합니다. 「A는 B가 락을 풀기를 기다렸고, B는 C의 RPC 응답을 기다렸고, C는 디스크 I/O를 기다렸다」── 이 사슬을 뿌리까지 따라간 것이 지연의 크리티컬 패스입니다.7
flowchart TB
accTitle: 대기 분석에서 따라가는 크리티컬 패스 사슬
accDescr: 지연된 스레드 A의 NewThreadStack에서 멈출 때 무엇을 했는지 보고, ReadyThreadStack과 ReadyingProcess에서 깨운 쪽 B를 특정한 뒤, B를 같은 절차로 뿌리 디스크 I/O까지 조사한다
a["스레드 A(지연된 작업)"] -->|"락 대기"| b["스레드 B(락을 쥐고 있음)"]
b -->|"RPC 대기"| c["프로세스 C"]
c -->|"동기 I/O 대기"| d["디스크 I/O(뿌리)"]
d -.->|"완료가 C를 깨움"| c
c -.->|"응답이 B를 깨움"| b
b -.->|"락 해제가 A를 깨움"| a
그림 9: 지연된 스레드 A의 NewThreadStack에서 멈출 때 무엇을 했는지 보고, ReadyThreadStack과 ReadyingProcess에서 깨운 쪽 B를 특정한 뒤, B를 같은 절차로 뿌리 디스크 I/O까지 조사한다.
「멀티스레드로 만들었는데 빨라지지 않았다」는 경우, 이 절차는 모든 워커가 하나의 락에 줄 서 있는 모습을 그대로 보여 줍니다. 설계로 락 경합을 피하는 법은 「멀티스레드 실무 베스트 프랙티스: .NET 편」에서, 동기 I/O로 기다리지 않고 완료 알림으로 도는 Windows 메커니즘은 「I/O 완료 포트(IOCP)와 .NET 스레드 풀」에서 다룹니다. WPA에서 「누구를 기다렸는지」를 집어내고, 그 설계 논거로 고치는 것이 하나의 연속된 흐름입니다.
7. 디스크와 파일 I/O ── 「누군가가 디스크를 훑고 있다」를 특정하기
「PC 전체가 느리다」의 고전적 범인은 CPU가 아니라 디스크입니다. Storage 분류의 Disk Usage와 File I/O로 조사합니다.15
Disk Usage는 디스크 I/O의 기록이며, 중요한 열이 둘입니다. Disk Service Time은 디스크 장치가 그 I/O를 실제로 처리한 시간이고, IO Time은 I/O가 OS 큐에 들어간 뒤 완료될 때까지의 시간입니다. IO Time은 항상 Service Time 이상이며 큐잉분만큼 길므로, IO Time이 Service Time보다 훨씬 길면 그 I/O는 「큐에서 기다리고 있던」 것입니다.6 다만 그것만으로는 큐를 만든 범인이 다른 프로세스인지, 그 프로세스 자신의 무거운 I/O가 느린 장치에 줄 선 것인지는 정해지지 않습니다. 여기서 결론 내지 말고, Service Time(장치 자체의 응답)과 다음의 프로세스·경로·스택별 내역으로 정산합니다.
이어서 Utilization by Process, Path Name, Stack 프리셋으로, 어느 프로세스가 어느 파일에 어느 스택에서 I/O를 냈는지를 IO Time 또는 Size 내림차순으로 봅니다.15 현장에서 자주 나오는 답은 이 둘입니다.
- 백신이 모든 파일을 스캔하고 있었다. 앱 기동이 느린 창에서 백신 프로세스가 대량 읽기를 내는 것이 보입니다. 프로세스 이름, 경로, 볼륨이 제외 논의의 증거 그대로입니다.
- 다른 프로세스가 대량으로 쓰고 있었다. 백업, 인덱서, 로그를 너무 많이 씀 등. 쓰기가 디스크에 도달하는 시점에는 캐시 관리자가 관여하므로, 「쓴 순간」과 「디스크가 바쁜 순간」이 어긋날 수 있다는 점도 「캐시 관리자 ── WriteFile은 언제 디스크에 도달하는가?」에서 다룹니다.
File I/O는 한 층 위, 앱이 낸 파일 조작(Create/Read/Write 등)의 기록이며, Duration by Process, Thread, Type 같은 프리셋으로 파일 이름과 조작별 시간을 집계할 수 있습니다.15 파일 시스템이나 필터 드라이버에서 시간을 쓰고 디스크에 도달하지 않는 경우는 Disk Usage에 나타나지 않으므로, 「Disk Usage는 평온한데 File I/O가 느리다」는 어긋남 자체가 단서입니다. 동기·비동기 I/O의 구조부터 보고 싶으면 「동기 I/O와 비동기 I/O ── OVERLAPPED의 진짜 의미」를 보십시오.
flowchart TB
accTitle: File IO와 Disk Usage가 보는 서로 다른 층
accDescr: 앱의 파일 조작은 파일 시스템과 필터 드라이버를 지나 OS I/O 큐에서 디스크 장치로 간다. File IO는 위층의 조작을 기록하고, Disk Usage는 디스크에 도달한 I/O를 기록하며, IO Time과 Disk Service Time의 차이가 큐 시간이다
app["앱: ReadFile / WriteFile"] --> fio["FS와 필터(File I/O)"]
fio --> queue["OS I/O 큐"]
queue --> dev["디스크 장치(Disk Usage)"]
fio -.-> n1["Disk Usage에 빠진다"]
queue -.-> n2["IO Time − Service Time"]
dev -.-> n3["Service Time = 장치"]
그림 10: 앱의 파일 조작은 파일 시스템과 필터 드라이버를 지나 OS I/O 큐에서 디스크 장치로 간다. File IO는 위층의 조작을 기록하고, Disk Usage는 디스크에 도달한 I/O를 기록하며, IO Time과 Disk Service Time의 차이가 큐 시간이다.
「메모리가 모자라 스와핑하는 것 아닌가」라는 생각은 WPA로 가기 전에 작업 관리자와 리소스 모니터로 1차 분리할 수 있습니다. 다만 커밋 메모리만으로 기각하지 마십시오── 커밋에 여유가 있어도, 물리 메모리 압력이 워킹 셋을 줄이고 하드 폴트가 이어지는 상황은 가능합니다. 사용 가능 물리 메모리와 리소스 모니터의 「Hard Faults/sec」도 확인하십시오. 읽는 법은 「Windows의 「메모리 사용량」은 무엇을 나타내는가?」에 있습니다.
8. 느린 부팅과 로그온 ── 부트 트레이스의 입구
「기동에 3분이 걸린다」형은 wpr -start를 손으로 돌리기 전에 끝납니다. WPR에는 부트 트레이스가 있어, 다음 부팅에서 OS가 자동으로 기록을 시작하게 할 수 있습니다.3
:: 1. Arrange automatic recording on the next boot
wpr -boottrace -addboot GeneralProfile -filemode
:: 2. Restart (reproduce the slow boot)
:: 3. After boot, stop recording and save (the arrangement is also cleared)
mkdir C:\temp 2>nul
wpr -boottrace -stopboot C:\temp\boot.etl "Issue where boot takes 3 minutes"
flowchart TB
accTitle: 부트 트레이스 흐름
accDescr: addboot로 다음 부팅의 자동 기록을 예약하고 다시 시작하면, OS가 부팅 때 자동으로 기록을 시작한다. 로그온 뒤 stopboot로 저장하면 예약도 해제된다. 포기하려면 cancelboot로 지운다
add["wpr -boottrace -addboot"] --> rebootpc["다시 시작(느린 부팅)"]
rebootpc --> auto["OS가 부팅 때 기록"]
auto --> stop2["로그온 뒤: -stopboot"]
add -.-> cancel["포기: -cancelboot"]
그림 11: addboot로 다음 부팅의 자동 기록을 예약하고 다시 시작하면, OS가 부팅 때 자동으로 기록을 시작한다. 로그온 뒤 stopboot로 저장하면 예약도 해제된다. 포기하려면 cancelboot로 지운다.
예전에 xbootmgr이 맡던 부팅·종료 측정도 현재 WPR에서는 -onoffscenario Boot 같은 옵션으로 돌릴 수 있습니다.3 캡처한 트레이스는 앞 장과 같은 도구로 읽습니다. Processes 그래프에서 어느 프로세스가 언제 태어났는지를 타임라인으로 보고, 부팅이 막힌 창으로 줌한 뒤 CPU·대기·디스크로 분류하면── 기동 앱이 무언가에 직렬로 기다리거나, 서비스 기동이 특정 I/O에 막힌 것이 보입니다. 부팅 분석은 그 자체로 깊은 전문 영역이므로, 이 글은 「손으로 못 잡는 문제도 WPR로 캡처할 수 있다」는 입구까지만 갑니다. 먼저 GeneralProfile 부트 트레이스로 전체 그림을 잡으십시오.
9. 작업 패턴 ── 분류 → 줌 → 스택을 반복한다
도구가 분명해졌으니, 조사 전체의 패턴입니다.
- 현상의 시각을 고정합니다. 「느렸다」가 아니라 「10:23:40–10:24:10이 느렸다」. 앱 로그, 이벤트 로그, 조작한 사람의 메모── 무엇이든 됩니다. 자기 앱이 ETW나 이벤트 로그에 체크포인트를 쓰면, 트레이스 안의 이벤트가 그대로 시간 말뚝이 됩니다.
- 그 구간만으로 줌합니다. 트레이스 전체의 집계는 평균화되어, 중요한 이상이 희석됩니다. WPA 분석은 항상 「이상이던 구간」과 「정상이던 구간」의 비교입니다.
- 먼저 「CPU인가, 대기인가, I/O인가」로 분류합니다. CPU Usage (Sampled)를 보고 타고 있으면 5장. 타고 있지 않으면 CPU Usage (Precise)의 Waits(6장). Disk Usage IO Time이 부풀어 있으면 7장. 이 세 갈래를 먼저 타면 길을 잃지 않습니다.
- 가설 → 줌 → 스택을 반복합니다. 「백신인가?」라고 생각하면 그 프로세스로 좁혀 스택으로 뒷받침합니다. 성립하지 않으면 다음 가설. 스택까지 따라가 뒷받침하기 전에 결론을 내지 않는 것이 이런 조사의 규율입니다.
flowchart TB
accTitle: 성능 조사의 반복 루프
accDescr: 현상의 시각을 고정하고, 구간으로 줌하고, CPU / 대기 / I/O로 분류하고, 가설을 세워 좁히고, 스택으로 뒷받침한다. 성립하면 원인이 확정되고, 아니면 다음 가설로 반복한다
time["시각을 고정"] --> zoomstep["그 구간으로 줌"]
zoomstep --> triage["CPU / 대기 / I/O로 분류"]
triage --> hypo["가설과 좁히기"]
hypo --> stack["스택으로 뒷받침"]
stack -->|"성립"| fix["원인 확정"]
stack -->|"아님"| hypo
그림 12: 현상의 시각을 고정하고, 구간으로 줌하고, CPU / 대기 / I/O로 분류하고, 가설을 세워 좁히고, 스택으로 뒷받침한다. 성립하면 원인이 확정되고, 아니면 다음 가설로 반복한다.
마지막으로 캡처 파일의 취급입니다. ETL 파일은 시스템의 내부를 넓게 반영합니다: 모든 프로세스의 이름, 연 파일의 경로, 로드한 모듈, (프로필에 따라) 레지스트리 키 이름. 표준 GeneralProfile 캡처에는 통신 내용 같은 데이터 본문은 들어가지 않지만, 커스텀 프로바이더를 켰다면 그 이벤트의 페이로드(앱이 기록한 문자열 등)가 그대로 들어갑니다. 켠 프로바이더가 무엇을 내는지 확인한 뒤, 사외로 나가도 될 만큼 기밀한 파일로 취급하십시오. 패킷 캡처와 같이 필요한 최소 캡처, 넘기는 상대와의 합의, 보관 기간과 삭제를 절차에 넣습니다.
flowchart TB
accTitle: ETL 파일이 반영하는 것과 취급
accDescr: ETL은 모든 프로세스 이름, 연 파일의 경로, 모듈, 프로필에 따라 레지스트리 키 이름을 반영하고, 커스텀 프로바이더를 켜면 그 페이로드도 들어간다. 필요한 최소 캡처, 상대와의 합의, 보관 기간과 삭제로 기밀로 취급한다
etl["ETL 파일"] --> a1["이름, 경로, 모듈"]
a1 --> a2["레지스트리 키(일부)"]
a2 --> a3["커스텀 페이로드"]
a3 -.-> rule["기밀로 취급한다"]
그림 13: ETL은 모든 프로세스 이름, 연 파일의 경로, 모듈, 프로필에 따라 레지스트리 키 이름을 반영하고, 커스텀 프로바이더를 켜면 그 페이로드도 들어간다. 필요한 최소 캡처, 상대와의 합의, 보관 기간과 삭제로 기밀로 취급한다.
10. 정리
- 작업 관리자가 설명하지 못하는 「PC 전체가 느리다」는 OS 전체 ETW 트레이스로 조사합니다── WPR로 캡처하고 WPA로 읽습니다. wpr.exe는 Windows 8.1 이후 OS에 포함되므로, 고객 환경에서 캡처하고 ETL을 가져와 자기 PC의 WPA로 읽는 분리가 성립합니다.
- 캡처는
wpr -start GeneralProfile -filemode→ 재현 →wpr -stop trace.etl의 세 단계입니다. 재현할 수 있으면 수분 이내의 File 모드, 기다리면 Memory 모드(링 버퍼). 길수록 좋은 것이 아닙니다. - WPA는 표의 황금률(금색 막대 왼쪽 = 그룹화), 시간 범위 줌, 심볼 설정(자기 앱은 PDB가 필요) 세 점을 받아들이면 시작할 수 있습니다.
- CPU가 높으면 CPU Usage (Sampled)에서 프로세스 → 스택 → 함수를 따라갑니다. CPU가 낮은데도 느리면 CPU Usage (Precise)에서 NewThreadStack(멈출 때 무엇을 했는지) → Waits(얼마나 기다렸는지) → ReadyingProcess와 ReadyThreadStack(누가 깨웠는지)의 사슬을 뿌리까지 따라갑니다.
- 디스크는 Disk Usage IO Time과 Service Time의 차이로 「큐에서 쓴 시간」을 보고, Service Time과 프로세스·경로·스택별 내역으로 원인(장치 자체가 느린지, 누가 큐를 만들었는지)을 특정합니다. 느린 부팅은
wpr -boottrace로 캡처할 수 있습니다. - 작업 패턴은 (1) 시각을 고정 (2) 구간으로 줌 (3) CPU·대기·I/O로 분류 (4) 가설 → 줌 → 스택을 반복. ETL은 내부 정보가 들어가므로 기밀로 취급합니다.
WPA 화면은 위압적이고, 누구나 첫 한 시간은 길을 잃습니다. 다만 「금색 막대 왼쪽이 그룹화」와 「Sampled는 어디서 태웠는지, Precise는 누구를 기다렸는지」라는 두 뼈대가 들어가면, 나머지는 같은 조작의 반복입니다. 다음에 「CPU에 여유가 있는데도 느리다」는 상담이 오면, 작업 관리자를 닫고 트레이스를 캡처하십시오.
관련 기사
- PerfView와 dotnet-trace로 “느림”의 원인을 찾는다 ── .NET 성능 조사 실무 입문
- Process Monitor(ProcMon) 실전 가이드 ── 「설정이 반영되지 않는다」「ACCESS DENIED」를 10분 만에 특정하기
- Windows 이벤트 로그·ETW 입문 ── 업무 앱의 로그를 OS 표준 체계에 올리기
- Windows의 「메모리 사용량」은 무엇을 나타내는가 ── Working Set·Private Bytes·Commit·페이지 파일을 올바르게 읽는 법
- Windows의 「프로세서 스케줄링」을 「백그라운드 서비스」로 바꾸면 무슨 일이 일어나는가 - quantum, 우선도 부스트, P 코어 / E 코어까지 정리
- PDB(프로그램 데이터베이스)란 무엇인가 - 디버그 정보·심볼·Source Link 이해하기
관련 상담 영역
합동회사 코무라소프트에서는 「PC 전체가 느려졌는데 이유를 모르겠다」, 「CPU에 여유가 있는데도 앱이 느리다」, 「특정 환경만 기동이 극단적으로 느리다」 같은 시스템 전체 성능 문제의 조사를 다룹니다. WPR/WPA 캡처 설계(어느 환경에서, 어느 프로필로, 얼마나 캡처할지), 트레이스 분석, 그 결과로 이어지는 앱 측·설정 측 수정을 하나의 연속된 의뢰로 다룹니다.
참고 링크
-
Microsoft Learn, Introduction to WPR. WPR이 ETW 기반 성능 기록 도구라는 점, 명령줄 판 WPR.exe가 Windows 8.1 이후 OS에 포함되어 추가 설치가 없다는 점, GUI 판 WPRUI.exe와의 관계, 기록 프로필의 생각에 대해. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Windows Performance Analyzer. WPA가 Windows ADK에 포함되며, WPR·Xperf 등이 기록한 ETW 이벤트로 그래프와 데이터 표를 만드는 분석 도구이고, 임의의 ETL 파일을 열어 분석할 수 있다는 점에 대해. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, WPR Command-Line Options. wpr -start/-stop/-cancel/-status/-profiles의 구문, -filemode(기본은 메모리 모드), 여러 프로필의 동시 지정, -boottrace(addboot/stopboot/cancelboot)의 부트 트레이스, -onoffscenario로 Boot 같은 On/Off 전이를 기록하는 점에 대해. ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, CPU Analysis. CPU Usage (Precise) 그래프 열(NewThreadStack, ReadyThreadStack, ReadyingProcess, Waits 등)의 정의, ReadyThreadStack을 펼쳐 ReadyingProcess/ReadyingThread를 따라가 대기의 근본 원인까지 가는 절차, KiTimerExpiration(타이머 대기)이나 I/O 완료에서의 깨움을 구별하는 법에 대해. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Troubleshoot processes and threads by using WPR and WPA. CPU 사용률이 높을 때 CPU Usage (Sampled)를 Process→Stack으로 읽는 구성, 대기 분석에서 CPU Usage (Precise)의 Readying Process, Readying Thread, Readying Stack, Wait 열을 쓰는 구성, 증상별 프로필과 그래프의 대응표에 대해. ↩ ↩2
-
Microsoft Learn, Exercise 2 - Evaluate Fast Startup Using Windows Performance Toolkit. CPU Usage (Sampled)가 약 1밀리초 간격의 샘플링이며 샘플 사이의 짧은 활동은 기록되지 않는다는 점, 프로세스 → 스레드 → 스택을 따라가 CPU 소비 내역을 특정하는 절차, Disk Usage IO Time(큐 시간 포함)과 Disk Service Time(디스크 처리 시간)의 의미에 대해. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Exercise 3 - Understand Critical Path and Wait Analysis. 크리티컬 패스 분석의 생각(Running / Ready / Waiting 분류), CPU Usage (Precise) 표 열 NewThreadStack, ReadyThreadStack, ReadyingProcess, Waits, Ready 등의 의미, 깨운 스레드를 차례로 따라가 지연 사슬을 푸는 절차에 대해. ↩ ↩2 ↩3
-
Microsoft Learn, Loading Symbols. _NT_SYMBOL_PATH가 비어 있으면 WPA가 기본적으로 Microsoft 공개 심볼 서버(msdl.microsoft.com)를 참조한다는 점, 자기 구성 요소용 PDB 경로 추가, WPR이 .NET 관리 심볼용 PDB를 트레이스 옆 .ngenpdb 폴더에 만들고 WPA가 자동 참조한다는 점에 대해. ↩ ↩2 ↩3
-
Microsoft Learn, Built-in Recording Profiles. WPR에 내장된 기록 프로필 목록(CPU usage, Disk I/O activity, File I/O activity, Registry I/O activity, Networking I/O activity 등)과 각 프로필이 기록하는 것에 대해. ↩
-
Microsoft Learn, Logging Mode. 기록 모드가 File(연속 파일)과 Memory(메모리상 링 버퍼)이며 기본이 Memory라는 점, Memory는 시점을 모르는 문제에 맞고 오래된 이벤트는 덮어쓴다는 점, File의 상한은 디스크 여유뿐이며 너무 큰 파일은 WPA에서 분석할 수 없게 된다는 점에 대해. ↩ ↩2
-
Microsoft Learn, WPR How-to Topics. WPRUI에서 기록을 시작하고 멈추는 절차, 프로필·상세 수준·Logging mode 선택, 긴 기록이 파일을 거대하게 만들어 WPA에서 분석할 수 없게 되므로 Memory 모드를 고르라는 주의에 대해. ↩ ↩2
-
Microsoft Learn, Graph Explorer. Graph Explorer 창이 System Activity, Computation, Storage, Memory 같은 분류의 그래프 썸네일을 나열한다는 점, 그래프를 Analysis 탭으로 끌면 표와 함께 표시된다는 점에 대해. ↩
-
Microsoft Learn, Graphs (WPA Features). WPA의 Flame 그래프 표시, 금색 막대 왼쪽 열이 그룹화이고 파란색 막대 오른쪽 열이 집계인 표 구조, CPU Usage (Sampled)의 Flame by Process, Stack 프리셋에 대해. ↩ ↩2
-
Microsoft Learn, Load Symbols or Configure Symbol Paths. WPA Trace 메뉴의 Load Symbols로 심볼을 로드하는 점, Configure Symbol Paths 대화상자에서 심볼 경로를 설정·변경하는 절차에 대해. ↩
-
Microsoft Learn, List of WPA Graphs. WPA에서 쓸 수 있는 그래프 목록. Disk Usage의 IO Time by Process, IO Type, Service Time by Process, Path Name, Stack, Utilization by Process, Path Name, Stack 같은 프리셋, File I/O의 Duration by Process, Thread, Type 같은 프리셋에 대해. ↩ ↩2 ↩3
관련 기사
같은 태그를 공유하는 최신 기사입니다. 더 가까운 주제로 지식을 넓힐 수 있습니다.
PerfView와 dotnet-trace로 “느림”의 원인을 찾는다 ── .NET 성능 조사 실무 입문
업무 앱이 “느리다”, “CPU를 다 써버린다”, “가끔 멈춘다”고 할 때, 어떤 도구로 무엇을 봐야 할까요. PerfView와 dotnet-trace의 역할 분담, CPU 샘플링을 읽는 방법(inclusive/exclusive), ThreadT...
절전에서 재개하면 깨지는 앱 ── Windows 전원 이벤트의 구조와 살아남는 업무 앱
노트북을 열었더니 업무 앱의 연결이 죽어 있었다 ── 원인은 절전을 전제하지 않은 설계입니다. WM_POWERBROADCAST 알림의 흐름, Modern Standby의 동작, 끊김/재연결 설계, 절전 억제, 조사 명령을 1차 정보로 정리합니다.
DllMain과 로더 락 ── 「DLL 초기화에서는 아무것도 하지 말라」는 말의 진짜 이유
DllMain에서 LoadLibrary를 호출하거나 다른 스레드와 동기화해서는 안 되는 이유를 설명합니다. 로더 락이 모든 DLL 알림을 직렬화하는 구조, 데드락이 구조적으로 확정되는 고전적 시나리오, 초기화를 미루는 올바른 설계, 행(hang)...
「응답 없음」의 정체 ── Windows가 앱의 행을 판단하는 방식과 멈추지 않는 설계
Windows의 「응답 없음」은 창이 5초 동안 메시지를 꺼내지 않았다고 OS가 판단해 고스트 창으로 바꾸는 메커니즘입니다. 그 판단의 내부, 행의 고전적 원인, 무거운 작업을 UI 스레드에서 떼어 내는 설계, 행 조사 절차를 정리합니다.
Windows 이벤트 로그·ETW 입문 ── 업무 앱의 로그를 OS 표준 체계에 올리기
Windows 업무 앱의 로그를 파일 로그만으로 해결하고 있지는 않은가요? 이벤트 로그와 ETW는 운영 담당자나 OS 표준 도구에서 보이는 또 다른 계층의 기록입니다. 3가지 수단을 구분해서 쓰는 방법, .NET에서 기록하는 방법, EventSo...
관련 토픽
이 기사와 가까운 토픽 페이지입니다. 기사를 출발점 삼아 관련 서비스와 다른 기사로 이어집니다.
Windows 기술 토픽
Windows 개발, 장애 조사, 기존 자산 활용에 관한 KomuraSoft LLC 기사를 모은 토픽 허브입니다.
이 주제와 연결되는 서비스
이 기사는 다음 서비스 페이지로 이어집니다. 가까운 입구부터 확인해 주세요.
Windows 앱 개발
상주 처리, 장비 연동, 운영 로그, 유지 보수 가능한 구조가 필요한 Windows 데스크톱 애플리케이션을 지원합니다.
자주 묻는 질문
이 기사 주제에 대해 상담 시 자주 나오는 질문을 모았습니다.
- WPR과 WPA는 어디서 구하나요? 소프트웨어를 설치할 수 없는 고객 환경에서도 쓸 수 있나요?
- 캡처 도구 wpr.exe(명령줄 판)는 Windows 8.1 이후 OS에 포함되어 있어 추가 설치 없이 쓸 수 있습니다. GUI 판인 WPRUI와 분석 도구 WPA(Windows Performance Analyzer)는 Windows ADK(Windows Assessment and Deployment Kit)에 들어 있어 별도 설치가 필요합니다. 실무에서는 「고객 환경에서는 OS 표준 wpr.exe만으로 ETL 파일을 캡처하고, 가져와서 자기 PC의 WPA로 분석한다」로 나누면, 소프트웨어를 추가할 수 없는 현장에서도 시스템 전체 성능을 조사할 수 있습니다.
- 작업 관리자에서 CPU에 여유가 있는데도 느린 이유는 무엇인가요? WPA에서는 무엇을 볼 수 있나요?
- CPU 사용률이 낮은데도 느릴 때는, 작업이 CPU를 쓰지 못하는 것이 아니라 「무언가를 기다리며」 멈춰 있는 것입니다. 락 경합, 동기 I/O 완료 대기, 다른 프로세스의 응답 대기가 전형입니다. 작업 관리자는 결과인 사용률만 보여 주고, WPA의 CPU Usage (Precise)는 컨텍스트 스위치 단위 기록에서 스레드가 어디서 대기를 시작했는지(NewThreadStack), 얼마나 기다렸는지(Waits), 누가 깨웠는지(ReadyingProcess, ReadyThreadStack)를 보여 줍니다. 기다리게 한 쪽을 따라가면 「느림의 범인」을 함수까지 특정할 수 있습니다.
- 트레이스는 얼마나 캡처해야 하나요? 파일이 거대해지지 않나요?
- 재현할 수 있으면, 재현 직전에 시작하고 직후에 멈춰 수분 이내로 두는 것이 기준입니다. WPR의 기본은 Memory 모드로, 메모리상의 링 버퍼에 기록하며 오래된 이벤트는 덮어쓰므로, 시점을 모르는 장애를 기다릴 때 맞습니다. -filemode의 File 모드는 연속 파일에 모두 남기지만, 상한은 디스크 여유뿐이고 너무 큰 파일은 WPA에서 분석할 수 없게 됩니다. 오래 기다릴 때는 Memory 모드, 짧고 확실한 재현에는 File 모드를 씁니다.
- PerfView와 WPA는 어떻게 구분하나요?
- 둘 다 ETW 트레이스를 다루지만 강점이 다릅니다. PerfView는 .NET 런타임을 깊게 이해하고 GC, 할당, JIT 같은 관리 앱 고유 조사에 강합니다. WPA는 OS 전체의 CPU, 디스크, 파일 I/O, 전원을 그래프와 표로 읽는 데 맞고, 「특정 앱이 아니라 PC 전체가 느리다」, 「여러 프로세스가 관여한다」, 「앱 밖(백신, 드라이버, 다른 프로세스)이 의심된다」일 때 첫 선택입니다. 자기 .NET 앱만의 느림은 PerfView, 시스템 전체의 느림은 WPR/WPA가 경험칙입니다.
- 고객의 운영 환경에서 WPR을 돌려도 되나요?
- 짧은 캡처는 실무에서 흔하지만, 무조건 안전한 것은 아닙니다. ETW는 가볍지만 스택까지 대량 이벤트를 기록하면 CPU와 메모리를 어느 정도 씁니다. 재현 직전에 시작하고 직후에 멈추기, 캡처를 수분 이내로 두기, 업무 영향이 적은 시간에 돌리기를 보통의 변경과 같은 승인 절차에 넣으십시오. 또한 ETL 파일에는 프로세스 이름, 파일 경로, 실행 파일 정보 같은 시스템 내부 정보가 들어가므로, 사외로 나갈 때의 취급(최소화, 보관 기간, 삭제)을 캡처 전에 정해 두십시오.