- 浏览: 1597884 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
jsrgzhangzhiyong:
关于null值的转换还是感觉不太友好,就像 mapstruct ...
我也造了个轮子:BeanMapping(属性拷贝) -
he037:
a417930422 写道引用使用EPHEMERAL会引出一个 ...
基于zookeeper的分布式lock实现 -
seancheer:
qianshangding 写道首先节点启动后,尝试读取本地的 ...
zookeeper学习记录三(session,watcher,persit机制) -
雪夜归人:
您好,我想咨询一下,开源的canal都能支持mysql的哪些版 ...
Canal BinlogChange(mysql5.6) -
zhoudengyun:
copy 一份做记录,后续学习,请知悉
阿里巴巴开源项目: 基于mysql数据库binlog的增量订阅&消费
背景
周五下班回家,在公司班车上觉得无聊,看了下btrace的源码(自己反编译)。 一些关于btrace的基本内容,可以看下我早起的一篇记录:btrace记忆
上一篇主要介绍的是btrace的一些基本使用以及api,这里我想从btrace源码本身进行下介绍。至于btrace的优势,能用来干些什么,自己上他的官网看下或者google一下,花个半小时就能明白了。
至于为什么会去反编译查看btrace源码,主要是会在部门整个关于btrace的分享。同时btrace的相关技术文档缺乏,javadoc很多时候说的不明不白,作者也没有提供源码开源,所以就有了这次的分享。
Btrace涉及相关技术
- asm
- instrument http://download.oracle.com/javase/6/docs/api/java/lang/instrument/package-summary.html
- JVM TI(java tool api) http://download.oracle.com/javase/6/docs/jdk/api/attach/spec/com/sun/tools/attach/VirtualMachine.html
- Java Compiler Api http://download.oracle.com/javase/6/docs/api/javax/tools/package-summary.html
大家可以先去预备一下知识。
Btrace的大体设计
下面来看一个Btrace的设计图:
说明:
1. BtraceClient : 为我们使用的btrace的本地api,一般我们使用的bin/btrace会在本地启动一个btrace jvm,其内部使用了Java Complier Api, JVMTI技术,以及创建了一个socket。
- Java Complier Api:动态的将我们传递的监控的java源文件动态编译成.class文件
- JVMTI: 主要是利用了java 1.6之后的VirtaulMachine技术,动态的attach到一个已启动的jvm上,为他去启动一个BtraceAgent。该Agent会为BtraceClient启动一个server socket进行通讯。(多进程之间的通讯)
- 本地socket: BtraceClient和BtraceAgent之间的数据通讯,比如生成的.class发送到BtraceAgent,还有一些Event事件等等。BtraceAgent同样可以将服务端print()的数据通过socket的方式回传给BtraceClient进行打印
- BtraceAgent的启动可以有两种方式: BtaceClient动态attach后进行启动, 另一种就是在目标jvm启动之前添加agent参数进行启动。
- BtraceAgent会启动一个server socket,与BtraceClient客户端进行交互,客户端可以将监控的.class文件通过socket发送,同样也可以在jvm启动时直接指定对应本地的.class做为监控脚本。
- BtraceAgent在接受到监控指令后,会遍历当前所有已经加载的class类,挨个进行匹配检查,并生成相应的监控字节码(监控方法)。
btrace的包结构:
- btrace-client.jar
- btrace-boot.jar
- btrace-agent.jar
btrace-client
一般我们通常直接使用的命令,比如:
bin/btrace $pid Btrace.java
都是直接调用了btrace-client包中的代码。
几个核心类介绍:
1. com.sun.btrace.client.Main (btrace的启动入口)
- 调用Client进行complier(Java Compiler Api)和attach(JVM TI)处理
2. com.sun.btrace.client.Client
- compiler方法 : 调用了Java Complier Api进行动态编译你的Btrace.java文件
this.compiler = ToolProvider.getSystemJavaCompiler(); this.stdManager = this.compiler.getStandardFileManager(null, null, null); Verifier btraceVerifier = new Verifier(this.unsafe); //指定了btrace特定的语法校验器
- attach方法: 调用VirtualMachine.attach(pid);vm.loadAgent(agentPath, agentArgs);动态加载btrace-agent.jar包
传递给agent程序的几个参数: debug=true unsafe=true dumpClass=true dumpDir=xx trackRetransforms=true ##是否记录instrument行为 bootClassPath= xx ##agent.jar使用 systemClassPath =xx ##agent.jar使用 probeDescPath=xx
- submit方法: 调用提交对应的instrument指令,并传递对应code的byte[]
this.sock = new Socket("localhost", this.port); this.oos = new ObjectOutputStream(this.sock.getOutputStream()); ... WireIO.write(this.oos, new InstrumentCommand(code, args));
几点说明:
* 在调用了attach方法后,会通过btrace-agent.jar中的com.sun.btrace.agent.Main启动一个ServerSocket
int port = 2020; String p = (String)argMap.get("port"); .... ServerSocket ss; try { (isDebug()) debugPrint(new StringBuilder().append("starting server at ").append(port).toString()); System.setProperty("btrace.port", String.valueOf(port)); if ((scriptOutputFile != null) && (scriptOutputFile.length() > 0)) { System.setProperty("btrace.output", scriptOutputFile); } ss = new ServerSocket(port); } catch(Exception e) .... while (true) { if (!isDebug()) continue; debugPrint("waiting for clients"); Socket sock = ss.accept(); if (!isDebug()) continue; debugPrint(new StringBuilder().append("client accepted ").append(sock).toString()); Client client = new RemoteClient(inst, sock); handleNewClient(client); continue; }
* 所以在submit中,会通过一个本地socket进行连接server,并提交相应的Btrace.java中的监控代码(这时应该是编译后的字节码).
3. com.sun.btrace.compiler.Verifier btrace自定义的语法校验器
-
Boolean value = this.unsafe ? Boolean.TRUE : (Boolean)ct.accept(new VerifierVisitor(this), null); // 注意下unsafe的判断
4. com.sun.btrace.compiler.VerifierVisitor (具体的一些检查规则)
- visitBinary String字符串的+限制
- visitClass class的检查,不允许有父类,不允许有接口类,不允许非static变量,必须有Btrace @标签
- visitDoWhileLoop 不允许do while循环
- visitForLoop 不允许for循环
- visitMethod 必须为static public ,不允许出现synchronized标记
- visitNewArray 不允许出现new Array
- visitNewClass 不允许出现new 对象
- visitReturn 不允许有返回值
- visitSynchronized 不允许有同步快
- visitThrow visitTry 不允许有try catch的动作
- visitOther 除上面允许之外的,不允许有其他的
说明:
* 看完Verifier和VerifierVisitor后,相信大家都应该明白了Btrace所谓的诸多限制,只是针对.java需要动态编译。如果我们预先生成.class文件,Btrace在1.2版本中并不会作类型合法性检查。(在将code发送给btrace-agent后,会在目标的jvm内部进行一次简单的Btrace语法检查,具体见后面Btrace-agent介绍)
5. com.sun.btrace.comm.XXX Btrace的各种command指令
btrace-agent
大致了解了Client类中的attach和submit方法后,相信也能猜到对应agent的一些设计。简单的看一下
1. com.sun.btrace.agent.Main 为attach上之后agent的总入口,会调用agentmain()方法
- main方法: 首先解析参数,然后会启动一个agentThread(Daemon线程)
- parseArgs : 对应的参数解析,客户端在attach时,提交给agent后的一些参数
bootClassPath=xx.jar //需要动态增加的jar systemClassPath=xx.jar noServer=true/false //是否启动server socket debug=true/false unsafe=true/false dumpClasses=true/false dumpDir=路径 trackRetransforms=true/false probeDescPath=路径 stdout=true/false script=文件 scriptdir=路径 scriptOutputFile=文件路径特别注意下相比于Btrace-client提交的参数中,多了几个script,scriptdir等参数,允许在Client调用服务端一个指定的Btrace script文件进行处理
- loadBTraceScript : 装载指定的script(注意是script和scriptDir中指定的script),必须是.class文件,会调用FileClient进行处理,最后调用handleNewClient进行统一处理,最后调用handleNewClient进行统一instrument处理。
- startServer : main启动的agentThread会调用该方法,这里会启动一个serversocket,和btrace-client的客户端socket进行通讯,使用RemoteClient,最后调用handleNewClient进行统一instrument处理。
- handleNewClient : 启动一个异步线程进行class Transformer,根据提交的byte[] code进行类文件重写
说明:
* 目前instrument进行字节码重写时,会重新load所有的class进行处理。(Btrace可以使用正则,父类的方式进行匹配,只能是挨个Class进行处理,看下是否有匹配的OnMethod)
* 相比于btrace-client提交过来的参数中,btrace-agent支持的参数中多了几个script,scriptdir等,允许在Client调用服务端一个指定的Btrace script文件进行处理,注意这里的script必须是编译后的.class文件。和通过socket提交的btrace在处理上没有太大的差异。
2. com.sun.btrace.agent.RemoteClient / FileClient : (RemoteClient为通过socket提交的script , FileClient为script和scriptDir指定的script文件)
- 两者的不同无非就是对应的结果输出方式不同,一个是传回给客户端,另一个是直接终端输出
- 会调用Client.loadClass()进行btrace数据解析,主要是解析对应的OnMethod和OnProbe数据。
3. com.sun.btrace.agent.ProbeDescriptorLoader
- 会解析对应Btrace script中出现的@OnProbe,解析xml文件中对应的@OnMethod信息
4. com.sun.btrace.agent.Client: (RemoteClient和FileClient的共同父类)
- instument中的Transformer的实现类
- loadClass()方法: btrace script脚本解析
- verify 首先进行class的校验, 调用Verity类进行检查(可手工执行:java com.sun.btrace.runtime.Verifier <.class file>)
- runtime.defineClass(codeBuf); 使用反射重新装载class byte
- transform: 核心的方法(isBTraceClass(cname)) || (isSensitiveClass(cname)) 过滤btrace内部类,已经一些Object,ThreadLocal,sun/reflect类)
- instrument方法 : 方法中调用asm的ClassReader进行class对象解析,并设置Instrumentor进行Class对象处理
5. com.sun.btrace.runtime.Instrumentor : 是Btrace实现代码监控增强处理的核心逻辑
可以直接调用:
java com.sun.btrace.runtime.Instrumentor <btrace-class> <target-class>]
Btrace的几点总结
1. btrace支持的监控方式
- 本地jvm监控:目前大多数都是用的是btrace和监控的目标java是在同一机器上
- 远程jvm监控:需要在远程服务器启动时添加btrace-agent.jar,需要重写btrace客户端,完成和serversocket建立通讯,完成btrace script发送监控。
1. VirtualMachine动态attach不支持远程操作,所以无法动态的进行agent添加。
- java 1.4以及之前 : 不支持,Instrument在jdk 1.5之后才出现。
- java 1.5 : 必须手动在jvm启动时添加btrace-agent.jar,因为VirtualMachine是在jdk 1.6之后才出现。
- java 1.6 : 推荐使用
java -Xshare:off -javaagent:${BTRACE_HOME}/build/btrace-agent.jar=dumpClasses=false,debug=false,unsafe=false,probeDescPath=.,noServer=true,script=$1 具体的参数见上面的代码分析
3. btrace的支持的script方式有多种。
- client上的.java文件
会进行动态编译,会有比较多的语法限制,btrace一堆的你不能做的事 - client上的.class文件
没什么好讲的,自己写Btrace script时导入btrace-client.jar,写好后生成一个.class文件,再通过btrace pid Btrace.class进行启动。 - remote上的.class文件
1. 修改btrace-client中的Client类,支持script和scriptDir的一些参数提交。
2. 在remote机器上放置对应的btrace.class文件
4. btrace的使用是否会对java进程造成影响?(影响是肯定的,不过影响不大)
装载时的影响:
- btrace每次使用,都会重新load所有的class。当然如果OnMethod不匹配,是不会被重新装载。所以跟你的OnMethod的匹配规则很有关系,如果使用+java.lang.Object。那就死定了。
- btrace监控每次退出后,原先所有的class都不会被恢复,你的所有的监控代码依然一直在运行
public InstrumentServer(String ip, String port) { $btrace$com$agapple$btrace$Instrumentor$InstrumentTracer$bufferMonitor(this); this.ip = ip; this.port = port; } private static void $btrace$com$agapple$btrace$Instrumentor$InstrumentTracer$bufferMonitor(@Self Object arg0) { if (!BTraceRuntime.enter(InstrumentTracer.runtime)) return; try { Field ipField = BTraceUtils.field("com.agapple.btrace.Instrumentor.InstrumentServer", "ip"); Field portField = BTraceUtils.field("com.agapple.btrace.Instrumentor.InstrumentServer", "port"); String ip = (String)BTraceUtils.get(ipField, self); String port = (String)BTraceUtils.get(portField, self); BTraceUtils.println(BTraceUtils.strcat(BTraceUtils.strcat(BTraceUtils.strcat("ip : ", BTraceUtils.str(ip)), " port : "), BTraceUtils.str(port))); BTraceRuntime.leave(); return; } catch (Throwable localThrowable) { BTraceRuntime.handleException(localThrowable); } }
private volatile boolean disabled; public static boolean enter(BTraceRuntime current) { if (current.disabled) return false; return map.enter(current); }
5. btrace诸多的使用限制,你必须得知道:
can not create new objects. can not create new arrays. can not throw exceptions. can not catch exceptions. can not make arbitrary instance or static method calls - only the public static methods of com.sun.btrace.BTraceUtils class or methods declared in the same program may be called from a BTrace program. (pre 1.2) can not have instance fields and methods. Only static public void returning methods are allowed for a BTrace class. And all fields have to be static. can not assign to static or instance fields of target program's classes and objects. But, BTrace class can assign to it's own static fields ("trace state" can be mutated). can not have outer, inner, nested or local classes. can not have synchronized blocks or synchronized methods. can not have loops (for, while, do..while) can not extend arbitrary class (super class has to be java.lang.Object) can not implement interfaces. can not contains assert statements. can not use class literals.
说明:
- 在btrace-client和btrace-agent分别都有对诸多限制的检查。
- btrace-client代码类: http://kenai.com/projects/btrace/sources/hg/content/src/share/classes/com/sun/btrace/compiler/VerifierVisitor.java
- btrace-agent代码类: http://kenai.com/projects/btrace/sources/hg/content/src/share/classes/com/sun/btrace/runtime/MethodInstrumentor.java
补充说明:
- 正因为btrace有这诸多的限制,才可以让我们的监控代码可以更加的放心,这也正是btrace能普及的一个很重要的原因。
- 不得不说的一个点:对String的"+"限制使用,让我们使用起来很不爽,不过还好在btrace 1.2之后,作者提供了一个StringBuilder。相比于strcat已经好用多了
6. btrace对string字符串的处理
- 可以参看总结3,突破对应的限制。不是非常建议,因为总结4中提出即使btrace client退出后,服务端一直会运行btrace script。所以一旦有写的动作,会是一个长期持续的过程
- btrace 1.2 release说明中,已经提到增加了StringBuilder进行字符串处理,至少比先前的strcat使用上已经方便很多了。具体查看:http://kenai.com/jira/browse/BTRACE-38
7. btrace的相关源码:
- 官网上找到一个,不过是web版本: http://kenai.com/projects/btrace/sources/hg/show/src/share/classes/com/sun/btrace
- 附件中自己使用了jd-gui反编译生成了一个。
8. btrace中对OnMethod的Location使用上,以及一些annotation使用不明确,可以查看:http://kenai.com/projects/btrace/sources/hg/content/src/share/classes/com/sun/btrace/runtime/Instrumentor.java
说明: self, ProbeClassName , ProbeMethodName 在任何的Kind中都支持,所以就不在每个表格中赘述。
Kind | Where.BEFORE | Where.AFTER |
ARRAY_GET | 数组长度(int) , 数组类型(type) | @return , 数组长度(int) , 数组类型(type) |
ARRAY_SET | 原始数组类型(type) , 数组长度(int) , 目标数组类型(type) | @return,原始数组类型(type) , 数组长度(int) , 目标数组类型(type) |
CALL | 方法参数 , @TargetInstance , @TargetMethodOrField | 方法参数, @return , @TargetInstance , @TargetMethodOrField |
CATCH | 异常类型(type) | 异常类型(type) |
CHECKCAST | 转型的目标类型 | 转型的目标类型 |
ENTRY | 方法参数 | 方法参数 |
ERROR | 异常类型(throwable type) | 异常类型(throwable type) |
FIELD_GET | @TargetInstance,@TargetMethodOrField | @TargetInstance,@TargetMethodOrField,@return |
FIELD_SET | fldValueIndex,@TargetInstance,@TargetMethodOrField | fldValueIndex,@TargetInstance,@TargetMethodOrField |
INSTANCEOF | 转型的目标类型 | 转型的目标类型 |
LINE | 行数 | 行数 |
NEW | 对象类名 | @return |
NEWARRAY | 数组内部对象类名,类名 | 数组内部对象类名,类名, @return |
RETURN | 无 | 参数,@return , @Duration |
SYNC_ENTRY | sync对象 | sync对象 |
SYNC_EXIT | sync对象 | sync对象 |
THROW | 异常类型 | 异常类型 |
最后
花了多个小时时间整理了这份blog,希望能给大家理解btrace,掌握btrace的使用能带来一些帮助!!
有问题和交流,欢迎站内联系
- btrace-agent.src.zip (117.3 KB)
- 下载次数: 228
- btrace-client.src.zip (139.1 KB)
- 下载次数: 225
评论
我想请教下,你是如何分析btrace,透彻了解btrace的工作机制的。
多看看btrace的源码
我想请教下,你是如何分析btrace,透彻了解btrace的工作机制的。
暂时没,都是直接在服务器上运行btrace
你需要做远程监控?
7. btrace的相关源码:
- 官网上找到一个,不过是web版本:http://kenai.com/projects/btrace/sources/hg/show/src/share/classes/com/sun/btrace
- 附件中自己使用了jd-gui反编译生成了一个。
呃…BTrace明明是GPLv2开源的。源码用Mercurial管理着。
要源码的话这样就好了:
hg clone https://hg.kenai.com/hg/btrace~hg
正常的话会看到:
$ hg clone https://hg.kenai.com/hg/btrace~hg destination directory: btrace~hg requesting all changes adding changesets adding manifests adding file changes added 421 changesets with 2192 changes to 692 files (+4 heads) updating working directory 299 files updated, 0 files merged, 0 files removed, 0 files unresolved
代码就抓下来了…
呵呵,我土了, 先前不了解mercurial工具。 只用过svn和git,长见识了
7. btrace的相关源码:
- 官网上找到一个,不过是web版本:http://kenai.com/projects/btrace/sources/hg/show/src/share/classes/com/sun/btrace
- 附件中自己使用了jd-gui反编译生成了一个。
呃…BTrace明明是GPLv2开源的。源码用Mercurial管理着。
要源码的话这样就好了:
hg clone https://hg.kenai.com/hg/btrace~hg
正常的话会看到:
$ hg clone https://hg.kenai.com/hg/btrace~hg destination directory: btrace~hg requesting all changes adding changesets adding manifests adding file changes added 421 changesets with 2192 changes to 692 files (+4 heads) updating working directory 299 files updated, 0 files merged, 0 files removed, 0 files unresolved
代码就抓下来了…
用btrace最大的好处就是监控业务数据,而且允许在jvm运行之后动态attach,完全是一种无嵌入的监控模式。你不需要像jwebap一样,丢个jar包到你的运行容器中。
一般常用:
1. 某方法调用的性能监控
2. 某方法调用的参数内容监控,记录等。(排查问题特别有用)
3. ......
相关推荐
《深入解析BTrace:源代码探索之旅》 BTrace,全称为"Berkeley Trace",是一款强大的、动态的、安全的Java生产环境诊断工具。它允许开发者在运行时对Java应用程序进行性能分析和故障排查,而无需修改或重启应用。...
5. build:这个目录可能包含了构建BTrace项目的源码或者构建脚本,如Ant或Maven配置文件,用于从源代码构建可执行程序。 6. samples:这是示例代码或脚本的集合,帮助用户了解如何在实际应用中使用BTrace,通过这些...
3. **安全机制**:BTrace有内置的安全策略,以防止不安全或破坏性的脚本执行。在生产环境中使用时,需要确保脚本经过充分测试并符合安全规范。 4. **运行BTrace**:通过命令行工具`btrace`运行BTrace脚本,指定要...
标题 "BTrace二三事之二:OnMethod子类匹配BUG(怀疑)" 指向的是一个关于BTrace工具在处理子类方法匹配时可能存在的问题。BTrace是一款动态跟踪工具,它允许开发者在运行时对Java应用程序进行诊断和监控,通过插入...
"cck.jar" 可能是 BTrace 示例或工具的一部分,它可能包含了 BTrace 的客户端工具或者一些示例脚本,供用户学习和参考。 6. **BCEL.jar**: BCEL (Byte Code Engineering Library) 是一个用于处理 Java 字节码的...
本资源包集合了一些非常炫的Android特效源码,这些源码涵盖了多个方面,包括动画、界面交互、视觉效果等,对于Android开发者来说,是学习和借鉴的好材料。 1. **动画特效**:Android提供了丰富的动画库,如属性动画...
4. **动态链接库(DLL)**:在易语言中,可能需要编写或使用DLL来实现一些底层操作,如时间修改、系统调用拦截等。 5. **游戏API接口识别**:为了改变游戏速度,需要识别并模拟游戏的API接口。这可能涉及到逆向工程...
最赚钱的项目,帮您打造本地商业门户,机遇不可错过。网上订餐系统_网络订餐系统_外卖网站建设.订餐网,外卖网源码,带积分商城,商家系统,外卖网站建设! 系统特点: 周密策划、项目为先 "项目指导技术,技术服从...
6. **性能优化**:源码中可能包含了一些性能优化技巧,如减少网络请求、优化渲染速度、合理使用缓存等,这些都是提升用户体验的关键。 7. **实践案例**:每一套源码都是一个实际运行的案例,开发者可以从中学习到...
ssh框架项目源码ssh框架项目源码ssh框架项目源码ssh框架项目源码ssh框架项目源码ssh框架项目源码ssh框架项目源码ssh框架项目源码ssh框架项目源码ssh框架项目源码ssh框架项目源码ssh框架项目源码ssh框架项目源码ssh...
在这个源码中,你可以深入理解PHP编程语言以及其在构建动态网站中的应用,同时也可以了解到电子商务系统的一些基本功能和架构设计。 首先,源码的主体部分可能是由PHP脚本构成的,PHP是一种广泛使用的开源服务器端...
移动医疗APP源码是开发医疗健康应用的核心组成部分,它包含了应用程序的所有逻辑和界面设计。在Android平台上,这种源码通常是用Java或Kotlin语言编写的,并使用Android Studio作为集成开发环境(IDE)。在这个案例...
源码01 销售管理系统 源码02 彩票分析系统 源码03 餐饮管理系统 源码04 C#点名程序 源码05 象棋游戏 源码06 变色球游戏 源码07 多功能计算器 源码08 记事本 源码09 简易画图程序 源码10 成绩管理系统 源码11 BBS论坛...
《cocos creator完整麻将源码解析与开发指南》 cocos Creator是一款强大的2D游戏开发引擎,被广泛应用于游戏开发,尤其...无论是对于个人项目还是商业应用,掌握这样的源码分析和利用能力,都将为你的IT事业添砖加瓦。
教程版权归原作者所有,本人只是负责搜集整理,本人不承担任何技术及版权问题。教程仅提供学习参考,不得用于商业用途,请在下载后在24小时内删除。 目录: 0001-2科技发展有限公司升级版源码 0001科技发展有限公司...
分类源码,信息发布网站源码,信息源码,信息港网站源码,asp信息港源码,信息类网站源码,多种分类源码,信息网源码下载,asp信息网源码,信息发布系统源码,物流信息源码,房产信息网源码.net源码,公安信息网源码,家教信息...
1. **UI设计与布局**:通过分析源码,你可以了解如何使用Android Studio中的XML布局文件创建用户界面,包括线性布局、相对布局、网格布局等。同时,你还能学习到自定义View和动画效果的实现。 2. **事件处理**:...
以下是对供应链管理系统源码的一些详细知识点: 1. **供应链管理**:供应链管理涵盖了从原材料采购到产品制造,再到最终用户交付的所有环节。它包括供应商管理、库存控制、生产计划、物流、分销和客户服务等关键...
为了进行二次开发,你需要具备一定的编程基础,理解源码使用的编程语言,并且对游戏开发有基本的认知。你可以根据需求修改现有的房间、物品、命令或者添加新的功能。例如,你可以扩展游戏世界,增加新的地图和剧情;...
网络注册登录 源码 ...6.我不知道这次还会不会上传失败(前几次我上传的源码失败了几次 额....) 7.如果上传成功....大家下载后对源码满意 就给我顶顶吧... 8.本源码100%由我本人所创..........