O maior portal de MU Online do Brasil — desde 2003
Tutorial Avançado Cliente

Como depurar crash do cliente de MU (análise de dump)

Transforme aquele crash misterioso do Main.exe em causa raiz usando dumps de memória, WinDbg, símbolos e leitura de stack trace, com um fluxo repetível para achar o módulo culpado e corrigir.

BR Bruno · Atualizado em 14 jul 2025 · ⏱ 25 min de leitura
Resposta rápida

Um jogador abre um ticket: "o jogo fecha sozinho quando entro em Lorencia". Sem informação, isso vira adivinhação — troca driver, reinstala, tenta de novo, e o problema volta. A forma profissional de resolver é transformar o crash num artefato analisável: um dump de memória. Com o dump certo, símbol

Um jogador abre um ticket: "o jogo fecha sozinho quando entro em Lorencia". Sem informação, isso vira adivinhação — troca driver, reinstala, tenta de novo, e o problema volta. A forma profissional de resolver é transformar o crash num artefato analisável: um dump de memória. Com o dump certo, símbolos configurados e uma leitura metódica da pilha, você sai de "fecha sozinho" para "acesso a ponteiro nulo dentro de d3d9.dll chamado a partir do render de partículas na build 1.04g". Este tutorial monta esse fluxo do começo ao fim, repetível para qualquer crash do Main.exe. Todos os offsets, nomes de módulo e caminhos aparecem como exemplo e variam por season/cliente.

Pré-requisitos

Este guia assume um cliente que já roda na maioria das máquinas e crasha em situações específicas. Se você ainda está montando a base do servidor, veja antes como criar um servidor de MU Online. Depurar crash faz sentido quando o cliente já funciona no caso comum e falha na exceção.

RequisitoDetalhe (exemplo — varia por season/cliente)
WinDbgDo Windows SDK (WinDbg clássico ou WinDbg Preview)
Símbolos da MicrosoftCache local + srv público
PDB do Main.exeDa build exata, se você compila o cliente
Process HackerPara gerar dump sob demanda
Máquina saudável de referênciaOnde o cliente roda sem crashar
O dump do crashMinidump da máquina afetada

O item que separa análise rápida de sofrimento é o PDB correto. Sem ele, você lê a pilha em offsets; com ele, em nomes de função. Se você tem o código do cliente, arquive o PDB de cada versão lançada junto ao binário — dias depois, um dump antigo sem o PDB certo é quase inútil para os frames do próprio cliente.

Etapa 1 — Capturar o dump

Sem dump não há análise. Há três formas, em ordem de praticidade para produção.

Passo 1 — WER automático (recomendado)

Configure o Windows Error Reporting para capturar todo crash do Main.exe numa pasta local. Isso pega o crash no ato, sem depender de o jogador reproduzir com uma ferramenta aberta.

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps\Main.exe]
"DumpFolder"=hex(2):43,00,3a,00,5c,00,43,00,72,00,61,00,73,00,68,00,00,00
"DumpType"=dword:00000001
"DumpCount"=dword:0000000a

DumpType=1 gera minidump; 2 geraria full dump. O dump aparece na pasta configurada assim que o Main.exe fechar por exceção. Distribua esse ajuste no seu launcher para coletar dumps de jogadores que aceitarem enviar.

Passo 2 — Dump sob demanda com Process Hacker

Quando o crash é um "trava" antes de fechar, ou quando você quer o estado no momento certo: abra o Process Hacker, ache Main.exe, botão direito → Create dump file. Gera um minidump manual do processo vivo. Útil para hangs (não crashes), quando o processo não morre sozinho.

Passo 3 — MiniDumpWriteDump no próprio cliente

Se você compila o cliente, instale um handler de exceção não tratada que chame MiniDumpWriteDump. Assim o cliente gera seu próprio dump com contexto extra (versão, mapa atual, jogador) gravado junto num log. É o mais rico, porque você controla o que capturar.

// Esboço — instalar cedo, antes do resto do init
LONG WINAPI MeuHandler(EXCEPTION_POINTERS* ep) {
    HANDLE h = CreateFile(L"C:\\Crash\\main_crash.dmp", GENERIC_WRITE, 0,
                          nullptr, CREATE_ALWAYS, 0, nullptr);
    MINIDUMP_EXCEPTION_INFORMATION mei{ GetCurrentThreadId(), ep, FALSE };
    MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), h,
                      MiniDumpNormal, &mei, nullptr, nullptr);
    return EXCEPTION_EXECUTE_HANDLER;
}
// SetUnhandledExceptionFilter(MeuHandler);

Etapa 2 — Preparar o WinDbg

Símbolos mal configurados fazem uma pilha legível virar sopa de offsets. Configure antes de abrir qualquer dump.

Passo 4 — Caminho de símbolos

No WinDbg, defina o cache local mais o servidor público da Microsoft e recarregue:

.sympath srv*C:\Symbols*https://msdl.microsoft.com/download/symbols
.reload /f

Isso resolve ntdll, kernel32, d3d9, user32 e afins. Para os frames do próprio Main.exe, adicione a pasta com o PDB da build:

.sympath+ C:\MU\pdb\1.04g
.reload /f Main.exe

Passo 5 — Abrir o dump

File → Open Crash Dump, selecione o .dmp. O WinDbg carrega os módulos e para no contexto da exceção. A primeira coisa a rodar é a análise automática.

Etapa 3 — Análise automática e leitura da pilha

Passo 6 — !analyze -v

!analyze -v

Esse comando faz o trabalho pesado inicial: identifica o código da exceção, o endereço que falhou, o módulo culpado provável e imprime a pilha do thread que crashou. Leia com atenção estes campos:

CampoO que diz
EXCEPTION_CODETipo do erro (ex.: c0000005 = access violation)
FAULTING_IPInstrução/endereço onde estourou (ex.: Main+0x3f21a)
MODULE_NAMEMódulo apontado como culpado
STACK_TEXTA pilha de chamadas até o crash
PROCESS_NAMEDeve ser Main.exe

c0000005 (access violation) é de longe o mais comum: o cliente tentou ler/escrever num endereço inválido — quase sempre um ponteiro nulo ou já liberado. Se o endereço que falhou é próximo de zero (ex.: 0x00000008), é ponteiro nulo com offset de campo de struct: alguém acessou objeto->campo com objeto == null.

Passo 7 — Ler a pilha manualmente

k

k imprime a pilha do thread atual. Leia de baixo para cima (mais antigo embaixo, o crash no topo). Você quer responder: a culpa é do cliente, de um DLL de terceiros ou do sistema?

# exemplo ilustrativo — varia por season/cliente
00 d3d9!CDevice::DrawPrimitive+0x2a     <- topo: onde estourou
01 Main+0x3f21a                         <- código do cliente chamando o render
02 Main+0x41008
03 Main+0x51c30
04 kernel32!BaseThreadInitThunk         <- base: início do thread

Aqui o topo está em d3d9, mas quem chamou com um argumento ruim foi Main+0x3f21a. Isso aponta para render/gráfico do cliente. Se o topo estivesse num módulo estranho (ex.: overlay_x.dll ou um antivírus), a suspeita mudaria para software de terceiros injetado.

Passo 8 — Listar módulos e achar o intruso

lm

lm lista todos os módulos carregados. Compare com os de uma máquina saudável. Módulos que só aparecem no dump problemático — overlays de gravação, injetores, hooks de antivírus — são fortes suspeitos, especialmente se aparecem na pilha.

Etapa 4 — Triagem por padrão de crash

Com pilha e módulos em mãos, classifique. A maioria dos crashes de cliente de MU cai em poucos padrões.

Padrão na pilhaCausa provávelDireção da correção
Topo em d3d9/d3d8 chamado pelo clienteRecurso gráfico nulo, textura/efeito ausenteConferir asset faltando; atualizar driver GPU
Topo em Main.exe com c0000005 perto de zeroPonteiro nulo no código do clienteLocalizar offset no PDB; corrigir no fonte
Módulo de terceiros no topoOverlay/AV/injetorRemover software; testar sem ele
Stack overflow (c00000fd)Recursão infinitaRever loop/callback do cliente
Heap corruptionEscrita fora de bufferFull dump + gflags/PageHeap
Crash só ao carregar um mapaAsset corrompido daquele mapaRepatch dos arquivos daquele mapa

Passo 9 — Do offset para a linha (com PDB)

Se o crash está no Main.exe e você tem o PDB, resolva o endereço:

ln Main+0x3f21a

ln mostra o símbolo mais próximo — nome da função e deslocamento. Com fonte, você chega à linha. Sem PDB, Main+0x3f21a ainda é útil: ele é estável para aquela build, então crashes repetidos no mesmo offset confirmam o mesmo bug, e você pode comparar relatos de vários jogadores.

Etapa 5 — Reproduzir e confirmar

Análise sem reprodução é hipótese. Tente reproduzir na máquina de referência seguindo o cenário do jogador (mesmo mapa, mesma ação). Se reproduzir, gere um dump seu e confira se cai no mesmo offset/módulo. Bater confirma a causa; não bater indica fator de ambiente (driver, overlay, DLL local do jogador).

Para crashes de heap intermitentes, ligue o PageHeap para o Main.exe com gflags antes de reproduzir — ele faz a corrupção estourar no ato da escrita inválida, não depois, deixando a pilha apontar o verdadeiro culpado.

Erros comuns e soluções

Sintoma na análiseCausaSolução
Pilha só com offsets, sem nomesPDB/símbolos ausentesConfigurar .sympath; arquivar PDB por build
!analyze -v aponta módulo do sistemaCulpa real é de quem chamouLer k e achar o frame do cliente que chamou
Dump vazio ou truncadoMinidump insuficienteGerar full dump para o caso
Crash não reproduz na sua máquinaFator de ambiente do jogadorComparar lm; suspeitar de overlay/driver/AV
Offset muda a cada relatoBuilds diferentes entre jogadoresPadronizar versão do cliente via launcher
WinDbg não abre o dumpDump de arquitetura diferente (x86/x64)Usar WinDbg correspondente à arquitetura
Símbolos não baixamFirewall bloqueia o msdlLiberar acesso ou pré-popular o cache local

Boas práticas para não depurar às cegas

Depuração começa antes do crash. Padronize a versão do cliente com um launcher para que todos os dumps sejam da mesma build; arquive o PDB de cada versão lançada; embuta um handler de exceção que grave dump + contexto (mapa, versão, jogador); e mantenha uma máquina de referência saudável para comparação de módulos. Com isso, cada ticket "fecha sozinho" chega já com dump e contexto, e a análise vira rotina em vez de arqueologia.

Checklist de lançamento

  • WER configurado para capturar dumps do Main.exe
  • Handler de exceção com MiniDumpWriteDump no cliente (se compila)
  • Contexto (versão, mapa, jogador) gravado junto ao dump
  • PDB de cada build arquivado junto ao binário
  • WinDbg com .sympath para cache local + servidor MS
  • Máquina de referência saudável disponível para comparação
  • Fluxo !analyze -vklmln documentado para a equipe
  • Versão do cliente padronizada via launcher
  • Procedimento para o jogador enviar o dump com segurança definido
  • Crashes catalogados por offset/módulo para detectar recorrência
  • PageHeap/gflags disponível para caçar corrupção de heap
  • Correção validada por reprodução no mesmo offset antes de fechar o caso

Perguntas frequentes

Qual a diferença entre minidump e full dump?

O minidump é pequeno (poucos MB) e contém pilhas de threads, registradores e módulos carregados, suficiente para a maioria das análises de crash. O full dump inclui toda a memória do processo, é enorme (pode passar de 1 GB) e só vale a pena quando você precisa inspecionar heap, buffers e estados de objetos que o minidump não guarda. Comece sempre pelo minidump.

Preciso do código-fonte do Main.exe para analisar o dump?

Ajuda muito, mas não é obrigatório. Sem fonte e sem símbolos (PDB), você ainda vê a pilha em termos de módulos e offsets (ex.: Main.exe+0x3F21A), identifica se o crash é no cliente, num DLL de terceiros ou no sistema, e reconhece padrões como acesso a ponteiro nulo. Com símbolos você ganha nomes de função; com fonte, a linha exata.

Onde o Windows salva os dumps de crash?

Depende da configuração. O WER (Windows Error Reporting) costuma gravar em %LOCALAPPDATA%\\CrashDumps quando habilitado por registro. Você também pode gerar sob demanda com o Process Hacker/Task Manager (Create dump file) ou programaticamente via MiniDumpWriteDump no próprio cliente. Configurar o WER para capturar o Main.exe automaticamente é o mais prático em produção.

O crash só acontece na máquina de alguns jogadores, e agora?

Crash específico de máquina quase sempre é ambiente: driver de GPU antigo, DLL de terceiros injetada (overlay, antivírus), DirectX/redistributables faltando, ou modo de compatibilidade errado. Peça o dump ao jogador, compare os módulos carregados com os de uma máquina saudável e procure o módulo estranho na pilha. Muitas vezes a correção é atualizar driver ou remover um overlay.

Como configurar símbolos no WinDbg?

Aponte o caminho de símbolos para o servidor da Microsoft mais uma pasta local de cache, por exemplo com .sympath e .reload. Isso resolve os nomes das funções do sistema (ntdll, kernel32, d3d9). Para o próprio Main.exe, você precisa do PDB correspondente àquela build; sem ele, os frames do cliente aparecem só como offsets. Guarde os PDBs de cada versão que você lança.

BR
Editor de eventos, mapas e itens

Bruno é especialista em eventos, mapas, bosses e economia de itens do MU Online. Documenta cada detalhe com base em jogo real.

Continue lendo

Artigos relacionados