“Paso” al depurar progtwigs multiproceso en Visual Studio

Una cosa que me molesta al depurar progtwigs en Visual Studio (2005 en mi caso) es que cuando uso “step over” (presionando F10 ) para ejecutar la siguiente línea de código, a menudo termino llegando a esa línea de código en particular en un hilo totalmente diferente al que estaba mirando. Esto significa que todo el contexto de lo que estaba haciendo se perdió.

¿Cómo trabajo alrededor de esto?

Si esto es posible en versiones posteriores de Visual Studio, me gustaría saberlo también.

Establecer un punto de interrupción en la próxima línea de código que tiene un salto condicional solo para este hilo no es la respuesta que estoy buscando, ya que es demasiado trabajo para ser útil para mí 🙂

Creo que solo hay una respuesta a su pregunta, que ha descartado como “demasiado trabajo”. Sin embargo, creo que es porque lo estás haciendo mal. Permítanme presentar los pasos para agregar un punto de interrupción condicional en Thread ID, que son extremadamente fáciles, pero no obvios hasta que los conozca.

  1. Detenga el depurador en un punto en el que esté en el hilo correcto que desea continuar depurando ( que supongo que suele ser el primer hilo que llega ).

  2. Ingrese $TID en la ventana del reloj.

  3. Agregue un punto de interrupción con la condición $TID == < valor de $ TID desde la ventana Inspección > ,
    Ejemplo : $TID == 0x000016a0

  4. Continuar la ejecución.

$TID es una variable mágica para los comstackdores de Microsoft (al menos Visual Studio 2003) que tiene el valor de la identificación actual del subproceso. Lo hace mucho más fácil que mirar (FS + 0x18) [0x24]. = D

Dicho esto, puede obtener el mismo comportamiento que los puntos de corte One-Shot del depurador con algunas macros simples. Cuando te pones de pie, el depurador, detrás de las escenas, establece un punto de interrupción, corre hacia ese punto de interrupción y luego lo elimina. La clave para una interfaz de usuario consistente es eliminar esos puntos de interrupción si se alcanza CUALQUIER punto de interrupción.

Las siguientes dos macros proporcionan Paso a Paso y Ejecutar al Cursor para el hilo actual. Esto se lleva a cabo de la misma manera que el depurador, con los puntos de interrupción eliminados después de la ejecución, independientemente de qué punto de interrupción se golpee.

Deberá asignar una combinación de teclas para ejecutarlos.

NOTA : Una advertencia: la macro Paso a paso solo funciona correctamente si el cursor está en la línea que desea sobrepasar. Esto se debe a que determina la ubicación actual por la ubicación del cursor y simplemente agrega una al número de línea. Es posible que pueda reemplazar el cálculo de ubicación con información sobre el punto de ejecución actual, aunque no pude localizar esa información desde el IDE de macro.

¡Aquí están y buena suerte, caza de errores!

Para usar estas macros en Visual Studio:
1. Abra la macro IDE (desde el menú, seleccione: Herramientas-> Macros-> Macro IDE ... )
2. Agregue un nuevo archivo de código (del Menú: seleccione: Proyecto-> Agregar nuevo elemento ... , elija Archivo de código y haga clic en Agregar )
3. Pegue este código.
4. Guarde el archivo.

Para agregar combinaciones de teclas para ejecutar estas macros en Visual Studio:
1. Abra las Opciones (del Menú, seleccione: Herramientas-> Opciones )
2. Expandir a Medio ambiente-> Teclado
3. En los comandos Mostrar que contengan:, escriba Macros. para ver todas tus macros
4. Seleccione una macro, luego haga clic en Pulsar teclas de método abreviado:
5. Escriba el combo que desea usar ( retroceso elimina combinaciones combinadas)
6. haga clic en Asignar para configurar su acceso directo para ejecutar la macro seleccionada.

 Imports System Imports EnvDTE Imports EnvDTE80 Imports System.Diagnostics Public Module DebugHelperFunctions Sub RunToCursorInMyThread() Dim textSelection As EnvDTE.TextSelection Dim myThread As EnvDTE.Thread Dim bp As EnvDTE.Breakpoint Dim bps As EnvDTE.Breakpoints ' For Breakpoints.Add() Dim FileName As String Dim LineNumber As Integer Dim ThreadID As String ' Get local references for ease of use myThread = DTE.Debugger.CurrentThread textSelection = DTE.ActiveDocument.Selection LineNumber = textSelection.ActivePoint.Line FileName = textSelection.DTE.ActiveDocument.FullName ThreadID = myThread.ID ' Add a "One-Shot" Breakpoint in current file on current line for current thread bps = DTE.Debugger.Breakpoints.Add("", FileName, LineNumber, 1, "$TID == " & ThreadID) ' Run to the next stop DTE.Debugger.Go(True) ' Remove our "One-Shot" Breakpoint For Each bp In bps bp.Delete() Next End Sub Sub StepOverInMyThread() Dim textSelection As EnvDTE.TextSelection Dim myThread As EnvDTE.Thread Dim bp As EnvDTE.Breakpoint Dim bps As EnvDTE.Breakpoints ' For Breakpoints.Add() Dim FileName As String Dim LineNumber As Integer Dim ThreadID As String ' Get local references for ease of use myThread = DTE.Debugger.CurrentThread textSelection = DTE.ActiveDocument.Selection LineNumber = textSelection.ActivePoint.Line FileName = textSelection.DTE.ActiveDocument.FullName ThreadID = myThread.ID LineNumber = LineNumber + 1 ' Add a "One-Shot" Breakpoint in current file on current line for current thread bps = DTE.Debugger.Breakpoints.Add("", FileName, LineNumber, 1, "$TID == " & ThreadID) ' Run to the next stop DTE.Debugger.Go(True) ' Remove our "One-Shot" Breakpoint For Each bp In bps bp.Delete() Next End Sub End Module 

Descargo de responsabilidad : escribí estas macros en Visual Studio 2005 . Probablemente pueda usarlos bien en Visual Studio 2008 . Pueden requerir modificaciones para Visual Studio 2003 y versiones anteriores.

Puede congelar un hilo diferente o cambiar a otro hilo usando la ventana de depuración de Threads ( Ctrl + Alt + H ).

La forma simple de depurar un hilo en particular es congelar todos los otros hilos desde la ventana de Subprocesos.

[Ctrl + D, T] o [Ctrl + Alt + H] – Abre la ventana de subprocesos (utilizada para supervisar, congelar y nombrar hilos)

La ventana de subprocesos le permite seleccionar si desea mostrar la ubicación de los otros subprocesos en Visual Studio. Ese es un buen recordatorio para mí de que el hilo actual que estoy depurando no es el único en juego. Al pasar el marcador de hilo, obtendrá el nombre y la identificación de los hilos.

Se encuentran más consejos en: http://devpinoy.org/blogs/jakelite/archive/2009/01/10/5-tips-on-debugging-multi-threaded-code-in-visual-studio-net.aspx

Aparentemente, Visual Studio 2010 solo cambia a otros subprocesos si presiona F10 cuando el depurador tuvo que romper en ese subproceso antes o si se establece un punto de interrupción que será golpeado en este subproceso.

Usé el siguiente código para probar el comportamiento:

 class Program { static void Main(string[] args) { var t = new Thread(new ThreadStart(Work)); t.Start(); for (int i = 0; i < 20; i++) { Thread.Sleep(1000); Console.WriteLine("............"); } t.Join(); } static void Work() { for (int i = 0; i < 20; i++) { Thread.Sleep(1000); Console.WriteLine("ZZzzzzzzzzzzzzzz"); } } } 

Si se dirige al progtwig o agrega un punto de interrupción en el método Main() , al presionar F10 solo se avanza por el código del hilo principal.

Si agrega un punto de interrupción en el método Work() , el depurador pasa por ambos hilos.

Este comportamiento de Visual Studio tiene sentido, pero a mí me parece una característica no documentada ...

Recientemente tuve el mismo problema de depurar solo un determinado hilo. Si bien no descontaré la respuesta anterior como muy completa y valiosa (y desearía haberla encontrado hace 2 días), implementé lo siguiente para ayudar con la resolución de problemas “cotidianos”.

Esta idea es solo una solución simple cuando se ejecutan varias instancias de la misma clase en varios subprocesos, lo que nuestra aplicación está haciendo en todo momento.

Inicializamos todas las instancias de clase con un ID o nombre único, por lo que sabemos cómo o por qué se creó la instancia. Luego, cuando necesitamos depurar una instancia específica (es decir, un hilo) y no congelar otros hilos, agregamos lo siguiente:

 if (instance.ID == myID) { // Assert BreakPoint } 

En nuestro caso, establecemos ID fijas, por lo que conocemos la ID que queremos solucionar cuando tenemos un problema.