¿Cómo puedo saber qué partes del código nunca se usan?


Tengo código C++ heredado del que se supone que debo eliminar el código no utilizado. El problema es que la base de código es grande.

¿Cómo puedo averiguar qué código nunca se llama/nunca se usa?

Author: vrwim, 2011-01-27

18 answers

Hay dos variedades de código no utilizado:

  • el local, es decir, en algunas funciones algunas rutas o variables no se usan (o se usan pero de ninguna manera significativa, como escrito pero nunca leído)
  • el global: funciones que nunca se llaman, objetos globales a los que nunca se accede

Para el primer tipo, un buen compilador puede ayudar:

  • -Wunused (GCC, Clang) debe advertir sobre las variables no utilizadas, Clang analizador no utilizado incluso se ha incrementado para advertir sobre variables que nunca se leen (aunque se utilicen).
  • -Wunreachable-code (antiguo GCC, eliminado en 2010 ) debería advertir sobre bloques locales a los que nunca se accede (sucede con devoluciones tempranas o condiciones que siempre evalúan a true)
  • no hay ninguna opción que sepa para advertir sobre los bloques catch no utilizados, porque el compilador generalmente no puede probar que no se lanzará ninguna excepción.

Para el segundo tipo, es mucho más difícil. Estáticamente requiere entero análisis de programas, y a pesar de que la optimización del tiempo de enlace puede eliminar el código muerto, en la práctica el programa se ha transformado tanto en el momento en que se realiza que es casi imposible transmitir información significativa al usuario.

Por lo tanto, hay dos enfoques:

  • La teórica es usar un analizador estático. Una pieza de software que examinará todo el código a la vez con gran detalle y encontrará todas las rutas de flujo. En la práctica, no conozco a nadie que trabaja aquí.
  • La pragmática es usar una heurística: usar una herramienta de cobertura de código (en la cadena GNU es gcov. Tenga en cuenta que los indicadores específicos se deben pasar durante la compilación para que funcione correctamente). Ejecuta la herramienta de cobertura de código con un buen conjunto de entradas variadas (sus pruebas unitarias o pruebas de no regresión), el código muerto está necesariamente dentro del código no alcanzado... y así puedes empezar desde aquí.

Si usted está muy interesado en el tema, y tiene el tiempo y inclinación a trabajar realmente una herramienta por sí mismo, yo sugeriría el uso de las bibliotecas de Clang para construir una herramienta de este tipo.

  1. Utilice la biblioteca Clang para obtener un AST (abstract syntax tree)
  2. Realizar un análisis de marcado y barrido desde los puntos de entrada en adelante

Debido a que Clang analizará el código por usted y realizará una resolución de sobrecarga, no tendrá que lidiar con las reglas del lenguaje C++ y podrá concentrarse en el problema en cuestión.

Sin embargo esto el tipo de técnica no puede identificar las anulaciones virtuales que no se utilizan, ya que podrían ser llamadas por código de terceros que no puede razonar.

 192
Author: Matthieu M.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-07 14:03:04

Para el caso de funciones completas no utilizadas (y variables globales no utilizadas), GCC puede hacer la mayor parte del trabajo por usted siempre que esté utilizando GCC y GNU ld.

Al compilar el código fuente, use -ffunction-sections y -fdata-sections, luego al enlazar use -Wl,--gc-sections,--print-gc-sections. El enlazador ahora listará todas las funciones que podrían eliminarse porque nunca fueron llamadas y todas las globales que nunca fueron referenciadas.

(Por supuesto, también puede omitir la parte --print-gc-sections y dejar que el enlazador elimine las funciones silenciosamente, pero mantenlos en la fuente.)

Nota: esto solo encontrará funciones completas no utilizadas, no hará nada sobre el código muerto dentro de las funciones. Las funciones llamadas desde código muerto en funciones activas también se mantendrán alrededor.

Algunas características específicas de C++también causarán problemas, en particular:

  • funciones Virtuales. Sin saber qué subclases existen y cuáles son realmente instanciadas en tiempo de ejecución, no puede saber qué funciones virtuales necesidad de existir en el programa final. El enlazador no tiene suficiente información sobre eso, por lo que tendrá que mantenerlos a todos alrededor.
  • Globales con constructores, y sus constructores. En general, el enlazador no puede saber que el constructor de un global no tiene efectos secundarios, por lo que debe ejecutarlo. Obviamente, esto significa que el mundo mismo también debe mantenerse.

En ambos casos, cualquier utilizado por una función virtual o un constructor de variable global también tiene que quédate cerca.

Una advertencia adicional es que si está creando una biblioteca compartida, la configuración predeterminada en GCC exportará cada función en la biblioteca compartida, haciendo que se "use" en lo que respecta al enlazador. Para arreglar que necesita establecer el valor predeterminado para ocultar símbolos en lugar de exportar (usando por ejemplo -fvisibility=hidden), y luego seleccionar explícitamente las funciones exportadas que necesita exportar.

 32
Author: olsner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-01 01:07:00

Bueno, si usas g++ puedes usar esta bandera -Wunused

Según la documentación:

Avisar cuando una variable no se utilice aparte de su declaración, siempre que una función se declara estática pero nunca definido, cuando una etiqueta es declarado pero no utilizado, y siempre que declaración calcula un resultado que es explícitamente no utilizado.

Http://docs.freebsd.org/info/gcc/gcc.info.Warning_Options.html

Edit : Aquí hay otro bandera útil -Wunreachable-code Según la documentación:

Esta opción está destinada a advertir cuando el compilador detecta que al menos una línea completa de código fuente nunca será ejecutada, porque alguna condición nunca se cumple o porque es después de un procedimiento que nunca regresa.

Actualización : Encontré un tema similar Detección de código muerto en el proyecto C/C++ heredado

 25
Author: UmmaGumma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:02:42

Creo que está buscando una herramienta de cobertura de código . Una herramienta de cobertura de código analizará su código mientras se está ejecutando, y le permitirá saber qué líneas de código se ejecutaron y cuántas veces, así como cuáles no.

Podrías intentar darle una oportunidad a esta herramienta de cobertura de código abierto: TestCocoon - herramienta de cobertura de código para C/C++ y C#.

 18
Author: Carlos V,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-23 10:10:26

La verdadera respuesta aquí es: Nunca se puede saber con certeza.

Al menos, para casos no triviales, no puedes estar seguro de que lo has conseguido todo. Considere lo siguiente de Artículo de Wikipedia sobre código inalcanzable :

double x = sqrt(2);
if (x > 5)
{
  doStuff();
}

Como Wikipedia señala correctamente, un compilador inteligente puede ser capaz de atrapar algo como esto. Pero considere una modificación:

int y;
cin >> y;
double x = sqrt((double)y);

if (x != 0 && x < 1)
{
  doStuff();
}

¿Captará esto el compilador? Posiblemente. Pero para hacer eso, tendrá que hacer más que ejecutar sqrt contra un valor escalar constante. Tendrá que averiguar que (double)y siempre será un entero (fácil), y luego entender el rango matemático de sqrt para el conjunto de enteros (difícil). Un compilador muy sofisticado podría ser capaz de hacer esto para la función sqrt, o para cada función en matemáticas.h , o para cualquier función de entrada fija cuyo dominio pueda averiguar. Esto se vuelve muy, muy complejo, y la complejidad es básicamente ilimitada. Puede seguir agregando capas de sofisticación a su compilador, pero siempre habrá una manera de colarse en algún código que será inalcanzable para cualquier conjunto dado de entradas.

Y luego están los conjuntos de entrada que simplemente nunca se ingresan. Entrada que no tendría sentido en la vida real, o ser bloqueada por la lógica de validación en otro lugar. No hay manera de que el compilador sepa sobre eso.

El resultado final de esto es que, si bien las herramientas de software que otros han mencionado son extremadamente útiles, nunca lo sabrás seguro que has cogido todo a menos que vayas a través del código manualmente después. Incluso entonces, nunca estarás seguro de que no te perdiste nada.

La única solución real, en mi humilde opinión, es estar lo más vigilante posible, usar la automatización a su disposición, refactorizar donde pueda y buscar constantemente formas de mejorar su código. Por supuesto, es una buena idea hacer eso de todos modos.

 15
Author: Justin Morgan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-17 15:01:36

No lo he usado yo mismo, pero cppcheck, afirma encontrar funciones no utilizadas. Probablemente no resolverá el problema completo, pero podría ser un comienzo.

 12
Author: Mr Shark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-27 10:00:33

Puede intentar usar PC-lint/FlexeLint desde el software Gimple. Afirma que[3]}

Encuentra macros sin usar, tipos de caracteres, clases, miembros, declaraciones, etc. a lo largo de todo el proyecto

Lo he utilizado para el análisis estático y me pareció muy bueno, pero tengo que admitir que no lo he utilizado para encontrar específicamente código muerto.

 9
Author: Tony,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-27 10:08:25

Mi enfoque normal para encontrar cosas no utilizadas es

  1. asegúrese de que el sistema de compilación maneja el seguimiento de dependencias correctamente
  2. configure un segundo monitor, con una ventana de terminal de pantalla completa, ejecutando compilaciones repetidas y mostrando la primera pantalla de salida. watch "make 2>&1" tiende a hacer el truco en Unix.
  3. ejecute una operación buscar y reemplazar en todo el árbol de fuentes, agregando "//? "al principio de cada línea
  4. corrija el primer error marcado por el compilador, eliminando el "//?"en las líneas correspondientes.
  5. Repita hasta que no queden errores.

Este es un proceso algo largo, pero da buenos resultados.

 4
Author: Simon Richter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-27 12:56:07

Marque tantas funciones y variables públicas como privadas o protegidas sin causar error de compilación, mientras hace esto, intente refactorizar también el código. Al hacer que las funciones sean privadas y hasta cierto punto protegidas, redujo su área de búsqueda ya que las funciones privadas solo se pueden llamar desde la misma clase (a menos que haya macros estúpidos u otros trucos para eludir la restricción de acceso, y si ese es el caso, le recomendaría que encuentre un nuevo trabajo). Es mucho más fácil determinar que no necesita una función privada ya que solo la clase en la que está trabajando actualmente puede llamar a esta función. Este método es más fácil si su código base tiene clases pequeñas y está acoplado libremente. Si su base de código no tiene clases pequeñas o tiene un acoplamiento muy apretado, sugiero limpiarlos primero.

Lo siguiente será marcar todas las funciones públicas restantes y hacer un gráfico de llamadas para averiguar la relación entre las clases. A partir de este árbol, trate de averiguar qué parte de la rama se ve como se puede recortar.

La ventaja de este método es que puede hacerlo por módulo, por lo que es fácil seguir pasando su unittest sin tener un gran período de tiempo cuando tiene una base de código rota.

 4
Author: Lie Ryan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-27 15:38:33

Si está en Linux, es posible que desee buscar en callgrind, una herramienta de análisis de programas C/C++ que forma parte de la suite valgrind, que también contiene herramientas que comprueban fugas de memoria y otros errores de memoria (que también debería usar). Analiza una instancia en ejecución de su programa y produce datos sobre su gráfico de llamadas y sobre los costos de rendimiento de los nodos en el gráfico de llamadas. Por lo general, se utiliza para el análisis de rendimiento, pero también produce un gráfico de llamadas para sus aplicaciones, por lo que puede ver qué funciones se llaman, así como sus llamadores.

Esto es obviamente complementario a los métodos estáticos mencionados en otra parte de la página, y solo será útil para eliminar clases, métodos y funciones completamente no utilizados - no ayuda a encontrar código muerto dentro de los métodos que realmente se llaman.

 3
Author: Adam Higuera,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-31 22:15:56

Realmente no he usado ninguna herramienta que haga tal cosa... Pero, por lo que he visto en todas las respuestas, nadie ha dicho nunca que este problema es intransigible.

¿Qué quiero decir con esto? Que este problema no puede ser resuelto por ningún algoritmo en una computadora. Este teorema (que tal algoritmo no existe) es un corolario del Problema de Detención de Turing.

Todas las herramientas que usarás no son algoritmos sino heurísticas (es decir, no algoritmos exactos). No te darán exactamente todo el código que no se usa.

 3
Author: geekazoid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-18 16:43:53

Una forma es usar un depurador y la característica del compilador de eliminar el código de máquina no utilizado durante la compilación.

Una vez que se elimina algún código máquina, el depurador no le permitirá poner un breakpojnt en la línea correspondiente del código fuente. Así que pones puntos de interrupción en todas partes e inicias el programa e inspeccionas los puntos de interrupción-aquellos que están en el estado "sin código cargado para esta fuente" corresponden al código eliminado-ya sea que el código nunca se llama o se ha inlineado y tienes que realizar un análisis mínimo para encontrar cuál de esos dos ocurrió.

Al menos así es como funciona en Visual Studio y supongo que otros conjuntos de herramientas también pueden hacer eso.

Eso es mucho trabajo, pero supongo que más rápido que analizar manualmente todo el código.

 2
Author: sharptooth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-27 08:22:33

Depende de la plataforma que utilice para crear su aplicación.

Por ejemplo, si usa Visual Studio, podría usar una herramienta como . NET ANTS Profiler que es capaz de analizar y perfilar su código. De esta manera, debe saber rápidamente qué parte de su código se utiliza realmente. Eclipse también tiene plugins equivalentes.

De lo contrario, si necesita saber qué función de su aplicación es realmente utilizada por su usuario final, y si puede liberar su aplicación fácilmente, puede usar un archivo de registro para una auditoría.

Para cada función principal, puede rastrear su uso, y después de unos días/semana simplemente obtenga ese archivo de registro y eche un vistazo.

 1
Author: AUS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-23 10:07:40

CppDepend es una herramienta comercial que puede detectar tipos, métodos y campos no utilizados, y hacer mucho más. Está disponible para Windows y Linux (pero actualmente no tiene soporte de 64 bits), y viene con una prueba de 2 semanas.

Descargo de responsabilidad: No trabajo allí, pero tengo una licencia para esta herramienta (así como NDepend, que es una alternativa más poderosa para el código.NET).

Para aquellos que son curiosos, aquí hay un ejemplo de regla incorporada (personalizable) para detectar muertos métodos, escritos en CQLinq :

// <Name>Potentially dead Methods</Name>
warnif count > 0
// Filter procedure for methods that should'nt be considered as dead
let canMethodBeConsideredAsDeadProc = new Func<IMethod, bool>(
    m => !m.IsPublic &&       // Public methods might be used by client applications of your Projects.
         !m.IsEntryPoint &&            // Main() method is not used by-design.
         !m.IsClassConstructor &&      
         !m.IsVirtual &&               // Only check for non virtual method that are not seen as used in IL.
         !(m.IsConstructor &&          // Don't take account of protected ctor that might be call by a derived ctors.
           m.IsProtected) &&
         !m.IsGeneratedByCompiler
)

// Get methods unused
let methodsUnused = 
   from m in JustMyCode.Methods where 
   m.NbMethodsCallingMe == 0 && 
   canMethodBeConsideredAsDeadProc(m)
   select m

// Dead methods = methods used only by unused methods (recursive)
let deadMethodsMetric = methodsUnused.FillIterative(
   methods => // Unique loop, just to let a chance to build the hashset.
              from o in new[] { new object() }
              // Use a hashet to make Intersect calls much faster!
              let hashset = methods.ToHashSet()
              from m in codeBase.Application.Methods.UsedByAny(methods).Except(methods)
              where canMethodBeConsideredAsDeadProc(m) &&
                    // Select methods called only by methods already considered as dead
                    hashset.Intersect(m.MethodsCallingMe).Count() == m.NbMethodsCallingMe
              select m)

from m in JustMyCode.Methods.Intersect(deadMethodsMetric.DefinitionDomain)
select new { m, m.MethodsCallingMe, depth = deadMethodsMetric[m] }
 1
Author: Roman Boiko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-05 00:00:44

No creo que se pueda hacer automáticamente.

Incluso con herramientas de cobertura de código, debe proporcionar suficientes datos de entrada para ejecutarse.

Puede ser una herramienta de análisis estático muy compleja y de alto precio, como el compilador de Coverity o LLVM podría ser útil.

Pero no estoy seguro y preferiría revisar el código manualmente.

ACTUALIZADO

Bueno.. solo eliminar variables no utilizadas, funciones no utilizadas no es difícil aunque.

ACTUALIZADO

Después de leer otras respuestas y comentarios, estoy más convencido de que no se puede hacer.

Debe conocer el código para tener una medida de cobertura de código significativa, y si sabe que la edición manual será más rápida que preparar/ejecutar/revisar los resultados de la cobertura.

 0
Author: 9dan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-27 08:55:59

Un amigo me hizo esta misma pregunta hoy, y miré a mi alrededor algunos desarrollos prometedores de Clang, por ejemplo, ASTMatchers y el Static Analyzer que podrían tener suficiente visibilidad en los eventos durante la compilación para determinar las secciones de código muerto, pero luego encontré esto:{[5]]}

Https://blog.flameeyes.eu/2008/01/today-how-to-identify-unused-exported-functions-and-variables

Es más o menos una descripción completa de cómo usar unos cuantos GCC banderas que aparentemente están diseñadas con el propósito de identificar símbolos no referenciados!

 0
Author: Steven Lu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-11 03:43:00

El problema general de si alguna función será llamada es NP-Complete. Usted no puede saber de antemano de una manera general si se llamará a alguna función, ya que no sabrá si una máquina de Turing alguna vez se detendrá. Puedes obtener si hay algún camino (estáticamente) que va desde main () a la función que has escrito, pero eso no te garantiza que nunca se llamará.

 0
Author: Luis Colorado,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-12 04:22:37

Bueno, si usas g++ puedes usar esta bandera-Wunused

Según la documentación:

Warn whenever a variable is unused aside from its declaration, whenever a function is declared static but never defined, whenever a label is declared but not used, and whenever a statement computes a result that is explicitly not used.

Http://docs.freebsd.org/info/gcc/gcc.info.Warning_Options.html

Editar: Aquí hay otra documentación útil de flag-Wunreachable-code:

This option is intended to warn when the compiler detects that at least a whole line of source code will never be executed, because some condition is never satisfied or because it is after a procedure that never returns.
 -3
Author: ram singh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-27 10:51:41