Time Travel Debugging — Grabar y rebobinar los errores que no se reproducen en aplicaciones de larga duración

· Actualizado el: · · WinDbg, Time Travel Debugging, Depuración, Investigación de fallos, Windows, Desarrollo para Windows, .NET, C++, Consultoría técnica

Historial de revisiones (primera versión, publicada el 2 Sep 2026)
Primera publicación

«Se cae una vez al mes, solo a medianoche.» «La misma operación nunca lo reproduce en mi máquina.» «Tenemos un volcado, pero mirar el lugar del fallo no nos dice por qué el valor llegó a ser lo que es.» Entre las investigaciones de errores de aplicaciones Windows de larga duración, estos son los casos que más tiempo consumen. En un artículo anterior, «WinDbg + SOS para leer volcados de memoria», vimos cómo leer un volcado de memoria, que es una sola fotografía. Este artículo continúa desde ahí y cubre la herramienta para las situaciones en las que una fotografía no basta: Time Travel Debugging (TTD).

TTD es una función de WinDbg que graba la ejecución entera de un proceso y permite reproducirla más tarde, hacia adelante y hacia atrás. En lugar de intentar una y otra vez reproducir un error, puede «rebobinar» la sesión del depurador.1 Los lectores previstos son desarrolladores y responsables de mantenimiento de aplicaciones Windows, .NET y C++ que ya han hecho su parte de investigación con volcados y registros y aún tienen casos cuya causa no alcanzan. El entorno previo es Windows 10/11 o Windows Server 2016 o posterior con el WinDbg actual, y la grabación requiere privilegios de administrador.12 El nivel de dificultad es intermedio.

Supuestos de este artículo

Elemento Contenido
Lectores previstos Desarrolladores y responsables de mantenimiento de aplicaciones Windows con errores de larga duración o intermitentes cuya causa no alcanzan los volcados ni los registros
Conocimientos previos Experiencia abriendo un volcado en WinDbg y ejecutando !analyze -v o !clrstack. Se da por conocido el contenido del artículo de análisis SOS
Entorno previo Windows 10/11 o Windows Server 2016/2019/2022/2025, WinDbg (versión actual), TTD.exe, privilegios de administrador2
Fuera de alcance Integración con el Snapshot Debugger de Visual Studio Enterprise; modo kernel (TTD es solo modo usuario3)

1. Primero, lo esencial

  • Un volcado conserva el «estado»; TTD conserva el «camino». La documentación oficial indica que los volcados tienden a perder el estado y el camino de ejecución que llevaron al fallo.1 Si una fotografía del momento del fallo no le dice la causa, lo que necesita a continuación no son más fotografías, sino una grabación.
  • Grabar es pesado. Durante la grabación, el proceso de destino corre de 5 a 20 veces más lento o peor, y el archivo de traza crece de 5 a 50 MB por segundo mientras el proceso está activo, sin tope.24 No es una herramienta que se acopla incondicionalmente a una aplicación de larga duración.
  • Para aplicaciones de larga duración, diseñe «qué grabar». El capítulo 5 cubre los cuatro puntos de entrada que ofrece TTD.exe: -ring/-maxFile (conservar solo los últimos N MB), -module (grabar solo mientras se ejecuta su propio módulo), -recordmode Manual (dejar que la aplicación especifique el intervalo de grabación) y -monitor (grabar cada arranque).2
  • La reproducción gira en torno a tres cosas: posiciones (!tt), eventos (dx @$curprocess.TTD.Events) y consultas (TTD.Calls / TTD.Memory). Combine ba (un punto de interrupción por acceso) con g- (ejecución inversa) y el depurador responde directamente «¿quién escribió este valor por última vez?».5
  • Una traza contiene el contenido de la memoria. Puede incluir información personal y confidencial, como rutas de archivos, datos del Registro y el contenido de la memoria y de los archivos.1 Diseñe el intercambio y el almacenamiento como lo haría para un archivo confidencial.
  • Sepa qué no puede hacer TTD antes de empezar. No puede grabar el modo kernel, no puede inyectarse en procesos protegidos (PPL), no puede desacoplarse por sí mismo una vez acoplado, y la memoria no se puede modificar durante la reproducción.32

En el diagrama, una línea continua marca una relación que siempre se cumple y una línea discontinua una relación condicional (las condiciones están en la explicación de cada relación en la página de detalle). La lista completa de relaciones (32 en total, con evidencia y grado de certeza) y las definiciones de los conceptos principales están reunidas en la página de detalle del mapa de conocimiento (en japonés). Datos: JSON-LD / Turtle

2. Donde un volcado no basta

Un volcado de memoria es una copia de la memoria y de los registros en el momento en que el proceso falló (o se detuvo). Como se describe en el artículo de análisis SOS, !clrstack le dice dónde falló y !dumpheap -stat qué consume el montículo. Lo que no puede decirle es qué ocurrió en el camino hacia ese estado.

Qué captura un volcado y qué captura TTDUn volcado de memoria captura solo el estado en el momento del fallo, no el camino que llevó hasta allí. TTD conserva la ejecución completa de instrucciones desde el inicio de la grabación hasta el final, de modo que el camino se preserva junto con el estadoVolcado de memoria: estado en el momento del falloNo muestra por qué el valor llegó a ser lo que esTraza TTD: ejecución de instrucciones del intervalo grabadoPuede volver a la posición donde se escribió el valorEste hueco es lo que alarga las investigaciones de larga duración

Figura 1: Un volcado es estado; TTD es el camino. En los errores de larga duración, lo que suele querer es lo segundo.

Los casos típicos se ven así.

  • Un campo de una estructura en el montículo contiene un valor imposible. El volcado muestra el valor corrompido, pero no quién lo escribió ni cuándo.
  • Se conoce el lugar de la excepción, pero no por qué el argumento que se le pasó era inválido. Al subir más por los llamadores, la función que produjo el valor por el camino ya no está en la pila.
  • Los identificadores o la memoria crecen a lo largo de un mes. Un solo volcado solo puede decir «está creciendo»; el camino de llamadas que lo hizo crecer no está.
Qué se le escapa a un volcado en casos típicos de larga duraciónEl valor corrompido se captura pero no quién lo escribió, la posición de la excepción se captura pero la función que produjo el argumento ya no está en la pila, y el crecimiento del recurso se captura pero no el camino que lo hizo crecer; los tres tipos de caso lo compartenCampo corrompidoNingún registro de quién lo escribió ni cuándoExcepción por un argumento inválidoLa función que produjo el valor ya no está en la pilaRecurso que crece a lo largo de un mesNingún registro del camino de llamadas que lo hizo crecerPunto común: hay estado, pero no camino

Figura 2: Los tres casos tienen «estado pero no camino». Más fotografías no rellenan el camino.

La documentación de Microsoft compara las fortalezas y las debilidades de los métodos de investigación como sigue.1

Método Fortalezas Debilidades
Depuración en vivo Interactiva, muestra el flujo de ejecución y permite cambiar el estado Detiene el trabajo del usuario. Reproducir repetidamente cuesta esfuerzo. A menudo inutilizable en producción. Difícil volver del punto de fallo a la causa
Volcados No hace falta cambiar el código de antemano. Baja intrusión, se pueden recoger con un disparador. Sobrecarga casi nula cuando no se usan Incluso con instantáneas consecutivas, la vista del «tiempo transcurrido» es grosera
Telemetría y registros Ligeros. Ligados a escenarios de negocio No hay registros en caminos de código inesperados. Profundidad de datos insuficiente, y embebida de forma estática en el código
TTD Fuerte en errores complejos. No hace falta cambiar el código de antemano. Se puede reproducir sin conexión tantas veces como se quiera y lo graba todo Gran sobrecarga durante la grabación. Puede recoger más datos de los necesarios. Los archivos se hacen grandes

Hay una propiedad más importante que señala el tutorial oficial de TTD. Cuando el depurador se detiene en el punto de fallo, ese punto suele estar dentro de código de tratamiento de errores varios pasos más allá de la causa real.5 Un volcado siempre se toma en esta posición «varios pasos más tarde». Con TTD puede retroceder desde ahí, instrucción a instrucción.

El hueco entre el punto de fallo y la causa realEl punto de fallo donde se toma un volcado suele estar dentro del tratamiento de errores varios pasos más allá de la causa real, y con TTD puede rebobinar desde ese punto instrucción a instrucción hasta la causaVolcadoTTDCausa real (la instrucción que corrompió el valor)Varios pasos adelantePunto de fallo (excepción, tratamiento de errores)Congelado aquíVolver con p- / t- / g-

Figura 3: Un volcado queda congelado en el punto de fallo. TTD puede caminar del punto de fallo a la causa.

3. Cómo funciona TTD y qué cuesta

3.1 Qué se está grabando

TTD inyecta un motor de grabación en el proceso de destino y grava las instrucciones ejecutadas, instrucción a instrucción. En palabras de la documentación oficial, «codifica una traza completa a nivel de instrucción a una media de menos de un byte por instrucción»; en la práctica cae en algún punto entre un bit y un byte por instrucción. Los programas que ejecutan pocos tipos de funciones y manejan pocos datos producen trazas más pequeñas; lo contrario produce más grandes.24

Una grabación produce dos archivos.1

Archivo Papel Tamaño aproximado
.run La traza en sí. Almacena la ejecución de instrucciones durante la grabación Crece de 5 a 50 MB por segundo en actividad. No crece en reposo4
.idx El índice. Datos auxiliares que permiten a WinDbg reproducir y consultar la memoria con eficiencia. Se crea al detener la grabación, y también se genera automáticamente cuando WinDbg abre el archivo .run De 1 a 2 veces el tamaño de la traza4
El flujo de la grabación TTD a la reproducciónSe inyecta un motor de grabación en el proceso de destino y la ejecución de instrucciones se graba en un archivo .run; cuando WinDbg abre el archivo .run crea un índice .idx y reproduce hacia adelante y hacia atrás con posiciones, eventos y consultasProceso de destinoMotor de grabación inyectado (TTDRecordCPU).run (registro de la ejecución de instrucciones)Abrir en WinDbgGenerar .idx (índice)Reproducir hacia adelante y hacia atrás con posiciones, eventos y consultas

Figura 4: La grabación la hacen TTD.exe o WinDbg; la lectura la hace WinDbg. Basta con compartir solo el archivo .run.

El tiempo dentro de un archivo .run se expresa como una «posición». Toma la forma de dos números hexadecimales separados por dos puntos, como 12:0 o 1A0:12F; la primera mitad es el número de secuenciación (correspondiente a un evento de secuenciación) y la segunda mitad el número aproximado de instrucciones desde ese evento.6 FFFFFFFFFFFFFFFE:0 significa el final de la traza.7 Las posiciones ocupan el centro del capítulo 6.

Cómo se expresan las posiciones en una trazaUna posición es un número de secuenciación hexadecimal y un recuento de pasos separados por dos puntos; el inicio está cerca de 0, el final se expresa como FFFFFFFFFFFFFFFE:0, y también puede moverse a una posición aproximada por porcentajePosición xx:yy (hexadecimal)xx: número de secuenciaciónyy: instrucciones desde ese eventoEl final es FFFFFFFFFFFFFFFE:0Los porcentajes como !tt 50 también funcionan

Figura 5: Una posición es «número de evento:recuento de instrucciones». No es hora de reloj de pared, pero se puede convertir a hora de reloj de pared como se muestra en el capítulo 6.

3.2 El coste

La documentación oficial misma describe TTD como una «tecnología invasiva».2 Aquí está el coste en números.

Elemento Contenido
Velocidad El proceso de destino corre de 5 a 20 veces más lento o peor durante la grabación (según la aplicación y las opciones de grabación). Puede pasar desapercibido en la interfaz, pero se percibe en operaciones pesadas como un cuadro de diálogo Abrir archivo23
Crecimiento del archivo De 5 a 50 MB por segundo en actividad. Unos minutos de grabación pueden alcanzar varios GB. No hay tope fijado4
Disco lleno Si el disco se llena durante la grabación, TTD escribe la última página y luego espera de hecho hasta que pueda escribir de nuevo. WinDbg sigue mostrando el cuadro de diálogo de grabación y no emite ni error ni advertencia. El resultado es una traza incompleta4
Memoria La grabación añade la sobrecarga de CPU virtuales a la memoria del proceso de destino (de forma predeterminada 55 en x64/ARM64, 32 en x86). Redúzcala con -numVCpu solo cuando la memoria escasee2
No se puede desacoplar Una vez acoplado, TTD no puede desacoplarse por sí mismo. Cuando termine la grabación, cierre la aplicación o termine el proceso. Si el proceso es esencial para el sistema, hay que reiniciar el sistema operativo2
El coste de la grabación y su efecto en aplicaciones de larga duraciónLa ralentización durante la grabación, el crecimiento del archivo, la espera silenciosa cuando el disco se llena y la imposibilidad de desacoplarse tras acoplarse son los cuatro costes que impiden acoplar TTD incondicionalmente a una aplicación de larga duraciónGrabación TTDDe 5 a 20 veces más lentoCrece de 5 a 50 MB por segundo, sin topeEspera en silencio cuando el disco se llenaNo se puede desacoplar tras acoplarseLa grabación continua incondicional no es viable

Figura 6: Cualquiera de los cuatro costes descarta la grabación continua. Por eso hace falta el «diseño de grabación» del capítulo 5.

3.3 Lo que no puede hacer

  • Solo modo usuario. Solo puede grabar la ejecución en modo usuario de un proceso; el código que corre en modo kernel, como los controladores, no se puede depurar.3
  • Procesos protegidos. TTD no puede inyectarse en procesos protegidos de Windows como Protected Process Light (PPL).3
  • La reproducción es de solo lectura. Puede volver atrás en el tiempo, pero no puede cambiar la historia. Los comandos que leen memoria funcionan; los que la modifican, no.3
  • Incompatibilidad con software antivirus y de supervisión de memoria. Como TTD se engancha al proceso, entra en conflicto con software que rastrea o refleja las llamadas de memoria del sistema. Si la grabación produce un error que parece de permisos insuficientes, desactive temporalmente ese software para aislar el problema. El marco Electron es también un conflicto conocido; aunque la grabación tenga éxito, el proceso de destino puede entrar en interbloqueo o fallar.3
  • Las aplicaciones UWP no se pueden iniciar y grabar (acoplarse a una aplicación UWP ya en ejecución es posible). Los «procesos inusuales» que se ejecutan en otra sesión o en un contexto de seguridad distinto tampoco están admitidos actualmente.8
Lo que TTD no puede grabar ni hacerEl código en modo kernel, los procesos protegidos, la grabación al arranque de aplicaciones UWP y los procesos en otra sesión o contexto de seguridad no se pueden grabar; la memoria no se puede modificar durante la reproducción; y el software antivirus y Electron pueden entrar en conflictoLimitaciones de TTDNo se puede grabarLimitaciones y conflictosCódigo en modo kernel (controladores, etc.)Procesos protegidos (PPL)Grabación al arranque de UWP (acoplarse es posible)Otras sesiones y contextos de seguridadLa reproducción es de solo lecturaPuede entrar en conflicto con antivirus y Electron

Figura 7: Las limitaciones vienen en dos clases: «no se puede grabar» y «entra en conflicto». Esta última hay que aislarla por entorno.

El último punto tropieza a quien quiere grabar un servicio. La documentación de TTD.exe describe -attach como pensado para «investigar servicios y aplicaciones de larga duración» y -monitor como grabar «cada vez que un programa o servicio arranca»,2 lo cual no encaja de forma simple con lo que dice la página de solución de problemas. En la práctica, el enfoque seguro es verificar por entorno en este orden: confirmar que se pueden grabar ping.exe o cmd.exe en la misma configuración que producción, y luego probar el proceso de destino.8

4. Grabar — La interfaz de WinDbg y TTD.exe

Hay dos puntos de entrada para la grabación: la interfaz de WinDbg, o TTD.exe en la línea de comandos.

4.1 Grabar desde la interfaz de WinDbg

Ejecute WinDbg como administrador (la elevación es obligatoria para TTD1), elija File > Start debugging > Launch executable (advanced), especifique el ejecutable y marque Record with Time Travel Debugging. Elegir Configure and Record permite fijar la ubicación del archivo de traza y Record subset of execution (limitar los módulos grabados con una lista separada por comas como notepad.exe,kernelbase.dll). Para un proceso que ya se está ejecutando, elija File > Start debugging > Attach to process y marque igualmente Record Process with Time Travel Debugging.9

Durante la grabación aparece un cuadro de diálogo pequeño con los botones «Stop and Debug» y «Cancel». Cuando la aplicación sale (o falla), la traza se cierra y WinDbg la abre automáticamente y construye el índice.5

El flujo de la grabación desde la interfaz de WinDbgEn WinDbg iniciado como administrador, elegir Launch executable (advanced) o Attach to process, marcar Record with Time Travel Debugging, fijar la ubicación de guardado y el filtro de módulo en Configure and Record, pasar por el cuadro de diálogo de grabación, y cuando la aplicación sale la traza se cierra y se indexa automáticamenteIniciar WinDbg como administradorLaunch executable (advanced) / Attach to processMarcar Record with Time Travel DebuggingConfigure and Record: ubicación de guardado, filtro de móduloCuadro de diálogo de grabación (Stop and Debug)La aplicación sale, la traza se cierra y se indexa automáticamente

Figura 8: Grabar desde la interfaz tiene cinco pasos. Si se salta «iniciar como administrador», se queda en el primer cuadro de diálogo.

4.2 Grabar con TTD.exe

Cuando necesita grabar en un PC donde no se puede instalar WinDbg, o para automatizar la grabación, use TTD.exe por sí solo. Se instala mediante App Installer desde https://aka.ms/ttd/download, y tras la instalación puede verificarlo con ttd.exe -help. Para entornos sin conexión, Microsoft también ofrece un procedimiento oficial (con un script de PowerShell) para desempaquetar el paquete MSIX a mano y extraer solo los binarios.2 La grabación requiere privilegios de administrador y normalmente se ejecuta desde un símbolo del sistema de administrador.2

Hay tres modos de grabación.2

Los tres modos de grabación de TTD.exelaunch inicia un proceso nuevo con argumentos y lo graba, pero se ejecuta con privilegios elevados. attach se acopla a un proceso en ejecución por PID. monitor graba cada vez que arranca el programa especificado, y el arranque ocurre con privilegios normalesModos de grabación de TTD.exe-launch: iniciar y grabar-attach: acoplarse a un PID en ejecución-monitor: grabar cada arranqueSe inicia con privilegios de administradorConserva privilegios normalesCamino de arranque normal, adecuado para automatización

Figura 9: Solo -launch puede pasar argumentos, pero se ejecuta con privilegios elevados. Para grabar un comportamiento cercano a producción, use -attach o -monitor.

:: Iniciar y grabar (modo predeterminado; -launch se puede omitir)
TTD.exe -out C:\traces MyApp.exe --config prod.json

:: Acoplarse a un proceso en ejecución (crear primero el directorio de salida)
TTD.exe -attach 21440 -out C:\traces\MyApp.run

:: Grabar cada arranque (Ctrl+C termina la supervisión; -out exige una ruta completa)
TTD.exe -out C:\traces\ -monitor MyApp.exe
  • -launch es el único modo que puede pasar argumentos, pero el programa se inicia con los mismos privilegios (de administrador) que TTD.exe. En aplicaciones cuyo comportamiento cambia con los privilegios, grabe con -attach o -monitor para conservar privilegios normales.2
  • -attach exige que el directorio de salida ya exista. Si especifica un nombre de archivo, no puede existir un archivo con ese nombre.2
  • -monitor instala un controlador de supervisión de arranques de proceso y graba cada vez que arranca el programa especificado (se pueden indicar varios). Sigue vigente hasta el reinicio y se detiene con Ctrl+C. Sus ventajas son que no tiene que armar el arranque usted mismo, que el destino corre con privilegios normales y que encaja con la automatización por script. Añadir -cmdLineFilter "string" graba solo los arranques cuya línea de comandos contiene esa cadena.2
  • -children graba también los procesos hijo, pero cada proceso recibe su propio archivo .run, y WinDbg solo puede abrir uno a la vez.2

Durante la grabación aparece una interfaz pequeña con dos botones: «Tracing Off» (detener la grabación y dejar que la aplicación continúe) y «Exit App» (cerrar la aplicación y terminar la grabación). Para automatización, ocúltela con -noUI y acepte el CLUF con -accepteula.2 El registro de la grabación se guarda en un archivo .out en la misma ubicación que el archivo .run, donde puede leer las horas de reloj de pared de inicio y fin de la grabación, la duración de la sesión de grabación (tiempo de simulación), si fue un arranque o un acoplamiento, y la versión del sistema operativo. Cuando una grabación falla, algunos mensajes de error aparecen solo en el archivo .out.2

5. Diseño de grabación para aplicaciones de larga duración

Este es el corazón del artículo. Dados los costes del capítulo 3, atrapar un error que ocurre en un momento desconocido en un proceso que corre durante días exige un diseño de grabación. TTD.exe ofrece cuatro medios, y se elige según la naturaleza del síntoma.

Elección del alcance de grabación según el síntoma de una aplicación de larga duraciónSi no sabe cuándo ocurrirá, conserve solo la última parte con un búfer anular; si sabe qué módulo es sospechoso, grabe solo ese módulo; si puede modificar la aplicación, especifique el intervalo con la API de grabación manual; si aparece solo al arranque o en determinados arranques, grabe cada arranque con el modo de supervisiónSe desconoce cuándo ocurreSolo al arranque o en determinados arranquesEl módulo sospechoso está claroSe puede modificar la aplicación¿Naturaleza del síntoma?-ring / -maxFile: conservar solo el final-monitor: grabar cada arranqueAñadir -moduleEspecificar el intervalo con -recordmode Manual

Figura 10: Los cuatro no se excluyen. Combinar -ring con -module es la combinación más práctica para aplicaciones de larga duración.

5.1 Búfer anular — Conservar solo los últimos N MB

Con -ring, la traza se escribe en un búfer anular del tamaño dado por -maxFile, y el archivo nunca crece más allá de ese límite. Lo que queda es solo la última parte de la grabación que cabe en ese tamaño.2 La unidad de -maxFile es MB; en modo búfer anular el valor predeterminado es 2048 MB, el mínimo 1 MB y el máximo 32768 MB (el valor predeterminado del anillo en memoria de un proceso de 32 bits es 256 MB).2

:: Acoplarse a una aplicación de supervisión en ejecución y conservar solo los últimos 4 GB de ejecución
TTD.exe -accepteula -noUI -attach 21440 -ring -maxFile 4096 -out C:\traces\MyApp.run

:: Cuando se detecta el síntoma, detener la grabación (la aplicación sigue corriendo)
TTD.exe -stop 21440

-stop acepta un nombre de proceso, un PID o all, y detiene esa grabación. -wait <seconds> espera hasta que haya terminado cada sesión de grabación del sistema (-1 de forma indefinida), y se usa en scripts de automatización para imponer el orden «detener, luego recoger el archivo».2

Línea de tiempo de una grabación con búfer anularLa grabación empieza al acoplarse, las partes más antiguas se empujan fuera del búfer anular, y cuando aparece el síntoma y se emite stop, solo queda como traza la porción maxFile más recienteIniciar la grabación con -attach -ringLos intervalos más antiguos se empujan fueraAparece el síntomaDetener la grabación con -stopSolo queda la porción maxFile más recienteDimensionarlo para que los instantes anteriores al síntoma quepan en el búfer

Figura 11: El búfer anular es un dispositivo para conservar «los instantes anteriores al síntoma». Calcule -maxFile hacia atrás a partir de «el tiempo desde notar el síntoma hasta detener, multiplicado por el crecimiento por segundo».

Hay dos puntos de diseño.

  1. Haga el búfer lo bastante grande para absorber el tiempo desde notar hasta detener. En un proceso activo la traza crece de 5 a 50 MB por segundo,4 así que un anillo de 4 GB corresponde a uno o dos minutos bajo carga pesada y algo más de diez minutos bajo carga ligera. Necesita un mecanismo que mantenga el desfase entre detectar el síntoma (una línea de registro concreta, un umbral de contador, una alerta de la supervisión) y -stop dentro de esa ventana.
  2. Detener no desacopla. -stop detiene la grabación, pero TTD no se desacopla por sí mismo del proceso de destino.2 Terminar la grabación por completo exige terminar el proceso, así que incluya «reiniciar en la siguiente ventana de mantenimiento después de recoger la traza» en el procedimiento operativo.
Detener una grabación con búfer anular en coordinación con la supervisiónCuando el lado de supervisión detecta el síntoma por registros o contadores, llama a TTD.exe stop, recoge el archivo .run finalizado y reinicia el proceso en la siguiente ventana de mantenimiento para quitar TTDProceso de destinoTTD.exeSupervisión (registros, contadores)Proceso de destinoTTD.exeSupervisión (registros, contadores)Detectar el síntoma-stop PIDDetener la grabación (el proceso continúa).run queda finalizadoRecoger .run, cifrar y almacenarReiniciar en la siguiente ventana de mantenimiento

Figura 12: Solo cuando el desfase entre la detección y -stop cabe en el búfer quedan en la traza los instantes anteriores al síntoma. El reinicio forma parte de la operación.

5.2 Filtro de módulo — Grabar solo mientras corre su propio código

-module <module name> graba solo el módulo especificado (el ejecutable mismo o una DLL cargada; se pueden indicar varios) y el código que ese módulo llama. El proceso de destino corre a toda velocidad hasta que se ejecuta código del módulo especificado; la grabación empieza cuando la ejecución entra en el módulo, se detiene cuando lo deja, y el proceso vuelve a toda velocidad. Como encender y apagar la grabación es caro, la grabación sigue activa mientras el módulo especificado llama a otros módulos del proceso.2

:: Grabar solo mientras corre nuestra DLL de lógica de medición (combinado con un anillo)
TTD.exe -accepteula -noUI -attach 21440 -module MeasureCore.dll -ring -maxFile 2048 -out C:\traces\

Una traza hecha así simplemente se salta los intervalos en los que la grabación estaba apagada, trata «la siguiente instrucción» como la primera instrucción tras reanudar la grabación, y la depura sin diferencia respecto a una traza de todo el proceso.2 En aplicaciones de larga duración, el bucle inactivo de la interfaz y el procesamiento interno del marco tienden a representar la mayor parte de las instrucciones ejecutadas, así que restringir la grabación al propio módulo por sí solo reduce de forma sustancial la sobrecarga y el tamaño del archivo.

Cómo se comporta la grabación filtrada por móduloEl proceso de destino corre a toda velocidad fuera del módulo especificado; la grabación empieza al entrar en el código del módulo especificado, continúa mientras ese módulo llama a otros módulos, y se detiene cuando la ejecución sale del móduloFuera del módulo especificado: toda velocidadEntrada en el módulo especificado: empieza la grabaciónOtros módulos que llama: la grabación continúaSalida del módulo especificado: se detiene la grabación

Figura 13: Como también se graban las API de Win32 y el entorno de ejecución que llama su DLL, esto basta para rastrear «qué entregó nuestro código al sistema operativo».

5.3 Grabación manual — Dejar que la aplicación especifique el intervalo

Con -recordmode Manual, el proceso sigue corriendo a toda velocidad incluso después de inyectar TTD, y la grabación ocurre solo cuando el programa llama a la API de grabación en proceso de TTD (el valor predeterminado, Automatic, graba desde el momento de la inyección).2 La documentación de la API y los ejemplos están en el repositorio WinDbg-Samples de GitHub.10

Si puede modificar la aplicación, este es el enfoque menos derrochador. Puede incorporar lógica que empiece a grabar cuando la propia aplicación sabe que una anomalía es inminente y se detenga cuando las cosas vuelven a la normalidad: «fallaron tres reintentos de comunicación consecutivos», «el atraso de la cola superó el umbral», y así sucesivamente. En una configuración de proceso supervisor como la que cubre el artículo sobre Job Objects, el supervisor puede decidir el intervalo en su lugar.

5.4 Modo de supervisión — Grabar cada arranque

Para errores que aparecen solo justo después del arranque o en determinados arranques, -monitor encaja. La tabla de comparación oficial también describe el modo de supervisión como pensado para «atrapar problemas intermitentes y problemas de arranque».2 Cuando graba el mismo programa muchas veces, los nombres de archivo secuenciales predeterminados (MyApp01.run, MyApp02.run, etc.) se vuelven ineficaces porque hay que recorrer los archivos existentes, así que use -timestampFilename para nombres con marca de tiempo. El número de grabaciones simultáneas se puede limitar con -maxConcurrentRecordings.2

Cómo se comporta el modo de supervisiónLa opción monitor instala un controlador de supervisión de arranques de proceso, estrecha el destino con el filtro de línea de comandos cada vez que arranca el programa especificado, lo graba, crea un archivo de traza distinto por arranque y continúa hasta Ctrl+C o un reinicioNoInstalar el controlador de supervisión de arranquesDetectar un arranque del programa especificado¿Coincide con -cmdLineFilter?Grabar ese arranque (archivo distinto por arranque)No grabarEsperar el siguiente arranque (hasta Ctrl+C o reinicio)

Figura 14: El modo de supervisión «acecha el arranque». Encaja con errores que aparecen en cada arranque y con errores que se pueden estrechar por las condiciones de arranque.

5.5 Dónde poner el disco

Para grabaciones de larga duración, ponga las trazas en un volumen dedicado e incluya el crecimiento del archivo .run en su supervisión. Como se indica en la sección 3.2, cuando el disco se llena, la grabación simplemente espera en silencio, sin error. La solución oficial es igual de primitiva: «comprobar el espacio libre en el Explorador» y «comprobar que el archivo .run crece con regularidad».4 Sin supervisión del espacio libre, acaba con una traza incompleta en la que el instante que más quería nunca se escribió.

Cómo un disco lleno produce una traza incompletaCuando el disco se llena durante la grabación, TTD escribe la última página y espera en silencio sin error ni advertencia, de modo que el síntoma que ocurre después no se graba, dejando una traza incompleta que se abre pero a la que le falta la parte crucial. Evítelo con un volumen dedicado y supervisión del crecimiento de .runevitaEl disco se llenaEscribe la última página y espera en silencioSin error, sin advertenciaEl síntoma ocurre despuésTraza incompleta sin el síntomaVolumen dedicado + supervisión del crecimiento de .run

Figura 15: Una traza que «se abre pero a la que le falta la parte crucial» nace de un hueco en la supervisión del disco.

6. Reproducir — Posiciones, eventos y rebobinado

6.1 Abrir

Cuando abre un archivo .run en WinDbg, si no hay índice, !index se ejecuta automáticamente y construye el archivo .idx mientras cuenta fotogramas clave (posiciones en la traza generadas automáticamente para la indexación; las trazas más grandes tienen más). Cuanto mayor es la traza, más tarda.5 El estado del índice se puede comprobar con !index -status; si informa algo distinto de «Index file loaded», reconstruya con !index -force. Si aun así falla, cierre el depurador, elimine el archivo .idx y vuelva a abrir el archivo .run. Reconstruir el índice no modifica el archivo .run, así que no se pierde ningún dato.8

Una salvedad: cuando se mejoró la indexación de trazas grandes en TTD 1.11.611, cambió el formato de índice, y las trazas existentes hay que reindexarlas.11 Si lleva archivos .idx antiguos, tropieza aquí.

Comprobar y reconstruir el índiceTras abrir la traza, comprobar el estado con !index -status; si es algo distinto de Index file loaded, reconstruir con !index -force, y si aun así falla, cerrar el depurador, eliminar el archivo .idx y volver a abrir el archivo .run. Reconstruir no modifica el archivo .runIndex file loadedCualquier otra cosaFallaTiene éxitoAbrir la traza!index -statusPasar al análisisReconstruir con !index -forceCerrar, eliminar .idx, reabrir .run

Figura 16: Reconstruir el índice no toca el archivo .run. Ante la duda, elimínelo y vuelva a abrir.

6.2 Moverse por posición

Pasar una posición a !tt se mueve a ese punto en el tiempo.6

!tt 0          ; inicio de la traza
!tt 50         ; aproximadamente la posición del 50 %
!tt 100        ; final de la traza
!tt 1A0:12F    ; a la posición 1A0:12F

Una posición es un par de números hexadecimales, número de secuenciación:recuento de pasos.6 El objeto Position tiene las propiedades Percent (la fracción de la traza), Sequence y Steps, más SeekTo(), que se mueve a esa posición, y ToSystemTime(), que devuelve la hora aproximada de reloj de pared (UTC).7 En investigaciones de larga duración, ToSystemTime() marca la diferencia, porque permite emparejar las marcas de tiempo del registro de la aplicación con posiciones de la traza. Los resultados de TTD.Calls también llevan SystemTimeStart / SystemTimeEnd.12

Emparejar posiciones con marcas de tiempo de registrosPartiendo de una marca de tiempo en el registro de la aplicación, seguir la hora aproximada de reloj de pared del objeto Position para identificar la posición en la traza, moverse allí con SeekTo y leer la ejecución de alrededorRegistro de la aplicación: hora de la anomalíaEncontrar una posición cuyo ToSystemTime esté cercaMoverse allí con SeekToLeer las llamadas y los valores de alrededor

Figura 17: «¿Qué estaba haciendo justo antes de esta línea de registro?» se consulta por la correspondencia entre posiciones y hora de reloj de pared.

Las posiciones también sirven para compartir. Cuando entrega una traza a un colega, adjunte la posición !tt x:y y podrá empezar a mirar desde el mismo instante. Escribir rangos de posición en informes de error es también una práctica recomendada oficialmente.2

6.3 Rebobinar

Añadir - a los comandos de paso habituales se mueve hacia atrás en el tiempo.13

Comando Significado Botón de la cinta
p- Retroceder una instrucción (o una línea de origen). Una llamada a función cuenta como un paso Step Over Back
t- Retroceder una instrucción (o una línea de origen). Entra en las llamadas a función Step Into Back
g- Ejecutar en inverso. Se detiene al alcanzar un punto de interrupción, en un evento o al inicio de la traza Go Back

Los eventos que detienen g- son los mismos que detienen un g hacia adelante.13 En otras palabras, ponga un ba (punto de interrupción por acceso) o un bp y ejecute g-, y vuelve derecho a «la última posición en la que se cumplía esa condición». Ese es el movimiento básico del capítulo 7.

Elección entre los comandos inversosp- retrocede un paso saltando las llamadas a función, t- retrocede una instrucción hacia dentro de las funciones, y g- vuelve derecho a un punto de interrupción, un evento o el inicio de la traza. Lo que detiene un g hacia adelante también detiene g-p-t-g-Posición actualRetroceder un paso saltando las llamadasRetroceder una instrucción hacia dentro de las funcionesVolver derecho a la siguiente condición de paradaba / bp / evento / inicio de la traza

Figura 18: Para mirar con cuidado lo cercano, use t-; para saltar a una causa lejana, use ba + g-.

6.4 Entrar por los eventos

Si no está seguro de por dónde empezar a leer, empiece por la lista de eventos. @$curprocess.TTD.Events enumera la creación y la terminación de subprocesos, la carga y la descarga de módulos y las excepciones como eventos.14

dx -g @$curprocess.TTD.Events
dx @$curprocess.TTD.Events.Where(t => t.Type == "Exception").Select(e => e.Exception)

Un evento de excepción contiene la posición, el tipo (Software / Hardware), el código de excepción y el contador de programa en ese momento, y hacer clic en el enlace [Time Travel] de la salida se mueve a esa posición.15 El tutorial oficial sigue este flujo: salta a la posición de una violación de acceso (0xc0000005), sospecha corrupción de pila porque el puntero de pila y el puntero base no coinciden, y retrocede tres instrucciones con t- para comprobar los valores.5 La ventana Timelines de WinDbg visualiza excepciones, puntos de interrupción, accesos a memoria y llamadas a función como una línea de tiempo, y un doble clic en una excepción emite el mismo SeekTo().16

6.5 Subprocesos y posiciones

!positions muestra cada subproceso activo en la posición actual junto con la posición de cada subproceso en la traza.17 Aquí hay una trampa. Cambiar de subproceso con ~<number>s no mueve la posición en la traza. La posición que usa el depurador para leer memoria no cambia, así que para mirar la memoria de otro subproceso «en ese instante», muévase con el enlace de posición de la salida de !positions o con !tt x:y.13

El camino básico de una reproducciónAbrir la traza y construir el índice, moverse de la lista de eventos a la posición de la excepción, retroceder paso a paso hasta la causa y, si hace falta, moverse a la posición de otro subproceso con positionsAbrir la traza e indexarlaEncontrar la excepción en TTD.EventsMoverse a la posición con [Time Travel]Retroceder con t- / p- / g-Comprobar las posiciones de otros subprocesos con !positions~s no mueve la posición de la traza

Figura 19: «Entrar por un evento, caminar hacia atrás» es el camino básico de una reproducción. Cambiar de subproceso no es mover posiciones.

7. Encontrar «cuándo» con consultas — TTD.Calls y TTD.Memory

La verdadera fuerza de la reproducción es que puede consultar toda la traza. Los objetos de TTD se exponen a través del modelo de datos del depurador (el comando dx), y puede filtrarlos, ordenarlos y agregarlos al estilo LINQ.18

7.1 TTD.Calls — Buscar llamadas a función

@$cursession.TTD.Calls("module!symbol") recoge las llamadas a la función especificada de toda la traza. Se admiten comodines, y cada llamada lleva sus posiciones de inicio y fin (TimeStart / TimeEnd), el identificador de subproceso (con un UniqueThreadId que nunca se reutiliza), los argumentos (Parameters[]), el valor de retorno (ReturnValue) y la dirección de retorno (ReturnAddress).12

El ejemplo de la documentación oficial es GetLastError. Agregue las llamadas cuyo valor de retorno no es cero por código de error y obtiene una lista de qué errores ocurrieron cuántas veces durante la traza.18

dx -g @$cursession.TTD.Calls("kernelbase!GetLastError").Where(x => x.ReturnValue != 0).GroupBy(x => x.ReturnValue).Select(x => new { ErrorNumber = x.First().ReturnValue, ErrorCount = x.Count() }).OrderByDescending(p => p.ErrorCount),d

Para «¿desde dónde se llamó MessageBox por última vez?», tome la última llamada con OrderBy(c => c.TimeStart).Last() y muévase a través del enlace [Time Travel] de su TimeStart.18

Construcción de una consulta TTD.CallsRecoger llamadas por nombre de función, filtrar por valor de retorno o argumentos, agregar por código de error y similares, ordenar por tiempo y moverse a la posición de la llamada deseada a través del enlace Time TravelTTD.Calls (nombre de función, comodines)Where: filtrar por valor de retorno o argumentosGroupBy: agregar por código de error, etc.OrderBy: ordenar por tiempoMoverse a través de [Time Travel] en TimeStart

Figura 20: El modo de usar las consultas es partir no de «dónde» sino de «cuándo, cuántas veces y con qué argumentos».

Una palabra sobre los símbolos. TTD determina el número y los tipos de los argumentos de una función, su tipo de retorno y su convención de llamada a partir de la información de símbolos PDB. Con private symbols obtiene el nombre de la función y los argumentos correctos. Solo con public symbols obtiene el nombre de la función y argumentos predeterminados (cuatro enteros de 64 bits sin signo). Un módulo sin ningún símbolo recibe el nombre de función UnknownOrMissingSymbols.1218 Sobre los tipos de PDB y cómo conservarlos, véase «¿Qué es un PDB?».

Disponibilidad de símbolos y resultados de TTD.CallsCon private symbols obtiene el nombre de la función y los argumentos correctos; solo con public symbols el nombre de la función y los cuatro argumentos enteros de 64 bits predeterminados; sin símbolos el nombre de función se convierte en UnknownOrMissingSymbolsprivate symbolspublic symbolsninguno¿Símbolos para el módulo?Nombre de función + argumentos y valor de retorno correctosNombre de función + argumentos predeterminados (4 x enteros de 64 bits)UnknownOrMissingSymbols

Figura 21: Haber conservado los PDB de sus propios módulos determina de forma directa cuán útiles son las consultas.

Calls implica cálculo, así que cuanto mayor es la traza, más tarda y más uso de CPU hay. Los resultados se almacenan en caché en memoria, así que la segunda consulta y las posteriores de la misma función son más rápidas.12 Cuando una consulta no devuelve nada, hay cuatro causas: cómo está escrito el llamamiento (compruebe el nombre del módulo con el comando x; si vuelve en mayúsculas, use eso), la DLL de destino aún no está cargada en esa posición (muévase a una posición posterior a la carga y vuelva a ejecutarla), la función está expandida en línea (no se puede seguir), o el comodín es demasiado amplio (estréchelo).18

7.2 TTD.Memory — Buscar accesos a memoria

@$cursession.TTD.Memory(start address, end address, "access type") recoge los accesos al rango de memoria especificado de toda la traza. Los tipos son r (lectura), w (escritura), rw, e (ejecución), rwe y ec (ejecución/cambio).19 «¿Quién escribió esta variable por última vez?» se responde recogiendo con "w", tomando .Last() y moviéndose a esa posición.5

dx -g @$cursession.TTD.Memory(0x00a4fca0, 0x00a4fca4, "w")
dx @$cursession.TTD.Memory(0x00a4fca0, 0x00a4fca4, "w").Last().TimeStart.SeekTo()

Si solo necesita buscar hacia adelante o hacia atrás desde la posición actual, @$curprocess.TTD.PrevMemoryAccess("w", address, size) / NextMemoryAccess son más ligeros y aceptan varios rangos a la vez. La posición en la que cambió un registro se encuentra con @$curthread.TTD.PrevRegisterWrite("rcx").6

7.3 ba + g- — Hacer que el depurador responda «¿quién lo corrompió?»

Condensar el procedimiento mostrado en el tutorial oficial en una forma usable para investigaciones de larga duración da lo siguiente.5

  1. Moverse a la posición de la excepción con TTD.Events
  2. Retroceder con t- e hipotetizar qué variable contiene el valor corrompido
  3. Obtener la dirección de esa variable con dx &variable
  4. Poner un punto de interrupción de escritura con ba w4 <address>
  5. Ejecutar g- para volver derecho a la posición donde esa variable se escribió por última vez
  6. Comprobar si ese punto (o unas instrucciones antes) es la causa. Si el valor escrito venía de otra variable, ponga ba en esa variable y ejecute g- de nuevo
  7. Repetir hasta llegar a la instrucción que lo corrompió
Rastrear el origen de un valor con ba y g-Poner un punto de interrupción de escritura en la dirección del valor corrompido y ejecutar en inverso, detenerse en la instrucción que lo escribió por última vez, y si ese valor venía de otra variable repetir los mismos pasos hasta llegar a la instrucción que lo corrompióNoIdentificar la dirección del valor corrompidoPoner un punto de interrupción de escritura con ba wEjecutar en inverso con g-Detenerse en la instrucción que lo escribió por última vez¿El valor venía de otra variable?La instrucción que corrompe = la causa

Figura 22: Una investigación que termina en «está corrompido» con un volcado avanza de forma mecánica hasta «quién lo corrompió» con TTD.

TTD 1.11.553 y posteriores también añaden @$curframe.TTD.VariableHistory(), que devuelve el historial de los valores de las variables locales de un marco. Muestra una tabla de los nombres de variable y qué valores tuvo cada variable en qué rangos de posición.11 En situaciones como la corrupción de pila en las que quiere saber «desde cuándo el valor está mal», es útil para estrechar dónde poner ba primero.

8. Aplicar esto a errores de larga duración

Ahora aplicamos estas herramientas a los tres tipos de caso listados al principio.

Tipos de errores de larga duración y el punto de entrada TTD de cada unoPara excepciones intermitentes y corrupción de datos, volver del evento con ba y g-; para crecimiento de recursos, emparejar llamadas de adquisición y liberación con TTD.Calls; para No responde y cadenas de esperas, seguir posiciones y horas de reloj de pared de llamadas a API de esperaExcepciones intermitentes, corrupción de datosTTD.Events, luego ba + g-Crecimiento de identificadores y memoriaEmparejar adquisición y liberación con TTD.CallsNo responde, cadenas de esperas!positions y horas de reloj de pared de las API de esperaGrabar con -module limitado a su propia DLL

Figura 23: El punto de entrada difiere según el tipo. Lo que comparten es estrechar el alcance de grabación antes de empezar a leer.

Tipo 1: Excepciones intermitentes y corrupción de datos. Conserve los instantes anteriores al síntoma con el búfer anular de la sección 5.1, salte a la posición de la excepción desde la lista de eventos de la sección 6.4 y rastree el origen del valor con el ba + g- de la sección 7.3. La diferencia respecto a una investigación con volcado es que la investigación no termina cuando encuentra «la variable corrompida»; desde ahí sigue de forma mecánica hacia atrás.

Tipo 2: Crecimiento de identificadores y memoria. Este es el tipo de caso diseccionado en «Investigación de un crash en una cámara industrial tras un largo funcionamiento - Edición fuga de handles». No puede grabar un mes, así que restrinja la grabación a su propia DLL con el -module de la sección 5.2, combínelo con -ring y conserve «los pocos minutos mientras está creciendo». En la traza, recoja TTD.Calls("kernelbase!CreateFileW") y TTD.Calls("kernelbase!CloseHandle") y empareje el valor de retorno de CreateFileW (el valor de identificador) con el primer argumento de CloseHandle (Parameters[0]). El ReturnValue de CloseHandle es un indicador booleano de éxito, así que no sirve para emparejar. Si queda un valor de identificador sin cerrar, el ReturnAddress (el llamador) de esa llamada a CreateFileW le da el llamador que está filtrando.12 Dicho esto, observar si existe una fuga y de qué tamaño es más barato con Application Verifier o recuentos de identificadores; la división de trabajo correcta es traer TTD en la fase de clavar qué camino está filtrando. Tenga en cuenta que grabar con Application Verifier activado degrada de forma notable el rendimiento de la reproducción por cómo se usa la memoria, así que desactívelo mientras graba.8

Tipo 3: «No responde» y cadenas de esperas. Como está escrito en «Qué es realmente «No responde»», un cuelgue es una pregunta de quién espera a quién. Una traza no crece en reposo,4 así que el tiempo de espera de un subproceso que espera en el kernel no aparece como instrucciones. Lo que aparece es la llamada justo antes de entrar en la espera y las instrucciones después de que volvió. Mire la posición de cada subproceso con !positions,17 y alinee «qué subproceso empezó a esperar qué, y cuándo» a partir del SystemTimeStart / SystemTimeEnd de TTD.Calls("kernelbase!WaitForSingleObject") y similares; emparejado con las marcas de tiempo de los registros, eso permite reconstruir la cadena de esperas.12 Los «errores dependientes del orden» como DllMain y el bloqueo del cargador o los despertares espurios de las variables de condición son un ámbito en el que TTD, que graba el orden mismo, encaja bien.

9. TTD con aplicaciones .NET

La documentación oficial de TTD indica que el código administrado se puede depurar con TTD en WinDbg usando la extensión SOS (sos.dll) que se ejecuta en modo de 64 bits.1 Cargarla es lo mismo que en el capítulo 3 del artículo de análisis SOS (.loadby sos coreclr o carga automática), y !clrstack y !pe funcionan en cada posición de la traza. El camino básico es moverse a la posición de la excepción con TTD.Events y luego leer la pila administrada con !clrstack.

Preparación para leer una traza TTD de una aplicación .NETAbrir la traza TTD en WinDbg, cargar la extensión SOS de 64 bits, moverse a la posición del evento de excepción y leer el estado administrado con !clrstack y !pe. Usar TTD.Calls para llamadas a través de fronteras nativasTraza TTD (.run)WinDbgExtensión SOS (64 bits)Moverse a la posición de la excepción con TTD.EventsLeer con !clrstack / !peTTD.Calls para llamadas a través de fronteras nativas

Figura 24: Estado administrado a través de SOS, búsquedas de llamadas en fronteras nativas. Separe los papeles y no se perderá.

Dos salvedades. Primera, lo que Microsoft garantiza es «SOS en modo de 64 bits». No se dice nada sobre aplicaciones .NET compiladas para x86, así que ejecute el objetivo de investigación como x64 si puede. Segunda, TTD.Calls se apoya en la información de símbolos PDB.12 No cuente con buscar por nombre métodos administrados compilados JIT; el uso fiable es seguir llamadas a través de fronteras nativas como destinos P/Invoke en DLL nativas, COM y las API de Win32. Los problemas de montículo administrado como los de «Cómo distinguir la espera de GC de una fuga de memoria en .NET» se persiguen primero con dotnet-counters / dotnet-gcdump / !gcroot en un volcado, y TTD sale en la fase en la que interviene una frontera nativa.

10. Elegir entre volcados, registros, ETW y TTD

TTD no es una panacea y no sustituye las herramientas existentes. Aquí se alinea frente a las herramientas cubiertas en nuestros artículos.

Lo que quiere saber Herramienta a usar primero Cuándo entra TTD
El estado en el momento del fallo Volcado de memoria (WER LocalDumps / ProcDump) Cuando el volcado muestra «está corrompido» pero no quién lo corrompió
Qué archivo o clave del Registro falló Process Monitor Cuando también necesita cómo se construyeron los argumentos pasados a la API que falla
Rendimiento de todo el PC a lo largo de mucho tiempo WPR/WPA, PerfView (ETW) Cuando es un problema de corrección más que de rendimiento y necesita el orden a nivel de instrucción
La secuencia de eventos a nivel de negocio Los registros de la aplicación Cuando ocurrió en un camino de código sin registro (TTD lo graba todo sin cambios de código previos1)
Quién escribió ese valor, el orden de las llamadas TTD
Elección de un método de investigaciónPrimero captar el estado con volcados y registros ligeros, investigar las API que fallan con ProcMon y el rendimiento con ETW, y pasar a TTD solo cuando aún necesita quién pasó qué y cuándoNoSíntomaCaptar el estado con volcados y registros (ligero)API que fallan: ProcMonRendimiento: WPR/WPA, PerfView¿Necesita quién, cuándo y qué se pasó?Diseñar el alcance de grabación y grabar con TTDResuelto con herramientas ligeras

Figura 25: Capte primero el «resultado» con herramientas ligeras, y saque TTD solo para los casos en los que el «camino» resulta necesario.

El orden es «lo más ligero primero». Los volcados y los registros casi no cuestan recogerlos, así que déjelos en su sitio de forma permanente; saque TTD, tras el diseño del capítulo 5, para los casos en los que leer el volcado mostró que hace falta el camino. Dada la sobrecarga de TTD y el hecho de que las trazas contienen información confidencial, no hay motivo para invertir este orden.

11. Notas operativas

  • Trate las trazas como archivos confidenciales. Una grabación contiene contenido de memoria y puede incluir información personal y relacionada con la seguridad, como rutas de archivos, datos del Registro y el contenido de la memoria y de los archivos.12 Cuando grabe en un entorno de cliente, comprenda de antemano qué puede incluirse (cadenas de conexión, tokens, datos de clientes) y decida un canal de transferencia cifrado, una ubicación de almacenamiento y un plazo de retención.
  • Comparta solo el archivo .run. El archivo .idx es aproximadamente tan grande como el archivo .run y se genera automáticamente cuando WinDbg lo abre. Los archivos .run se comprimen bien. Al informar de un error en el propio TTD, adjunte también el archivo .out.2
  • Mantenga las versiones alineadas. TTD sigue actualizándose junto con WinDbg; 1.11.611 incluye una corrección de fallos de grabación en programas que usan AVX/AVX512 y un cambio de formato de índice.11 Mezclar una versión antigua en el lado de grabación o en el de reproducción implica reindexar o volver a grabar.
  • Compruebe -replayCpuSupport al reproducir en una CPU distinta. El valor predeterminado favorece la portabilidad, y MostConservative se ofrece para casos en los que la CPU de grabación y la de reproducción difieren (por ejemplo, reproducir una traza de Intel en arm64). A la inversa, si sabe que la CPU de reproducción es igual o mejor, puede elegir una grabación más pequeña y más rápida.2
  • Windows Server también se puede grabar. TTD.exe admite Windows Server 2016/2019/2022/2025.2
  • Aislar un fallo de grabación. Pruebe primero si se pueden grabar ping.exe o cmd.exe; si no, sospeche un conflicto con software invasivo como antivirus o virtualización de aplicaciones.8
Procedimiento para tratar las trazasComo una traza grabada contiene contenido de memoria, comprender qué información puede contener, comprimir solo el archivo .run y entregarlo por un canal cifrado, decidir la ubicación de almacenamiento y el plazo de retención, y en el lado de análisis abrirlo con la misma versión de WinDbg y construir el índiceGrabación completa (.run / .idx / .out)Comprender qué información puede contenerComprimir solo .run, cifrar y entregarDecidir la ubicación de almacenamiento y el plazo de retenciónAbrir con la misma versión de WinDbg y generar .idx

Figura 26: Entregar una traza es entregar un archivo confidencial. Decida el procedimiento antes de grabar.

12. Resumen

  • Un volcado es «estado»; TTD es el «camino». En casos que necesitan el origen de un valor corrompido o el orden de las llamadas, TTD convierte la investigación en trabajo mecánico
  • La grabación es de 5 a 20 veces más lenta, crece de 5 a 50 MB por segundo y no se puede desacoplar. Para aplicaciones de larga duración, diseñe primero el alcance de grabación con -ring/-maxFile, -module, -recordmode Manual y -monitor
  • La reproducción es «entrar por un evento, caminar hacia atrás»: TTD.Events, luego [Time Travel], luego t-/g-, y ba + g- para que el depurador responda «quién lo corrompió»
  • TTD.Calls y TTD.Memory son consultas sobre toda la traza. Empareje con las marcas de tiempo de los registros con ToSystemTime() y SystemTimeStart
  • Para .NET, lea el estado con SOS de 64 bits y use TTD.Calls en las fronteras nativas
  • Una traza es un archivo confidencial. Comparta solo el archivo .run y decida primero el canal y el almacenamiento

Los casos en los que se capturó un volcado pero la causa está fuera de alcance, o en los que el trabajo se ha estancado porque el error no se reproduce, se resuelven con bastante frecuencia con TTD una vez que se puede diseñar el alcance de grabación. También podemos encargarnos de todo, desde el diseño de grabación hasta el análisis del archivo .run; no dude en ponerse en contacto con sus volcados y registros.

Artículos relacionados

Áreas de consultoría relacionadas

KomuraSoft LLC se ocupa de la investigación de causa raíz de errores de aplicaciones Windows que aparecen solo tras largas ejecuciones o de forma intermitente, combinando volcados de memoria, registros y trazas TTD; de la construcción de un dispositivo de investigación que incluye el diseño del alcance de grabación; y del aislamiento de fallos que involucran fronteras nativas (COM, P/Invoke, SDK de dispositivos). Póngase en contacto ya en la fase de «tenemos un volcado pero no alcanzamos la causa».

Referencias

  1. Microsoft Learn, Time Travel Debugging - Overview. Sobre que TTD graba la ejecución de un proceso y la reproduce hacia adelante y hacia atrás, que los volcados tienden a perder el estado y el camino de ejecución que llevaron al fallo, que la grabación requiere privilegios de administrador, que las grabaciones pueden contener información personal y relacionada con la seguridad, la tabla comparativa de métodos de investigación, los papeles de .run/.idx y la depuración de código administrado con la extensión SOS en modo de 64 bits.  2 3 4 5 6 7 8 9 10

  2. Microsoft Learn, Time Travel Debugging - TTD.exe command line utility. Sobre la ralentización de 5 a 20 veces o más, la imposibilidad de desacoplarse por sí mismo tras acoplarse, la admisión de Windows Server 2016 a 2025, la instalación y el despliegue sin conexión, los tres modos -launch/-attach/-monitor, las opciones -out/-noUI/-accepteula/-stop/-wait/-tracingOff/-children/-cmdLineFilter/-timestampFilename/-ring/-maxFile/-maxConcurrentRecordings/-numVCpu/-replayCpuSupport/-module/-recordmode, la lectura del archivo .out y los consejos para compartir trazas.  2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

  3. Microsoft Learn, Time Travel Debugging - Overview - Things to look out for. Sobre la incompatibilidad con software antivirus y de supervisión de memoria y Electron, que es solo modo usuario, que la reproducción es de solo lectura, la imposibilidad de inyectarse en procesos protegidos (PPL) y el impacto de rendimiento de unas 10 a 20 veces durante la grabación.  2 3 4 5 6 7

  4. Microsoft Learn, Time Travel Debugging - Working with Trace Files. Sobre los factores de tamaño de traza (un bit a un byte por instrucción), el crecimiento de 5 a 50 MB por segundo en actividad y nulo en reposo, la ausencia de tope de tamaño máximo, que el índice es de 1 a 2 veces la traza, y el comportamiento de la grabación y la indexación cuando el disco se llena junto con la solución.  2 3 4 5 6 7 8 9

  5. Microsoft Learn, Time Travel Debugging - Sample App Walkthrough. Sobre el procedimiento general de moverse a la posición de un evento de excepción y volver con ba y g- a la posición donde se escribió por última vez un valor inválido, que el punto de fallo suele estar en el tratamiento de errores varios pasos más allá de la causa real, que la traza se cierra en un fallo y WinDbg la indexa automáticamente, y el uso de TTD.Memory y .Last() 2 3 4 5 6 7

  6. Microsoft Learn, !tt (time travel). Sobre la especificación de posiciones para !tt (porcentaje o xx:yy), el significado de los dos componentes de una posición (número de secuenciación y recuento de pasos) y TTD.PrevRegisterWrite/PrevMemoryAccess/NextMemoryAccess 2 3 4

  7. Microsoft Learn, TTD Position Objects. Sobre Percent/Sequence/Steps del objeto Position, SeekTo(), que ToSystemTime() devuelve la hora aproximada de reloj de pared (UTC) y que FFFFFFFFFFFFFFFE:0 denota el final de la traza.  2

  8. Microsoft Learn, Time Travel Debugging - Troubleshooting. Sobre que se requiere elevación, que la grabación al arranque de aplicaciones UWP no está admitida, que los «procesos inusuales» en otra sesión o contexto de seguridad quedan fuera de alcance, el aislamiento con ping.exe/cmd.exe, que la reproducción se ralentiza cuando se usa Application Verifier al mismo tiempo, y la reconstrucción del índice con !index -status/!index -force 2 3 4 5

  9. Microsoft Learn, Time Travel Debugging - Record a trace. Sobre la grabación desde Launch executable (advanced) / Attach to process en la interfaz de WinDbg, la casilla Record with Time Travel Debugging, el ajuste de la ubicación de guardado con Configure and Record y la limitación de módulos con Record subset of execution

  10. Microsoft, WinDbg-Samples - TTD in-process recording API (GitHub). Sobre la documentación de la API de grabación en proceso que, combinada con -recordmode Manual, deja que el programa controle el inicio y la detención de la grabación. 

  11. Microsoft Learn, Time travel debugging release notes. Sobre la corrección de grabación para programas que usan AVX/AVX512 y el cambio de formato de índice (hace falta reindexar) en 1.11.611, y @$curframe.TTD.VariableHistory() añadido en 1.11.553.  2 3

  12. Microsoft Learn, TTD Calls Objects. Sobre los argumentos de TTD.Calls, las propiedades ThreadId/UniqueThreadId/Function/ReturnValue/ReturnAddress/Parameters[]/TimeStart/TimeEnd/SystemTimeStart/SystemTimeEnd, los valores predeterminados cuando falta información de símbolos PDB (cuatro argumentos enteros de 64 bits sin signo, UnknownOrMissingSymbols) y que el cálculo lleva tiempo y los resultados se almacenan en caché.  2 3 4 5 6 7

  13. Microsoft Learn, Time Travel Debugging - Replay a trace. Sobre la ejecución inversa con p-/t-/g-, que g- se detiene en los mismos eventos que la ejecución hacia adelante, !positions y que ~s no cambia la posición en la traza.  2 3

  14. Microsoft Learn, TTD Event Objects. Sobre los tipos de evento (ThreadCreated/ThreadTerminated/ModuleLoaded/ModuleUnloaded/Exception) y los objetos hijo Position, Module, Thread y Exception. 

  15. Microsoft Learn, TTD Exception Objects. Sobre Type (Software/Hardware), ProgramCounter, Code, Flags y Position del objeto de excepción. 

  16. Microsoft Learn, WinDbg: Timelines. Sobre que la ventana Timelines visualiza excepciones, puntos de interrupción, accesos a memoria y llamadas a función, y que un doble clic en una excepción emite Position.SeekTo()

  17. Microsoft Learn, !positions. Sobre mostrar cada subproceso activo y la posición de cada subproceso en la traza.  2

  18. Microsoft Learn, Introduction to Time Travel Debugging objects. Sobre los objetos @$curprocess.TTD / @$cursession.TTD, las consultas con OrderBy/Where/Select/GroupBy, los ejemplos de agregar errores de GetLastError y de encontrar la última llamada a MessageBoxW, el significado de UnknownOrMissingSymbols y las cuatro razones por las que Calls no devuelve nada.  2 3 4 5

  19. Microsoft Learn, TTD Memory Objects. Sobre los tipos de acceso de TTD.Memory (r/w/rw/e/rwe/ec) y el movimiento a una posición a través del enlace [Time Travel] de los resultados. 

Artículos recientes con las mismas etiquetas para profundizar en temas cercanos.

Estas páginas sitúan el tema en un contexto más amplio de servicios y decisiones.

El artículo está directamente relacionado con los siguientes servicios.

Preguntas frecuentes

Preguntas habituales en las consultas sobre el tema del artículo.

¿Cuál es la diferencia entre Time Travel Debugging (TTD) y un volcado de memoria?
Un volcado de memoria es una fotografía de la memoria en el momento del fallo: muestra el estado en ese instante, pero no el camino que llevó hasta allí. Una traza TTD es una grabación completa de la ejecución de instrucciones del proceso que después se puede reproducir hacia adelante y hacia atrás, de modo que puede rebobinar y comprobar directamente quién escribió por última vez en una variable o qué se llamó justo antes de una excepción. La documentación oficial de Microsoft también indica que los volcados tienden a perder el estado y el camino de ejecución que llevaron al fallo. El precio es que el proceso corre de 5 a 20 veces más lento mientras se graba, y el archivo de traza crece unos 5 a 50 MB por segundo mientras el proceso está activo.
¿Puedo dejar TTD acoplado a una aplicación de producción que corre durante días?
No tal cual. TTD ralentiza considerablemente el proceso mientras graba, la traza crece de 5 a 50 MB por segundo en un proceso activo y no hay tope de tamaño de archivo. Usarlo en una aplicación de larga duración presupone un diseño de grabación: el búfer anular de TTD.exe (-ring) con -maxFile para conservar solo los últimos N MB, -module para grabar solo mientras se ejecuta su propio módulo, o -recordmode Manual para que la aplicación elija el intervalo de grabación. Además, una vez acoplado, TTD no puede desacoplarse por sí mismo, así que debe decidir de antemano cómo terminará la grabación, lo que implica terminar (reiniciar) el proceso.
¿Se pueden grabar servicios y procesos en otras sesiones?
La documentación de TTD.exe describe -attach como pensado para investigar servicios y aplicaciones de larga duración, y -monitor como grabar cada vez que un programa o servicio arranca. Por otro lado, la página de solución de problemas indica que los procesos inusuales que se ejecutan en otra sesión o en un contexto de seguridad distinto no están admitidos actualmente para la grabación. Si realmente funciona depende del entorno, así que primero grabe un proceso simple como ping.exe o cmd.exe en la misma configuración que producción, luego pruebe el proceso de destino y solo entonces lo incorpore a la operación.
¿Se puede usar TTD en trazas de aplicaciones .NET?
Sí. La documentación oficial indica que la extensión SOS (sos.dll) que se ejecuta en modo de 64 bits se puede usar en una traza TTD de WinDbg para depurar código administrado. El patrón básico es ejecutar comandos SOS como !clrstack y !pe en cada posición de la traza, ir a la posición de un evento de excepción y luego leer la pila administrada. La consulta TTD.Calls, que busca llamadas por nombre de símbolo, se apoya en la información de símbolos PDB, así que en aplicaciones .NET el uso fiable es seguir llamadas a través de fronteras nativas como P/Invoke, COM y las API de Win32.
¿Es seguro enviar un archivo de traza (.run) a otra empresa o fuera de la organización?
No tal cual. Una grabación TTD contiene el contenido de la memoria del proceso, y la documentación oficial indica explícitamente que puede incluir información personal o confidencial, como rutas de archivos, datos del Registro y el contenido de la memoria y de los archivos. Si envía una, comprenda primero qué se grabó (cadenas de conexión, tokens, datos de clientes, etc.) y luego decida un canal de transferencia cifrado y una ubicación de almacenamiento. Compartir solo el archivo .run basta; el archivo de índice (.idx) se genera automáticamente cuando WinDbg lo abre.
TTD.Calls no devuelve nada cuando busco una función. ¿Por qué?
Hay cuatro causas principales. Primera, los símbolos: las funciones de un módulo sin PDB se llaman UnknownOrMissingSymbols, y el nombre del módulo puede estar en mayúsculas, así que compruebe el nombre de símbolo real con el comando x. Segunda, es posible que la DLL de destino aún no esté cargada en esa posición; muévase a una posición posterior a la carga de la DLL y consulte de nuevo. Tercera, si la función está expandida en línea, el motor de consulta no puede seguirla. Cuarta, el comodín puede ser demasiado amplio y coincidir con demasiadas funciones; estreche el patrón.

Perfil del autor

Página de presentación del autor del artículo.

Go Komura

Representante de KomuraSoft LLC

Especializado en desarrollo de software para Windows, consultoría técnica e investigación de fallos, sobre todo en proyectos con sistemas existentes y errores difíciles de reproducir.

Volver al blog