├── resource ├── WALA-mindnode.pdf └── PLDI_WALA_Tutorial.pdf ├── doc ├── call-graph.md ├── analysis-scope.md ├── pointer-analysis.md └── intermediate-representation.md ├── .gitignore └── README.md /resource/WALA-mindnode.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Leo0426/WALA-improve/HEAD/resource/WALA-mindnode.pdf -------------------------------------------------------------------------------- /resource/PLDI_WALA_Tutorial.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Leo0426/WALA-improve/HEAD/resource/PLDI_WALA_Tutorial.pdf -------------------------------------------------------------------------------- /doc/call-graph.md: -------------------------------------------------------------------------------- 1 | # 调用图 2 | WALA中CallGraph类,通过方法的逻辑克隆,表示潜在的上下文相关的调用关系图。每一个调用图的节点(CGNode)都表示了上下文(Context)中的 3 | 一个方法(IMethod). 4 | - 什么是Context呢? 5 | ``` 6 | 基本上,一个Context只是一个名称,是IMethod的克隆。 7 | 对于上下文无关的调用图,在实现上下文无关的算法时,用 Everywhere.EVERYWHERE 作为默认的上下文的标示。 8 | ``` 9 | 请注意对于给定的IMethod,在上下文相关的调用图中可能有多个表示该方法的节点。 我们可以使用方法 10 | `CallGraph.getNodes(MethodReference methodReference)`来获取所有的相关的节点。 11 | 12 | WALA 提供了一系列的即时调用图构造算法,整合了不敏感流指针分析,请查看Pointer Analysis查看详细说明。 13 | 14 | WALA 还通过快速类型分析(rapid type analysis,RTA)(`Util.makeRTABuilder()`)实现了调用图的构造,但是非常不推荐使用这种方式。 15 | 在PointerAnalysis也包含了0-CFA(ZeroCFA) 实现, 而0-CFA几乎总是更快,更精确的。 16 | 17 | 此外,RTA对字节码进行操作,其中一些字节可能在SSA构造期间被认证是无效的,因此未在SSA IR中表示。因此,就导致了一些不良的行为, 18 | 例如:RTA调用图返回的调用位置没有在SSA IR中出现。 19 | 20 | 最后,请注意,另一种情况是RTA调用本地方法的时候(e.g : Object.clone()),这种情况看起来是可以构造出所有可到达代码的调用图的, 21 | 但是其他情况就不确定了。 -------------------------------------------------------------------------------- /doc/analysis-scope.md: -------------------------------------------------------------------------------- 1 | # 分析范围 (analysis-scope) 2 | 一个AnalysisScope对象,指定了要被分析的应用和库代码; 3 | ## 常用构造方法 4 | 调用 5 | [`AnalysisScopeReader.makeJavaBinaryAnalysisScope()`](http://wala.sourceforge.net/javadocs/trunk/com/ibm/wala/util/config/AnalysisScopeReader.html#makeJavaBinaryAnalysisScope(java.lang.String,%20java.io.File)) 6 | 构建一个 `AnalysisScope` 对象,入参需要传入String类型的classpath和File类型的exclusions. 7 | 如果需要更多的参数,可以调用 8 | [`AnalysisScopeReader.readJavaScope()`](http://wala.sourceforge.net/javadocs/trunk/com/ibm/wala/util/config/AnalysisScopeReader.html#readJavaScope(java.lang.String,%20java.io.File,%20java.lang.ClassLoader)) 9 | 方法,包含丰富的构造参数,详情请查看java doc. 10 | 一个Scope包含包含以下信息: 11 | 12 | Classloader,Language,Type,Location 13 | 14 | 对于java语言来说,Classloader的类型为Primordial,Extension和Application中的一种. 15 | 16 | - Primordial 应用于Java标准库 17 | - Extension 应用于应用程序扩展的其他类型的库 18 | - Application 应用于应用程序本身的代码 19 | Type的支持类型说明: 20 | - classFile : 对于单个.class文件 21 | - binaryDir : 用于包含类文件的目录(具有标准的包对应关系) 22 | - jarFile : 用于.jar文件 23 | - sourceFile和sourceDir : 用于源文件,用于-Java source frontend 24 | 25 | # Note 26 | ## TODO 27 | sourceFile和sourceDir 这个究竟是二进制,还是源文件 这个不太确定,可以看看SourceDirCallGraph里面,等我完全理解这里以后,回来补充; -------------------------------------------------------------------------------- /doc/pointer-analysis.md: -------------------------------------------------------------------------------- 1 | # 指针分析 (别名分析) 2 | WALA提供了一个Andersen风格的不敏感流指针分析框架。你可以通过多种方式自定义上下文相关的指针分析策略。我们发现一些特定的客户端,使用自定义指针 3 | 分析策略解决一些问题是很有用的。 4 | 当前,所有指针分析实现都执行即时调用图构造。 5 | 一个上下文相关的指针分析策略可以在两方面变化: 6 | - heapModel: 指针分析模型如何抽象指针和堆位置? 7 | - ContextSelector: 调用图如何基于上下文克隆方法? 8 | 9 | 你可以通过自定义堆模型和上下文选择器来定义自己的指针分析策略。 此外,WALA 提供了许多内置策略。 10 | 11 | --- 12 | 13 | ## 堆模型(Heap model) 14 | 一个HeapModel提供给WALA指针分析怎么去抽象指针和堆的位置。关键的类: 15 | - PointerKey: 表示抽象指针 16 | - InstanceKey: 表示抽象堆位置 17 | 18 | 例如,PointerKey可以表示本地变量或静态字段,或者特定分配点分配的对象的实例字段,或者是其他的变量。更深入地说,PointerKey是来自具体程序的等效类 19 | 指针的名称,这些指针在抽象过程中被收集到单个表示中。 20 | 21 | - InstanceKey可能表示所有的特别类型的对象或者所有在特别分配点分配的对象,或者是在特别地上下文中特别的分配点分配的所有的对象,或者是其它的对象。 22 | - HeapModel为指针分析提供回调,以在分析期间创建PointerKeys对象和InstanceKeys对象。 您可以通过实现自己的HeapModel来自定义策略。 23 | 24 | --- 25 | 26 | ## 堆图(Heap graph) 27 | 堆图提供了一种方便的方式来获取指针分析的结果。在图中,节点表示的是PointKey和InstanceKeys.如果当且仅当指针分析表明P可能指向I时, 28 | 从PointerKey到InstanceKey才存在一条边。当且仅当P代表由I建模的对象实例的字段,或者P代表数组实例I的数组内容时,从InstanceKey 29 | 到PointerKey才存在一条边。 30 | 例如,给定HeapGraph h,假设您想知道所有可能为特定PointerKey p。 首先,您使用h.getSuccNodes(p)查找p可能指向的 31 | 所有InstanceKey,对于每个这样的InstanceKey i,使用h.getPredNodes(i)查找可能为p别名的其他PointerKey。 32 | 33 | ## 上下文选择器 34 | ContextSelector上下文选择器控制在构建调用图构建上下文的策略。调用图节点代表了在特定上下文中一个方法。 35 | 最简单的上下文策略:Everywhere.EVERYWHERE ,它代表了一个单个的全局的方法。 36 | 其他策略可以表示调用字符串上下文,命名接收者对象来实现对象敏感性的上下文或其他变体。 37 | 你可以自定义上下文敏感策略,通过自定义提供的 ContextSelector 对象。 38 | 39 | ## 入口点 40 | 41 | 42 | 43 | 44 | ## 内置策略 45 | 46 | 47 | ## 提升可扩展性 48 | 49 | ## 需求驱动指针分析 50 | 51 | 52 | -------------------------------------------------------------------------------- /doc/intermediate-representation.md: -------------------------------------------------------------------------------- 1 | # Intermediate Representation (IR) 2 | 3 | ## 概览 4 | WALA IR(中间表示)是表示特定方法指令的中央数据结构。 IR用接近JVM字节码的语言表示方法的指令,但使用基于SSA的寄存器传输语言来表示,该语言消除了 5 | 堆栈抽象,而是依赖于一组符号寄存器。 IR在基本块的控制流程图中组织指令,就像编译原理书中说的一样。 6 | 7 | IR是不可变的。它不支持通过程序转换代码,并且不支持从IR生成代码。其原理是,可以在生成分析信息的过程中使用IR,以用于支持其他一些工具, 8 | 例如IDE或编译器。(可以使用shrike包在WALA中以字节码级别进行程序转换)。通常,分析将建立从IR构造到相关分析信息(例如抽象和数据流)的各种结构和映射。 9 | 10 | ## IR Basics 11 | WALA IR类提供了基于SSA的特定方法的中间表示。 IR由基本块的控制流程图和一组指令组成。 可以尝试通过PDFWalaIR的实例来查看IR。 12 | 比起处理基础实现,可以用下面公开的入口处理比较方便: 13 | - IR.iterateAllInstructions(): 以未定义的顺序返回所有指令。对流不敏感地分析很有用。 14 | - IR.getControlFlowGraph(): 返回控制流图,即IBasicBlock的图。 15 | - IR.getControlFlowGraph().iterateNodes(): 返回控制流图中的节点(基本块)的迭代集合。 16 | - IBasicBlock.iterateAllInstructions(): 迭代特定基本块中的所有指令。 17 | 18 | 如果您关心迭代指令的顺序,则应遍历基本块,然后迭代每个基本块中的指令。 19 | 20 | 通常,如果构建一个调用图,然后通过CGNode.getIR()获取特定节点的IR。 (您也可以使用AnalysisCache.getIR(IMethod)方法直接为特定的IMethod 21 | 构建IR。) 22 | 23 | 请注意,根据CGNode表示的上下文,WALA可能为单个IMethod构建不同的IR。 例如,每个克隆节点在定义接收方类型的上下文中表示克隆IMethod。 关于此上下 24 | 文,每个克隆节点的IR都是专门的。 25 | 26 | ## Control Flow Graph 27 | 在异常处理上,仅使用声明的类型来确定异常流,假设每个潜在排除指令(PEI)都可能引发异常。 没有智能优化可以消除不可行的异常控制流程。 28 | 29 | 有时您可能希望调整对CFG的视图。 例如,也许客户希望忽略异常边缘的CFG视图。 为此,请检查PrunedCFG类。 30 | 31 | 要对CFG进行一些基本导航,请在com.ibm.wala.cfg中检出Util类。 32 | 33 | ## Value Numbering 34 | IR中的每个变量都有一个唯一的ID,称为值编号。 因此,您可以想到名称为v1,v2,v3,...的IR中的变量。 35 | 36 | 按照惯例,方法的值编号从1开始。因此,对于非静态方法,v1表示this参数。 接下来是方法的参数(v2,v3等)。 对于静态方法,v1表示第一个参数。 37 | 大多数SSA指令最多定义一个变量,并使用一些数字变量。 每条指令均带有其定义和使用的变量编号,可通过getDef()和getUses()访问。 38 | 实际上,SSAInvokeInstructions可以另外定义代表异常返回值的第二个变量。 39 | 40 | 相反,每个变量(值编号)将恰好具有一个def和一些引用。 使用DefUse类查找定义和使用特定值数字的指令。 如果DefUse返回null作为值编号的def, 41 | 则该值编号表示参数或常量。 有关常量的信息,请参见下文。 注意,定义可以是phi语句; 请参阅下面的更多细节。 42 | 43 | 在SSA转换期间,堆栈位置和本地变量都转换为符号虚拟寄存器。 对于给定的SSA值编号和指令索引,可以使用IR.getLocalNames()来查找相应本地 44 | 名称(如果有)。 您可以查看SSABuilder.SSA2LocalMap以查看IR如何在内部跟踪此信息。 45 | 46 | ## Phi statements 47 | 按照SSA格式的标准,WALA IR包含phi语句,以处理可能会使用多种定义的情况。由于历史原因,这些phi语句(SSAPhiInstructions)不会作为常规指令 48 | 存储在IR中。相反,它们存储在IR的控制流图中的BasicBlocks上。 要在IR中查看所有phi指令,请调用IR.iteratePhis()。要查看特定基本块上 49 | 的phi语句,请调用BasicBlock.iteratePhis()。逻辑上,基本块上的phi语句在基本块的开头执行,即在该块中的任何普通指令之前执行。 50 | 51 | ## Type inference 52 | 您可以使用TypeInference类在IR中在过程中发现值类型的类型。例如: 53 | ``` 54 | IR ir = ...; 55 | boolean doPrimitives = ...; // infer types for primitive vars? 56 | TypeInference ti = TypeInference.make(ir, doPrimitives); 57 | TypeAbstraction type = ti.getType(vn); 58 | ``` 59 | ## Constant Values 60 | IR提供原始常量和字符串常量,每个常量都有一个对应的变量,但是IR中没有明确的声明将常量分配给变量。要发现常量, 61 | 请通过调用IR.getSymbolTable()获得的IR的SymbolTable。SymbolTable.isConstant()会告诉您特定变量是否表示一个常量 62 | (将变量的值编号作为参数传递),SymbolTable.getConstantValue()会为您提供表示的值。 63 | 64 | ## Pi nodes (advanced) 65 | 66 | 67 | ## From IR to bytecode? 68 | WALA不是编译器; 它没有代码生成后端,并且IR并非为转换而设计。 我们不知道有任何直接从WALA SSA IR生成字节码的实现。 69 | 一个可行的选择是在IR上进行分析,将分析结果映射回Shrike表示形式,并在Shrike中进行字节码转换。 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### macOS template 3 | # General 4 | .DS_Store 5 | .AppleDouble 6 | .LSOverride 7 | 8 | # Icon must end with two \r 9 | Icon 10 | 11 | # Thumbnails 12 | ._* 13 | 14 | # Files that might appear in the root of a volume 15 | .DocumentRevisions-V100 16 | .fseventsd 17 | .Spotlight-V100 18 | .TemporaryItems 19 | .Trashes 20 | .VolumeIcon.icns 21 | .com.apple.timemachine.donotpresent 22 | 23 | # Directories potentially created on remote AFP share 24 | .AppleDB 25 | .AppleDesktop 26 | Network Trash Folder 27 | Temporary Items 28 | .apdisk 29 | 30 | ### JetBrains template 31 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 32 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 33 | 34 | # User-specific stuff 35 | .idea/**/workspace.xml 36 | .idea/**/tasks.xml 37 | .idea/**/usage.statistics.xml 38 | .idea/**/dictionaries 39 | .idea/**/shelf 40 | .idea 41 | # Generated files 42 | .idea/**/contentModel.xml 43 | 44 | # Sensitive or high-churn files 45 | .idea/**/dataSources/ 46 | .idea/**/dataSources.ids 47 | .idea/**/dataSources.local.xml 48 | .idea/**/sqlDataSources.xml 49 | .idea/**/dynamic.xml 50 | .idea/**/uiDesigner.xml 51 | .idea/**/dbnavigator.xml 52 | 53 | # Gradle 54 | .idea/**/gradle.xml 55 | .idea/**/libraries 56 | 57 | # Gradle and Maven with auto-import 58 | # When using Gradle or Maven with auto-import, you should exclude module files, 59 | # since they will be recreated, and may cause churn. Uncomment if using 60 | # auto-import. 61 | # .idea/artifacts 62 | # .idea/compiler.xml 63 | # .idea/jarRepositories.xml 64 | # .idea/modules.xml 65 | # .idea/*.iml 66 | # .idea/modules 67 | # *.iml 68 | # *.ipr 69 | 70 | # CMake 71 | cmake-build-*/ 72 | 73 | # Mongo Explorer plugin 74 | .idea/**/mongoSettings.xml 75 | 76 | # File-based project format 77 | *.iws 78 | 79 | # IntelliJ 80 | out/ 81 | 82 | # mpeltonen/sbt-idea plugin 83 | .idea_modules/ 84 | 85 | # JIRA plugin 86 | atlassian-ide-plugin.xml 87 | 88 | # Cursive Clojure plugin 89 | .idea/replstate.xml 90 | 91 | # Crashlytics plugin (for Android Studio and IntelliJ) 92 | com_crashlytics_export_strings.xml 93 | crashlytics.properties 94 | crashlytics-build.properties 95 | fabric.properties 96 | 97 | # Editor-based Rest Client 98 | .idea/httpRequests 99 | 100 | # Android studio 3.1+ serialized cache file 101 | .idea/caches/build_file_checksums.ser 102 | 103 | ### Gradle template 104 | .gradle 105 | **/build/ 106 | !src/**/build/ 107 | 108 | # Ignore Gradle GUI config 109 | gradle-app.setting 110 | 111 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 112 | !gradle-wrapper.jar 113 | 114 | # Cache of project 115 | .gradletasknamecache 116 | 117 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 118 | # gradle/wrapper/gradle-wrapper.properties 119 | 120 | !/.idea/ 121 | .idea/* 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WALA-improve 2 | - 学习wala和静态代码分析,以及涉及到的一些概念。 3 | - Study WALA and static code analysis . This project introduces the required knowledge and study roadmap. 4 | 5 | 6 | # 数据流分析技术 (Data Flow Analysis) 7 | 8 | 数据流分析是一种程序静态分析技术,它能从程序代码中收集程序的语义信息,并通过代数的方法确定变量的定义和使用。通过数据流分析,可以不必实际运行程序就能 9 | 够发现程序运行行为方面的特性,这样可以帮助人们理解程序。数据流分析被广泛用于解决编译优化、程序验证、理解、分片、调试、测试以及并行化等问题。 程序静 10 | 态数据流分析是程序测试所采用的一种重要手段。借助于程序静态分析工具,能更直接地暴露和定位程序中的错误。数据流分析作为一种非常重要的程序静态分析技术, 11 | 能够在保证软件质量与可靠性方面起到重要的作用. 12 | 13 | # 静态代码分析 (Static code Analysis) 14 | 15 | 静态代码分析是指在不运行代码的方式下,通过词法分析、语法分析、控制流、数据流分析等技术对程序代码进行扫描的技术。它的目的是验证代码是否满足规范性、 16 | 安全性、可靠性、可维护性的要求。静态代码扫描处于分层自动化测试的最底层,它和单元测试同级别。为了保证公司代码的规范性、安全性、可靠性的要求,通过 17 | 定制公司级的静态代码扫描规范、扫描规则和扫描实施流程保证实施高效落地。 18 | 19 | # DevOps & Static code Analysis 20 | 21 | DevOps在CI过程中需要确保软件的构建方式正确。DevOps中涉及的责任很多,其中包括支持开发流程,自动化测试,确保质量,提高生产力.....并最终实现持续 22 | 部署。良好的代码质量是实现所有这些目标的必要条件,尽管不是充分条件。静态代码扫描可在任何构建/测试/部署步骤中添加的代码质量检验门槛,能够自动执行一 23 | 组统一的质量标准,从而确保组织交付更好的软件。 24 | 25 | # 白盒测试 (white-box testing) 26 | 27 | 白盒测试(white-box testing)又称透明盒测试(glass box testing)、结构测试(structural testing)等,软件测试的主要方法之一,也称结 28 | 构测试、逻辑驱动测试或基于程序本身的测试。测试应用程序的内部结构或运作,而不是测试应用程序的功能(即黑盒测试)。在白盒测试时,以编程语言的角度 29 | 来设计测试案例。测试者输入资料验证资料流在程序中的流动路径,并确定适当的输出,类似测试电路中的节点。测试者了解待测试程序的内部结构、算法等信息, 30 | 这是从程序设计者的角度对程序进行的测试。白盒测试设计技术包括以下代码覆盖标准: 31 | - 控制流测试 32 | - 数据流测试 33 | - 分支测试 34 | - 语句覆盖 35 | - 判定覆盖 36 | - 修正条件/判定覆盖 37 | - 主要路径测试 38 | - 路径测试 39 | 40 | # WALA介绍 41 | WALA为Java字节码和相关语言以及JavaScript提供了静态分析功能;最初诞生于IBM TJ Watson研究中心,2006年,IBM将其捐赠给开源社区。 42 | 43 | # wala的特性 (WALA features) 44 | - Java类型系统和类层次结构分析 45 | - 支持Java和JavaScript 46 | - 过程间数据流分析(RHS求解器) 47 | - 基于上下文相关的列表切片器 48 | - 指针分析和调用图构造 49 | - 基于SSA的寄存器传输语言IR 50 | - 迭代数据流的通用框架 51 | - 通用分析工具和数据结构 52 | - 字节码检测库(Shrike) 53 | 54 | 55 | # 下载并运行WALA 56 | ## download project 57 | - 下载 git clone https://github.com/wala/WALA.git 58 | - 通过gradle编译项目 59 | - WALA基于java8运行 60 | - wala提供了很多test用例,可以debug用; 61 | 62 | ## configuring properties. 63 | - 拷贝com.ibm.wala.core项目,resource下wala.properties.sample到resource下wala.properties 64 | - 配置java_runtime_dir地址,以macos为例, 65 | jvm默认安装路径为/Library/Java/JavaVirtualMachines/jdk1.8.0_261.jdk/Contents/Home/jre/lib,确保路径下包含rt.jar,如果找不到, 66 | 在MacOSX上,它被也被称为classes.jarand,位于/System/Library/Frameworks/Classes 67 | - 配置output_dir地址,如果测试依赖于SWT不需要配置,如果依赖文件导出,如PDFTypeHierarchy,如果不配置导出路径,执行方法会失败 68 | 69 | ## run test demo 70 | - 在com.ibm.wala.core项目tests下,同样拷贝并编辑wala.properties,以支持项目正常运行; 71 | - 配置完成后可以在wala项目tests相关项目中,执行对应的测试代码。 72 | 73 | ## note 74 | windows环境下路径的分隔符应该是'/'而不是'\',我没有windows环境未验证,猜测应该是代码适配了平台。 75 | 76 | # 通常WALA执行顺序 77 | - WALA提供了一组用于程序分析的库。通常情况下wala将通过一下的顺序来执行这些库,来执行过程间数据流分析: 78 | 79 | 1. 建立ClassHierarchy,程序将源文件(如:字节码)读入内存,并根据预定义的类型解析一些基础信息。一个类层次对象代表了一组代码分析范围。 80 | 2. 建立CallGraph,通过使用及时调用图执行指针分析,来处理动态调度调用的目标,并生成可能是程序调用结构的CGNode图对象。 81 | 3. 通过构建的CallGraph,执行一些分析大多数类型的分析都是可以被分析出来的。WALA IR 编码了指令集和一些特别的方法的控制流。 82 | IR是由SSA提供的表示指令集的不可变的控制流图。 83 | 84 | # 项目模块 85 | - com.ibm.wala.core -- 核心包 86 | - com.ibm.wala.core.tests -- 核心包测试 87 | - com.ibm.wala.shrike -- Shrike字节码,包含ShrikeCT和ShrikeBT,WALA依赖Shrike去读取class文件,Shrike也可以做一些字节码操作。 88 | - com.ibm.wala.cast -- CAst支持,Common Abstract Syntax Tree 通用抽象语法树 89 | - com.ibm.wala.cast.test -- CAst测试 90 | - com.ibm.wala.cast.java -- 基于CAst的Java前端,两种实现,一种是polyglot 一种是Eclipse JDT 91 | - com.ibm.wala.cast.java.test -- 基于CAst的Java前端测试 92 | - com.ibm.wala.cast.java.test.data -- 基于CAst的Java前端的测试数据 93 | - com.ibm.wala.ide.jdt -- 基于JDT和CAst的Java前端 94 | - com.ibm.wala.ide.jdt.test -- JDT前端测试 95 | - com.ibm.wala.cast.java.polyglot -- 基于Polyglot和CAst的Java前端 96 | - com.ibm.wala.cast.java.polyglot.test -- 基于Polyglot和CAst的Java前端测试 97 | 98 | 99 | # 核心技术介绍 100 | - [分析范围](./doc/analysis-scope.md) 101 | - [调用图](./doc/call-graph.md) 102 | - [指针分析](./doc/pointer-analysis.md) 103 | - [CAst_Call_Graph] 104 | - Java前端和JavaScript调用图 105 | - 类层次结构图 106 | - IR 107 | - 映射源代码 108 | - 映射Java实体名称 109 | - 本地代码 110 | - Slicer 111 | 112 | # 问题交流 113 | 推荐通过issue/gitter的方式获取帮助; 114 | - gitter : https://gitter.im/WALAHelp/Lobby 115 | - issue : https://github.com/wala/WALA/issues 116 | --------------------------------------------------------------------------------