¿Qué herramientas o estándares se pueden usar para mejorar la confiabilidad del código C incrustado?

9

Normalmente programo PIC en C, generalmente para convertidores de modo conmutado. He oído hablar de varias herramientas y estándares de análisis estático como MISRA C que pueden usarse para ayudar a mejorar la confiabilidad del código. Me gustaría saber más ¿Qué estándares o herramientas pueden ser apropiados para mi contexto?

Stephen Collings
fuente
1
¿Cómo estás en el lenguaje C?
Brian Drummond
1
Podría ser persuadido para cambiar a otra cosa si hubiera un buen caso para hacerlo. Pero tendría que ser un muy buen caso.
Stephen Collings
"Un muy buen caso" para cambiar de C no se puede hacer rápidamente, y para el PIC, posiblemente todavía no. Para AVR, ARM o MSP430, Ada valdría una mirada seria (¡a pesar de la negatividad que atrae, como puede ver!) Y para alta rel, SPARK vale la pena.
Brian Drummond el
Puede encontrar estos interesantes como información de fondo: SPARK vs MISRA-C spark-2014.org/entries/detail/… y este estudio de caso en curso: spark-2014.org/uploads/Nosegearpaper_1.pdf
Brian Drummond el
Tal vez sea mejor invertir tiempo para defender el cambio de PIC a algo moderno ... Especialmente si está diseñando el tipo de sistemas de misión crítica para los que MISRA y SPARK estaban destinados originalmente.
Lundin

Respuestas:

11

La validación de código incorporado es complicada, especialmente cuando se trata de partes de recursos limitados como PIC. A menudo no tiene el lujo de codificar en casos de prueba debido a las limitaciones de memoria de la pieza y la programación a menudo "en tiempo real" realizada en este tipo de dispositivos.

Estas son algunas de mis pautas:

  1. Escriba una especificación si no hay una: si no está codificando contra una especificación, documente qué se supone que debe hacer su código, cuáles son las entradas válidas, cuáles son las salidas esperadas, cuánto tiempo debe tomar cada rutina, qué puede y qué no puede obtener clobbered, etc. - una teoría de operación, diagramas de flujo, cualquier cosa es mejor que nada.

  2. Comente su código: el hecho de que algo sea obvio para usted no significa que sea obvio (o correcto) para otra persona. Los comentarios en lenguaje sencillo son necesarios tanto para la revisión como para el mantenimiento del código.

  3. Código defensivo: no solo incluya código para entradas normales. Maneje las entradas que faltan, las entradas que están fuera del rango, los desbordamientos matemáticos, etc. - Cuantas más esquinas cubra el diseño de su código, menos grados de libertad tendrá el código cuando se implemente.

  4. Use herramientas de análisis estático: puede ser humillante la cantidad de errores que las herramientas como PC-lint pueden encontrar en su código. Considere una ejecución de análisis estático limpio como un buen punto de partida para pruebas serias.

  5. Las revisiones por pares son esenciales: su código debe estar lo suficientemente limpio y bien documentado para que una parte independiente pueda revisarlo de manera eficiente. Revise su ego en la puerta y considere seriamente cualquier crítica o sugerencia hecha.

  6. La prueba es esencial: debe hacer su propia validación, así como tener una validación independiente del código. Otros pueden descifrar su código de formas que posiblemente no pueda imaginar. Pruebe cada condición válida y cada condición inválida que pueda imaginar. Use los PRNG y alimente los datos basura. Haga lo que pueda para romper cosas, luego repare e intente nuevamente. Si tiene suerte, podrá ejecutar su código en modo de depuración y echar un vistazo a los registros y las variables; de lo contrario, deberá ser astuto y alternar LED / señales digitales para tener una idea del estado de su dispositivo. Haga lo que sea necesario para obtener los comentarios que necesita.

  7. Mire debajo del capó: no tenga miedo de mirar el código de máquina generado por su compilador de C. Puede (¿encontrará?) Encontrar lugares donde su hermoso código C se ha convertido en decenas, si no cientos, de operaciones, donde algo que debería ser seguro (ya que es solo una línea de código, ¿verdad?) Toma tanto tiempo ejecutar múltiples interrupciones han despedido e invalidado las condiciones. Si algo se vuelve terriblemente ineficiente, refactorícelo e intente nuevamente.

Adam Lawrence
fuente
+1 Todos los consejos sonoros. Esperaría que cualquier desarrollador de firmware profesional solo sonriera y asintiera al leer esto.
Lundin
2
Un aspecto importante de las revisiones por pares es que la revisión es sobre el código, no sobre el programador. Si analiza su código con términos como "primero hago esto, luego hago eso", probablemente esté en problemas. "Primero el código hace esto, luego lo hace" es la forma correcta de pensarlo. Y lo mismo se aplica a los revisores: no "¿por qué hiciste esto?", Sino "¿por qué el código hace esto?".
Pete Becker
También podría considerar agregar: 1. Uso de la comprobación de Complejidad
Ciclomática
4

La mayoría de las mismas técnicas para crear software confiable en una PC también son aplicables al desarrollo integrado. Es útil separar sus algoritmos del código específico del hardware y probarlos por separado con pruebas unitarias, simulaciones, análisis estático y herramientas como Valgrind. De esa manera, hay mucho menos código que solo se prueba en el hardware.

No abandonaría C. Aunque los idiomas como Ada pueden ofrecer algunas garantías menores, es fácil caer en la trampa de pensar que el idioma promete más de lo que realmente hace.

Theran
fuente
Sin embargo, Valgrid podría ser un poco más relevante para PC que para un MCU de 8 bits :)
Lundin
Desafortunadamente, algunas técnicas para crear un buen software a nivel de PC son muy inadecuadas para micros pequeños, y algunas prácticas consideradas malas e incorrectas en PC son perfectamente aceptables en un entorno integrado.
John U
3

MISRA-C es realmente muy útil para mejorar la calidad del código general y minimizar los errores. Solo asegúrese de leer y comprender cada regla, la mayoría de ellas son buenas, pero algunas de ellas no tienen ningún sentido.

Una advertencia aquí. El documento MISRA supone que el lector es alguien con un amplio conocimiento del lenguaje C. Si no tiene un veterano C tan duro en su equipo, pero decide obtener un analizador estático y luego sigue ciegamente cada advertencia dada, lo más probable es que resulte en un código de menor calidad , ya que podría estar reduciendo la legibilidad e introducir errores por accidente. He visto que esto sucede muchas veces, convertir el código al cumplimiento de MISRA no es una tarea trivial.

Hay dos versiones del documento MISRA-C que pueden aplicarse. O bien MISRA-C: 2004, que sigue siendo el estándar de facto integrado de la industria actual. O el nuevo MISRA-C: 2012 que admite el estándar C99. Si nunca ha usado MISRA-C antes, le recomendaría que implemente el último.

Sin embargo, tenga en cuenta que los proveedores de herramientas generalmente se refieren a MISRA-C: 2004 cuando dicen que tienen una verificación de MISRA (a veces incluso se refieren a la versión obsoleta de MISRA-C: 1998). Hasta donde yo sé, el soporte de herramientas para MISRA-C: 2012 todavía es limitado. Creo que solo algunos analizadores estáticos lo han implementado hasta ahora: Klocwork, LDRA, PRQA y Polyspace. Podría ser más, pero definitivamente debe verificar qué versión de MISRA admite.

Antes de decidir, por supuesto, puede comenzar leyendo el documento MISRA y ver qué implica. Se puede comprar por £ 10 en misra.org , bastante asequible en comparación con los precios de las normas ISO.

Lundin
fuente
1

Mathworks (la gente de MATLAB) tiene una herramienta de análisis de código estático llamada Polyspace .

Además del análisis de código estático, pelusa y similares, sugeriría una definición y diseño cuidadosos de las interfaces (con un proceso de revisión formal) y un análisis de cobertura de código.

También puede consultar las pautas para el diseño de códigos críticos para la seguridad, incluidos MISRA, pero también los estándares UL1998 e IEC 61508.

Spehro Pefhany
fuente
No recomiendo ir cerca de IEC 61508 a menos que sea necesario. Menciona software, pero carece de fuentes científicas modernas para sus afirmaciones. Ese estándar llegó 30 años demasiado tarde: si se hubiera lanzado en los años 70 como la mayoría de sus llamadas "fuentes", podría haber sido útil.
Lundin
1

Para obtener una respuesta completa a esta pregunta, suprimiría el pensamiento sobre la "confiabilidad del código" y en su lugar pensaría en la "confiabilidad del diseño", porque el código es solo la expresión final del diseño.

Entonces, comience con los requisitos y escriba e inspeccione esos. Si no tiene un documento de requisitos, señale una línea de código aleatoria y pregúntese "¿por qué se necesita esa línea?" La necesidad de cualquier línea de código eventualmente debería ser rastreable a un requisito, incluso si es tan simple / obvio como "la fuente de alimentación emitirá 5VDC si la entrada está entre 12-36VDC". Una forma de pensar en esto es que si esa línea de código no se puede rastrear hasta un requisito, entonces, ¿cómo sabe que es el código correcto o que es necesario?

A continuación, verifique su diseño. Está bien si está completamente en el código (por ejemplo, en los comentarios), pero eso hace que sea más difícil saber si el código está haciendo lo que realmente significa. Por ejemplo, el código puede tener una línea que dice output = 3 * setpoint / (4 - (current * 5)); ¿Es current == 4/5una entrada válida que podría causar un bloqueo? ¿Qué se debe hacer en este caso para evitar la división por cero? ¿Evita la operación por completo o degrada la salida en su lugar? Tener una nota general en su documento de diseño sobre cómo manejar estos casos extremos hace que sea mucho más fácil verificar el diseño a un nivel superior. Entonces, ahora la inspección del código es más fácil porque se trata de verificar si el código implementa ese diseño correctamente.

Junto con eso, la inspección del código debe verificar si hay errores comunes que su IDE no detecta (está utilizando un IDE, ¿verdad?) Como '=' cuando quiso decir '==', faltan llaves que cambian el significado de 'if 'declaraciones, punto y coma donde no deberían estar, etc.

Mientras escribo esto, se me ocurre que es realmente difícil resumir años de capacitación / experiencia en calidad de software en una sola publicación. Escribo código para dispositivos médicos y lo anterior es un resumen extremadamente simplificado de cómo lo abordamos.

Lyndon
fuente
Tengo entendido que la parte del código en un dispositivo médico se prueba casi como si fuera un dispositivo separado. ¿Es eso exacto?
Scott Seidman
@ScottSeidman Es más probable que se pruebe según los requisitos, como se menciona en esta respuesta. Para cada requisito, debe tener un módulo de código, y para cada módulo de código de este tipo debe tener una prueba. Entonces, esencialmente, cada requisito tiene una prueba correspondiente y el código es el medio para cumplir el requisito. Este tipo de rastreo de requisitos es una práctica común en cualquier sistema de misión crítica, mucho antes de que apareciera la palabra de moda "TDD".
Lundin
Me refería específicamente a la guía de la FDA, como fda.gov/downloads/RegulatoryInformation/Guidances/ucm126955.pdf El software realmente requiere más de lo que podría pensar si es parte de un dispositivo médico, comenzando desde la etapa de planificación y los controles de diseño.
Scott Seidman
Scott, nunca lo había pensado de esa manera, pero tienes razón. Nuestro personal de Garantía de Calidad del Software verifica el software por separado del resto del sistema (tanto como sea posible) antes de entregarlo a un grupo diferente que es responsable de la Verificación y Validación del Sistema.
lyndon