El mayor portal de MU Online de Brasil — desde 2003
Tutorial Avanzado Cliente

Cómo depurar un crash del cliente de MU (análisis de dump)

Transforma ese crash misterioso del Main.exe en causa raíz usando dumps de memoria, WinDbg, símbolos y lectura de stack trace, con un flujo repetible para encontrar el módulo culpable y corregir.

BR Bruno · Actualizado el 14 jul 2025 · ⏱ 25 min de lectura
Respuesta rápida

Un jugador abre un ticket: "el juego se cierra solo cuando entro en Lorencia". Sin información, eso se vuelve adivinanza — cambias el driver, reinstalas, intentas de nuevo, y el problema vuelve. La forma profesional de resolverlo es transformar el crash en un artefacto analizable: un dump de memoria

Un jugador abre un ticket: "el juego se cierra solo cuando entro en Lorencia". Sin información, eso se vuelve adivinanza — cambias el driver, reinstalas, intentas de nuevo, y el problema vuelve. La forma profesional de resolverlo es transformar el crash en un artefacto analizable: un dump de memoria. Con el dump correcto, símbolos configurados y una lectura metódica de la pila, pasas de "se cierra solo" a "acceso a puntero nulo dentro de d3d9.dll llamado desde el render de partículas en la build 1.04g". Este tutorial arma ese flujo de principio a fin, repetible para cualquier crash del Main.exe. Todos los offsets, nombres de módulo y rutas aparecen como ejemplo y varían por season/cliente.

Requisitos previos

Esta guía asume un cliente que ya corre en la mayoría de las máquinas y crashea en situaciones específicas. Si aún estás montando la base del servidor, mira antes cómo crear un servidor de MU Online. Depurar un crash tiene sentido cuando el cliente ya funciona en el caso común y falla en la excepción.

RequisitoDetalle (ejemplo — varía por season/cliente)
WinDbgDel Windows SDK (WinDbg clásico o WinDbg Preview)
Símbolos de MicrosoftCaché local + srv público
PDB del Main.exeDe la build exacta, si compilas el cliente
Process HackerPara generar el dump bajo demanda
Máquina sana de referenciaDonde el cliente corre sin crashear
El dump del crashMinidump de la máquina afectada

El elemento que separa un análisis rápido del sufrimiento es el PDB correcto. Sin él, lees la pila en offsets; con él, en nombres de función. Si tienes el código del cliente, archiva el PDB de cada versión lanzada junto al binario — días después, un dump antiguo sin el PDB correcto es casi inútil para los frames del propio cliente.

Etapa 1 — Capturar el dump

Sin dump no hay análisis. Hay tres formas, en orden de practicidad para producción.

Paso 1 — WER automático (recomendado)

Configura el Windows Error Reporting para capturar todo crash del Main.exe en una carpeta local. Esto atrapa el crash en el acto, sin depender de que el jugador lo reproduzca con una herramienta abierta.

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 genera un minidump; 2 generaría un full dump. El dump aparece en la carpeta configurada apenas el Main.exe se cierra por excepción. Distribuye este ajuste en tu launcher para recolectar dumps de los jugadores que acepten enviarlos.

Paso 2 — Dump bajo demanda con Process Hacker

Cuando el crash es un "cuelgue" antes de cerrarse, o cuando quieres el estado en el momento justo: abre Process Hacker, encuentra Main.exe, clic derecho → Create dump file. Genera un minidump manual del proceso vivo. Útil para hangs (no crashes), cuando el proceso no muere solo.

Paso 3 — MiniDumpWriteDump en el propio cliente

Si compilas el cliente, instala un handler de excepción no manejada que llame a MiniDumpWriteDump. Así el cliente genera su propio dump con contexto extra (versión, mapa actual, jugador) grabado junto en un log. Es el más rico, porque controlas qué capturar.

// Esbozo — instalar temprano, antes del resto del 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 el WinDbg

Los símbolos mal configurados hacen que una pila legible se vuelva una sopa de offsets. Configúralos antes de abrir cualquier dump.

Paso 4 — Ruta de símbolos

En WinDbg, define el caché local más el servidor público de Microsoft y recarga:

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

Esto resuelve ntdll, kernel32, d3d9, user32 y similares. Para los frames del propio Main.exe, agrega la carpeta con el PDB de la build:

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

Paso 5 — Abrir el dump

File → Open Crash Dump, selecciona el .dmp. WinDbg carga los módulos y se detiene en el contexto de la excepción. Lo primero a ejecutar es el análisis automático.

Etapa 3 — Análisis automático y lectura de la pila

Paso 6 — !analyze -v

!analyze -v

Este comando hace el trabajo pesado inicial: identifica el código de la excepción, la dirección que falló, el módulo culpable probable e imprime la pila del thread que crasheó. Lee con atención estos campos:

CampoQué dice
EXCEPTION_CODETipo del error (ej.: c0000005 = access violation)
FAULTING_IPInstrucción/dirección donde estalló (ej.: Main+0x3f21a)
MODULE_NAMEMódulo señalado como culpable
STACK_TEXTLa pila de llamadas hasta el crash
PROCESS_NAMEDebe ser Main.exe

c0000005 (access violation) es de lejos el más común: el cliente intentó leer/escribir en una dirección inválida — casi siempre un puntero nulo o ya liberado. Si la dirección que falló está cerca de cero (ej.: 0x00000008), es un puntero nulo con offset de campo de struct: alguien accedió a objeto->campo con objeto == null.

Paso 7 — Leer la pila manualmente

k

k imprime la pila del thread actual. Léela de abajo hacia arriba (lo más antiguo abajo, el crash arriba). Quieres responder: ¿la culpa es del cliente, de una DLL de terceros o del sistema?

# ejemplo ilustrativo — varía por season/cliente
00 d3d9!CDevice::DrawPrimitive+0x2a     <- arriba: donde estalló
01 Main+0x3f21a                         <- código del cliente llamando al render
02 Main+0x41008
03 Main+0x51c30
04 kernel32!BaseThreadInitThunk         <- base: inicio del thread

Aquí la parte de arriba está en d3d9, pero quien llamó con un argumento malo fue Main+0x3f21a. Esto apunta al render/gráfico del cliente. Si la parte de arriba estuviera en un módulo extraño (ej.: overlay_x.dll o un antivirus), la sospecha cambiaría hacia software de terceros inyectado.

Paso 8 — Listar módulos y encontrar al intruso

lm

lm lista todos los módulos cargados. Compáralos con los de una máquina sana. Los módulos que solo aparecen en el dump problemático — overlays de grabación, inyectores, hooks de antivirus — son fuertes sospechosos, especialmente si aparecen en la pila.

Etapa 4 — Triaje por patrón de crash

Con la pila y los módulos en mano, clasifica. La mayoría de los crashes de cliente de MU cae en pocos patrones.

Patrón en la pilaCausa probableDirección de la corrección
Arriba en d3d9/d3d8 llamado por el clienteRecurso gráfico nulo, textura/efecto ausenteRevisar asset faltante; actualizar driver GPU
Arriba en Main.exe con c0000005 cerca de ceroPuntero nulo en el código del clienteLocalizar el offset en el PDB; corregir en la fuente
Módulo de terceros arribaOverlay/AV/inyectorQuitar el software; probar sin él
Stack overflow (c00000fd)Recursión infinitaRevisar loop/callback del cliente
Heap corruptionEscritura fuera de bufferFull dump + gflags/PageHeap
Crash solo al cargar un mapaAsset corrupto de ese mapaRepatch de los archivos de ese mapa

Paso 9 — Del offset a la línea (con PDB)

Si el crash está en el Main.exe y tienes el PDB, resuelve la dirección:

ln Main+0x3f21a

ln muestra el símbolo más cercano — nombre de la función y desplazamiento. Con la fuente, llegas a la línea. Sin PDB, Main+0x3f21a aún es útil: es estable para esa build, así que crashes repetidos en el mismo offset confirman el mismo bug, y puedes comparar reportes de varios jugadores.

Etapa 5 — Reproducir y confirmar

Un análisis sin reproducción es una hipótesis. Intenta reproducirlo en la máquina de referencia siguiendo el escenario del jugador (mismo mapa, misma acción). Si lo reproduces, genera un dump tuyo y comprueba si cae en el mismo offset/módulo. Que coincida confirma la causa; que no coincida indica un factor de entorno (driver, overlay, DLL local del jugador).

Para crashes de heap intermitentes, activa el PageHeap para el Main.exe con gflags antes de reproducir — hace que la corrupción estalle en el acto de la escritura inválida, no después, dejando que la pila apunte al verdadero culpable.

Errores comunes y soluciones

Síntoma en el análisisCausaSolución
Pila solo con offsets, sin nombresPDB/símbolos ausentesConfigurar .sympath; archivar el PDB por build
!analyze -v señala un módulo del sistemaLa culpa real es de quien lo llamóLeer k y encontrar el frame del cliente que lo llamó
Dump vacío o truncadoMinidump insuficienteGenerar un full dump para el caso
El crash no se reproduce en tu máquinaFactor de entorno del jugadorComparar lm; sospechar de overlay/driver/AV
El offset cambia en cada reporteBuilds diferentes entre jugadoresEstandarizar la versión del cliente vía launcher
WinDbg no abre el dumpDump de arquitectura diferente (x86/x64)Usar el WinDbg correspondiente a la arquitectura
Los símbolos no descarganEl firewall bloquea el msdlLiberar el acceso o pre-poblar el caché local

Buenas prácticas para no depurar a ciegas

La depuración empieza antes del crash. Estandariza la versión del cliente con un launcher para que todos los dumps sean de la misma build; archiva el PDB de cada versión lanzada; incrusta un handler de excepción que grabe dump + contexto (mapa, versión, jugador); y mantén una máquina de referencia sana para comparar módulos. Con eso, cada ticket "se cierra solo" llega ya con dump y contexto, y el análisis se vuelve rutina en vez de arqueología.

Lista de verificación de lanzamiento

  • WER configurado para capturar dumps del Main.exe
  • Handler de excepción con MiniDumpWriteDump en el cliente (si compilas)
  • Contexto (versión, mapa, jugador) grabado junto al dump
  • PDB de cada build archivado junto al binario
  • WinDbg con .sympath para caché local + servidor MS
  • Máquina de referencia sana disponible para comparación
  • Flujo !analyze -vklmln documentado para el equipo
  • Versión del cliente estandarizada vía launcher
  • Procedimiento para que el jugador envíe el dump con seguridad definido
  • Crashes catalogados por offset/módulo para detectar recurrencia
  • PageHeap/gflags disponible para cazar corrupción de heap
  • Corrección validada por reproducción en el mismo offset antes de cerrar el caso

Preguntas frecuentes

¿Cuál es la diferencia entre minidump y full dump?

El minidump es pequeño (pocos MB) y contiene pilas de threads, registros y módulos cargados, suficiente para la mayoría de los análisis de crash. El full dump incluye toda la memoria del proceso, es enorme (puede pasar de 1 GB) y solo vale la pena cuando necesitas inspeccionar heap, buffers y estados de objetos que el minidump no guarda. Empieza siempre por el minidump.

¿Necesito el código fuente del Main.exe para analizar el dump?

Ayuda mucho, pero no es obligatorio. Sin fuente y sin símbolos (PDB), aún ves la pila en términos de módulos y offsets (ej.: Main.exe+0x3F21A), identificas si el crash es en el cliente, en una DLL de terceros o en el sistema, y reconoces patrones como acceso a puntero nulo. Con símbolos ganas nombres de función; con fuente, la línea exacta.

¿Dónde guarda Windows los dumps de crash?

Depende de la configuración. El WER (Windows Error Reporting) suele grabar en %LOCALAPPDATA%\\CrashDumps cuando está habilitado por registro. También puedes generarlo bajo demanda con Process Hacker/Administrador de Tareas (Create dump file) o programáticamente vía MiniDumpWriteDump en el propio cliente. Configurar el WER para capturar el Main.exe automáticamente es lo más práctico en producción.

El crash solo ocurre en la máquina de algunos jugadores, ¿y ahora?

Un crash específico de máquina casi siempre es de entorno: driver de GPU antiguo, DLL de terceros inyectada (overlay, antivirus), DirectX/redistributables faltantes, o modo de compatibilidad equivocado. Pídele el dump al jugador, compara los módulos cargados con los de una máquina sana y busca el módulo extraño en la pila. Muchas veces la corrección es actualizar el driver o quitar un overlay.

¿Cómo configurar los símbolos en WinDbg?

Apunta la ruta de símbolos al servidor de Microsoft más una carpeta local de caché, por ejemplo con .sympath y .reload. Esto resuelve los nombres de las funciones del sistema (ntdll, kernel32, d3d9). Para el propio Main.exe, necesitas el PDB correspondiente a esa build; sin él, los frames del cliente aparecen solo como offsets. Guarda los PDB de cada versión que lances.

BR
Editor de eventos, mapas e ítems

Bruno es especialista en eventos, mapas, bosses y economía de ítems de MU Online. Documenta cada detalle basándose en el juego real.

Sigue leyendo

Artículos relacionados