热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

使用Xref删除Erlang死代码

如果你不去管死代码(就像那些不在任何地方使用的函数),它们就会堆积在大项目中。使用Xref最被低估的特性之一,你将能够检测和删除不再需要的死代码。我们已经在这个博客里写了几篇关于我们如何大量使用ErlangOTP来构建我们的实时竞价平台服务器的文章。这些系统很大,到现在已经存在了很长时间。就像任何大型旧系统一样,它们包含一些不再使用的代码片段。需要明确的是:它们没有被破坏,它们甚至被测试所覆盖,但它们

如果你不去管死代码(就像那些不在任何地方使用的函数),它们就会堆积在大项目中。使用Xref最被低估的特性之一,你将能够检测和删除不再需要的死代码。

我们已经在这个博客里写了几篇关于我们如何大量使用Erlang/OTP来构建我们的实时竞价平台服务器的文章。

这些系统很大,到现在已经存在了很长时间。 就像任何大型旧系统一样,它们包含一些不再使用的代码片段。 需要明确的是:它们没有被破坏,它们甚至被测试所覆盖,但它们都没有在生产中使用。

在Erlang中,这些死代码表现为未使用的函数。 确切地说:它们是未使用的 导出函数 ,因为在编译时会检测到未导出并且未使用的函数。

在一个大系统里找出未使用的导出函数是很困难的。幸运的是,Erlang/OTP已经给我们提供了一个 工具 来做这个事情,它就是: Xref 。

Xerf是一个交叉引用工具,可用于查找函数、模块、应用程序和发布之间的依赖关系。

如果你使用 rebar3 管理你的项目,那么你就可以运行如下简单的命令来使用 Xref

$ rebar3 xref

如果你没有在 rebar.config 文件里为 Xref 进行任何配置的话,它将会检查你整个项目,并且进行所有可能的检查。这样的话,对于大项目来说,警告列表就会大量生成,因此,人们常常在 rebar.config 里进行如下配置:

{xref_checks,  [
	undefined_function_calls,
	locals_not_used,
	deprecated_function_calls
]}.

也就是说,这样的配置产生的报告就是如下三种:

  • 调用的函数不存在(undefined_function_calls)
  • 未使用未导出函数(locals_not_used)
  • 调用过期函数(deprecated_function_calls)

你可以在里找到所有可检查的列表,但是,我想要你注意的是,后两项是编译器已经检查到的(如果你启用了正确的警告),而真正有效的检查是对 undefined_function_calls 的执行。这是一个很好的运行检查,但它不会帮助我们解决原始的死代码问题。

那么让我们来看看我们没有执行的检查。 通常, undefined_functions 将报告与 undefined_function_calls 相同的结果,但是没有用实际的函数调用(不是很有用)。 deprecated_functionsdeprecated_function_calls 也是如此。 但是,我们有 exports_not_used ,这正是我们正在寻找的检查。

exports_not_used 添加到我们的配置列表里,它将会报告那些我们导出了但是没有使用的函数。真是太棒了!

但是为什么没有人使用它呢?

使用exports_not_used时有一些注意事项。 我现在列出它们,我会告诉你如何解决或至少让它们正常运作。

动态调用的函数

Xref将为在代码中找不到使用的每个导出函数报告一个警告。但是,Xref找不到它在哪里被使用,并不意味着实际使用函数的地方不存在。例如,Xref不能处理动态函数调用,但是它们是完全有效的使用方式。假设你有一个模块看起来像下面代码所示(别问我为什么):

-module(sample).
-exports([some_function/1, some_other_function/1]).

some_function(M)->
	M:some_other_function(an_argument).

some_other_function(X)->
	{called,  X}.

而在另一些模块里,你可能这么调用: sample:some_function(sample)Xref 没有足够聪明到探测到 sample:some_other_function/1 被真正使用了,因为它仅仅是通过动态评估来进行检查。上面的例子只是执行函数动态调用的其中一种方式。你可以通过如下示例看看其他方式:

% 经典的动态调用
Module:Function(Argument, Argument2),

% 使用 erlang:apply/3
erlang:apply(Module, Function, Arguments),

% 使用 spawn[_link]/3
erlang:spawn(Module, Function, Arguments),

% 使用 timer:tc/3
timer:tc(Module, Function, Arguments),

% 在监督者的规格说明里
{ChildName, {Module, Function, Arguments}, permanent, 5000, worker, dynamic},

注:如果添加{xref_warnings,true}. 到你的 rebar.config文件中,Xref至少会为这些无法解析的动态调用打印警告,如下所示:

sample:  1  unresolved  call

无论如何,只要你的系统比原型稍微大一点,你就会开始在代码的各处出现导出而未使用的函数。但不要惊慌,实际上有一种方法可以避免这些警告,而且它还有一些额外的好处。这种方法就是使用 ignore_xref

-ignore_xref是一个属性,你可以把它添加到你到模块中来阻止Xref对特定对函数发出警告。它的使用看起来如下所示:

-module(sample).
-exports([some_function/1, some_other_function/1]).

%% This function should be dynamically invoked through sample:some_function/1
-ignore_xref([{?MODULE, some_other_function,1}]).

...

现在如果你查看OTP中Xref的文档,你不会找到有关这个属性的片言只语。这是因为它不是官方属性。ignore_xref 是 rebar3 xref( xref_run 也一样) 的未公开文档属性。这个属性可以被添加到你的模块中,在当中列出那些你不想被Xref检查的函数。它的语法如下所示:

-ignore_xref([{module(), function(), arity()} | {module(), function()}]).

使用这种方式,你可以有效地移除掉有关那些被导出的只被动态调用的函数的警告。另外,如上例所示,你可以在这个属性的上方添加注释,这些函数将在哪里被使用。

动态生成的代码

你可能不是一个动态生成代码的发烧友,但是有时候你还真不可避免地要遇到动态生成的代码。

例如,我们在系统的几个地方使用了 protobuf ,导致我们使用了 gpb 和它的 rebar3 插件。当用gpb写模块的时候,它是不知道这个模块是如何被使用的,因此它就无法判断函数是否不需要被导出。这就意味着,当我们使用 Xref 的时候,会得到由gpb生成的所有被导出而未被使用的函数的警告。

如何避免这些警告呢?我们不能使用 -ignore_xref ,因为代码不是我们手写的,而是gpb自动生成的。事实证明,还有另一种方式。 我们可以在rebar.config中使用读起来怪怪的的xref_ignores属性。 它基本上允许你拥有一个可以在任何地方被忽略的全局的函数列表。 它看起来像这样:

{xref_ignores, [
    {my_gpb_generated_module, some_function, 1},
    {my_gpb_generated_module, some_other_function, 0},
    {my_gpb_generated_module, a_function_with_various_arities},
    ...
]}.

目前还没有办法忽略一个模块里的所有函数,不过我已经给这个项目提了 issue 。或许你也可以把这个问题作为一个 hacktoberfest 项目来攻克。

导出函数被系统外部使用

如果你有一些函数仅仅因为当你远程登录到生产中的服务器时,会在 shell 中使用它们而被导出,那会怎么样?如果它们被外部脚本在你的节点执行RPC调用的时候被使用或诸如此类的情况,那会怎么样?

在这种情况下,我建议你使用 -ignore_xref ,并在那里添加一个适当的注释,说明如何/何时/在哪里使用这些函数。我保证,这么做将来会有回报的。

导出函数仅用于测试

我有时候会遇到另一种不同的场景(特别是有关遗留代码的时候),就是被导出只是为了它们可以被测试。我们的想法是模拟它们或访问一些内部逻辑,否则这些内部逻辑应该隐藏在生产系统中。

首先,如果你使用,你就不需要导出那些函数。你可以在测试中使用非导出的函数。

现在,如果你使用通用测试或其他需要在被测模块外部编写测试的框架,那就是另一回事了。 我认为重要的是要考虑导出函数只是为了在测试中使用它们一般是不可取的,因为……

  • 如果你的函数未导出和未使用,则编译器会检测它们,如前所述,这允许你更早地发现错误。
  • 如果要添加在生产中不可用的函数并且/或者做不应该在生产中完成的功能,那么你的测试不会准确模拟真实场景,这可能会导致测试通过代码仍然无法如预期一样正常工作。

尽管如此,有时候还是没有办法解决它:你需要模拟一些外部世界看不到的东西,你需要验证一些只以非常复杂的格式暴露的数据或者检测真正难以捕获的副作用。 在这些场景中, ignore_xref 以及一个简洁的注释对于未来的开发人员来说是避免意外和挫折的好工具,使得他们可以发现一个未使用的函数,决定删除它。

库接口

最后,还有另外一种情况,你确实需要导出应用程序中没有实际使用的函数:当你的应用程序是一个库时(例如,当你正在构建一个应用程序以便在其他系统中用作依赖项时)。在这种情况下,一些函数构成了应用程序的接口,它们不会被应用程序使用。它们是公开的,所以你的用户可以在他们的应用程序中调用它们。

这些函数都将被报告为未使用的导出函数,并且必须为所有这些函数编写 ignore_xref / xref_ignore ,这样做不是好的办法。但是真正好的办法是在测试中覆盖它们。如果你这样做了,你就有了避免警告的方法并且实际上只为导出、未使用和未测试的函数生成警告。你可以像如下方式运行 Xref

$ rebar3 as test xref

使用 test profilerebar3 将把所有测试模块包含到分析中,并且由于你的接口函数将在这里使用,所以它不会警告您。

总结

虽然 Xref 是一个强大的工具,但是它需要一些调整来挖掘它的全部潜力。

首先,你必须使用正确的检查。我们的推荐检查列表是:

{xref_checks, [
    undefined_function_calls,
    exports_not_used
]}.

然后,你必须适当地使用 -ignore_xref 属性和 xref_ignores 配置参数来标识有意导出和未使用的所有函数。 如果你正在编写库,则还应该使用 rebar3 as test xref 来考虑分析中的测试。

有了这些,你应该会得到0个警告的报告,因此你可以确定项目中没有任何死代码。

嗯,实际上,你没有任何死函数(未使用的导出)。 但你仍然会有未使用的函数子句,未使用的case子句等形式的死代码,这些代码还不少, Xref 将不会检测到这些问题。

为此,你需要一个更强大的工具: dialyzer 。 我们不会在本文中介绍它,但请继续关注后续的文章。

原文链接: http://tech.adroll.com/blog/dev/2018/10/09/remove-erlang-dead-code-xref.html


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 我们


推荐阅读
  • 如何去除Win7快捷方式的箭头
    本文介绍了如何去除Win7快捷方式的箭头的方法,通过生成一个透明的ico图标并将其命名为Empty.ico,将图标复制到windows目录下,并导入注册表,即可去除箭头。这样做可以改善默认快捷方式的外观,提升桌面整洁度。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • 本文介绍了在Linux下安装Perl的步骤,并提供了一个简单的Perl程序示例。同时,还展示了运行该程序的结果。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • 【shell】网络处理:判断IP是否在网段、两个ip是否同网段、IP地址范围、网段包含关系
    本文介绍了使用shell脚本判断IP是否在同一网段、判断IP地址是否在某个范围内、计算IP地址范围、判断网段之间的包含关系的方法和原理。通过对IP和掩码进行与计算,可以判断两个IP是否在同一网段。同时,还提供了一段用于验证IP地址的正则表达式和判断特殊IP地址的方法。 ... [详细]
  • 本文介绍了禅道作为一款国产开源免费的测试管理工具的特点和功能,并提供了禅道的搭建和调试方法。禅道是一款B/S结构的项目管理工具,可以实现组织管理、后台管理、产品管理、项目管理和测试管理等功能。同时,本文还介绍了其他软件测试相关工具,如功能自动化工具和性能自动化工具,以及白盒测试工具的使用。通过本文的阅读,读者可以了解禅道的基本使用方法和优势,从而更好地进行测试管理工作。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • switch语句的一些用法及注意事项
    本文介绍了使用switch语句时的一些用法和注意事项,包括如何实现"fall through"、default语句的作用、在case语句中定义变量时可能出现的问题以及解决方法。同时也提到了C#严格控制switch分支不允许贯穿的规定。通过本文的介绍,读者可以更好地理解和使用switch语句。 ... [详细]
  • 成功安装Sabayon Linux在thinkpad X60上的经验分享
    本文分享了作者在国庆期间在thinkpad X60上成功安装Sabayon Linux的经验。通过修改CHOST和执行emerge命令,作者顺利完成了安装过程。Sabayon Linux是一个基于Gentoo Linux的发行版,可以将电脑快速转变为一个功能强大的系统。除了作为一个live DVD使用外,Sabayon Linux还可以被安装在硬盘上,方便用户使用。 ... [详细]
  • 本文介绍了Linux Shell中括号和整数扩展的使用方法,包括命令组、命令替换、初始化数组以及算术表达式和逻辑判断的相关内容。括号中的命令将会在新开的子shell中顺序执行,括号中的变量不能被脚本余下的部分使用。命令替换可以用于将命令的标准输出作为另一个命令的输入。括号中的运算符和表达式符合C语言运算规则,可以用在整数扩展中进行算术计算和逻辑判断。 ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • 本文总结了Linux下多线程执行shell脚本的4种方法,包括切换到工作目录执行、使用绝对路径执行、直接使用bash或sh执行。同时介绍了为什么需要加上"./"来执行脚本的原因。 ... [详细]
  • 解决Sharepoint 2013运行状况分析出现的“一个或多个服务器未响应”问题的方法
    本文介绍了解决Sharepoint 2013运行状况分析中出现的“一个或多个服务器未响应”问题的方法。对于有高要求的客户来说,系统检测问题的存在是不可接受的。文章详细描述了解决该问题的步骤,包括删除服务器、处理分布式缓存留下的记录以及使用代码等方法。同时还提供了相关关键词和错误提示信息,以帮助读者更好地理解和解决该问题。 ... [详细]
  • Apache Shiro 身份验证绕过漏洞 (CVE202011989) 详细解析及防范措施
    本文详细解析了Apache Shiro 身份验证绕过漏洞 (CVE202011989) 的原理和影响,并提供了相应的防范措施。Apache Shiro 是一个强大且易用的Java安全框架,常用于执行身份验证、授权、密码和会话管理。在Apache Shiro 1.5.3之前的版本中,与Spring控制器一起使用时,存在特制请求可能导致身份验证绕过的漏洞。本文还介绍了该漏洞的具体细节,并给出了防范该漏洞的建议措施。 ... [详细]
  • 安装oracle软件1创建用户组、用户和目录bjdb节点下:[rootnode1]#groupadd-g200oinstall[rootnode1]#groupad ... [详细]
author-avatar
hushuoni_133
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有