Come posso sapere quali parti del codice non vengono mai utilizzate?


312

Ho un codice C ++ legacy da cui dovrei rimuovere il codice inutilizzato. Il problema è che la base di codice è grande.

Come posso sapere quale codice non viene mai chiamato / mai utilizzato?


4
Penso che un linguaggio di query di codice ti darà una visione migliore del tuo progetto nel suo insieme. Non sono sicuro del mondo c ++ ma sembra che ci sia cppdepend.com (questo non è gratuito), che sembra abbastanza decente. Potrebbe essere qualcosa del genere potrebbe essere disponibile gratuitamente. L'altra cosa è, prima di fare qualsiasi tipo di refactoring, la cosa sana da fare sarebbe avere test unitari se non si dispone in questo momento. Con i test unitari ciò che puoi fare è avere il tuo codice degli strumenti di copertura del codice che ti aiuterà a rimuovere il codice morto se non riesci a coprire quel codice.
Biswanath,

3
Dai un'occhiata al riferimento qui: en.wikipedia.org/wiki/Unreachable_code
Martin York,

6
Trovo un argomento simile. stackoverflow.com/questions/229069/...
UmmaGumma

3
Sì, una delle cose divertenti di C ++ è che la rimozione di funzioni "non utilizzate" può ancora alterare il risultato di un programma.
MSalters il

1
@MSalters: È interessante ... Per questo, dovremmo parlare di quale funzione in un set di sovraccarico è stata scelta per una determinata chiamata, giusto? Per quanto ne sappia, se ci sono 2 funzioni con entrambi i nomi f()e una chiamata per f()risolvere in modo inequivocabile la prima, allora non è possibile far sì che quella chiamata si risolva nella seconda semplicemente aggiungendo una terza funzione denominata f()- il "peggio che puoi fare "aggiungendo che la 3a funzione serve a rendere la chiamata ambigua e quindi a impedire la compilazione del programma. Mi piacerebbe (= essere inorridito) per vedere un controesempio.
j_random_hacker,

Risposte:


197

Esistono due varietà di codice non utilizzato:

  • quello locale, cioè in alcune funzioni alcuni percorsi o variabili sono inutilizzati (o usati ma in nessun modo significativi, come scritti ma mai letti)
  • quello globale: funzioni che non vengono mai chiamate, oggetti globali a cui non si accede mai

Per il primo tipo, un buon compilatore può aiutare:

  • -Wunused(GCC, Clang ) dovrebbe avvertire di variabili non utilizzate, l'analizzatore di Clang inutilizzato è stato addirittura incrementato per avvertire di variabili che non vengono mai lette (anche se utilizzate).
  • -Wunreachable-code(GCC precedente, rimosso nel 2010 ) dovrebbe avvisare dei blocchi locali a cui non si accede mai (ciò accade con rendimenti anticipati o condizioni che vengono sempre valutate come vere)
  • non esiste alcuna opzione che conosco per avvertire di catchblocchi inutilizzati , poiché il compilatore generalmente non può dimostrare che non verrà generata alcuna eccezione.

Per il secondo tipo, è molto più difficile. Staticamente richiede un'analisi completa del programma e anche se l'ottimizzazione del tempo di collegamento può effettivamente rimuovere il codice morto, in pratica il programma è stato così tanto trasformato nel momento in cui viene eseguito che è quasi impossibile trasmettere informazioni significative all'utente.

Esistono quindi due approcci:

  • Quello teorico è usare un analizzatore statico. Un software che esaminerà l'intero codice in una volta in grande dettaglio e troverà tutti i percorsi di flusso. In pratica non conosco nessuno che possa funzionare qui.
  • Quello pragmatico è usare un'euristica: usare uno strumento di copertura del codice (nella catena GNU è gcov. Nota che durante la compilazione dovrebbero essere passati specifici flag per farlo funzionare correttamente). Esegui lo strumento di copertura del codice con un buon set di input vari (i tuoi test unitari o test di non regressione), il codice morto è necessariamente all'interno del codice non raggiunto ... e quindi puoi iniziare da qui.

Se sei estremamente interessato all'argomento e hai il tempo e la propensione a elaborare uno strumento da solo, ti suggerirei di utilizzare le librerie di Clang per costruire tale strumento.

  1. Utilizzare la libreria Clang per ottenere un AST (albero di sintassi astratto)
  2. Eseguire un'analisi mark-and-sweep dai punti di ingresso in poi

Poiché Clang analizzerà il codice per te ed eseguirà la risoluzione del sovraccarico, non dovrai occuparti delle regole dei linguaggi C ++ e sarai in grado di concentrarti sul problema in questione.

Tuttavia, questo tipo di tecnica non è in grado di identificare le sostituzioni virtuali non utilizzate, poiché potrebbero essere chiamate da codice di terze parti di cui non è possibile ragionare.


7
Molto bello, +1. Mi piace il fatto che tu distingua tra il codice che può essere staticamente determinato per non essere mai eseguito in nessuna circostanza e il codice che non viene eseguito in una determinata esecuzione, ma potenzialmente potrebbe. Il primo è quello importante che penso, e come dici tu, un'analisi di raggiungibilità usando l'AST dell'intero programma è il modo per ottenerlo. (Impedire foo()di essere contrassegnato come "chiamato" quando appare solo in if (0) { foo(); }sarebbe un bonus ma richiede intelligenze extra.)
j_random_hacker

@j_random_hacker: forse usare il CFG (Control-Flow Graph) sarebbe meglio ora che ci penso (grazie al tuo esempio). So che Clang è desideroso di fare osservazioni su confronti tautologici come quello che hai citato e quindi usando il CFG potremmo probabilmente individuare un codice morto all'inizio.
Matthieu M.

@Matthieu: Sì, forse CFG è anche quello che intendo, invece di AST :) Quello che voglio dire è: un digrafo in cui i vertici sono funzioni e c'è un vantaggio dalla funzione x per funzionare y ogni volta che x potrebbe eventualmente chiamarti. (E con l'importante proprietà che le funzioni sovraccaricate sono tutte rappresentate da vertici distinti - sembra che Clang lo faccia per te, accidenti!)
j_random_hacker,

1
@j_random_hacker: in realtà il CFG è più complicato di un semplice digraph, poiché rappresenta tutto il codice da eseguire in blocchi con collegamenti da un blocco all'altro in base a istruzioni condizionali. Il vantaggio principale è che è naturalmente adatto al codice di potatura che può essere staticamente determinato come morto (crea blocchi non raggiungibili che possono essere identificati), quindi sarebbe meglio sfruttare il CFG rispetto all'AST per costruire il digraph che stai parlando di ... penso :)
Matthieu M.

1
@j_random_hacker: in realtà l'AST di Clang lo fa, rende tutto esplicito (o quasi ...) perché è pensato per lavorare con il codice, non semplicemente per compilarlo. Al momento c'è una discussione, perché a quanto pare c'è un problema con gli elenchi di inizializzatori in cui una tale conversione implicita non appare nell'AST, ma suppongo che verrà risolto.
Matthieu M.

35

Nel caso di intere funzioni inutilizzate (e variabili globali inutilizzate), GCC può effettivamente svolgere la maggior parte del lavoro per te, a condizione che tu stia utilizzando GCC e GNU ld.

Quando si compila la fonte, usare -ffunction-sectionse -fdata-sections, quindi quando si collega l'uso -Wl,--gc-sections,--print-gc-sections. Il linker ora elencherà tutte le funzioni che potrebbero essere rimosse perché non sono mai state chiamate e tutti i globali a cui non è mai stato fatto riferimento.

(Ovviamente, puoi anche saltare la --print-gc-sectionsparte e lasciare che il linker rimuova silenziosamente le funzioni, ma tienile nella fonte.)

Nota: questo troverà solo funzioni complete inutilizzate, non farà nulla sul codice morto all'interno delle funzioni. Anche le funzioni chiamate dal codice morto nelle funzioni live verranno mantenute.

Alcune funzionalità specifiche di C ++ causeranno anche problemi, in particolare:

  • Funzioni virtuali. Senza sapere quali sottoclassi esistono e quali sono effettivamente istanziati in fase di esecuzione, non è possibile sapere quali funzioni virtuali sono necessarie per esistere nel programma finale. Il linker non ha abbastanza informazioni a riguardo, quindi dovrà tenerle tutte in giro.
  • Globali con costruttori e loro costruttori. In generale, il linker non può sapere che il costruttore per un globale non ha effetti collaterali, quindi deve eseguirlo. Ovviamente questo significa che anche il globale stesso deve essere mantenuto.

In entrambi i casi, anche tutto ciò che viene utilizzato da una funzione virtuale o da un costruttore di variabili globali deve essere mantenuto.

Un ulteriore avvertimento è che se stai costruendo una libreria condivisa, le impostazioni predefinite in GCC esporteranno ogni funzione nella libreria condivisa, facendola "utilizzare" per quanto riguarda il linker. Per risolvere il problema, è necessario impostare il valore predefinito per nascondere i simboli anziché esportare (utilizzando ad esempio -fvisibility=hidden), quindi selezionare esplicitamente le funzioni esportate che è necessario esportare.


Ottimo consiglio pratico. Il solo fatto di ottenere un elenco delle funzioni che non sono note per l'uso da nessuna parte (anche se, come dici tu, questo elenco non è completo) otterrò un sacco di frutti a bassa quota che penso.
j_random_hacker

Non penso che nulla di tutto ciò funzioni per modelli non comprovati .
Jakub Klinkovský,

25

Bene, se usi g ++ puoi usare questo flag -Wunused

Secondo la documentazione:

Avvisa ogni volta che una variabile non viene utilizzata a parte la sua dichiarazione, ogni volta che una funzione viene dichiarata statica ma mai definita, ogni volta che un'etichetta viene dichiarata ma non utilizzata e ogni volta che un'istruzione calcola un risultato esplicitamente non utilizzato.

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

Modifica : ecco altri utili flag -Wunreachable-code Secondo la documentazione:

Questa opzione ha lo scopo di avvisare quando il compilatore rileva che almeno un'intera riga di codice sorgente non verrà mai eseguita, perché alcune condizioni non sono mai soddisfatte o perché è dopo una procedura che non ritorna mai.

Aggiornamento : ho trovato un argomento simile Rilevamento codice morto nel progetto C / C ++ legacy


4
Questo non catturerà le intestazioni di quel prototipo di funzioni che non vengono mai chiamate. O metodi di classe pubblica che non vengono chiamati. Può solo verificare se all'interno di tale ambito vengono utilizzate variabili con ambito locale.
Falmarri,

@Falmarri Non ho mai usato questa bandiera. Sto cercando di capire da solo che tipo di codici morti riesco a trovare con esso.
UmmaGumma,

-Wunusedmette in guardia sulle variabili dichiarate (o dichiarate e definite in una volta sola) ma che in realtà non vengono mai utilizzate. Abbastanza fastidioso con le guardie scoped tra l'altro: p Esiste un'implementazione sperimentale in Clang per avvertire anche di variabili non volatili che vengono scritte ma da cui non hanno mai letto (di Ted Kremenek). -Wunreachable-codemette in guardia sul codice all'interno di una funzione che non può essere raggiunto, può essere il codice situato dopo una throwo returndichiarazione o il codice in un ramo che non è mai preso (il che accade in caso di confronti tautologiche) per esempio.
Matthieu M.

18

Penso che tu stia cercando uno strumento di copertura del codice . Uno strumento di copertura del codice analizzerà il tuo codice mentre è in esecuzione e ti farà sapere quali righe di codice sono state eseguite e quante volte, e quali no.

Potresti provare a dare una possibilità a questo strumento di copertura del codice open source: TestCocoon - strumento di copertura del codice per C / C ++ e C #.


7
La chiave qui è "mentre è in esecuzione": se i dati di input non esercitano un percorso di codice tale percorso non verrà riconosciuto come utilizzato, vero?
sharptooth,

1
È corretto. Senza eseguire il codice non c'è modo di sapere quali linee non vengono raggiunte. Mi chiedo quanto sarà difficile impostare alcuni test unitari per emulare alcune serie normali.
Carlos V,

1
@drhishch Penso che la maggior parte di tale codice inutilizzato debba trovare linker e non compilatore.
UmmaGumma,

1
@drhirsch Vero, il compilatore può occuparsi di parte del codice che è irraggiungibile, come le funzioni dichiarate ma non chiamate e alcune valutazioni di corto circuito, ma che dire del codice che dipende dall'azione dell'utente o dalle variabili di runtime?
Carlos V,

1
@golcarcol Ok, abbiamo la funzione void func()in a.cpp, che è usata in b.cpp. Come può controllare il compilatore, che func () viene utilizzato nel programma? È lavoro dei linker.
UmmaGumma,

15

La vera risposta qui è: non puoi mai saperlo con certezza.

Almeno, per i casi non banali, non puoi essere sicuro di averlo ottenuto tutto. Considera quanto segue dall'articolo di Wikipedia sul codice non raggiungibile :

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

Come osserva correttamente Wikipedia, un compilatore intelligente potrebbe essere in grado di catturare qualcosa del genere. Ma considera una modifica:

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

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

Il compilatore lo prenderà? Può essere. Ma per farlo, dovrà fare di più che correre sqrtcontro un valore scalare costante. Dovrà capire che (double)ysarà sempre un numero intero (facile) e quindi comprendere l'intervallo matematico di sqrtper l'insieme di numeri interi (difficile). Un compilatore molto sofisticato potrebbe essere in grado di farlo per la sqrtfunzione, o per ogni funzione in math.h , o per qualsiasi funzione di input fisso di cui può capire il dominio. Questo diventa molto, molto complesso e la complessità è sostanzialmente illimitata. Puoi continuare ad aggiungere livelli di raffinatezza al tuo compilatore, ma ci sarà sempre un modo per intrufolarsi in un codice che sarà irraggiungibile per qualsiasi dato set di input.

E poi ci sono i set di input che semplicemente non vengono mai inseriti. Input che non avrebbero alcun senso nella vita reale o che sarebbero bloccati dalla logica di convalida altrove. Non c'è modo per il compilatore di conoscerli.

Il risultato finale di questo è che mentre gli strumenti software che altri hanno menzionato sono estremamente utili, non saprai mai con certezza che hai preso tutto a meno che non passi il codice manualmente in seguito. Anche allora, non sarai mai sicuro di non aver perso nulla.

L'unica vera soluzione, IMHO, è quella di essere il più vigile possibile, utilizzare l'automazione a tua disposizione, il refactor dove puoi e cercare costantemente modi per migliorare il tuo codice. Certo, è una buona idea farlo comunque.


1
Vero e non lasciare il codice morto in! Se rimuovi una funzione, uccidi il codice morto. Lasciandolo lì "per ogni evenienza" provoca solo un gonfiamento che (come hai discusso) è difficile da trovare in seguito. Lascia che il controllo di versione faccia l'archiviazione per te.
Razze di leggerezza in orbita

12

Non l'ho usato da solo, ma cppcheck afferma di trovare funzioni inutilizzate. Probabilmente non risolverà il problema completo ma potrebbe essere un inizio.


Sì, è in grado di trovare variabili e funzioni locali non referenziate.
Chugaister,

Sì, cppcheck --enable=unusedFunction --language=c++ .per trovare queste funzioni inutilizzate.
Jason Harris,

9

Puoi provare a usare PC-lint / FlexeLint di Gimple Software . Afferma di

trova macro non utilizzate, typedef, classi, membri, dichiarazioni, ecc. in tutto il progetto

L'ho usato per l'analisi statica e l'ho trovato molto buono, ma devo ammettere che non l'ho usato per trovare specificamente il codice morto.


5

Il mio approccio normale alla ricerca di cose inutilizzate è

  1. assicurarsi che il sistema di compilazione gestisca correttamente il rilevamento delle dipendenze
  2. imposta un secondo monitor, con una finestra terminale a schermo intero, eseguendo build ripetute e mostrando la prima schermata di output. watch "make 2>&1"tende a fare il trucco su Unix.
  3. eseguire un'operazione di ricerca e sostituzione sull'intero albero dei sorgenti, aggiungendo "//?" all'inizio di ogni riga
  4. correggere il primo errore contrassegnato dal compilatore, rimuovendo il "//?" nelle righe corrispondenti.
  5. Ripetere fino a quando non rimangono più errori.

Questo è un processo un po 'lungo, ma dà buoni risultati.


2
Ha merito, ma richiede molta manodopera. Inoltre, è necessario assicurarsi di decommentare tutti i sovraccarichi di una funzione contemporaneamente: se è applicabile più di una, rimuovere il commento da una meno preferita potrebbe consentire il completamento della compilazione ma comportare un comportamento errato del programma (e un'idea errata di quale vengono utilizzate le funzioni).
j_random_hacker,

Ho solo commentato le dichiarazioni nel primo passo (tutti i sovraccarichi) e nella successiva iterazione vedo quali definizioni mancano; in questo modo, posso vedere quali sovraccarichi vengono effettivamente utilizzati.
Simon Richter,

@Simon: È interessante notare che in un commento sulla domanda principale, MSalters sottolinea che anche la presenza / assenza di una dichiarazione per una funzione che non viene mai chiamata può influire su quale delle altre 2 funzioni si trova con la risoluzione del sovraccarico. Certamente questo richiede una configurazione estremamente bizzarra e inventata, quindi è improbabile che si tratti di un problema in pratica.
j_random_hacker,

4

Contrassegna tutte le funzioni e le variabili pubbliche come private o protette senza causare errori di compilazione, mentre fai ciò, prova a refactoring anche il codice. Rendendo le funzioni private e in una certa misura protette, hai ridotto la tua area di ricerca poiché le funzioni private possono essere chiamate solo dalla stessa classe (a meno che non ci siano macro stupide o altri trucchi per aggirare la limitazione dell'accesso, e in tal caso ti consiglierei trovare un nuovo lavoro). È molto più facile determinare che non hai bisogno di una funzione privata poiché solo la classe su cui stai lavorando può chiamare questa funzione. Questo metodo è più semplice se la tua base di codice ha classi piccole ed è liberamente accoppiata. Se la tua base di codice non ha classi piccole o ha un accoppiamento molto stretto, ti suggerisco di ripulirle prima.

Il prossimo sarà quello di contrassegnare tutte le restanti funzioni pubbliche e creare un grafico di chiamata per capire la relazione tra le classi. Da questo albero, prova a capire quale parte del ramo sembra che possa essere tagliata.

Il vantaggio di questo metodo è che puoi farlo in base al modulo, quindi è facile continuare a passare il tuo unittest senza avere un lungo periodo di tempo quando hai una base di codice rotta.


3

Se sei su Linux, potresti voler esaminare callgrinduno strumento di analisi del programma C / C ++ che fa parte divalgrind suite, che contiene anche strumenti che controllano perdite di memoria e altri errori di memoria (che dovresti usare anche tu). Analizza un'istanza in esecuzione del programma e produce dati sul relativo grafico delle chiamate e sui costi delle prestazioni dei nodi sul grafico delle chiamate. Di solito viene utilizzato per l'analisi delle prestazioni, ma produce anche un grafico delle chiamate per le applicazioni, in modo da poter vedere quali funzioni vengono chiamate, così come i loro chiamanti.

Ciò è ovviamente complementare ai metodi statici menzionati altrove nella pagina e sarà utile solo per eliminare classi, metodi e funzioni totalmente inutilizzate - non aiuta a trovare il codice morto all'interno dei metodi che sono effettivamente chiamati.


3

Non ho davvero usato nessuno strumento che fa una cosa del genere ... Ma, per quanto ho visto in tutte le risposte, nessuno ha mai detto che questo problema è impensabile.

Cosa intendo con questo? Che questo problema non può essere risolto da nessun algoritmo su un computer. Questo teorema (che un tale algoritmo non esiste) è un corollario del problema di arresto di Turing.

Tutti gli strumenti che userete non sono algoritmi ma euristiche (cioè non algoritmi esatti). Non ti daranno esattamente tutto il codice che non viene utilizzato.


1
Penso che l'OP voglia principalmente trovare funzioni che non sono richiamate da nessuna parte, il che non è certo imputabile - la maggior parte dei linker moderni può farlo! Si tratta solo di estrarre quelle informazioni con la minima quantità di dolore e fatica.
j_random_hacker,

Hai ragione, non ho visto l'ultimo commento alla domanda principale. A proposito, potrebbero esserci funzioni indicate nel codice che non sono effettivamente utilizzate. Questo tipo di cose potrebbe non essere rilevato.
geekazoid,

2

Un modo è utilizzare un debugger e la funzione di compilazione per eliminare il codice macchina inutilizzato durante la compilazione.

Una volta eliminato il codice macchina, il debugger non consente di inserire un breakpojnt sulla riga corrispondente del codice sorgente. Quindi metti punti di interruzione ovunque e avvii il programma e controlli i punti di interruzione - quelli che si trovano nello stato "nessun codice caricato per questa fonte" corrispondono al codice eliminato - o quel codice non viene mai chiamato o è stato incorporato e devi eseguire un minimo analisi per scoprire quale di questi due è successo.

Almeno è così che funziona in Visual Studio e immagino che anche altri set di strumenti possano farlo.

È un sacco di lavoro, ma immagino più veloce dell'analisi manuale di tutto il codice.


4
Penso che la domanda dell'OP sia su come trovare un sottoinsieme più piccolo e più gestibile del codice sorgente, non tanto per assicurarsi che il binario compilato sia efficiente.
j_random_hacker,

@j_random_hacker Ci ho dato un pensiero, e risulta che l'eliminazione del codice può anche essere utilizzata per rintracciare il codice sorgente originale.
sharptooth,

hai bisogno di alcuni flag di compilatore specifici su Visual Studio per ottenerlo? e funziona solo in modalità di rilascio o funzionerà anche in debug?
Naveen,

Che dire delle linee utilizzate ma ottimizzate dal compilatore?
Itamar Katz,

@Naveen: in Visual C ++ 9 devi attivare l'ottimizzazione e utilizzare / OPT: ICF
sharptooth,

2

CppDepend è uno strumento commerciale in grado di rilevare tipi, metodi e campi inutilizzati e fare molto di più. È disponibile per Windows e Linux (ma al momento non ha supporto a 64 bit) e viene fornito con una versione di prova di 2 settimane.

Disclaimer: non lavoro lì, ma possiedo una licenza per questo strumento (così come NDepend , che è un'alternativa più potente per il codice .NET).

Per coloro che sono curiosi, ecco una regola incorporata (personalizzabile) di esempio per rilevare metodi morti, scritta in 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] }

Aggiornamento: il supporto a 64 bit per Linux è stato aggiunto nella versione 3.1.
Roman Boiko,

1

Dipende dalla piattaforma utilizzata per creare l'applicazione.

Ad esempio, se si utilizza Visual Studio, è possibile utilizzare uno strumento come .NET ANTS Profiler in grado di analizzare e profilare il codice. In questo modo, dovresti sapere rapidamente quale parte del tuo codice viene effettivamente utilizzata. Eclipse ha anche plugin equivalenti.

Altrimenti, se è necessario sapere quale funzione dell'applicazione viene effettivamente utilizzata dall'utente finale e se è possibile rilasciare facilmente l'applicazione, è possibile utilizzare un file di registro per un controllo.

Per ciascuna funzione principale, è possibile rintracciarne l'utilizzo e, dopo alcuni giorni / settimana, è sufficiente ottenere quel file di registro e dargli un'occhiata.


1
.net ANTS Profiler sembra che sia per C # - sei sicuro che funzioni anche per C ++?
j_random_hacker,

@j_random_hacker: per quanto ne so, funziona con il codice gestito. Quindi .net ANTS non sarà certamente in grado di analizzare il codice C ++ "standard" (cioè compilato con gcc, ...).
AUS,

0

Non penso che possa essere fatto automaticamente.

Anche con gli strumenti di copertura del codice, è necessario fornire dati di input sufficienti per l'esecuzione.

Potrebbe essere utile uno strumento di analisi statica molto complesso e costoso come quello del compilatore Coverity o LLVM .

Ma non ne sono sicuro e preferirei la revisione manuale del codice.

AGGIORNATO

Bene ... rimuovendo solo le variabili non utilizzate, le funzioni non utilizzate non sono tuttavia difficili.

AGGIORNATO

Dopo aver letto altre risposte e commenti, sono più fortemente convinto che non si possa fare.

Devi conoscere il codice per avere misure di copertura del codice significative e se sai che molta modifica manuale sarà più veloce della preparazione / esecuzione / revisione dei risultati della copertura.


2
la formulazione della tua risposta è fuorviante, non c'è niente di caro in LLVM ... è gratis!
Matthieu M.

la modifica manuale non ti aiuterà con le variabili di runtime che passano attraverso i rami logici nel tuo programma. Cosa succede se il tuo codice non soddisfa mai determinati criteri e quindi segue sempre lo stesso percorso?
Carlos V,

0

Oggi un amico mi ha posto questa domanda e ho dato un'occhiata ad alcuni promettenti sviluppi di Clang, ad esempio ASTMatcher e Static Analyzer che potrebbero avere una visibilità sufficiente in corso durante la compilazione per determinare le sezioni del codice morto, ma poi ho trovato questo:

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

È praticamente una descrizione completa di come usare alcuni flag GCC che sono apparentemente progettati allo scopo di identificare simboli senza riferimento!


0

Il problema generale se una funzione verrà chiamata è NP-Complete. Non è possibile sapere in anticipo in modo generale se alcune funzioni verranno chiamate in quanto non si saprà se una macchina di Turing si fermerà mai. Puoi ottenere se c'è un percorso (staticamente) che va da main () alla funzione che hai scritto, ma ciò non ti garantisce che verrà mai chiamato.


-3

Bene, se usi g ++ puoi usare questo flag -Wunused

Secondo la documentazione:

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

Modifica: ecco altri utili flag -Wunreachable-code Secondo documentazione:

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.

6
Queste informazioni esatte sono già state menzionate nella risposta attualmente più votata. Leggere le risposte esistenti per evitare inutili duplicazioni.
j_random_hacker,

1
Ora puoi guadagnare il tuo badge Peer Pressure!
Andrew Grimm,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.