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

312

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

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

usuario63898
fuente
44
Creo que un lenguaje de consulta de código le dará una mejor vista de su proyecto en su conjunto. No estoy seguro sobre el mundo de C ++, pero parece que hay cppdepend.com (esto no es gratis), que se ve lo suficientemente decente. Puede haber algo como esto puede estar disponible de forma gratuita. La otra cosa es que, antes de hacer cualquier tipo de refactorización, lo más sensato sería realizar pruebas unitarias si no las tiene en este momento. Con las pruebas unitarias, lo que puede hacer es que sus herramientas de cobertura de código analicen su código, lo que en sí mismo ayudará a eliminar el código muerto si no puede cubrir ese código.
Biswanath
3
Mira la referencia aquí: en.wikipedia.org/wiki/Unreachable_code
Martin York
66
Encuentro un tema similar. stackoverflow.com/questions/229069/…
UmmaGumma
3
Sí, una de las cosas divertidas de C ++ es que eliminar las funciones "no utilizadas" aún puede alterar el resultado de un programa.
MSalters
1
@MSalters: Esa es una pregunta interesante ... Para que ese sea el caso, tendríamos que estar hablando sobre qué función en un conjunto de sobrecarga se elige para una llamada determinada, ¿correcto? Que yo sepa, si hay 2 funciones nombradas ambas f(), y una llamada para f()resolver inequívocamente a la 1ra, entonces no es posible hacer que esa llamada se resuelva a la 2da simplemente agregando una 3a función llamada f()- lo "peor que puede hacer "al agregar que la tercera función es hacer que la llamada se vuelva ambigua y, por lo tanto, evitar que el programa se compile. Me encantaría (= estar horrorizado) ver un contraejemplo.
j_random_hacker

Respuestas:

197

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 no de 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 variables no utilizadas, incluso el analizador de Clang no utilizado se ha incrementado para advertir sobre variables que nunca se leen (aunque se usen).
  • -Wunreachable-code(GCC anterior, eliminado en 2010 ) debería advertir sobre los bloques locales a los que nunca se accede (sucede con retornos tempranos o condiciones que siempre se evalúan como verdaderas)
  • No conozco ninguna opción para advertir sobre catchbloques 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 un análisis completo del programa, y ​​aunque 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:

  • El teórico es usar un analizador estático. Un software que examinará todo el código de una vez con gran detalle y encontrará todas las rutas de flujo. En la práctica, no sé nada que funcione aquí.
  • El pragmático es usar una heurística: use una herramienta de cobertura de código (en la cadena GNU es gcov. Tenga en cuenta que se deben pasar banderas específicas 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 puede comenzar desde aquí.

Si está extremadamente interesado en el tema y tiene el tiempo y la inclinación para desarrollar una herramienta por su cuenta, le sugiero que use las bibliotecas de Clang para crear dicha herramienta.

  1. Use la biblioteca de Clang para obtener un AST (árbol de sintaxis abstracta)
  2. Realice un análisis de marca 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 de los lenguajes de C ++, y podrá concentrarse en el problema en cuestión.

Sin embargo, este 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 sobre el que no puede razonar.

Matthieu M.
fuente
77
Muy bien, +1. Me gusta que distingas entre el código que puede determinarse estáticamente para que nunca se ejecute bajo ninguna circunstancia y el código que no se ejecuta en una ejecución en particular, pero que potencialmente podría hacerlo. El primero es el importante, creo, y como usted dice, un análisis de accesibilidad utilizando el AST de todo el programa es la forma de obtenerlo. (Evitar que foo()se marque como "llamado" cuando aparece solo en if (0) { foo(); }sería una bonificación pero requiere inteligencia adicional.)
j_random_hacker
@j_random_hacker: tal vez sería mejor usar el CFG (Control-Flow Graph) ahora que lo pienso (gracias a tu ejemplo). Sé que Clang está ansioso por comentar sobre comparaciones tautológicas como la que mencionaste y, por lo tanto, utilizando el CFG, posiblemente podamos detectar el código muerto desde el principio.
Matthieu M.
@Matthieu: Sí, quizás CFG es lo que quiero decir también, en lugar de AST :) Lo que quiero decir es: un dígrafo en el que los vértices son funciones y hay una ventaja de la función x a la función y siempre que x pueda llamar a y. (Y con la importante propiedad de que las funciones sobrecargadas están representadas por vértices distintos, ¡suena como Clang lo hace por ti, uf!)
J_random_hacker
1
@j_random_hacker: en realidad, el CFG es más complicado que un simple dígrafo, ya que representa todo el código que se ejecutará en bloques con enlaces de un bloque a otro basado en declaraciones condicionales. La principal ventaja es que es naturalmente adecuado para el código de poda que se puede determinar estáticamente como muerto (crea bloques inalcanzables que se pueden identificar), por lo que sería mejor explotar el CFG que el AST para construir el dígrafo. hablando de ... Creo :)
Matthieu M.
1
@j_random_hacker: en realidad, el AST de Clang lo hace, lo hace todo explícito (o casi ...) porque está destinado a trabajar con el código, no simplemente para compilarlo. En realidad, hay una discusión en este momento porque aparentemente hay un problema con las listas de inicializadores donde tal conversión implícita no aparece en el AST, pero supongo que se solucionará.
Matthieu M.
35

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 la fuente, use -ffunction-sectionsy -fdata-sections, luego, al vincular el uso -Wl,--gc-sections,--print-gc-sections. El enlazador ahora enumerará 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 --print-gc-sectionsparte y dejar que el enlazador elimine las funciones en silencio, pero las mantenga 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 el código muerto en las funciones en vivo también se mantendrán.

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

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

En ambos casos, cualquier cosa utilizada por una función virtual o un constructor de variable global también debe mantenerse.

Una advertencia adicional es que si está creando una biblioteca compartida, la configuración predeterminada en GCC exportará todas las funciones de la biblioteca compartida, haciendo que se "use" en lo que respecta al vinculador. Para solucionarlo, debe 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.

olsner
fuente
Grandes consejos prácticos. Solo obtener una lista de las funciones que se sabe que no se usarán en ningún lugar (incluso si, como usted dice, esta lista no está completa) obtendrá una gran cantidad de fruta de bajo perfil, creo.
j_random_hacker 01 de
No creo que nada de esto funcione para plantillas desinstaladas .
Jakub Klinkovský
25

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

De acuerdo con la documentación:

Avise siempre que una variable no se use aparte de su declaración, cada vez que una función se declare estática pero nunca se defina, cada vez que se declare una etiqueta pero no se use, y cada vez que una instrucción calcule un resultado que explícitamente no se usa.

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

Editar : Aquí hay otra bandera útil -Wunreachable-code Según la documentación:

Esta opción tiene como objetivo advertir cuando el compilador detecta que al menos una línea completa de código fuente nunca se ejecutará, 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 proyecto C / C ++ heredado

UmmaGumma
fuente
44
Esto no capturará los encabezados de las funciones prototipo que nunca se llaman. O métodos de clase pública que no reciben llamadas. Solo puede verificar si las variables de ámbito local se utilizan dentro de ese ámbito.
Falmarri
@Falmarri Nunca usé esta bandera. Estoy tratando de descubrir qué tipo de códigos muertos puedo encontrar con él.
UmmaGumma
-Wunusedadvierte sobre las variables que se declaran (o declaran y definen de una vez) pero que en realidad nunca se usan. Por cierto, es bastante molesto con los guardias con alcance: p Hay una implementación experimental en Clang para que también advierta sobre variables no volátiles que se escriben pero nunca se leen (por Ted Kremenek). -Wunreachable-codeadvierte sobre el código dentro de una función que no puede ser alcanzado, puede ser código ubicado después de una throwo returnestado de cuenta o código de una rama que no se toma (lo que ocurre en el caso de las comparaciones tautológicas) por ejemplo.
Matthieu M.
18

Creo que estás 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 informará qué líneas de código se ejecutaron y cuántas veces, así como cuáles no.

Puede intentar darle una oportunidad a esta herramienta de cobertura de código fuente abierto: TestCocoon : herramienta de cobertura de código para C / C ++ y C #.

Carlos v
fuente
77
La clave aquí es "mientras se está ejecutando": si sus datos de entrada no ejercen alguna ruta de código, esa ruta no se reconocerá como utilizada, ¿verdad?
Sharptooth
1
Eso es correcto. Sin ejecutar el código no hay forma de saber qué líneas no se están alcanzando. Me pregunto qué tan difícil será configurar algunas pruebas unitarias para emular algunas ejecuciones normales.
Carlos V
1
@drhishch, creo, que la mayoría de los códigos no utilizados deben encontrar el enlazador y no el compilador.
UmmaGumma
1
@drhirsch Es cierto, el compilador puede encargarse de parte del código que es inalcanzable, como las funciones que se declaran pero no se llaman y algunas evaluaciones de cortocircuito, pero ¿qué pasa con el código que depende de la acción del usuario o las variables de tiempo de ejecución?
Carlos V
1
@golcarcol Ok, tengamos una función void func()en a.cpp, que se usa en b.cpp. ¿Cómo puede verificar el compilador que func () se usa en el programa? Es trabajo de enlazadores.
UmmaGumma
15

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

Al menos, para casos no triviales, no puede estar seguro de haberlo recibido todo. Considere lo siguiente del 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 detectar algo como esto. Pero considere una modificación:

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

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

¿El compilador captará esto? Tal vez. Pero para hacer eso, tendrá que hacer más que correr sqrtcontra un valor escalar constante. Tendrá que descubrir que (double)ysiempre será un número entero (fácil), y luego comprender el rango matemático de sqrtpara el conjunto de enteros (difícil). Un compilador muy sofisticado podría hacer esto para la sqrtfunción, o para cada función en math.h , o para cualquier función de entrada fija cuyo dominio pueda resolver. 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 forma de introducir un código que será inalcanzable para cualquier conjunto 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 quedaría bloqueada por la lógica de validación en otro lugar. No hay forma 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 sabrás con certeza que capturaste todo a menos que revises el 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 alerta 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.

Justin Morgan
fuente
1
¡Es cierto y no dejes código muerto! Si elimina una función, elimine el código muerto. Dejarlo allí "por si acaso" solo provoca una hinchazón que (como ya mencionó) es difícil de encontrar más adelante. Deje que el control de versiones acapare por usted.
Carreras ligeras en órbita
12

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.

Señor tiburón
fuente
Sí, es capaz de encontrar variables y funciones locales no referidas.
Chugaister
Sí, use cppcheck --enable=unusedFunction --language=c++ .para encontrar estas funciones no utilizadas.
Jason Harris
9

Puede intentar usar PC-lint / FlexeLint del software Gimple . Reclama

encuentre macros no utilizados, typedef's, clases, miembros, declaraciones, etc. en todo el proyecto

Lo he usado para análisis estático y lo encontré muy bueno, pero tengo que admitir que no lo he usado para encontrar específicamente un código muerto.

Tony
fuente
5

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, ejecute compilaciones repetidas y muestre la primera pantalla llena de resultados. watch "make 2>&1"tiende a hacer el truco en Unix.
  3. ejecutar una operación de buscar y reemplazar en todo el árbol de origen, agregando "//?" al comienzo de cada línea
  4. corrige el primer error marcado por el compilador, quitando el "//?" en las líneas correspondientes.
  5. Repita hasta que no queden errores.

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

Simon Richter
fuente
2
Tiene mérito, pero requiere mucha mano de obra. También debe asegurarse de descomentar todas las sobrecargas de una función al mismo tiempo; si hay más de una aplicable, descomentar una menos preferida podría permitir que la compilación tenga éxito pero dar como resultado un comportamiento incorrecto del programa (y una idea incorrecta de qué Se utilizan funciones).
j_random_hacker
Solo descomento declaraciones en el primer paso (todas las sobrecargas), y en la siguiente iteración veo qué definiciones faltan; de esa manera, puedo ver qué sobrecargas se usan realmente.
Simon Richter
@ Simon: Curiosamente, en un comentario sobre la pregunta principal, MSalters señala que incluso la presencia / ausencia de una declaración para una función que nunca se llama puede afectar a cuál de las otras 2 funciones se encuentra mediante la resolución de sobrecarga. Es cierto que esto requiere una configuración extremadamente extraña y artificial, por lo que es poco probable que sea un problema en la práctica.
j_random_hacker
4

Marque tantas funciones y variables públicas como privadas o protegidas sin causar un error de compilación, mientras hace esto, intente también refactorizar 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 invocar desde la misma clase (a menos que haya estúpidos macro u otros trucos para evitar la restricción de acceso, y si ese es el caso, le recomendaría encontrar 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 base de código tiene clases pequeñas y está débilmente acoplada. Si su código base no tiene clases pequeñas o tiene un acoplamiento muy apretado, sugiero limpiarlas primero.

Lo siguiente será marcar todas las funciones públicas restantes y hacer un gráfico de llamadas para descubrir la relación entre las clases. Desde este árbol, intente averiguar qué parte de la rama parece recortada.

La ventaja de este método es que puede hacerlo por módulo, por lo que es fácil seguir pasando la prueba de la unidad sin tener un largo período de tiempo cuando tiene una base de código dañada.

Lie Ryan
fuente
3

Si está en Linux, es posible que desee buscar callgrinduna herramienta de análisis de programas C / C ++ que forme parte de la valgrindsuite, que también contiene herramientas que comprueban las pérdidas 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 usa para el análisis de rendimiento, pero también produce un gráfico de llamadas para sus aplicaciones, para que pueda ver qué funciones se llaman, así como sus llamadores.

Obviamente, esto es 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.

Adam Higuera
fuente
3

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

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

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

geekazoide
fuente
1
Creo que el OP principalmente quiere encontrar funciones a las que no se llama desde cualquier lugar, lo que ciertamente no es incuestionable, ¡la mayoría de los enlazadores modernos pueden hacerlo! Es solo cuestión de extraer esa información con la menor cantidad de dolor y trabajo pesado.
j_random_hacker
Tienes razón, no vi el último comentario a la pregunta principal. Por cierto, puede haber funciones referidas en el código que en realidad no se usan. Ese tipo de cosas pueden no ser detectadas.
geekazoid
2

Una forma es usar un depurador y la función de compilación para eliminar el código de máquina no utilizado durante la compilación.

Una vez que se elimina algún código de máquina, el depurador no le permitirá poner un breakpojnt en la línea correspondiente del código fuente. Por lo tanto, coloca puntos de interrupción en todas partes e inicia el programa e inspecciona los puntos de interrupción, aquellos que están en el estado "sin código cargado para esta fuente" corresponden al código eliminado, o ese código nunca se llama o se ha insertado y debe realizar un mínimo análisis para encontrar cuál de esos dos sucedió.

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

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

diente filoso
fuente
44
Creo que la pregunta del OP es acerca de cómo encontrar un subconjunto de código fuente más pequeño y manejable, no tanto para asegurarse de que el binario compilado sea eficiente.
j_random_hacker
@j_random_hacker Lo pensé un poco, y resulta que la eliminación del código incluso se puede usar para rastrear el código fuente original.
Sharptooth
¿Tienes algunos indicadores específicos del compilador en Visual Studio para lograrlo? y funciona solo en modo de lanzamiento o funcionará también en depuración?
Naveen
¿Qué pasa con las líneas que el compilador usa pero está optimizado?
Itamar Katz
@Naveen: en Visual C ++ 9 tienes que activar la optimización y usar / OPT: ICF
sharptooth
2

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 tienen curiosidad, aquí hay un ejemplo de regla incorporada (personalizable) para detectar métodos muertos, escrita 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] }
Roman Boiko
fuente
Actualización: el soporte de 64 bits para Linux se ha agregado en la versión 3.1.
Roman Boiko
1

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

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

De lo contrario, si necesita saber qué función de su aplicación utiliza realmente 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 obtener ese archivo de registro y echarle un vistazo.

AUS
fuente
1
Parece que .net ANTS Profiler es para C #. ¿Estás seguro de que también funciona para C ++?
j_random_hacker
@j_random_hacker: por lo que sé, funciona con código administrado. Por lo tanto, .net ANTS ciertamente no podrá analizar el código C ++ 'estándar' (es decir, compilado con gcc, ...).
AUS
0

No creo que se pueda hacer automáticamente.

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

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 de ayuda.

Pero no estoy seguro y preferiría la revisión manual del código.

ACTUALIZADO

Bueno ... solo eliminando variables no utilizadas, las funciones no utilizadas no son difíciles.

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 mucha edición manual será más rápida que preparar / ejecutar / revisar los resultados de cobertura.

9dan
fuente
2
la redacción de su respuesta es engañosa, no hay nada caro en LLVM ... ¡es gratis!
Matthieu M.
la edición manual no lo ayudará con las variables de tiempo de ejecución que pasan por las ramas lógicas en su programa. ¿Qué sucede si su código nunca cumple con ciertos criterios y, por lo tanto, siempre sigue el mismo camino?
Carlos V
0

Un amigo me hizo esta misma pregunta hoy, y miré a mi alrededor algunos desarrollos prometedores de Clang, por ejemplo, ASTMatcher s y el Analizador estático que podrían tener suficiente visibilidad en el proceso durante la compilación para determinar las secciones de código muerto, pero luego encontró esto:

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

Es casi una descripción completa de cómo usar algunos indicadores de GCC que aparentemente están diseñados con el propósito de identificar símbolos sin referencia.

Steven Lu
fuente
0

El problema general de si se llamará a alguna función es NP-Complete. No puede saber de antemano de manera general si se llamará a alguna función, ya que no sabrá si una máquina Turing alguna vez se detendrá. Puede obtener si hay alguna ruta (estáticamente) que va desde main () a la función que ha escrito, pero eso no garantiza que alguna vez se llamará.

Luis Colorado
fuente
-3

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

De acuerdo con 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 otro indicador útil: Código inalcanzable Según la documentación:

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.
ram singh
fuente
66
Esta información exacta ya se ha mencionado en la respuesta mejor calificada actualmente. Lea las respuestas existentes para evitar duplicaciones innecesarias.
j_random_hacker
1
¡Ahora puedes ganar tu insignia de presión de grupo!
Andrew Grimm