如何分析netbean module之间的调用关系
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何分析netbean module之间的调用关系相关的知识,希望对你有一定的参考价值。
参考技术A # build-android-arm-linux-androideabi/:第三方库。# modules/:模块代码。
# modules/demux: 解复用模块代码。
# modules/codec: 解码模块相关代码。
# modules/access: 访问模块相关代码。
# 其他:未详细分析。
# src/: VLC架构核心代码。
# src/config/: 从命令行和配置文件加载配置,提供功能模块的读取和写入配置。
# src/control/: 提供动作控制功能,如播放/暂停,音量管理,全屏,日志等。
# src/extras/: 平台特殊性相关代码。
# src/modules/: 模块管理。
# src/network/: 提供网络接口。
# src/posix/: 多线程相关。
# src/osd/: 显示屏幕上的操作。
# src/interface/ : 提供代码中可以调用的接口中,如按键后硬件作出反应。
# src/playlist/: 管理播放功能,如停止,播放,下一首,随机播放等。
# src/text/: 字符集。
# src/input/: 输入流相关代码。
# src/video_output/ : 初始化视频播放器,把从解码器获得的数据处理后播放。
# src/audio_output/ : 初始化音频混合器,把从解码器获得的数据处理后播放。
# src/stream_output/: 输出音频流和视频流到网络。
# src/test/: libvlc测试模块。
# src/misc/: libvlc使用的其他部分功能,如线程系统,消息队列,CPU的检测,对象查找系统,或平台的特定代码。
# 其他:未详细分析。
Soot 静态分析框架模块分析
Java 9里开始支持模块化,以一个独立的开源项目jigsaw而来, 具体可以参考链接,
https://openjdk.java.net/projects/jigsaw/ 同时也可以参考JSR376标准
1. module-info 分析
在模块化的时候,需要构建module_info.java来声明模块之间的关系,
在Module声明里面定义了requires ,exports, opens, uses, provides 5种类型,关于这几种类型的作用就不在这里详细介绍了。
在字节码种并没有单独的opcode,只是用一种固定的顺序进行定义,以requires ,exports, opens, uses, provides这个顺序来排序
我们来看一下以下的一段bytecode
2 // requires #8,0 // pointto #0 #10,8000 // "java.base" ACC_MANDATED #0 0 // exports 0 // opens 1 // uses #12 // testRequire/service 1 // provides #12 // testRequire/service with ... 1 #14 // ... with testRequire/ServiceImpl |
第一个2表示requires,同时表示有几个requires,下面的缩进行代表着有哪2个,#10,8000表示的是8000表示的是16进制的ACC_Flag 修饰符
其中requires的ACC独有的修饰符(TRANSITIVE,STATIC)
SYNTHETIC,MANDATED是共用的ACC修饰符
每个Flag定义的值为
TRANSITIVE | 0x20 |
STATIC | 0x40 |
SYNTHETIC | 0x1000 |
MANDATED | 0x8000 |
其本质上是通过bit位来设置不同的值的合集
第二个0代表着exports,第三个0代表着opens第四个1代表着uses,第五个1代表着provides,当值为非0的时候下一行就会缩进,代表着共有几个这样的类型,格式比较简单。但是我们从这种结构看出只能增加新的类型,同时类型顺序无法修改和删除,否则很难向下兼容。
这里简单的提一下这几个关键字
uses, provides,这个主要是对Java的SPI机制
Opens 主要是对Java的类反射机制,都是很有针对性的对Java提供的原生态的能力进行了管控。
2.Soot模块支持
Soot 的原来主版本目前并不支持模块分析,在Soot的Java9的branch孵化,目前已经合到主版本
2.1 Soot设置模块
Soot 需要显示的设置模块的相关信息
Options.v().set_soot_modulepath(path); |
Soot会通过设置的模块的路径去加载类
需要注意的是:基础模块java.base
你不需要设置基础模块的地址,Soot里面会去自己寻找基础模块,如何寻找请参考下面的章节。
Soot 本身是使用运行的jdk 加载基础模块的,那就是说Soot的加载基础模块是绑定你的运行的jdk的,你无法通过设置模块相关参数来设置基础模块,通常基础模块都被打包成了jmod文件,Soot解析jmod文件是通过运行soot的jdk来解析的,jmod基础类的读取就的依赖运行jdk的能力,如果你想用一个运行jdk的版本来分析不同的版本jdk基础类,就会变的困难,不过好在java的基础类是向下兼容的,确保升级到最新的版本,可以确保能分析低版本的基础类。Soot内部会添加VIRTUAL_FS_FOR_JDK这个值到modulepath中去,用来标示基础类的路径。
如果你运行的Soot的jdk是低版本的,或许还有条路可以试试,解开jmod文件,设置到你的模块路径下,这种方式适合在不高于jdk8的版本方式上,同时你还的设置:
Options.v().set_prepend_classpath(false); |
5.2 Soot 加载模块
ModulePathSourceLocator是查找模块路径和类文件的核心模块:
- 模块名字和模块的路径
- 获取模块下的类文件
通过ModulePathSourceLocator.
FoundFile lookUpInModulePath(String filename)方法去查找你想要获取的模块里的class
- Soot首先会去查找模块所绑定的路径,在soot开始分析的时候,初始化参数里并没有设置模块的路径,只能通过顺序查找你定义的modulepath下的模块路径
- 顺序解析模块路径下的模块:简单解析路径下的所有模块的module-info文件,只是为了获取模块的定义,并分析该路径下的模块以及该模块下的所有的类,构建模块和class的关系,(当然这个时候并不会去解析class),而是构建className和模块的关系,但是soot在这里并没有缓存这个关系,而是构建和缓存了模块名字和路径的关系,直到找到模块的路径
3. 深度解析该模块所在路径下的module-info 文件,分析requires的部分,构建模块间的依赖,分析依赖模块下的module-info,细节可参考5.2.5注:在Java虚拟机编译中,会对自定义的module-info的类,在编译过程中自动添加java.base的模块依赖,默认依赖java.base的基本模块。在这种场景下,soot会分析基础模块java.base的module-info, 通过查找基础模块的路径(重复步骤a,b), 找到java.base的module-info进行解析
4. 找到模块所对应的class文件,将FoundFile返回,如何找到所对应的模块请参考下面
5.3 模块的依赖关系
Java使用模块的方式进行类的管控,Soot里在LoadClass的时候需要准确的找到模块里的类,以确定该类是否能被模块加载,核心代码在ModuleUtil里findModuleThatExports方法,其原则如下:
Java里的module管控是基于Package的,只需要鉴权Package就足够。
- 首先分析要加载的模块的Module-info,该模块下的包是否包含该Package
- 如果不包含,读取该模块的Requires的模块,对依赖的模块进行module-info的分析
- 如果该module-info的export里包含该package,一种是export to 指定到该模块,如果没有to代表所有模块都可以使用。
- 链式访问requires transitive模块,对模块的module-info进行分析,分析逻辑和步骤3一致,直到链路的最后一层模块
5.4 如何获取模块下的class文件
通过ModulePathSourceLocator可以获取模块下的class文件,这里涉及到模块的两种表示方式
1. 自定义的模块:打包成jar或者直接class,这和原来获取jar下的class一样,区别主要是需要解析module-info.class类,获取类的目的是为了获取模块的依赖关系,同时也需要加载require的模块。
2. Java的基础类:清楚Java 模块的知道Java9将原来的rt.jar 替换成了模块的方式,在java_home下会有一个jmods的目录,里面保存着将rt.jar 拆解成多个不同的jmod文件,在Java下面我们可以使用jmod的命令来查看jmod文件的内容。Soot 并没有使用jmod去解析默认的jdk下的基础的这些jmod文件,而是通过jrt的方式去加载jmod,也就是
jrt:/ |
的方式通过NIO File 去解析文件,其本质是通过lib下的jrt-fs.jar去解析,所以Soot会先去判断一下该文件是否存在。如果存在,Soot会将jrt:/加到需要分析的模块逻辑中。
注意:如果是jar的方式,分析module只需要直接设置路径就可以了,而如果是jmod 的方式,需要jrt:/的方式去分析
jrt的访问方式默认的起始路径是以modules,也就是对应的jmods目录
jrt:/modules/ |
访问JRT的方式可以使用NIO的Path,Path是支持jrt的方式,而传统的File是不支持jrt的路径访问方式,这里要特别注意。
5.5 加载分析类
在前面的段落里提到过Soot的核心函数Scene,对模块的解析,Soot提供了新的ModuleScene 来支持,当加载class的时候,在模块的方式下需要使用ModuleScene来加载类,代码如下:
public static SootClass loadClass(String name, boolean main, String module) SootClass c = ModuleScene.v().loadClassAndSupport(name, Optional.of(module)); c.setApplicationClass(); if (main) Scene.v().setMainClass(c); return c;
|
需要使用name和模块名字来加载class,而原来的方式只需要提供class的名字
private static SootClass loadClass(String name, boolean main) SootClass c = Scene.v().loadClassAndSupport(name); c.setApplicationClass(); if (main) Scene.v().setMainClass(c); return c;
|
5.6 加载模块特有的module-info类
解析模块下的类,Soot 沿用原来的框架思路通过Scene来进行加载,本质上使用SootResolver来进行class解析,而不同的是定义了新ModuleScene类来进行加载,使用SootModuleResolver 对模块下的class解析,思路类似:先构建class的引用,然后加到工作队列中,最后获取工作队列进行类的解析(ResolveClass),具体流程可以参考下面的图:
在分析Module-info的类的bytecode的时候,Soot继续沿用了objweb的asm解析框架,使用opcode v7.1的版本,可以支持最高java13的版本。和解析普通的类不一样的是Soot在SootClassBuilder通过构建SootModuleInfoBuilder 去实现asm提供的ModuleVisitor的接口。对Module-info.class里单独定义了SootModuleInfo类,而不是常见的SootClass,当然SootModuleInfo继承SootClass。
可见下图:
在上图可以看到SootModuleInfo里只是封装了export,open,require 这3种类型,具体的实现是在SootModuleInfoBuilder的代码中,Soot目前不支持uses, provides这2种类型,就是SPI的模式。
5.6.1 关键字requires
关键字requires里,是支持version有多个版本,在代码中是无法声明的,但是在编译的字节码里是声明的。
Module: #5,0 // "com.example.hello" #0 1 // requires #6,8000 // "java.base" ACC_MANDATED #7 // 11.0.5 1 // exports #8,0 // com/example/hello 0 // opens 0 // uses 0 // provides |
上面的例子里#7 11.0.5 代表的就是requires的版本,虽然require是有版本的,但是java里并不是必须的,我们可以从openjdk的项目声明看出:
http://openjdk.java.net/projects/jigsaw/spec/reqs/02#version-selection
虽然module里requires有明确的版本,但并没有希望在module里去严格执行版本管控,而是依赖于构建工具。模块系统只需要校验所选的模块集合满足每个模块的依赖即可,无需关注版本。在这个思路上Soot是一致的,Soot并没有对module-info中提供的版本构建版本的依赖分析,也就是并没有对依赖进行版本管控。
5.6.2 关键字exports
export 代表着允许被外部模块访问的package,同时exports 也和to搭档进行外部模块的指定
在上图中,在soot里exportPackage是个列表,每个列表里的key是packageName,而value是指定的多个模块名字。当没有没有指定to的时候,value里面soot保存的是EVERYONE_MODULE代表着任何模块都可以使用。
以上是关于如何分析netbean module之间的调用关系的主要内容,如果未能解决你的问题,请参考以下文章
jvisualvm 或 NetBeans 分析器是不是有可用的调用树视图?
如何检测在 Netbeans 中进行分析时未显示的内存泄漏? [关闭]
如何使用 Netbeans 和 Glassfish 分析企业应用程序?