¿Cuál es una buena manera de entender la estructura general de una base de código?

8

A veces es útil en mi trabajo modificar el código de fuente abierta de otra persona o descubrir cómo desarrollar cosas específicas para su propia aplicación. Sin embargo, no todo el software tiene buena documentación.

¿Cuál es una buena manera de entender la estructura general de una base de código?

Por ejemplo, qué rutinas llaman qué rutinas, etc. Podría utilizar una herramienta de documentación como Doxygen para este propósito, sin embargo, me preguntaba si hay una mejor estrategia.

Allan P. Engsig-Karup
fuente
55
Creo que esta es una pregunta razonable, pero también creo que probablemente debería hacerse a los programadores . no es estrictamente ciencia computacional, sino más programación conceptual.
Geoff Oxberry
2
¿Existen técnicas para hacer esto que sean específicas de los códigos científicos?
David Ketcheson
Uso Doxygen porque es fácil y puede dar buenas imágenes con gráficos que muestran cómo están conectadas las rutinas. Tenga en cuenta que puede ser necesario hacer una rutina principal que abarque todo, así que obtenga una imagen completa.
Allan P. Engsig-Karup
@DavidKetcheson: No creo que haya métodos para hacer esto específicos de los códigos científicos. Dada la fuerte tradición de los códigos de investigación indocumentados, tal vez debería existir.
Geoff Oxberry

Respuestas:

9

Los siguientes hilos están relacionados tangencialmente:

Para la primera parte de mi tesis, pasé 18 meses modificando el código Fortran indocumentado, y una de las primeras tareas fue tratar de comprender la estructura general de una base de código. Lo más importante que sugiero que hagas es tomar notas en un archivo de texto cada vez que descubras algo. No querrás tener que volver a aprender o redescubrir cosas durante este proceso frustrante y lento.

En mi caso, no había "API" para hablar, en el sentido de que los argumentos de las funciones no se auto documentaban, porque el programador anterior usaba un estilo similar al de Fortran 77 y, por lo tanto, identificadores cortos con poca o ninguna pista de qué ellos se referían a. No hubo pruebas, y dado que es Fortran, no hay encabezados. Añadiendo aún más diversión a la mezcla, había un par de funciones aquí y allá escritas en C o C ++.

Cosas que funcionaron para mí (suponiendo que trabajes en Linux):

  • grep. Aprende a amar grep; lo usará en el shell para encontrar dónde se declaran y llaman las funciones, y la salida le dirá en qué archivos buscar.
  • El comando "buscar" en su editor de código favorito o IDE. Una vez que sepa en qué archivo buscar, el comando "buscar" le ahorrará tiempo en buscar llamadas a funciones.
  • Refactorización agresiva, si puede cambiar la base del código. Hice que los nombres de las variables se auto-documentaran para no tener que gastar esfuerzo mental en volver a aprender cosas que ya había descubierto. También simplifiqué el diseño del código cuando lo descubrí para que fuera menos confuso. ¡No más programa principal de 1000 líneas de largo!
  • Comentarios agresivos. Nuevamente, si puede cambiar la base del código, comente cosas para que sepa lo que descubrió. No usé Doxygen, pero Doxygen es bueno para esto.
  • nm. Puede ser útil en las bibliotecas si no tiene código fuente para ellas, pero desea saber si una función que encontró está en esa biblioteca. Sin embargo, solo funciona si los símbolos de la biblioteca no se han eliminado.
  • Recorre el código con un depurador. Es mucho más eficiente que usar printdeclaraciones. dddy gdbson geniales, y en prácticamente todos los sistemas Linux que existen. Siéntase libre de usar su depurador favorito.
  • Bug los desarrolladores. Esta opción es realmente mejor para preguntas muy específicas. Si vas a ellos (como yo lo hice) y les dices: "No entiendo lo que está sucediendo aquí", podrían compadecerse de ti y tratar de explicar las cosas en detalle en general, pero será de uso limitado en A la larga. Tendrá que hacer el trabajo preliminar, porque los desarrolladores no lo hicieron por usted al escribir documentación y explicar la estructura de su base de código por escrito. Los desarrolladores son realmente buenos para cuando estás realmente atascado en cosas pequeñas (si recuerdan lo que hicieron).

Cosas que desearía haber pensado antes, o simplemente no eran opciones para mí:

  • Doxygen Si modifica las opciones de Doxyfile, Doxygen generará automáticamente mucha documentación para usted, incluso sin la sintaxis especial de comentarios de Doxygen, que podría ser un buen lugar para comenzar; He usado esto en proyectos posteriores que encontré y ha sido increíblemente útil.
  • Examen de la unidad. Si puede alterar la base del código, y tiene una idea de lo que se supone que debe hacer, escriba pruebas unitarias para varias funciones. (Es una habilidad útil para aprender independientemente).
  • Si está trabajando con C / C ++, mire los encabezados.
  • Escribir programas de ejemplo. No es una opción para mí en ese proyecto Fortran, pero me ha sido útil para elegir API de terceros. Además, mire sus programas de ejemplo, si tienen alguno.
  • Use gcovy lcovpara hacer un análisis de cobertura en ejecuciones típicas del código, si tiene ejemplos o ejecutables para trabajar. Si se supone que hay ejemplos que ejecutan grandes partes de la base del código, estas dos herramientas combinadas le dirán cuántas veces se visita cada línea de código. Es más útil con las marcas de depuración habilitadas. Si una parte del código no se visita en absoluto, probablemente sea menos importante entenderlo de inmediato. Si una parte del código se visita mucho, probablemente valga la pena entender de qué se trata. Tal vez el código se ejecuta mucho porque es un bucle sin importancia, o podría ser una función clave en la que se basan muchas otras funciones. No puede decir solo sobre el análisis de cobertura, pero el análisis de cobertura le da una idea de dónde enfocar su tiempo.
  • Las herramientas de análisis de código estático como splintpueden decirle si algo sospechoso está sucediendo en el código, como si algunas variables nunca se usan.
  • Perfilado Nuevamente, le brinda datos que no le dicen de inmediato qué es o no importante, sino que sugiere lo que podría ser importante. Si se pasa mucho tiempo de CPU llamando a una función, es posible que desee ver eso y ver qué hace. También puede usar la salida de creación de perfiles con doty graphvizpara generar gráficos de llamadas y ver cuántas veces se llaman las funciones, como el análisis de cobertura. Para códigos complejos, un análisis gráfico podría ser mucho más útil.
  • Si está trabajando en C, se supone que Frama-C es útil para analizar el código, pero nunca lo he usado porque parecía demasiado complicado para que valiera la pena el esfuerzo. Hago algo de trabajo en C puro, pero escribo principalmente código; Nunca he trabajado con código C no documentado.
Geoff Oxberry
fuente
2
¿Cuál es la forma más común de entender una aplicación C ++ muy grande? on Stack Overflow es específico de C ++, pero otra pregunta existente de cierta antigüedad.
dmckee --- ex-gatito moderador
Buena lista Pongo todo en git y uso "git grep". Y también el historial de git para archivos individuales, si está disponible.
Ondřej Čertík
3

Siempre les digo a mis alumnos que lean un código de abajo hacia arriba: comienzas en main () y ves cómo se llama. Por lo general, esto es solo un pequeño número de funciones. Luego, se examinan las funciones llamadas desde main () que generalmente definen el flujo general del algoritmo (bucle de tiempo, ensamblaje, solucionador, salida, etc.). Vaya más o menos dos niveles de profundidad para obtener una visión general del algoritmo de 30,000 pies. El resto a menudo se puede obtener de la documentación de doxygen, etc.

Pero como dije, el mensaje es: lea el código de abajo hacia arriba.

Wolfgang Bangerth
fuente
3
Su consejo está bien tomado, pero no suena como leer código de abajo hacia arriba. Más bien, suena como leer de arriba hacia abajo. Main () (o Program, en Fortran) es la unidad de programa superior en un código ejecutable. Además, su consejo requiere un pequeño ajuste cuando el código en cuestión es una biblioteca que define una API. No hay una función main (), en cuyo caso debe combinar su estrategia de ver qué funciones llaman con decidir qué funciones son importantes para usted, generalmente mirando (o codificando) programas de ejemplo.
Geoff Oxberry
Puede haber cierta confusión acerca de "desde abajo" porque los programadores que aprendieron Pascal desde el principio a menudo ponen el punto de entrada al final (o al final) del archivo --- porque Pascal solo hizo una pasada y todo tuvo que ser introducido antes fue llamado. Sin embargo, al igual que Geoff, prefiero la lógica en la que Main () está en la "cima" nocional de un árbol que crece hacia abajo.
dmckee --- ex-gatito moderador
Sí, me refería a la forma de dmckee de tener main () al final del archivo porque de lo contrario uno tendría que declarar todo hacia adelante.
Wolfgang Bangerth