jueves, enero 17, 2008

Net CF Loader Log nos ayuda a depurar aplicaciones.

Tenemos todo bien configurado, nuestros ambientes de prueba, servidor de integración continua, pruebas unitarias, hacemos pruebas de usabilidad, integración, etc, pero siempre estamos expuestos a que se nos escape algún bug. Escribi hace algún tiempo respecto a como tratar los bugs, a veces hay que tomar medidas alternas.

Los bugs más difíciles son aquellos que tienen que ver con cuestiones externas a tu aplicación, el sistema operativo, el explorador que usa tu cliente, cuestiones externas difíciles de reproducir como tipos de conexión (celular, wi-fi, etc), etc. Todo esto la mayoría de las veces termina siendo alguna babosada.

Esta es la historía. Como siempre, todo funciona bien en nuestro entorno de pruebas, se crea el installer, se distribuye usando UpdaterAgent (como ClickOnce para Mobile) y en algunas agendas funciona la app y en otras no.

Primer paso revisamos el log de errores.

----------------------
Version: 3.0.2933.38083
17/01/08 11:33:57 a.m.
Cerrando la aplicación. Error inesperado: InvalidProgramException
-----------------------
-----------------------
Version: 3.0.2933.38083
17/01/08 11:33:58 a.m.
System.InvalidProgramException: InvalidProgramException
en System.RuntimeType.InternalGetConstructors()
en System.RuntimeType.GetConstructors()
en Microsoft.Practices.Mobile.ObjectBuilder.ConstructorReflectionStrategy.GetMembers()
en Microsoft.Practices.Mobile.ObjectBuilder.ReflectionStrategy`1.BuildUp()
en Microsoft.Practices.Mobile.ObjectBuilder.BuilderStrategy.BuildUp()
en Microsoft.Practices.Mobile.ObjectBuilder.PolicyProviderStrategy.BuildUp()
en Microsoft.Practices.Mobile.ObjectBuilder.BuilderStrategy.BuildUp()
en Microsoft.Practices.Mobile.ObjectBuilder.SingletonStrategy.BuildUp()
en Microsoft.Practices.Mobile.ObjectBuilder.BuilderStrategy.BuildUp()
en Microsoft.Practices.Mobile.ObjectBuilder.TypeMappingStrategy.BuildUp()
en Microsoft.Practices.Mobile.ObjectBuilder.BuilderBase`1.DoBuildUp()
en Microsoft.Practices.Mobile.ObjectBuilder.BuilderBase`1.BuildUp()
en Microsoft.Practices.Mobile.CompositeUI.Collections.ManagedObjectCollection`1.BuildFirstTimeItem()
en Microsoft.Practices.Mobile.CompositeUI.Collections.ManagedObjectCollection`1.Build()
en Microsoft.Practices.Mobile.CompositeUI.Collections.ManagedObjectCollection`1.AddNew()
en Microsoft.Practices.Mobile.CompositeUI.Common.ControlledWorkItem`1.OnBuiltUp()
(más y más llamadas)

Nuestro stack es tan grande que ni sentido tiene reproducirlo. Pero hasta ahí es la parte importante. Tenemos un InvalidProgramException que no nos dice mucho. Lo más cerca que podemos llegar es hasta el Código de ConstructorReflectionStrategy.GetMembers, el resto es el framework. Vamos a depurar y encontramos que el error esta al querer obtener los constructores de una de nuestras clases, vamos y revisamos que ahí no este el problema. Tiene un constructor publico que no recibe parametros al igual que las clases de las que hereda, tal como sospechabamos el problema no es esa clase.

Vamos a ver que hay de partícular en las agendas que fallan. Empezamos por crear una prueba unitaria que no haga más que usar directamente reflecto para obtener los constructores de esa misma clase. Se prueba en el emulador y todo funciona, se prueba en una de las agendas que falla y falla. Excelente, ya aislamos el problema. ¿Y ahora? Seguimos sin saber el porque del error, sólo que truena al tratar de obtener los constructores. Seguramente es un problema en como se cargan los objetos y ahí entra el Loader Log.

Se empieza por activar usando el Net CF Logging Configuration que es parte de los .NET Compact Framework Powertoys para 3.5.

image

Siguiente paso, correr nuestra aplicación en un dispositivo que si funciona y copiar a nuestra maquina el archivo de log "netcf_Loader.log". Luego hay que hacer lo mismo en una agenda que no funcione. El log es muy fácil de interpretar y Steve Prtachner ya lo describio a detalle, así que no lo haré ahora, pero para lo que queríamos encontrar de diferencia ni siquiera necesitamos tanta información.

Con los dos archivos en nuestra máquina usamos Beyond Compare o alguna herramienta similar y vemos las diferencias.

image

Al principio todo es igual, salvo cosas no importantes como el ProcessID. La secuencia y que busca y resuelve cada referencia es identica hasta que llegamos al punto donde truena.

image

Vemos que después de cargar el Modulo ConnectionMonitor, trata de cargar el de SqlServerCE 3.5 en el log de la agenda que si funciona, sin embargo, la secuencia es muy distinta en las otras y trata de cargar aún la versión anterior del SqlCE.

Listo, problema resuelto, resulta que el GetConstructors, para obtener los members de esa clase carga los modulos hacía los que tiene dependencias, uno de esos es SqlServerCE 3.5 y no estaba bien instalado en la agenda.

En este momento podrías estar pensando, porque diablos no revisandon la instalación de todo antes de continuar, claramente era un problema de instalación. Claro que podríamos haber hecho un checklist y revisar que todo estuviera bien, sin embargo, no hicimos eso y tuvimos que darnos cuenta de una manera distinta, pero esa es otra lección aprendida y no la del tema de este blog. Por eso les decía que este tipo de problemas muchas veces termina siendo una babosada.

El Loader Log nunca me había resuelto nada, aunque lo había usado un par de veces sin ningún éxito. Lo importante de esta y tantas herramientas, de las que escribire más adelante es el conocerlas y saber que estan ahí instaladas en la máquina para que algún día, tal vez después de algunos años la uses y te saquen de algún problema. Esa es la lección del día.

No hay comentarios.: