![c8ada60f83147f33edffa666af12f64e.png](https://i-blog.csdnimg.cn/blog_migrate/0be568af194e246653b108d71d8954c9.jpeg)
之前debug spark源码,是通过写application debug,这个不是基于spark源码本身的调试。
现在做基于spark单元测试的调试,更方便对Spark源码做修改及测试, 方便更深入了解spark的运行原理。
最后,写了一个简单的单元测试,来获取sql对应的asttree 以及unresolved logical plan。
Spark 源码下载编译
https://github.com/apache/spark
![21002c269782b5e8efcf89ae97549d90.png](https://i-blog.csdnimg.cn/blog_migrate/cb18b5936eb71c7f1a1275ef165e201b.jpeg)
![666182bc12a3cd4d97b7a4a80d9bee39.png](https://i-blog.csdnimg.cn/blog_migrate/38aaa2de92cd2344e40205d3c29eb82b.jpeg)
代码down下来之后,等在idea中导入完成之后,用maven编译一下:
cd
![d39f07df015c6b19d1a43480af5f11c7.png](https://i-blog.csdnimg.cn/blog_migrate/0baae77c48b7a72013aaa75dad109eb0.jpeg)
编译成功,就可进行测试了
单元测试
Spark源码提供了大量的TestCase,我们可以学习和模仿。
基于idea
比如我们要运行core模块中 DAGSchedulerSuite.scala "SPARK-3353" 案例:
选中test,右键,可run ;如果打了断点的话,可以debug
![6d69957aa31b137f877355d18eda7cda.png](https://i-blog.csdnimg.cn/blog_migrate/550c36f3d77ceac6f09efe102bc46503.jpeg)
使用idea运行调试testcase简单方便,但由于一些原因,如果idea不能搞定的话,可以通过sbt来运行和调试testcase
基于SBT
SBT 配置
Spark SBT build 中包含多个 sub-project,日常开发调试时经常需要切换到不同的 sub-project。
简单调整一下 SBT 的 prompt string,以便显示当前的 project 名称:
vim
添加如下内容:
![4a7fcc416509e0191dc5adec770beb87.png](https://i-blog.csdnimg.cn/blog_migrate/3e67078230802bab09f40a17fc41f43c.jpeg)
这步只是个人喜好,并不是必须的,添加前,添加后对比如下:
添加前:
![a5693c92260526829584d8c70f06bb75.png](https://i-blog.csdnimg.cn/blog_migrate/208623158a8800fd0860af2f0a4d2d38.jpeg)
添加后:
![cb192eb1de7e6d7211b2724a77e31eae.png](https://i-blog.csdnimg.cn/blog_migrate/a0c518c2a8fadb2695515cad6444c9d5.jpeg)
使用sbt运行测试案例
在core模块中 DAGSchedulerSuite.scala "SPARK-3353" 案例 中加入 一行打印:
![bfafd5d3b76e0de53e31c9da1d2fcea1.png](https://i-blog.csdnimg.cn/blog_migrate/0677986f8d296d92a593b2d840411696.jpeg)
sbt (core)> testOnly *DAGSchedulerSuite -- -z "SPARK-3353"
![935146c51570dd26b2587a0f70605179.png](https://i-blog.csdnimg.cn/blog_migrate/4d4041b8843cec12a5f974c8feb53fa6.jpeg)
使用sbt debug
Run > Edit Configurations > + > Remote to open a default Remote Configuration template
![7aa90cb91108c4c28828383f928d3883.png](https://i-blog.csdnimg.cn/blog_migrate/fdb0a27fbef57d9b1a687830dbf6a8c8.jpeg)
Debugger mode 选项选 Listen to remote JVM
![450b534dcfc5089da0d6cd11fdb03699.png](https://i-blog.csdnimg.cn/blog_migrate/9a0fc37fe6edfea651b10778bd1730f9.jpeg)
打开debug远程,等着SBT 来联接:
![267c2fdc37ef05085a0770e0c1e05429.png](https://i-blog.csdnimg.cn/blog_migrate/8e1ad82d4c1bb44d6017c3150adf1614.jpeg)
在sbt命令中输入:
sbt (core)> set javaOptions in Test += "-agentlib:jdwp=transport=dt_socket,server=n,suspend=n,address=localhost:5005"
sbt (core)> testOnly *DAGSchedulerSuite -- -z "SPARK-3353"
可以看到sbt在断点处等待:
![a876e184a2366e1176e041bb665d0435.png](https://i-blog.csdnimg.cn/blog_migrate/76b0dd54e14d2f287e0289cdd9d8bba3.jpeg)
idea进入debug状态:
![eb4be2062ae46a99eacc1f8e06974191.png](https://i-blog.csdnimg.cn/blog_migrate/b08b92920ac323675a65e47c690dffb6.jpeg)
编写测试用例
Spark为了确保代码风格一致规范,在项目引入了scala-style checker(比如每个代码文件头部需要定义Apache的License注释;import的顺序等),如果代码不合规范,执行编译会出错。
我们可以模仿Spark源码提供的TestCase来写我们自己的TestCase,可以避免踩到代码风格检查的坑。
下面简单写一个XiaoluobuSuite.scala,主要用来从源码中 获取一个 sql语句对应的 AstTree,以及unresolved logical plan:
![6a256edabc937c3f39d44d3c19dbf4cf.png](https://i-blog.csdnimg.cn/blog_migrate/c7a276120fc5131f938c6b0555b7dccf.jpeg)
修改ParseDriver.scala中parsePlan部分源码如下:
![c799afade5bef1e4d097354b961c64b3.png](https://i-blog.csdnimg.cn/blog_migrate/3d44d76f02fbf931526788df694528e3.jpeg)
运行XiaoluobuSuite.scala中的test:
![40d9509c900c9c54e0e16d980b0595ea.png](https://i-blog.csdnimg.cn/blog_migrate/603e51f7306cdadab402f1c585cb5ad4.jpeg)
获取AstTree 和 unresolved logical plan:
![6ca336bd3db22208f0ddf2eb29e54e27.png](https://i-blog.csdnimg.cn/blog_migrate/b73adaa0bf4fe86449b22af247401702.jpeg)
spark中是借助开源的antlr4库把sql转化为AstTree的。
有关antlr, sql词法,语法解析及 AstTree相关理解,可以看之前写过的文章:
Hive源码系列(六)编译模块之词法、语法解析(上)
Hive源码系列(七)编译模块之词法、语法解析(中)
Hive源码系列(七)编译模块之词法、语法解析(下)
Spark SQL的语法规则文件是:SqlBase.g4
下面我们通过g4语法文件 生成一个AstTree:
![f34e8cb00a85afda17c24d102c872cae.png](https://i-blog.csdnimg.cn/blog_migrate/61cd708a2591c49e9b1dc332defec090.jpeg)
写sql:SELECT * FROM A 生成AstTree如下:
![7a21cd66fffe19d952f6ebb44d3dc8c0.png](https://i-blog.csdnimg.cn/blog_migrate/0e621ea582dfd6d13a5c1f1f169afeff.jpeg)
把我们从测试用例中获取的AstTree整理一下:
(
singleStatement
(
statement
(
query
(
queryTerm
(
queryPrimary
(
querySpecification
(
selectClause
SELECT
(
namedExpressionSeq (namedExpression (expression (booleanExpression (valueExpression (primaryExpression *)))))
)
)
(
fromClause
FROM
(
relation (relationPrimary (multipartIdentifier (errorCapturingIdentifier (identifier (strictIdentifier A)) errorCapturingIdentifierExtra)) tableAlias)
)
)
)
)
)
queryOrganization
)
)
<EOF>
)
两个是一样的,这样就还原了 spark中生成AstTree的步骤。
![5249005e52df74892fdaec6c6c69e65b.png](https://i-blog.csdnimg.cn/blog_migrate/be32cc5e87545c2bfa440a05b1742c60.jpeg)
Hey!
我是小萝卜算子
在成为最厉害最厉害最厉害的道路上
很高兴认识你
如果觉得有用的话
关注公众号,看更多干货
参考文档
https://spark.apache.org/developer-tools.html