게임 클라이언트 크래시 미니덤프 자동 심볼릭화와 AI 요약 리포팅 구축 가이드
Windows 게임 클라이언트의 미니덤프를 PDB 심볼 서버로 자동 심볼화하고 콜스택·빌드·재현 정보를 AI가 근거 중심으로 요약해 Slack과 이슈 트래커에 전달하는 운영 파이프라인을 설명합니다. 개인정보 필터링과 심볼 불일치 대응까지 다룹니다.

핵심 요약
게임 클라이언트 크래시가 발생하면 MiniDumpWriteDump로 Windows 미니덤프를 수집하고 크래시 당시 사용된 실행 파일과 정확히 일치하는 PDB를 심볼 서버에서 찾아 콜스택을 함수명과 소스 위치로 복원합니다. 이후 예외 코드, 장애 모듈, 심볼화된 스택, 빌드 ID, 재현 정보를 개인정보 필터링한 뒤 AI에 전달하면 원인 후보와 다음 조치를 포함한 운영 리포트를 자동 생성할 수 있습니다.
핵심은 AI가 덤프 자체를 해석하도록 맡기는 것이 아니라 먼저 WinDbg 또는 CDB로 사실을 추출하고 AI는 정규화된 증거를 요약하도록 역할을 분리하는 것입니다.
미니덤프 자동 심볼릭화란 무엇인가?
미니덤프는 크래시 순간의 프로세스 상태를 분석할 수 있도록 일부 메모리와 스레드 정보를 저장한 파일입니다. 파일 크기가 전체 메모리 덤프보다 작아 클라이언트에서 업로드하기 쉽지만 실행 파일과 PDB가 없으면 콜스택이 주소와 모듈 오프셋만 표시될 수 있습니다.
심볼릭화는 다음 정보를 복원하는 과정입니다.
| 항목 | 심볼화 전 | 심볼화 후 |
|---|---|---|
| 호출 주소 | 0x00007ff9... | RenderThread::Submit() |
| 모듈 | RenderCore.dll+0x1A42F | RenderCore.dll!RenderThread::Submit+0x2F |
| 소스 위치 | 알 수 없음 | RenderQueue.cpp:184 |
| 빌드 식별 | 파일명 중심 | PE 체크섬, PDB GUID, Age, Build ID |
Windows PDB는 파일명이 같다는 이유만으로 사용할 수 없습니다. 실행 파일의 디버그 디렉터리에 기록된 PDB GUID와 Age가 덤프 분석 대상 PDB의 값과 일치해야 합니다. 따라서 배포 시점에 실행 파일, PDB, 빌드 메타데이터를 함께 보관해야 합니다.
전체 크래시 분석 파이프라인은 어떻게 구성하는가?
flowchart LR
A[클라이언트 크래시] --> B[미니덤프 저장]
B --> C[업로드 큐]
C --> D[빌드 ID 검증]
D --> E[WinDbg 또는 CDB 심볼화]
E --> F[스택 정규화 및 개인정보 필터링]
F --> G[AI 요약]
G --> H[Slack 및 이슈 트래커]
E --> I[심볼 실패 격리]
I --> H
권장 순서는 수집 → 업로드 → 빌드 검증 → 심볼화 → 정규화 → AI 요약 → 알림 및 이슈 등록입니다. 어느 단계에서 실패했는지 추적할 수 있도록 각 단계의 상태와 처리 시간을 별도 메타데이터로 남겨야 합니다.
1단계: 클라이언트에서 미니덤프를 안전하게 생성하기
Windows 네이티브 클라이언트에서는 DbgHelp.dll의 MiniDumpWriteDump를 사용할 수 있습니다. 실제 운영 환경에서는 크래시가 발생한 프로세스가 직접 덤프를 쓰기보다 별도의 덤프 헬퍼 프로세스나 Windows Error Reporting LocalDumps를 사용하는 편이 안정적입니다. 크래시 프로세스 내부에서 덤프를 생성하면 힙 손상이나 로더 락 때문에 수집 자체가 실패할 수 있기 때문입니다.
다음 코드는 개념을 보여 주는 최소 예제입니다.
#include <windows.h>
#include <dbghelp.h>
#include <string>
#pragma comment(lib, "dbghelp.lib")
static LONG WINAPI CrashFilter(EXCEPTION_POINTERS* exceptionPointers)
{
HANDLE file = CreateFileW(
L"client-crash.dmp",
GENERIC_WRITE,
FILE_SHARE_READ,
nullptr,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
nullptr);
if (file == INVALID_HANDLE_VALUE)
return EXCEPTION_EXECUTE_HANDLER;
MINIDUMP_EXCEPTION_INFORMATION exceptionInfo{};
exceptionInfo.ThreadId = GetCurrentThreadId();
exceptionInfo.ExceptionPointers = exceptionPointers;
exceptionInfo.ClientPointers = FALSE;
const MINIDUMP_TYPE dumpType =
static_cast<MINIDUMP_TYPE>(
MiniDumpWithDataSegs |
MiniDumpWithHandleData |
MiniDumpWithThreadInfo |
MiniDumpWithUnloadedModules |
MiniDumpWithIndirectlyReferencedMemory);
MiniDumpWriteDump(
GetCurrentProcess(),
GetCurrentProcessId(),
file,
dumpType,
&exceptionInfo,
nullptr,
nullptr);
CloseHandle(file);
return EXCEPTION_EXECUTE_HANDLER;
}
덤프 타입은 분석 품질과 개인정보 위험 사이의 균형으로 결정해야 합니다.
| 덤프 옵션 | 용도 | 주의점 |
|---|---|---|
MiniDumpNormal | 예외 코드와 기본 스레드 정보 확인 | 메모리 기반 원인 분석이 제한됨 |
MiniDumpWithThreadInfo | 스레드 상태와 대기 관계 분석 | 파일 크기가 증가함 |
MiniDumpWithHandleData | 핸들 누수나 리소스 상태 확인 | 파일에 민감한 경로가 포함될 수 있음 |
MiniDumpWithIndirectlyReferencedMemory | 포인터가 가리키는 일부 메모리 분석 | 개인정보나 토큰이 포함될 가능성 있음 |
MiniDumpWithFullMemory | 심층 메모리 분석 | 파일 크기와 보안 위험이 매우 큼 |
비밀번호, 인증 토큰, 결제 정보, 채팅 내용이 메모리에 남을 수 있는 게임이라면 업로드 전에 보존 정책과 접근 권한을 먼저 정해야 합니다. 원본 덤프는 제한된 저장소에 보관하고 AI 처리에는 원본이 아닌 분석 텍스트만 전달하는 것이 안전합니다.
2단계: 빌드 산출물과 심볼 서버를 연결하기
자동 심볼화의 품질은 분석 도구보다 산출물 관리 체계에 더 크게 좌우됩니다. 최소한 다음 정보를 빌드 단위로 묶어야 합니다.
| 산출물 | 필수 메타데이터 |
|---|---|
| 실행 파일 및 DLL | 파일명, 아키텍처, PE 체크섬, 빌드 ID |
| PDB | PDB GUID, Age, 컴파일 구성, 소스 커밋 |
| 클라이언트 패키지 | 버전, 배포 채널, 지역, 플랫폼 |
| 크래시 메타데이터 | 발생 시각, 세션 ID, 맵, 모드, 그래픽 API |
심볼 저장소는 보통 다음처럼 분리합니다.
- 공개 심볼: Microsoft Symbol Server에서 운영체제 DLL 심볼을 조회합니다.
- 사내 심볼: 사설 심볼 서버나 아티팩트 저장소에 프로젝트 PDB를 보관합니다.
- 캐시: CI 또는 분석 워커의 로컬 심볼 캐시를 사용해 반복 다운로드를 줄입니다.
중요한 원칙은 배포가 완료된 뒤에도 해당 빌드의 PDB를 삭제하지 않는 것입니다. 최신 브랜치의 PDB로 과거 덤프를 분석하면 함수명이 비슷하게 보이더라도 잘못된 결과가 나올 수 있습니다.
3단계: CDB로 심볼화 결과를 자동 수집하기
WinDbg의 명령행 버전인 cdb.exe는 CI 워커나 백엔드 분석 작업에 적합합니다. 아래 예제는 사내 심볼 경로와 Microsoft Symbol Server를 함께 사용하고 분석 결과를 텍스트 파일로 저장합니다.
param(
[Parameter(Mandatory = $true)] [string] $DumpPath,
[Parameter(Mandatory = $true)] [string] $CdbPath,
[Parameter(Mandatory = $true)] [string] $PrivateSymbolPath,
[Parameter(Mandatory = $true)] [string] $OutputDir
)
New-Item -ItemType Directory -Force -Path $OutputDir | Out-Null
$cachePath = Join-Path $OutputDir 'symbol-cache'
New-Item -ItemType Directory -Force -Path $cachePath | Out-Null
$symbolPath = 'srv*' + $cachePath + '*https://msdl.microsoft.com/download/symbols;' + $PrivateSymbolPath
$command = '!analyze -v; .ecxr; kv 80; lmvm; q'
$analysisPath = Join-Path $OutputDir 'analysis.txt'
& $CdbPath '-z' $DumpPath '-y' $symbolPath '-c' $command 2>&1 |
Out-File -FilePath $analysisPath -Encoding utf8
$metadata = [ordered]@{
dump_sha256 = (Get-FileHash -Algorithm SHA256 -LiteralPath $DumpPath).Hash
analyzed_at = (Get-Date).ToUniversalTime().ToString('o')
symbol_path = $symbolPath
analysis_file = $analysisPath
}
$metadata | ConvertTo-Json | Out-File -FilePath (Join-Path $OutputDir 'metadata.json') -Encoding utf8
운영 코드에서는 lmvm 대상 모듈을 크래시 모듈 목록에서 동적으로 지정하고 cdb.exe의 종료 코드와 출력에 *** ERROR: Symbol file could not be found가 있는지 확인해야 합니다. 심볼을 찾지 못한 분석은 성공으로 처리하지 말고 partial_symbolication 상태로 분류하는 것이 좋습니다.
크래시 지문과 중복 제거는 어떻게 구현하는가?
동일한 버그가 수천 번 발생하면 발생 건수보다 고유 크래시 그룹 수가 더 중요합니다. 다음 필드를 조합해 크래시 지문을 생성할 수 있습니다.
- 예외 코드: 예를 들어
0xc0000005,0xc0000094 - 장애 모듈: DLL 또는 실행 파일 이름
- 심볼화된 최상위 함수 3~5개
- 클라이언트 Build ID
- 플랫폼과 그래픽 API
같은 함수라도 빌드가 바뀌면 소스와 메모리 배치가 달라질 수 있으므로 Build ID를 포함해야 합니다. 반대로 주소값 전체를 지문에 넣으면 컴파일 옵션이나 ASLR 때문에 동일한 원인이 여러 그룹으로 나뉠 수 있습니다.
4단계: AI 입력용 크래시 리포트를 정규화하기
AI에 전달할 입력은 원본 WinDbg 로그 전체가 아니라 필요한 사실을 구조화한 JSON이 좋습니다. 사용자명, 설치 경로, 세션 토큰, 채팅 문자열처럼 분석에 필요하지 않은 값은 마스킹합니다.
{
"build_id": "pc-1.4.2317.0",
"platform": "Windows x64",
"graphics_api": "DirectX 12",
"exception": {
"code": "0xc0000005",
"type": "access_violation",
"fault_address": "0x0000000000000010",
"access": "read"
},
"faulting_module": "RenderCore.dll",
"symbol_status": "exact_pdb_match",
"stack": [
"RenderCore.dll!RenderThread::Submit+0x2f at RenderQueue.cpp:184",
"RenderCore.dll!RenderQueue::Flush+0x91 at RenderQueue.cpp:327",
"Engine.dll!TaskWorker::Run+0x52 at TaskWorker.cpp:88"
],
"runtime_context": {
"map": "arena_07",
"mode": "ranked",
"session_age_seconds": 642,
"repro_rate": 0.31
},
"recent_changes": [
"DX12 command queue batching enabled in build pc-1.4.2317.0"
]
}
symbol_status는 반드시 별도 필드로 관리합니다. exact_pdb_match, partial, missing을 구분하지 않으면 AI가 불완전한 스택을 확정적인 원인처럼 요약할 수 있습니다.
5단계: AI 요약 프롬프트를 근거 중심으로 설계하기
AI의 역할은 원인을 창작하는 것이 아니라 분석 결과를 읽기 쉬운 운영 정보로 바꾸는 것입니다. 프롬프트에는 증거와 추론을 분리하라는 규칙을 명시해야 합니다.
당신은 게임 클라이언트 크래시 분석 리포터입니다.
입력은 WinDbg 또는 CDB에서 추출하고 개인정보를 제거한 구조화 데이터입니다.
규칙:
1. 입력에 없는 원인, 재현 조건, 수정 방법을 사실처럼 쓰지 마세요.
2. symbol_status가 missing이면 함수명 기반의 원인 단정을 하지 마세요.
3. 관찰된 사실과 원인 후보를 별도 항목으로 분리하세요.
4. 각 원인 후보에는 관련 스택 프레임 또는 예외 필드를 evidence로 연결하세요.
5. 확신도가 낮으면 unknown 또는 needs_more_data로 표시하세요.
6. 개발자가 바로 확인할 수 있는 다음 조치를 최대 3개만 제안하세요.
다음 JSON 형식으로만 답하세요.
{
"title": "짧은 크래시 제목",
"severity": "S1",
"summary": "한 문장 요약",
"observed_facts": [],
"cause_candidates": [
{
"hypothesis": "원인 후보",
"confidence": 0.0,
"evidence": []
}
],
"next_actions": [],
"missing_data": [],
"duplicate_key": "기존 이슈와 비교할 키"
}
실제 서비스에서는 사내 승인 모델만 사용하고 프롬프트와 결과에 포함되는 데이터의 보존 기간을 제한해야 합니다. 크래시 원인 분류가 배포 중단이나 긴급 패치 여부에 영향을 준다면 AI 결과를 자동 승인 신호로 사용하지 말고 QA 또는 클라이언트 엔지니어의 검토 대상으로 삼아야 합니다.
자동 리포트에는 어떤 필드를 포함해야 하는가?
Slack 알림은 짧게 만들고 상세 분석은 이슈 트래커 링크로 연결합니다. 다음과 같은 구조가 실용적입니다.
| 필드 | 목적 |
|---|---|
fingerprint | 동일 크래시 그룹화 |
build_id | 영향을 받는 배포 버전 확인 |
symbol_status | 분석 신뢰도 판단 |
exception_code | 예외 유형 분류 |
faulting_module | 담당 시스템 라우팅 |
top_frames | 개발자 초기 확인 |
affected_users | 영향도 산정 |
first_seen, last_seen | 회귀 시점 확인 |
ai_summary | 짧은 원인 후보와 다음 조치 |
raw_analysis_link | 원본 근거 확인 |
S1부터 S4까지의 등급을 팀 기준으로 정의하는 것도 중요합니다. 예를 들어 로그인 직후 전체 사용자가 재현하는 크래시는 S1, 특정 맵이나 그래픽 API에서만 발생하는 크래시는 S2 또는 S3으로 분류할 수 있습니다. 등급 기준은 게임의 서비스 영향도에 맞춰 운영 문서로 고정해야 합니다.
구현 순서는 어떻게 잡아야 하는가?
1. 빌드와 심볼 보존부터 고정하기
CI가 빌드 ID를 생성하고 실행 파일, DLL, PDB, 커밋 ID를 같은 아티팩트 레코드에 저장하도록 합니다. 배포 채널과 플랫폼도 함께 기록해야 크래시 영향 범위를 계산할 수 있습니다.
2. 덤프 수집과 업로드를 분리하기
클라이언트는 로컬 임시 폴더에 덤프와 최소 메타데이터를 저장하고 네트워크가 복구되면 업로드합니다. 업로드 실패가 게임 종료나 다음 실행을 방해하지 않도록 큐 크기, 재시도 횟수, 디스크 사용량 상한을 둡니다.
3. 심볼화 결과를 실패해도 보존하기
PDB를 찾지 못해도 예외 코드, 모듈명, 주소, 빌드 ID는 저장합니다. 이후 PDB가 복구되면 재분석할 수 있도록 원본 덤프의 해시와 분석 버전을 기록합니다.
4. AI는 정규화된 결과만 받게 하기
WinDbg 로그에서 개인정보를 제거하고 스택과 메타데이터를 JSON으로 변환한 뒤 AI에 전달합니다. AI 출력은 JSON Schema 검증을 통과한 경우에만 Slack이나 이슈 트래커로 전송합니다.
5. 사람의 검토 지점을 운영하기
AI가 제안한 원인 후보는 자동으로 라벨링할 수 있지만 긴급 패치나 배포 중단을 자동 실행하는 조건으로 사용해서는 안 됩니다. 엔지니어가 근거 링크와 심볼 상태를 확인한 뒤 최종 상태를 변경하도록 합니다.
자주 묻는 질문 FAQ
미니덤프만으로 항상 크래시 원인을 찾을 수 있나요?
아닙니다. 덤프 타입, 메모리 손상 정도, 심볼 보존 여부에 따라 분석 가능 범위가 달라집니다. symbol_status와 분석 신뢰도를 리포트에 표시해야 합니다.
PDB 파일명만 같으면 심볼화할 수 있나요?
아닙니다. 실행 파일이 참조하는 PDB GUID와 Age가 정확히 일치해야 합니다. 같은 버전 문자열을 가진 다른 빌드의 PDB를 사용하면 잘못된 스택이 생성될 수 있습니다.
AI에 미니덤프 원본을 직접 업로드해도 되나요?
권장하지 않습니다. 원본 덤프에는 사용자 데이터와 인증 정보가 남을 수 있습니다. WinDbg 또는 CDB로 필요한 분석 정보를 추출하고 개인정보를 마스킹한 구조화 텍스트만 승인된 AI 환경에 전달하는 편이 안전합니다.
마무리
미니덤프 자동 심볼릭화의 핵심은 AI가 아니라 정확한 빌드 산출물과 PDB의 연결입니다. MiniDumpWriteDump로 안정적으로 수집하고 CDB로 동일한 분석 명령을 반복 실행하며 예외 코드와 심볼화된 콜스택을 정규화하면 크래시 대응 시간을 크게 줄일 수 있습니다.
AI는 이 증거를 바탕으로 사람이 읽기 쉬운 요약과 다음 조치를 제공하는 보조 계층으로 배치하는 것이 좋습니다. 심볼 일치 여부, 개인정보 필터링, 원본 보존 정책, 사람의 승인 절차를 함께 설계해야 자동화가 운영 리스크가 아니라 실제 대응 속도 향상으로 이어집니다.



