Folklore de programadores e ingenieros (parte 1)

Folklore de programadores e ingenieros (parte 1)

Esta es una selección de historias de Internet sobre cómo los errores a veces tienen manifestaciones completamente increíbles. Quizás tú también tengas algo que contar.

Alergia en el coche al helado de vainilla

Una historia para ingenieros que entienden que lo obvio no siempre es la respuesta, y que no importa cuán descabellados puedan parecer los hechos, siguen siendo los hechos. La División Pontiac de General Motors Corporation recibió una queja:

Esta es la segunda vez que te escribo y no te culpo por no contestar, porque suena loco. Nuestra familia tiene la tradición de comer helado todas las noches después de cenar. Los tipos de helado cambian cada vez, y después de cenar, toda la familia elige qué helado comprar y luego yo voy a la tienda. Hace poco compré un Pontiac nuevo y desde entonces mis viajes a comprar helado se han convertido en un problema. Verás, cada vez que compro helado de vainilla y vuelvo de la tienda, el coche no arranca. Si llevo algún otro helado el coche arranca sin problema. Quiero hacer una pregunta seria, por estúpida que parezca: “¿Qué tiene el Pontiac que hace que no arranca cuando le traigo helado de vainilla, sino que arranca fácilmente cuando le traigo otro sabor de helado?”

Como puedes imaginar, el presidente de la división se mostró escéptico ante la carta. Sin embargo, por si acaso, envié a un ingeniero a comprobarlo. Le sorprendió encontrarse con un hombre rico y bien educado que vivía en una zona hermosa. Acordaron reunirse inmediatamente después de cenar para que los dos pudieran ir a la tienda a comprar un helado. Esa noche era vainilla y cuando regresaron al auto, no arrancaba.

El ingeniero vino tres noches más. La primera vez el helado fue de chocolate. El auto arrancó. La segunda vez fue helado de fresa. El auto arrancó. La tercera noche pidió llevar vainilla. El auto no arrancó.

Razonando racionalmente, el ingeniero se negó a creer que el coche fuera alérgico al helado de vainilla. Por lo tanto, acordé con el dueño del auto que continuaría con sus visitas hasta encontrar una solución al problema. Y en el camino empezó a tomar notas: anotaba toda la información, hora del día, tipo de gasolina, hora de llegada y regreso de la tienda, etc.

El ingeniero pronto se dio cuenta de que el propietario del coche dedicaba menos tiempo a comprar helado de vainilla. El motivo fue la distribución de la mercancía en la tienda. El helado de vainilla era el más popular y se guardaba en un congelador separado en la parte delantera de la tienda para que fuera más fácil de encontrar. Y todas las demás variedades estaban en la parte trasera de la tienda, y tomó mucho más tiempo encontrar la variedad adecuada y pagar.

Ahora la pregunta era para el ingeniero: ¿por qué el auto no arrancaba si había pasado menos tiempo desde que se apagó el motor? Como el problema era el tiempo, no el helado de vainilla, el ingeniero rápidamente encontró la respuesta: era una cerradura de gas. Ocurría todas las noches, pero cuando el dueño del auto pasaba más tiempo buscando helado, el motor logró enfriarse lo suficiente y arrancó fácilmente. Y cuando el hombre compró helado de vainilla, el motor todavía estaba demasiado caliente y la cerradura del gas no tuvo tiempo de disolverse.

Moraleja: Incluso los problemas completamente locos a veces son reales.

Crash Bandicoot

Es doloroso experimentar esto. Como programador, te acostumbras a culpar a tu código en primer, segundo, tercer lugar... y en algún punto del lugar diez mil culpas al compilador. Y más abajo en la lista ya le echas la culpa al equipo.

Aquí está mi historia sobre el error de hardware.

Para el juego Crash Bandicoot, escribí código para cargarlo y guardarlo en una tarjeta de memoria. Para un desarrollador de juegos tan engreído, fue como un paseo por el parque: pensé que el trabajo llevaría varios días. Sin embargo, terminé depurando el código durante seis semanas. En el camino, resolví otros problemas, pero cada pocos días volvía a este código durante unas horas. Fue una agonía.

El síntoma se veía así: cuando guardas la partida actual del juego y accedes a la tarjeta de memoria, casi siempre todo va bien... Pero a veces la operación de lectura o escritura se agota sin ninguna razón obvia. Una grabación breve suele dañar la tarjeta de memoria. Cuando un jugador intenta guardar, no sólo falla, sino que también destruye el mapa. Tonterías.

Después de un tiempo, nuestra productora de Sony, Connie Bus, empezó a entrar en pánico. No pudimos enviar el juego con este error y seis semanas después no entendía qué estaba causando el problema. A través de Connie, contactamos con otros desarrolladores de PS1: ¿alguien ha encontrado algo similar? No. Nadie tuvo problemas con la tarjeta de memoria.

Cuando no se tienen ideas para depurar, el único enfoque que queda es “dividir y conquistar”: eliminar más y más código del programa defectuoso hasta que quede un fragmento relativamente pequeño que todavía causa el problema. Es decir, cortas el programa pieza por pieza hasta que quede la parte que contiene el error.

Pero la cuestión es que es muy difícil recortar fragmentos de un videojuego. ¿Cómo ejecutarlo si eliminaste el código que emula la gravedad? ¿O dibujar personajes?

Por lo tanto, tenemos que reemplazar módulos completos con stubs que pretenden hacer algo útil, pero que en realidad hacen algo muy simple que no puede contener errores. Tenemos que escribir esas muletas para que el juego al menos funcione. Este es un proceso lento y doloroso.

En resumen, lo hice. Eliminé más y más fragmentos de código hasta que me quedé con el código inicial que configura el sistema para ejecutar el juego, inicializa el hardware de renderizado, etc. Por supuesto, en esta etapa no podía crear un menú para guardar y cargar, porque tendría que crear un código auxiliar para todo el código de gráficos. Pero podría fingir ser un usuario usando la pantalla (invisible) de guardar y cargar y pedir guardar y luego escribir en la tarjeta de memoria.

Esto me dejó con un pequeño fragmento de código que todavía tenía el problema anterior, ¡pero seguía sucediendo de forma aleatoria! La mayoría de las veces todo funcionaba bien, pero ocasionalmente surgían fallos. Eliminé casi todo el código del juego, pero el error seguía vivo. Esto fue desconcertante: el código restante en realidad no hizo nada.

En algún momento, probablemente alrededor de las tres de la mañana, se me ocurrió una idea. Las operaciones de lectura y escritura (entrada/salida) implican tiempos de ejecución precisos. Cuando trabajas con un disco duro, una tarjeta de memoria o un módulo Bluetooth, el código de bajo nivel responsable de leer y escribir lo hace de acuerdo con los pulsos del reloj.

Con la ayuda de un reloj, un dispositivo que no está conectado directamente al procesador se sincroniza con el código que se ejecuta en el procesador. El reloj determina la velocidad en baudios: la velocidad a la que se transfieren los datos. Si hay confusión con los tiempos, entonces el hardware o el software, o ambos, también están confundidos. Y esto es muy malo, porque los datos pueden dañarse.

¿Qué pasa si algo en nuestro código confunde los tiempos? Revisé todo lo relacionado con esto en el código del programa de prueba y noté que configuramos el temporizador programable en PS1 a 1 kHz (1000 ticks por segundo). Esto es bastante; por defecto, cuando se inicia la consola, funciona a 100 Hz. Y la mayoría de los juegos utilizan esta frecuencia.

Andy, el desarrollador del juego, configuró el cronómetro en 1 kHz para poder calcular los movimientos con mayor precisión. Andy tiende a exagerar y si emulamos la gravedad, ¡lo hacemos con la mayor precisión posible!

Pero, ¿qué pasaría si acelerar el temporizador afectara de alguna manera la sincronización general del programa y, por lo tanto, el reloj que regula la velocidad en baudios de la tarjeta de memoria?

Comenté el código del temporizador. El error no volvió a ocurrir. Pero esto no significa que lo hayamos solucionado, porque el fallo se produjo de forma aleatoria. ¿Y si simplemente tuviera suerte?

Unos días más tarde volví a experimentar con el programa de prueba. El error no volvió a ocurrir. Regresé al código base completo del juego y modifiqué el código de guardar y cargar para que el temporizador programable se reiniciara a su valor original (100 Hz) antes de acceder a la tarjeta de memoria, y luego se reiniciara a 1 kHz. No hubo más accidentes.

¿Pero por qué sucedió esto?

Regresé al programa de prueba nuevamente. Intenté encontrar algún patrón en la aparición de un error con un temporizador de 1 kHz. Finalmente me di cuenta de que el error ocurre cuando alguien juega con un controlador de PS1. Dado que rara vez haría esto yo mismo, ¿por qué necesitaría un controlador al probar el código para guardar y cargar? - Ni siquiera noté esta dependencia. Pero un día, uno de nuestros artistas estaba esperando a que terminara las pruebas (probablemente estaba maldiciendo en ese momento) y nerviosamente hacía girar el controlador en sus manos. Se ha producido un error. "¡¿Esperar lo?!" Bueno, ¡hazlo de nuevo!

Cuando me di cuenta de que estos dos eventos estaban interconectados, pude reproducir fácilmente el error: comencé a grabar en la tarjeta de memoria, moví el controlador y arruiné la tarjeta de memoria. A mí me pareció un error de hardware.

Me acerqué a Connie y le conté mi descubrimiento. Ella le transmitió la información a uno de los ingenieros que diseñó la PS1. "Imposible", respondió, "no puede ser un problema de hardware". Le pedí a Connie que organizara una conversación para nosotros.

El ingeniero me llamó y discutimos en su inglés deficiente y en mi japonés (extremadamente) deficiente. Finalmente dije: "Permítanme enviar mi programa de prueba de 30 líneas donde mover el controlador causa un error". El acepto. Dijo que era una pérdida de tiempo y que estaba terriblemente ocupado trabajando en un nuevo proyecto, pero que cedería porque éramos un desarrollador muy importante para Sony. Limpié mi programa de prueba y se lo envié.

La noche siguiente (estábamos en Los Ángeles y él en Tokio) me llamó y se disculpó tímidamente. Fue un problema de hardware.

No sé exactamente cuál fue el error, pero por lo que escuché en la sede de Sony, si configurabas el temporizador en un valor suficientemente alto, interfería con los componentes de la placa base cercanos al cristal del temporizador. Uno de ellos era un controlador de velocidad en baudios para la tarjeta de memoria, que también establecía la velocidad en baudios para los controladores. No soy ingeniero, así que es posible que haya cometido un error.

Pero la conclusión es que hubo interferencia entre los componentes de la placa base. Y al transmitir datos simultáneamente a través del puerto del controlador y el puerto de la tarjeta de memoria con un temporizador funcionando a 1 kHz, se perdieron bits, se perdieron datos y la tarjeta se dañó.

vacas malas

En la década de 1980, mi mentor Sergei escribió software para el SM-1800, un clon soviético del PDP-11. Este microordenador acaba de instalarse en una estación de ferrocarril cerca de Sverdlovsk, un importante centro de transporte de la URSS. El nuevo sistema fue diseñado para encaminar el tráfico de vagones y mercancías. Pero contenía un error molesto que provocaba fallos y fallos aleatorios. Las caídas siempre ocurrían cuando alguien regresaba a casa por la noche. Pero a pesar de una investigación exhaustiva al día siguiente, la computadora funcionó correctamente en todas las pruebas manuales y automáticas. Esto generalmente indica una condición de carrera o algún otro error competitivo que ocurre bajo ciertas condiciones. Cansado de las llamadas a altas horas de la noche, Sergei decidió llegar al fondo del asunto y, en primer lugar, comprender qué condiciones en el patio de clasificación provocaron la avería de la computadora.

Primero, recopiló estadísticas de todas las caídas inexplicables y creó un gráfico por fecha y hora. El patrón era obvio. Después de observar durante unos días más, Sergei se dio cuenta de que podía predecir fácilmente el momento de futuras fallas del sistema.

Pronto se enteró de que las interrupciones sólo ocurrían cuando la estación clasificaba trenes cargados de ganado del norte de Ucrania y el oeste de Rusia que se dirigían a un matadero cercano. Esto en sí mismo era extraño, porque el matadero era abastecido por granjas ubicadas mucho más cerca, en Kazajstán.

La central nuclear de Chernobyl explotó en 1986 y la lluvia radioactiva hizo que las zonas circundantes fueran inhabitables. Amplias zonas del norte de Ucrania, Bielorrusia y Rusia occidental quedaron contaminadas. Ante la sospecha de altos niveles de radiación en los vagones que llegaban, Sergei desarrolló un método para probar esta teoría. A la población se le prohibió tener dosímetros, por lo que Sergei se registró con varios militares en la estación de tren. Después de varios tragos de vodka, logró convencer a un soldado para que midiera el nivel de radiación en uno de los vagones sospechosos. Resultó que el nivel era varias veces superior a los valores normales.

El ganado no sólo emitió mucha radiación, sino que su nivel era tan alto que provocó la pérdida aleatoria de bits en la memoria del SM-1800, que estaba ubicado en un edificio al lado de la estación.

Había escasez de alimentos en la URSS y las autoridades decidieron mezclar la carne de Chernobyl con carne de otras regiones del país. Esto hizo posible reducir el nivel general de radiactividad sin perder recursos valiosos. Al enterarse de esto, Sergei inmediatamente rellenó los documentos de emigración. Y los fallos de la computadora se detuvieron por sí solos cuando el nivel de radiación disminuyó con el tiempo.

A través de las tuberías

Érase una vez, Movietech Solutions creó un software para cines, diseñado para contabilidad, venta de entradas y gestión general. La versión para DOS de la aplicación insignia fue bastante popular entre las cadenas de cines pequeñas y medianas de América del Norte. Por eso no sorprende que cuando se anunció una versión de Windows 95, integrada con las últimas pantallas táctiles y quioscos de autoservicio, y equipada con todo tipo de herramientas de generación de informes, rápidamente también se hiciera popular. La mayoría de las veces la actualización se realizó sin problemas. El personal de TI local instaló nuevos equipos, migró datos y el negocio continuó. Excepto cuando no duró. Cuando esto sucedía, la empresa enviaba a James, apodado "El Limpiador".

Aunque el apodo sugiere un tipo nefasto, el limpiador es solo una combinación de instructor, instalador y experto en todos los oficios. James pasaría unos días en el sitio del cliente juntando todos los componentes y luego pasaría un par de días más enseñando al personal cómo usar el nuevo sistema, resolviendo cualquier problema de hardware que surgiera y esencialmente ayudando al software durante su infancia.

Por lo tanto, no es sorprendente que durante estos tiempos agitados, James llegara a la oficina por la mañana y, antes de que pudiera llegar a su escritorio, fuera recibido por el gerente, lleno de cafeína más allá de lo habitual.

"Me temo que debes ir a Annapolis, Nueva Escocia, lo antes posible". Todo su sistema falló y después de una noche de trabajar con sus ingenieros, no podemos entender qué sucedió. Parece que la red ha fallado en el servidor. Pero sólo después de que el sistema hubiera estado funcionando durante varios minutos.

— ¿No volvieron al viejo sistema? - Respondió James completamente serio, aunque mentalmente abrió mucho los ojos por la sorpresa.

— Exacto: su especialista en TI “cambió de prioridades” y decidió irse con su antiguo servidor. James, instalaron el sistema en seis sitios y solo pagaron por soporte premium, y su negocio ahora funciona como en la década de 1950.

James se enderezó un poco.

- Ese es otro asunto. Bien, comencemos.

Cuando llegó a Annapolis, lo primero que hizo fue encontrar la primera sala del cliente que tenía un problema. En el mapa tomado en el aeropuerto, todo parecía decente, pero el área alrededor de la dirección deseada parecía sospechosa. No es un gueto, pero sí una reminiscencia del cine negro. Mientras James estacionaba en la acera del centro, una prostituta se le acercó. Dado el tamaño de Annapolis, lo más probable es que fuera el único en toda la ciudad. Su aparición inmediatamente me recordó al famoso personaje que ofrecía sexo por dinero en la pantalla grande. No, no sobre Julia Roberts, sino sobre Jon Voight [alusión a la película "Midnight Cowboy" - aprox. carril].

Después de despedir a la prostituta, James fue al cine. El área circundante había mejorado, pero todavía daba la impresión de estar deteriorada. No es que James estuviera demasiado preocupado. Ha estado en lugares miserables antes. Y esto era Canadá, donde incluso los atracadores son lo suficientemente educados como para decir "gracias" después de quitarle la billetera.

La entrada lateral al cine estaba en un callejón húmedo. James caminó hacia la puerta y llamó. Pronto crujió y se abrió ligeramente.

-¿Eres limpiadora? - llegó una voz ronca desde el interior.

- Sí, soy yo... vine a arreglarlo todo.

James entró en el vestíbulo del cine. Aparentemente no teniendo otra opción, el personal comenzó a repartir boletos impresos a los visitantes. Esto dificultó la presentación de informes financieros, y mucho menos detalles más interesantes. Pero el personal saludó a James con alivio e inmediatamente lo llevó a la sala de servidores.

A primera vista todo estaba bien. James inició sesión en el servidor y comprobó los lugares sospechosos habituales. Ningún problema. Sin embargo, por precaución, James apagó el servidor, reemplazó la tarjeta de red y revirtió el sistema. Inmediatamente empezó a trabajar a pleno rendimiento. El personal empezó a vender entradas nuevamente.

James llamó a Mark y le informó de la situación. No es difícil imaginar que James quiera quedarse y ver si sucede algo inesperado. Bajó las escaleras y empezó a preguntar a los empleados qué había pasado. Evidentemente el sistema ha dejado de funcionar. Lo apagaron y encendieron, todo funcionó. Pero después de 10 minutos el sistema se cayó.

Justo en ese momento sucedió algo similar. De repente, el sistema de emisión de billetes empezó a arrojar errores. El personal suspiró y tomó los boletos de papel, y James se apresuró a ir a la sala de servidores. Todo parecía bien con el servidor.

Entonces entró uno de los empleados.

— El sistema vuelve a funcionar.

James estaba desconcertado porque no había hecho nada. Más precisamente, nada que haga funcionar el sistema. Cerró la sesión, cogió su teléfono y llamó a la línea de soporte de su empresa. Pronto el mismo empleado entró en la sala de servidores.

- El sistema no funciona.

James miró al servidor. Un patrón interesante y familiar de formas multicolores bailaba en la pantalla: tuberías retorciéndose y entrelazadas caóticamente. Todos hemos visto este salvapantallas alguna vez. Estaba bellamente renderizado y literalmente hipnotizaba.


James presionó un botón y el patrón desapareció. Se apresuró a ir a la taquilla y en el camino se encontró con un empleado que regresaba hacia él.

— El sistema vuelve a funcionar.

Si puedes hacer un facepalm mental, eso es exactamente lo que hizo James. Protector de pantalla. Utiliza OpenGL. Y por tanto, durante el funcionamiento, consume todos los recursos del procesador del servidor. Como resultado, cada llamada al servidor finaliza con un tiempo de espera.

James regresó a la sala del servidor, inició sesión y reemplazó el protector de pantalla con las hermosas tuberías con una pantalla en blanco. Es decir, en lugar de un salvapantallas que consume el 100% de los recursos del procesador, instalé otro que no consume recursos. Luego esperé 10 minutos para comprobar mi suposición.

Cuando James llegó al siguiente cine, se preguntaba cómo explicarle a su manager que acababa de volar 800 km para desactivar el protector de pantalla.

Choque durante una determinada fase de la luna.

Historia verdadera. Un día surgió un error de software que dependía de la fase de la luna. Había una pequeña rutina que se utilizaba habitualmente en varios programas del MIT para calcular la aproximación a la verdadera fase de la Luna. GLS incorporó esta rutina en un programa LISP que, al escribir un archivo, generaba una línea con una marca de tiempo de casi 80 caracteres. Era muy raro que la primera línea de un mensaje terminara siendo demasiado larga y condujera a la siguiente. Y cuando el programa leyó más tarde este archivo, maldijo. La longitud de la primera línea dependía de la fecha y hora exactas, así como de la longitud de la especificación de fase en el momento en que se imprimió la marca de tiempo. Es decir, ¡el error dependía literalmente de la fase de la luna!

Primera edición en papel Archivo de jerga (Steele-1983) contenía un ejemplo de una línea que conducía al error descrito, pero el tipógrafo lo “arregló”. Desde entonces, esto se ha descrito como un "error de fase lunar".

Sin embargo, tenga cuidado con las suposiciones. Hace unos años, los ingenieros del CERN (Centro Europeo de Investigación Nuclear) encontraron errores en los experimentos realizados en el Gran Colisionador de Electrones y Positrones. Dado que las computadoras procesan activamente la enorme cantidad de datos generados por este dispositivo antes de mostrar el resultado a los científicos, muchos especularon que el software era de alguna manera sensible a las fases de la luna. Varios ingenieros desesperados llegaron al fondo de la verdad. ¡El error surgió debido a un ligero cambio en la geometría del anillo de 27 km de largo debido a la deformación de la Tierra durante el paso de la Luna! Esta historia ha entrado en el folklore de la física como “La venganza de Newton contra la física de partículas” y es un ejemplo de la conexión entre las leyes más simples y antiguas de la física y los conceptos científicos más avanzados.

Tirar la cadena del baño detiene el tren

El mayor error de hardware del que he oído hablar ocurrió en un tren de alta velocidad en Francia. El error provocó una frenada de emergencia del tren, pero sólo si había pasajeros a bordo. En cada uno de estos casos, el tren fue puesto fuera de servicio, revisado, pero no se encontró nada. Luego lo enviaron de regreso a la línea e inmediatamente se detuvo.

Durante uno de los controles, un maquinista que viajaba en el tren fue al baño. Pronto se lavó, ¡BOOM! Parada de emergencia.

El ingeniero contactó al conductor y le preguntó:

— ¿Qué hacías justo antes de frenar?

- Bueno, bajé la velocidad en el descenso...

Esto fue extraño, porque durante el funcionamiento normal el tren reduce la velocidad en las bajadas decenas de veces. El tren avanzó y en el siguiente descenso el conductor advirtió:

- Voy a reducir la velocidad.

No pasó nada.

— ¿Qué hiciste durante la última frenada? - preguntó el conductor.

- Bueno... estaba en el baño...

- ¡Pues entonces ve al baño y haz lo que hiciste cuando volvamos a bajar!

El maquinista fue al baño y, cuando el conductor le advirtió: "Estoy reduciendo la velocidad", tiró de la cadena. Por supuesto, el tren se detuvo inmediatamente.

Ahora podían reproducir el problema y necesitaban encontrar la causa.

Después de dos minutos, notaron que el cable del control remoto del freno del motor (el tren tenía un motor en cada extremo) estaba desconectado de la pared del gabinete eléctrico y yacía sobre el relé que controlaba el solenoide del tapón del inodoro... Cuando el relé estaba encendido, creaba interferencia en el cable del freno y la protección del sistema contra fallas simplemente incluía el frenado de emergencia.

La puerta de enlace que odiaba a FORTRAN

Hace unos meses notamos que las conexiones de red en el continente [esto fue en Hawaii] se estaban volviendo muy, muy lentas. Esto podría durar de 10 a 15 minutos y luego volver a ocurrir repentinamente. Después de un tiempo, mi colega se quejó de que las conexiones de red en el continente en general No funciona. Tenía un código FORTRAN que debía copiarse a una máquina en el continente, pero no pudo porque "la red no aguantó lo suficiente para que se completara la carga FTP".

Sí, resultó que se produjeron fallas en la red cuando un colega intentó enviar por FTP un archivo con código fuente en FORTRAN a una máquina en el continente. Intentamos archivar el archivo: luego se copió sin problemas (pero la máquina de destino no tenía un desempaquetador, por lo que el problema no se resolvió). Finalmente "dividimos" el código FORTRAN en pedazos muy pequeños y los enviamos uno a la vez. La mayoría de los fragmentos se copiaron sin problemas, pero algunas piezas no aprobaron o aprobaron después. numerosos intentos.

Cuando examinamos los pasajes problemáticos, descubrimos que tenían algo en común: todos contenían bloques de comentarios que comenzaban y terminaban con líneas compuestas por C mayúscula (como un colega prefería comentar en FORTRAN). Enviamos un correo electrónico a expertos en redes en el continente y les pedimos ayuda. Por supuesto, querían ver muestras de nuestros archivos que no se podían transferir vía FTP... pero nuestras cartas no les llegaron. Finalmente se nos ocurrió una sencilla describircómo se ven los archivos intransferibles. Funcionó :) [¿Me atrevo a agregar aquí un ejemplo de uno de los comentarios problemáticos de FORTRAN? ¡Probablemente no valga la pena!]

Al final logramos resolverlo. Recientemente se instaló una nueva puerta de enlace entre nuestra parte del campus y la red continental. ¡Tuvo ENORMES dificultades para transmitir paquetes que contenían bits repetidos de C mayúscula! Sólo unos pocos de estos paquetes podrían consumir todos los recursos de la puerta de enlace e impedir que la mayoría de los demás paquetes pasen. Nos quejamos al fabricante de la puerta de enlace... y ellos respondieron: “¡Oh, sí, te enfrentas a un error de C repetido! Ya sabemos de él”. Finalmente resolvimos el problema comprando una nueva puerta de enlace de otro fabricante (en defensa del primero, ¡la imposibilidad de transferir programas FORTRAN puede ser una ventaja para algunos!).

Tiempos difíciles

Hace unos años, mientras trabajaba en la creación de un sistema ETL en Perl para reducir los costos de los ensayos clínicos de fase 40, necesitaba procesar alrededor de 000 fechas. Dos de ellos no pasaron la prueba. Esto no me molestó demasiado porque estas fechas se tomaron de datos proporcionados por el cliente que a menudo eran, digamos, sorprendentes. Pero cuando verifiqué los datos originales, resultó que estas fechas eran el 1 de enero de 2011 y el 1 de enero de 2007. Pensé que el error estaba contenido en el programa que acababa de escribir, pero resultó que ya eran 30 años. viejo. Esto puede parecer misterioso para quienes no estén familiarizados con el ecosistema del software. Debido a la decisión de larga data de otra empresa de ganar dinero, mi cliente me pagó para corregir un error que una empresa había introducido por accidente y la otra a propósito. Para que entiendas de qué estoy hablando, necesito hablar sobre la compañía que agregó la característica que terminó convirtiéndose en un error, así como algunos otros eventos interesantes que contribuyeron al misterioso error que solucioné.

En los viejos tiempos, las computadoras Apple a veces cambiaban espontáneamente su fecha al 1 de enero de 1904. La razón era simple: utilizaba un “reloj del sistema” alimentado por baterías para realizar un seguimiento de la fecha y la hora. ¿Qué pasó cuando se agotó la batería? Las computadoras comenzaron a rastrear la fecha por el número de segundos desde el comienzo de una época. Por época nos referimos a la fecha original de referencia, y para Macintosh era el 1 de enero de 1904. Y después de que se agotó la batería, la fecha actual se restableció a la especificada. ¿Pero por qué sucedió esto?

Anteriormente, Apple usaba 32 bits para almacenar la cantidad de segundos desde la fecha original. Un bit puede almacenar uno de dos valores: 1 o 0. Dos bits pueden almacenar uno de cuatro valores: 00, 01, 10, 11. Tres bits: un valor entre ocho: 000, 001, 010, 011, 100 , 101, 110, 111, etc. Y 32 podría almacenar uno de 232 valores, es decir, 4 segundos. Para las fechas de Apple, esto equivalía a unos 294 años, por lo que las Mac más antiguas no pueden manejar fechas posteriores a 967. Y si la batería del sistema se agota, la fecha se restablece a 296 segundos desde el comienzo de la época, y debe configurar la fecha manualmente cada vez que enciende la computadora (o hasta que compre una batería nueva).

Sin embargo, la decisión de Apple de almacenar las fechas como segundos desde la época significó que no podíamos manejar fechas anteriores a la época, lo que tuvo consecuencias de gran alcance, como veremos. Apple introdujo una característica, no un error. Entre otras cosas, esto significaba que el sistema operativo Macintosh era inmune al “error del milenio” (lo que no se podía decir de muchas aplicaciones de Mac que tenían sus propios sistemas de fecha para eludir las restricciones).

Adelante. Usamos Lotus 1-2-3, la "aplicación asesina" de IBM que ayudó a lanzar la revolución de las PC, aunque las computadoras Apple tenían VisiCalc, que hizo que la computadora personal fuera un éxito. Para ser justos, si no hubiera aparecido el 1-2-3, las PC difícilmente habrían despegado y la historia de las computadoras personales podría haberse desarrollado de manera muy diferente. Lotus 1-2-3 trató incorrectamente a 1900 como un año bisiesto. Cuando Microsoft lanzó su primera hoja de cálculo, Multiplan, capturó una pequeña porción del mercado. Y cuando lanzaron el proyecto Excel, decidieron no sólo copiar el esquema de nombres de filas y columnas de Lotus 1-2-3, sino también garantizar la compatibilidad con errores al tratar deliberadamente 1900 como un año bisiesto. Este problema todavía existe hoy. Es decir, en 1-2-3 esto era un error, pero en Excel fue una decisión consciente que aseguró que todos los usuarios de 1-2-3 pudieran importar sus tablas a Excel sin cambiar los datos, incluso si eran incorrectos.

Pero había otro problema. Primero, Microsoft lanzó Excel para Macintosh, que no reconocía fechas anteriores al 1 de enero de 1904. Y en Excel, el 1 de enero de 1900 se consideraba el comienzo de la era. Por lo tanto, los desarrolladores hicieron un cambio para que su programa reconociera el tipo de era y almacenara datos dentro de sí de acuerdo con la era deseada. Microsoft incluso escribió un artículo explicativo sobre esto. Y esta decisión me provocó el error.

Mi sistema ETL recibió hojas de cálculo de Excel de clientes que se crearon en Windows, pero que también se podían crear en una Mac. Por lo tanto, el comienzo de la era en la tabla podría ser el 1 de enero de 1900 o el 1 de enero de 1904. ¿Cómo saberlo? El formato de archivo de Excel muestra la información necesaria, pero el analizador que utilicé no la mostró (ahora sí lo hace) y asumió que conoce la época de una tabla específica. Probablemente podría haber dedicado más tiempo a comprender el formato binario de Excel y enviar un parche al autor del analizador, pero tenía mucho más que hacer para el cliente, así que rápidamente escribí una heurística para determinar la época. Ella era sencilla.

En Excel, la fecha 5 de julio de 1998 se puede representar en el formato "07-05-98" (sistema americano inútil), "5 de julio de 98", "5 de julio de 1998", "5-julio-98" o algún otro formato Otro formato inútil (irónicamente, uno de los formatos que mi versión de Excel no ofrecía era ISO 8601). Sin embargo, dentro de la tabla, la fecha sin formato se almacenó como "35981" para la época-1900 o "34519" para la época-1904 (los números representan el número de días desde la época). Simplemente utilicé un analizador simple para extraer el año de la fecha formateada y luego usé el analizador de Excel para extraer el año de la fecha sin formato. Si ambos valores diferían en 4 años, entonces sabía que estaba usando un sistema de la época 1904.

¿Por qué no utilicé fechas formateadas? Porque el 5 de julio de 1998 se puede formatear como "julio 98" sin perder el día del mes. Recibimos tablas de tantas empresas que las crearon de tantas maneras diferentes que dependía de nosotros (en este caso, yo) determinar las fechas. Además, si Excel lo hace bien, ¡nosotros también deberíamos hacerlo!

Al mismo tiempo encontré 39082. Permítanme recordarles que Lotus 1-2-3 consideraba 1900 como un año bisiesto, y esto se repitió fielmente en Excel. Y dado que esto añadió un día al año 1900, muchas funciones de cálculo de fecha podrían estar equivocadas para ese mismo día. Es decir, 39082 podría haber sido el 1 de enero de 2011 (en Mac) o el 31 de diciembre de 2006 (en Windows). Si mi "analizador de años" extrajo el año 2011 del valor formateado, entonces todo está bien. Pero como el analizador de Excel no sabe qué época se está utilizando, por defecto es la época-1900, devolviendo el año 2006. Mi aplicación vio que la diferencia era de 5 años, lo consideró un error, lo registró y devolvió un valor sin formato.

Para solucionar esto, escribí esto (pseudocódigo):

diff = formatted_year - parsed_year
if 0 == diff
    assume 1900 date system
if 4 == diff
    assume 1904 date system
if 5 == diff and month is December and day is 31
    assume 1904 date system

Y luego las 40 fechas se analizaron correctamente.

En medio de grandes trabajos de impresión

A principios de la década de 1980, mi padre trabajaba en Storage Technology, una división ya desaparecida que creaba unidades de cinta y sistemas neumáticos para alimentación de cintas de alta velocidad.

Rediseñaron las unidades para que pudieran tener una unidad central “A” conectada a siete unidades “B”, y el pequeño sistema operativo en la RAM que controlaba la unidad “A” pudiera delegar operaciones de lectura y escritura a todas las unidades “B”.

Cada vez que se iniciaba la unidad “A”, era necesario insertar un disquete en la unidad periférica conectada a “A” para poder cargar el sistema operativo en su memoria. Era extremadamente primitivo: la potencia informática la proporcionaba un microcontrolador de 8 bits.

El público objetivo de este tipo de equipos eran empresas con almacenes de datos muy grandes (bancos, cadenas minoristas, etc.) que necesitaban imprimir muchas etiquetas de direcciones o extractos bancarios.

Un cliente tuvo un problema. En medio de un trabajo de impresión, una unidad “A” en particular podría dejar de funcionar, provocando que todo el trabajo se detuviera. Para restaurar el funcionamiento de la unidad, el personal tuvo que reiniciar todo. Y si esto sucedía en medio de una tarea de seis horas, entonces se perdía una gran cantidad de costoso tiempo de computadora y se interrumpía el cronograma de toda la operación.

Se enviaron técnicos de Storage Technologies. Pero a pesar de sus mejores esfuerzos, no pudieron reproducir el error en condiciones de prueba: parecía ocurrir en medio de trabajos de impresión grandes. El problema no fue el hardware, reemplazaron todo lo que pudieron: RAM, microcontrolador, unidad de disquete, cada parte imaginable de la unidad de cinta; el problema persistió.

Entonces los técnicos llamaron a la central y llamaron al Perito.

El experto tomó una silla y una taza de café, se sentó en la sala de computadoras (en aquellos días había salas dedicadas a las computadoras) y observó cómo el personal hacía cola para un trabajo de impresión de gran tamaño. El experto esperaba que se produjera un fallo, y así ocurrió. Todos miraron al Experto, pero él no tenía idea de por qué sucedió esto. Entonces ordenó que el trabajo se volviera a poner en cola y todo el personal y los técnicos volvieron a trabajar.

El experto volvió a sentarse en la silla y empezó a esperar el fallo. Pasaron unas seis horas y se produjo la falla. El Experto nuevamente no tenía ideas, excepto que todo sucedió en una habitación llena de gente. Ordenó que se reiniciara la misión, volvió a sentarse y esperó.

Al tercer fallo, el experto notó algo. La falla ocurrió cuando el personal cambió las cintas en una unidad externa. Además, la falla se produjo cuando uno de los empleados atravesó una determinada baldosa del suelo.

El piso elevado estaba hecho de baldosas de aluminio colocadas a una altura de 6 a 8 pulgadas. Numerosos cables de ordenadores pasaban por debajo del suelo elevado para evitar que alguien pudiera pisar accidentalmente un cable importante. Las baldosas se colocaron muy apretadas para evitar que los escombros se metieran debajo del piso elevado.

El perito se dio cuenta de que una de las tejas estaba deformada. Cuando un empleado pisó su esquina, los bordes de la losa rozaron las baldosas adyacentes. Las piezas de plástico que conectaban las baldosas también rozaban con ellas, lo que provocaba microdescargas estáticas que generaban interferencias de radiofrecuencia.

Hoy en día, la RAM está mucho mejor protegida de las interferencias de radiofrecuencia. Pero en aquellos años esto no era así. El experto se dio cuenta de que esta interferencia alteraba la memoria y con ella el funcionamiento del sistema operativo. Llamó al servicio de soporte, pidió nuevos mosaicos, los instaló él mismo y el problema desapareció.

¡Está la marea alta!

La historia tuvo lugar en una sala de servidores, en el cuarto o quinto piso de una oficina en Portsmouth (creo), en la zona de los muelles.

Un día, el servidor Unix con la base de datos principal falló. Lo reiniciaron, pero felizmente siguió cayendo una y otra vez. Decidimos llamar a alguien del servicio de soporte.

El chico de soporte... creo que se llamaba Mark, pero eso no importa... No creo que lo conozca. Realmente no importa. Sigamos con Mark, ¿vale? Excelente.

Entonces, unas horas más tarde llegó Mark (no hay mucha distancia entre Leeds y Portsmouth, ya sabes), encendió el servidor y todo funcionó sin problemas. Típico maldito soporte, el cliente se molesta mucho por esto. Mark revisa los archivos de registro y no encuentra nada extraño. Así que Mark vuelve al tren (o al medio de transporte en el que llegó, por lo que sé, podría haber sido una vaca coja... de todos modos, no importa, ¿vale?) y regresa a Leeds, después de haber desperdiciado El dia.

Esa misma noche el servidor vuelve a fallar. La historia es la misma... el servidor no sube. Mark intenta ayudar de forma remota, pero el cliente no puede iniciar el servidor.

Otro tren, autobús, merengue de limón o cualquier otra tontería, y Mark está de vuelta en Portsmouth. ¡Mira, el servidor arranca sin problemas! Milagro. Mark pasa varias horas comprobando que todo esté en orden con el sistema operativo o el software y se dirige a Leeds.

Alrededor del mediodía el servidor falla (¡tómatelo con calma!). Esta vez parece razonable contratar al personal de soporte de hardware para reemplazar el servidor. Pero no, al cabo de unas 10 horas también cae.

La situación se repitió durante varios días. El servidor funciona, falla después de aproximadamente 10 horas y no se inicia durante las siguientes 2 horas. Revisaron refrigeración, pérdidas de memoria, revisaron todo, pero no encontraron nada. Entonces los choques cesaron.

La semana transcurrió sin preocupaciones... todos estaban felices. Feliz hasta que todo empiece de nuevo. La imagen es la misma. 10 horas de trabajo, 2-3 horas de inactividad...

Y entonces alguien (creo que me dijeron que esta persona no tenía nada que ver con TI) dijo:

"¡Es la marea!"

La exclamación fue recibida con miradas en blanco, y la mano de alguien probablemente vaciló ante el botón de llamada de seguridad.

"Deja de funcionar con la marea".

Este parecería ser un concepto completamente extraño para los trabajadores de soporte de TI, quienes probablemente no leerán el Tide Yearbook mientras se sientan a tomar un café. Explicaron que esto no podía tener ninguna relación con la marea, pues el servidor llevaba una semana funcionando sin fallas.

"La semana pasada la marea estaba baja, pero esta semana está alta".

Un poco de terminología para aquellos que no tienen licencia de yate. Las mareas dependen del ciclo lunar. Y a medida que la Tierra gira, cada 12,5 horas la atracción gravitacional del Sol y la Luna crea un maremoto. Al comienzo del ciclo de 12,5 horas hay marea alta, a mitad del ciclo hay reflujo y al final vuelve a haber marea alta. Pero a medida que cambia la órbita de la luna, también cambia la diferencia entre marea alta y baja. Cuando la Luna está entre el Sol y la Tierra o en el lado opuesto de la Tierra (con luna llena o sin luna), obtenemos mareas Syzygyn: las mareas altas más altas y las mareas bajas más bajas. En media luna tenemos mareas en cuadratura: las mareas más bajas. La diferencia entre los dos extremos disminuye considerablemente. El ciclo lunar dura 28 días: sicigio - cuadratura - sicigio - cuadratura.

Cuando a los técnicos se les explicó la esencia de las fuerzas de marea, inmediatamente pensaron que debían llamar a la policía. Y bastante lógico. Pero resulta que el tipo tenía razón. Dos semanas antes, un destructor atracó no lejos de la oficina. Cada vez que la marea lo elevaba a cierta altura, el puesto de radar del barco terminaba al nivel del piso de la sala de servidores. Y el radar (o el equipo de guerra electrónica, o algún otro juguete militar) creó el caos en las computadoras.

Misión de vuelo del cohete

Se me asignó la tarea de trasladar un gran sistema de control y monitoreo de lanzamiento de cohetes (alrededor de 400 mil líneas) a nuevas versiones del sistema operativo, el compilador y el lenguaje. Más precisamente, desde Solaris 2.5.1 hasta Solaris 7, y desde Verdix Ada Development System (VADS), escrito en Ada 83, hasta el sistema Rational Apex Ada, escrito en Ada 95. Rational compró VADS y su producto fue obsoleto, aunque Rational intentó implementar versiones compatibles de paquetes específicos de VADS para facilitar la transición al compilador de Apex.

Tres personas me ayudaron a compilar el código de forma limpia. Fueron dos semanas. Y luego trabajé por mi cuenta para que el sistema funcionara. En resumen, era la peor arquitectura e implementación de un sistema de software que había encontrado, por lo que me tomó otros dos meses completar el puerto. Luego, el sistema se sometió a pruebas, lo que llevó varios meses más. Inmediatamente corrigí los errores que se encontraron durante las pruebas, pero su número disminuyó rápidamente (el código fuente era un sistema de producción, por lo que su funcionalidad funcionó de manera bastante confiable, solo tuve que eliminar los errores que surgieron durante la adaptación al nuevo compilador). Al final, cuando todo iba como debía, me transfirieron a otro proyecto.

Y el viernes antes del Día de Acción de Gracias, sonó el teléfono.

Se suponía que el lanzamiento del cohete se probaría en unas tres semanas, y durante las pruebas de laboratorio de la cuenta atrás, se bloqueó la secuencia de comandos. En la vida real, esto abortaría la prueba, y si el bloqueo se produjera a los pocos segundos de arrancar el motor, se producirían varias acciones irreversibles en los sistemas auxiliares, lo que requeriría una larga y costosa preparación del cohete. No habría comenzado, pero mucha gente habría estado muy molesta por la pérdida de tiempo y de mucho, mucho dinero. No deje que nadie le diga que el Departamento de Defensa gasta dinero imprudentemente; nunca he conocido a un gerente de contrataciones que no pusiera el presupuesto en primer o segundo lugar, seguido del cronograma.

En meses anteriores, este desafío de cuenta regresiva se había ejecutado cientos de veces en muchas variaciones, con solo algunos contratiempos menores. Así que la probabilidad de que esto ocurriera era muy baja, pero sus consecuencias fueron muy significativas. Multiplique ambos factores y comprenderá que las noticias predijeron una semana de vacaciones arruinada para mí y para docenas de ingenieros y gerentes.

Y me prestaron atención como la persona que portó el sistema.

Como ocurre con la mayoría de los sistemas críticos para la seguridad, se registraron muchos parámetros, por lo que fue bastante fácil identificar las pocas líneas de código que se ejecutaron antes de que el sistema fallara. Y, por supuesto, no había absolutamente nada inusual en ellas: las mismas expresiones se habían ejecutado con éxito literalmente miles de veces durante la misma ejecución.

Llamamos a la gente de Apex a Rational porque fueron ellos quienes desarrollaron el compilador y algunas de las rutinas que desarrollaron fueron llamadas en el código sospechoso. Ellos (y todos los demás) quedaron impresionados de que era necesario llegar a la raíz de un problema de importancia literalmente nacional.

Como no había nada interesante en las revistas, decidimos intentar reproducir el problema en un laboratorio local. Esta no fue una tarea fácil ya que el evento ocurrió aproximadamente una vez cada 1000 ejecuciones. Una razón sospechosa fue que una llamada a una función mutex desarrollada por el proveedor (parte del paquete de migración VADS) Unlock no condujo al desbloqueo. El hilo de procesamiento que llamó a la función procesó mensajes de latido, que nominalmente llegaban cada segundo. Subimos la frecuencia a 10 Hz, es decir, 10 veces por segundo, y empezamos a correr. Aproximadamente una hora después, el sistema se bloqueó. En el registro vimos que la secuencia de mensajes grabados era la misma que durante la prueba fallida. Hicimos varias carreras más, el sistema se bloqueó constantemente entre 45 y 90 minutos después del inicio, y cada vez el registro contenía la misma ruta. Aunque técnicamente estábamos ejecutando un código diferente (la frecuencia de los mensajes era diferente), el comportamiento del sistema era el mismo, por lo que estábamos seguros de que este escenario de carga estaba causando el mismo problema.

Ahora necesitábamos descubrir dónde ocurrió exactamente el bloqueo en la secuencia de expresiones.

Esta implementación del sistema utilizó el sistema de tareas Ada y lo utilizó increíblemente mal. Las tareas son una construcción ejecutable simultáneamente de alto nivel en Ada, algo así como subprocesos de ejecución, solo que están integrados en el lenguaje mismo. Cuando dos tareas necesitan comunicarse, "establecen una cita", intercambian los datos necesarios y luego detienen la cita y regresan a sus ejecuciones independientes. Sin embargo, el sistema se implementó de manera diferente. Después de que se reuniera una tarea objetivo, esa tarea objetivo se reuniría con otra tarea, que luego se reuniría con una tercera tarea, y así sucesivamente hasta que se completara algún procesamiento. Después de esto, todas estas citas se completaron y cada tarea debía volver a su ejecución. Es decir, estábamos ante el sistema de llamada a funciones más caro del mundo, que detenía todo el proceso de “multitarea” mientras procesaba parte de los datos de entrada. Y antes esto no daba lugar a problemas sólo porque el rendimiento era muy bajo.

Describí este mecanismo de tarea porque cuando se solicitaba o se esperaba que se completara una cita, podía ocurrir un "cambio de tarea". Es decir, el procesador podría comenzar a procesar otra tarea que esté lista para ser ejecutada. Resulta que cuando una tarea está lista para reunirse con otra tarea, puede comenzar a ejecutarse una tarea completamente diferente y, eventualmente, el control regresa al primer encuentro. Y pueden ocurrir otros eventos que provoquen que la tarea cambie; Uno de esos eventos es una llamada a una función del sistema, como imprimir o ejecutar un mutex.

Para comprender qué línea de código estaba causando el problema, necesitaba encontrar una manera de registrar el progreso a través de una secuencia de declaraciones sin activar un cambio de tarea, lo que evitaría que ocurriera un bloqueo. Así que no pude aprovechar Put_Line()para evitar realizar operaciones de E/S. Podría configurar una variable de contador o algo similar, pero ¿cómo puedo ver su valor si no puedo mostrarlo en la pantalla?

Además, al examinar el registro, resultó que, a pesar de la congelación en el procesamiento de mensajes de latido, que bloqueó todas las operaciones de E/S del proceso e impidió que se realizaran otros procesamientos, otras tareas independientes continuaron ejecutándose. Es decir, el trabajo no quedó bloqueado por completo, sólo una cadena (crítica) de tareas.

Esta fue la pista necesaria para evaluar la expresión de bloqueo.

Creé un paquete Ada que contenía una tarea, un tipo enumerado y una variable global de ese tipo. Los literales enumerables estaban vinculados a expresiones específicas de la secuencia problemática (p. ej. Incrementing_Buffer_Index, Locking_Mutex, Mutex_Unlocked), y luego insertó expresiones de asignación que asignaron la enumeración correspondiente a una variable global. Dado que el código objeto de todo esto simplemente almacenaba una constante en la memoria, el cambio de tarea como resultado de su ejecución era extremadamente improbable. Sospechábamos principalmente de las expresiones que podían cambiar la tarea, ya que el bloqueo se producía durante la ejecución en lugar de regresar al volver a cambiar la tarea (por varias razones).

La tarea de seguimiento simplemente se ejecutó en un bucle y se comprobó periódicamente para ver si el valor de la variable global había cambiado. Con cada cambio, el valor se guardó en un archivo. Luego una breve espera y un nuevo cheque. Escribí la variable en el archivo porque la tarea se ejecutó solo cuando el sistema la seleccionó para su ejecución al cambiar la tarea en el área del problema. Lo que suceda en esta tarea no afectará a otras tareas bloqueadas no relacionadas.

Se esperaba que cuando el sistema llegara al punto de ejecutar el código problemático, la variable global se restablecería al pasar a cada expresión siguiente. Entonces sucederá algo que hará que la tarea cambie, y dado que su frecuencia de ejecución (10 Hz) es menor que la de la tarea de monitoreo, el monitor podría capturar el valor de la variable global y escribirlo. En una situación normal, podría obtener una secuencia repetida de un subconjunto de enumeraciones: los últimos valores de la variable en el momento del cambio de tarea. Al colgar, la variable global ya no debería cambiar y el último valor escrito indicará qué expresión no se completó.

Ejecuté el código con seguimiento. Se quedó helado. Y el seguimiento funcionó como un reloj.

El registro contenía la secuencia esperada, que fue interrumpida por un valor que indicaba que se había llamado a un mutex. Unlock, y la tarea no se completa, como ocurre con miles de llamadas anteriores.

Los ingenieros de Apex estaban analizando frenéticamente su código en ese momento y encontraron un lugar en el mutex donde, teóricamente, podría ocurrir un bloqueo. Pero su probabilidad era muy baja, ya que sólo una determinada secuencia de eventos que ocurrían en un momento determinado podía provocar un bloqueo. La Ley de Murphy, muchachos, es la Ley de Murphy.

Para proteger el fragmento de código que necesitaba, reemplacé las llamadas a la función mutex (construidas sobre la funcionalidad mutex del sistema operativo) con un pequeño paquete mutex nativo de Ada para controlar el acceso mutex a ese fragmento.

Lo inserté en el código y ejecuté la prueba. Siete horas después, el código seguía funcionando.

Mi código fue enviado a Rational, donde lo compilaron, lo desensamblaron y comprobaron que no utilizaba el mismo enfoque que se utilizó en las funciones mutex problemáticas.

Esta fue la revisión de código más concurrida de mi carrera 🙂 Había alrededor de diez ingenieros y gerentes en la sala conmigo, otras diez personas estaban en una conferencia telefónica y todos examinaron alrededor de 20 líneas de código.

Se revisó el código, se ensamblaron nuevos archivos ejecutables y se enviaron para pruebas de regresión formales. Un par de semanas después, la prueba de cuenta regresiva fue exitosa y el cohete despegó.

Vale, eso está muy bien, pero ¿cuál es el punto de la historia?

Fue un problema absolutamente repugnante. Cientos de miles de líneas de código, ejecución paralela, más de una docena de procesos interactivos, arquitectura e implementación deficientes, interfaces para sistemas integrados y millones de dólares gastados. Sin presión, ¿verdad?

No fui el único que trabajó en este problema, aunque fui el centro de atención mientras realizaba la migración. Pero aunque lo hice, eso no significa que entendí los cientos de miles de líneas de código, o que incluso las hojeé. El código y los registros fueron analizados por ingenieros de todo el país, pero cuando me contaron sus hipótesis sobre las causas del fallo, sólo me llevó medio minuto refutarlas. Y cuando me pedían que analizara teorías, se las pasaba a otra persona, porque era obvio para mí que estos ingenieros iban por el camino equivocado. ¿Suena presuntuoso? Sí, esto es cierto, pero rechacé las hipótesis y solicitudes por otro motivo.

Entendí la naturaleza del problema. No sabía exactamente dónde estaba pasando ni por qué, pero sabía lo que estaba pasando.

A lo largo de los años, he acumulado muchos conocimientos y experiencia. Fui uno de los pioneros en utilizar Ada y entendí sus ventajas y desventajas. Sé cómo las bibliotecas en tiempo de ejecución de Ada manejan las tareas y se ocupan de la ejecución paralela. Y entiendo programación de bajo nivel a nivel de memoria, registros y ensamblador. En otras palabras, tengo un profundo conocimiento en mi campo. Y los usé para encontrar la causa del problema. No solo solucioné el error, entendí cómo encontrarlo en un entorno de ejecución muy sensible.

Estas historias de lucha con el código no son muy interesantes para quienes no están familiarizados con las características y condiciones de dicha lucha. Pero estas historias nos ayudan a comprender lo que se necesita para resolver problemas realmente difíciles.

Para resolver problemas realmente difíciles, es necesario ser algo más que un simple programador. Es necesario comprender el "destino" del código, cómo interactúa con su entorno y cómo funciona el entorno mismo.

Y luego tendrás tu propia semana de vacaciones arruinada.

Esta historia continuará.

Fuente: habr.com

Añadir un comentario