最近在搞 stf 的二次开发,看了网上许多文章,大多是对 stf 架构及改造方案方面的分享,但没找到关于 stf 断点调试的相关文章,所以在这里分享下自己探索到的方法。

什么是断点调试

断点调试,顾名思义,就是能够在程序运行过程中添加断点协助调试。结合 IDE(我用的是 WebStorm),能够十分方便地在运行过程中获取程序在断点停留时的各种信息,包括但不限于变量值、内存帧情况。同时也可以在断点处执行一些表达式,确定表达式的执行结果。

这是一张断点调试的图(来自 http://www.cnblogs.com/jinguangguo/p/4809886.html),可以看到处于断点时能获取到各种信息:

可以执行你想要执行的表达式:

可以看出,断点调试对于开发效率的提升是十分明显的。

stf 后端调试配置探究

大家都知道使用 stf local 可以启动 stf ,然而直接把它如下图那样配置到 webstorm 中:

直接 Run 不会出任何问题,然而进行 debug 的时候,就会有如下报错了:

经过多番查找,最终找到了的原因为:

stf local 命令中实际上是通过 child-process 的 fork 方法按照固定组合启动各个 stf 组件。默认情况下,child-process 创建的程序中使用的调试参数会和当前程序一致,因此会由于 debug 端口重复使用而报出上面的错误

经过查找,有一个专门解决这个问题的组件,叫 child-process-debug 。通过把 child-process 改为用这个组件后(修改 lib/util/procutil.js 这个文件就好,因为 local 的 fork 都是用这个文件里面的 fork 方法的。),调试参数一致的问题解决了,但还是有报错:

然后这段时间刚好也开始尝试使用 docker 一个一个组件地部署 stf ,灵机一动,也许调试的时候也可以这样做?

stf 后端调试配置

原理很简单,就是把原来 stf local 启动的各个进程改为一个一个配置来启动,配置的内容和 local 保持一致就好。

stf local 中的启动配置:

INF/util:procutil 45859 [*] Forking "/Users/chenhengjie/Projects/ppmoney/tools/stf/lib/cli.js triproxy app001 --bind-pub tcp://127.0.0.1:7111 --bind-dealer tcp://127.0.0.1:7112 --bind-pull tcp://127.0.0.1:7113"
INF/util:procutil 45859 [*] Forking "/Users/chenhengjie/Projects/ppmoney/tools/stf/lib/cli.js triproxy dev001 --bind-pub tcp://127.0.0.1:7114 --bind-dealer tcp://127.0.0.1:7115 --bind-pull tcp://127.0.0.1:7116"
INF/util:procutil 45859 [*] Forking "/Users/chenhengjie/Projects/ppmoney/tools/stf/lib/cli.js processor proc001 --connect-app-dealer tcp://127.0.0.1:7112 --connect-dev-dealer tcp://127.0.0.1:7115"
INF/util:procutil 45859 [*] Forking "/Users/chenhengjie/Projects/ppmoney/tools/stf/lib/cli.js processor proc002 --connect-app-dealer tcp://127.0.0.1:7112 --connect-dev-dealer tcp://127.0.0.1:7115"
INF/util:procutil 45859 [*] Forking "/Users/chenhengjie/Projects/ppmoney/tools/stf/lib/cli.js reaper reaper001 --connect-push tcp://127.0.0.1:7116 --connect-sub tcp://127.0.0.1:7111"
INF/util:procutil 45859 [*] Forking "/Users/chenhengjie/Projects/ppmoney/tools/stf/lib/cli.js provider --name chenhengjiedeMac-mini.local --min-port 7400 --max-port 7700 --connect-sub tcp://127.0.0.1:7114 --connect-push tcp://127.0.0.1:7116 --group-timeout 900 --public-ip localhost --storage-url http://localhost:7100/ --adb-host 127.0.0.1 --adb-port 5037 --vnc-initial-size 600x800"
INF/util:procutil 45859 [*] Forking "/Users/chenhengjie/Projects/ppmoney/tools/stf/lib/cli.js auth-mock --port 7120 --secret kute kittykat --app-url http://localhost:7100/"
INF/util:procutil 45859 [*] Forking "/Users/chenhengjie/Projects/ppmoney/tools/stf/lib/cli.js app --port 7105 --secret kute kittykat --auth-url http://localhost:7100/auth/mock/ --websocket-url http://localhost:7110/"
INF/util:procutil 45859 [*] Forking "/Users/chenhengjie/Projects/ppmoney/tools/stf/lib/cli.js api --port 7106 --secret kute kittykat --connect-push tcp://127.0.0.1:7113 --connect-sub tcp://127.0.0.1:7111"
INF/util:procutil 45859 [*] Forking "/Users/chenhengjie/Projects/ppmoney/tools/stf/lib/cli.js websocket --port 7110 --secret kute kittykat --storage-url http://localhost:7100/ --connect-sub tcp://127.0.0.1:7111 --connect-push tcp://127.0.0.1:7113"
INF/util:procutil 45859 [*] Forking "/Users/chenhengjie/Projects/ppmoney/tools/stf/lib/cli.js storage-temp --port 7102"
INF/util:procutil 45859 [*] Forking "/Users/chenhengjie/Projects/ppmoney/tools/stf/lib/cli.js storage-plugin-image --port 7103 --storage-url http://localhost:7100/"
INF/util:procutil 45859 [*] Forking "/Users/chenhengjie/Projects/ppmoney/tools/stf/lib/cli.js storage-plugin-apk --port 7104 --storage-url http://localhost:7100/"
INF/util:procutil 45859 [*] Forking "/Users/chenhengjie/Projects/ppmoney/tools/stf/lib/cli.js poorxy --port 7100 --app-url http://localhost:7105/ --auth-url http://localhost:7120/ --api-url http://localhost:7106/ --websocket-url http://localhost:7110/ --storage-url http://localhost:7102/ --storage-plugin-image-url http://localhost:7103/ --storage-plugin-apk-url http://localhost:7104/"

migrate 的一般情况下不需要 debug ,需要的时候直接 stf migrate 就好了,所以这里就不另外放上来了。

放一张 storage-tmp 的对应 webstorm 截图,大家可以参考下:

其中有一点要特别注意,由于 provider 每增加一台设备会增加一个子进程,所以 provider 一般情况下最好不要用 debug 模式打开,用 run 模式就好。

配置完后,就可以愉快地做断点调试啦~

stf 前端调试配置探究

大家都知道,通过 gulp build 命令可以把前端代码合并更新,然后网页刷新下就是新代码了。

而实际上 stf 前端用的是 webpack 进行的前端各个资源文件的合并,gulp build 的本质是调用了 gulp buildgulp webpack:build 两个子任务。也就是说,实际上代码的合并更新是通过 gulp webpack:build 完成的。

简单看了下 webpack:build 任务的源码,基本就是使用 webpack 本身提供的函数进行构建。

PS:想对 webpack 有个大致了解,强烈推荐花个 10 分钟左右把 官方 tutorials 过一下。

好了,webpack 作为一个已经比较成熟的框架,webstorm 肯定是提供了调试的支持的。果然,在官方找到了一个调试配置的文章: https://blog.jetbrains.com/webstorm/2015/09/debugging-webpack-applications-in-webstorm/

文章里面的点总结起来就是三个:

  1. webpack 配置里面设置 devtool: "source-map"
  2. 把 webpack 输出的文件夹 build exclude 掉
  3. 做一个 Mapping,把浏览器中的文件和代码中的文件映射起来

实际配置的时候,有一个坑,我发现无论怎么改 webpack.config.js 这个文件里面的 devtool ,scripts 标签页总是无法出现 webpack:// 这样的内容。结果像上面那样探究了一下 webpack:build 任务的源码,它竟然覆盖了 devtool 的配置。。。

// For production
gulp.task('webpack:build', function(callback) {
...

  // 覆盖了 devtool 的配置。。。
  myConfig.devtool = false

 ...
})

那么解决方法也很简单,新建一个 webpack:debug 的任务,内容直接拷贝 webpack:build 的,其中 devtool 改为 'source-map' 就好了。

stf 前端调试配置

好了,总结一下具体的配置方法吧:

gulp.task('webpack:debug', function(callback) {
  var myConfig = Object.create(webpackStatusConfig)
  myConfig.plugins = myConfig.plugins.concat(
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify('production')
      }
    })
  )
  // set devtool to source-map for debug
  myConfig.devtool = 'source-map'

  webpack(myConfig, function(err, stats) {
    if (err) {
      throw new gutil.PluginError('webpack:others', err)
    }

    gutil.log('[webpack:debug]', stats.toString({
      colors: true
    }))
    callback()
  })
})

配置就只有这么多了。下面介绍一下用法:

  1. 假设现在你的代码已经改好了,运行下 gulp webpack:debug 任务,生成最新的代码。
  2. 如果目前已经有 javascript 类型的调试器正在运行,停掉它。
  3. 重启下 app 的调试进程
  4. 自动启动的浏览器窗口如果过了 10 秒页面还是空白,直接刷新下,多刷新几次就好了。

总的来说,配置比较简单,但用法略复杂,每次改完代码都得做不少操作才能对新代码进行调试。

总结

stf 相对以前用过的 nodejs 程序(如 appium),相对复杂,调试方式并没有平时普通网站那样把 run 改为 debug 就可以,需要额外进行不少配置。但一旦配置好,开发效率将会有比较明显的提升,至少再也不用加很多 console.log 然后后面又得逐一去掉了。

我提供的方法目前都只能说能用,但说实话,并十分好用,特别是前端,每一次都要跑任务、重启调试器、重新进入当前页,真心累。

如果大家有更好的调试方法,非常欢迎大家在回帖中提出来,我确认后会更新到正文中,以帮助后来人更方便的进行 stf 的调试配置


↙↙↙阅读原文可查看相关链接,并与作者交流