8

很多时候,当我调试或重用某些代码时,文件开始获取不执行任何操作的行,尽管它们可能在某一时刻做了某些事情。

诸如向量和填充然后未使用之类的东西,已定义但从未使用过的类/结构,以及已声明但从未使用过的函数。

我知道在许多情况下,其中一些东西并不是多余的,因为它们可能从其他文件中可见,但在我的情况下,没有其他文件,只是我的文件中的无关代码。

虽然我理解从技术上讲,调用push_back会做一些事情,因此向量本身并没有被使用,但就我而言,它的结果未被使用。

那么:有没有办法做到这一点,或者使用编译器(clang、gcc、VS 等)或外部工具?

例子:

#include<vector>
using namespace std;
void test() {
    vector<int> a;
    a.push_back(1);
}
int main() {
    test();
    return 0;
}

应该变成:int main(){return 0};

4

4 回答 4

3

我们的 DMS Software Reengineering Toolkit 及其 C++11 前端可用于执行此操作;它目前没有现成的这样做。DMS 旨在为任意源语言提供自定义工具构建,并包含完整的解析器、名称解析器和各种流分析器以支持分析,以及根据分析结果对代码应用源到源转换的能力。

通常,您需要一个静态分析来确定是否使用了每个计算(结果,可能有几个,只考虑“x++”)。对于每个未使用的计算,实际上您想要删除未使用的计算,并重复分析。出于效率原因,您希望进行一次分析,确定结果的所有(点)使用情况;这本质上是一种数据流分析。当计算结果的使用集为空时,可以删除该计算结果(注意删除“x++”值结果可能会留下“x++”,因为仍然需要增量!)以及它所依赖的计算使用集可以调整以从已删除的引用中删除引用,这可能会导致更多删除。

要对任何语言进行此分析,您必须能够跟踪结果。对于 C(和 C++),这可能非常难看。在表达式中使用计算结果以及将其分配给局部/全局变量(在其他地方使用)的“明显”用途,以及通过指针、对象字段更新、任意强制转换的间接分配等。要了解这些影响,您的死代码分析工具必须能够读取整个软件系统,并计算其中的数据流。

为了安全起见,您希望该分析是保守的,例如,如果该工具没有证据证明某个结果未被使用,那么它必须假定该结果已被使用;您通常必须使用指针(或只是伪装的指针的数组索引)来执行此操作,因为通常您无法准确确定指针“指向”的位置。显然,可以通过假设使用所有结果来构建“安全”工具:-} 对于您没有源代码的库例程,您有时也会得到非常保守但必要的假设。在这种情况下,有一组预先计算的库副作用摘要会很有帮助(例如,“strcmp”没有,“sprintf”覆盖特定操作数,“push_back”修改其对象......)。由于图书馆可能非常大,

DMS 通常可以解析和整个源代码库,构建符号表(因此它知道哪些标识符是本地/全局的及其精确类型),进行控制和本地数据流分析,为每个函数构建本地“副作用”摘要,构建调用图和全局副作用,并进行全局指向分析,以适当的保守性提供这种“计算使用”信息。

DMS 已用于在包含 2600 万行代码的 C 代码系统上执行此计算(是的,这是一个非常大的计算;它需要 100Gb 的 VM 才能运行)。我们没有实现死代码消除部分(该项目有另一个目的),但是一旦你有了这些数据,那就很简单了。DMS 已经通过更保守的分析(例如,“没有使用标识符的提及”,这意味着对标识符的分配是死的)对大型 Java 代码进行了死代码消除,这会导致在许多实际代码中删除大量代码。

DMS 的 C++ 解析器目前可以构建符号表,并且可以对 C++98 进行控制流分析,而 C++11 就在手边。我们仍然需要本地数据流分析,这是一些努力,但是全局分析已经预先存在于 DMS 中并且可用于此效果。(如果您不介意更保守的分析,可以从符号表数据中轻松获得“不使用标识符”)。

在实践中,您不希望该工具只是默默地撕掉东西。有些实际上可能是您希望保留的计算。Java 工具的作用是产生两个结果:一个死计算列表,您可以检查它以确定您是否相信它,以及源代码的死代码删除版本。如果您相信死代码报告,则保留删除死代码的版本;如果您看到您认为不应该死的“死”计算,则修改代码以使其不死并再次运行该工具。拥有庞大的代码库,检查死代码报告本身可能会很困难;“你”如何知道团队中的“其他人”是否不重视某些明显死掉的代码?(如果你搞砸了,可以使用版本控制来恢复!)

一个我们没有(也没有我知道的工具)处理的非常棘手的问题是条件编译存在的“死代码”。(Java 没有这个问题;C 有这个问题,C++ 系统更少)。这真的很讨厌。想象一个条件,其中一个臂具有某些副作用,而另一个臂具有不同的副作用,或者另一种情况,其中一个由 GCC 的 C++ 编译器解释,而另一个臂由 MS 解释,编译器在构造的作用上存在分歧(是的,C++ 编译器确实在黑暗的角落不同意)。充其量我们可以在这里非常保守。

CLANG 有一定的流量分析能力;以及一些进行源转换的能力,因此它可能会被迫这样做。我不知道它是否可以进行任何全局流/指向分析。它似乎偏向于单个编译单元,因为它的主要用途是编译单个编译单元。

于 2013-04-16T10:15:23.587 回答
1

要捕获未使用的变量,您可以在 gcc 编译器上启用 -Wunused 标志。这将在编译时警告您未使用的参数、变量和计算值。我发现使用 -Wall -Wextra 和 -Werror 标志可确保编译器捕获您描述的一些问题。更多信息可以在这里找到:http: //gcc.gnu.org/onlinedocs/gcc/Warning-Options.html

至于查找未使用的类,一种选择是使用 IDE,比如 Eclipse,并使用“查找引用”功能来搜索可能使用该类/对象的位置。

于 2013-04-05T03:24:05.390 回答
1

简短的回答是“不”。通过对客户端代码的静态分析无法判断 vector 的push_back方法没有任何重要的副作用。就分析工具所知,它会写入某处的数据库并推动股票交易。

于 2013-04-05T16:04:17.960 回答
0

我建议使用版本控制软件 - SVN、Git、Mercurial、Perforce,... - 这样在调试后您可以使用所述版本控制工具来查找和删除调试剩余部分。这使得保持代码更精简变得非常容易。

此外,这种测试代码通常具有很少的测试覆盖率,因此如果您确实进行了单元测试,它们应该显示为未覆盖的代码。

然后有一些工具可以明确地寻找这种东西——Lint、Coverity 等等。不过大部分都是商业的。还可以尝试在 GCC 上使用 -O3,编译器可能会以这种方式识别更多实际未使用的变量,因为它会更积极地内联并消除代码。

于 2013-04-16T08:31:48.053 回答