Go调试工具—— Delve
参考https://github.com/go-delve/delve
安装
首先你必须有等于或高于1.8版本的Go,我的版本是:
userdeMBP:go-learning user$ go version
go version go1.11.4 darwin/amd64
我是用的是Mac,所以使用的是OSX安装方法:
然后使用go get 进行安装:
go get -u github.com/go-delve/delve/cmd/dlv
使用这种方法,你将无法使用delve的本机后端,但无论如何您都不需要它:macOS上的本机后端已经知道该操作系统最近出现的问题,并且目前没有进行维护。详情可见https://github.com/go-delve/delve/issues/1112
安装完后查看版本:
userdeMBP:go-learning user$ dlv version
Delve Debugger
Version: 1.2.
Build: $Id: 068e...57e34f0f08ce01466 $
使用:
1)首先先使用dlv或dlv --help来查看delve支持的命令:
userdeMBP:go-learning user$ dlv
Delve是Go程序的源代码级调试器. Delve通过控制进程的执行、评估变量以及提供线程/ goroutine状态、CPU寄存器状态等信息,使你能够与程序进行交互。 这个工具的目标是为调试Go程序提供一个简单而强大的接口. 使用“--”将标志传递给正在调试的程序,例如: `dlv exec ./hello -- server --config conf/config.toml` Usage:
dlv [command] Available Commands:
attach 连接到正在运行的流程并开始调试.
connect 连接到无头调试服务器.
core 检查核心转储.
debug 编译并开始调试当前目录下的主包或指定的包.
exec 执行预编译的二进制文件,并开始调试会话.
help 帮助信息
run 弃用的命令。使用“debug”替代它.
test 编译测试二进制文件并开始调试程序.
trace 编译并开始跟踪程序.
version 打印版本. Flags:
--accept-multiclient 允许无头服务器接受多个客户机连接。注意,服务器API不是可重入的,客户端必须协调.
--api-version int 无头时选择API版本. (default )
--backend string 后端选择:
default 在macOS上使用lldb,其他地方都是本地的.
native 本地后端.
lldb 使用lldb-server或debugserver.
rr 使用mozilla rr (https://github.com/mozilla/rr).
(default "default") 默认使用的是default
--build-flags string 生成标志,以传递给编译器.
--headless 仅在headless模式下运行调试服务器.
--init string 初始化文件,由终端客户端执行.
-l, --listen string 调试服务器监听地址. (default "localhost:0")
--log 启用调试服务器日志记录.
--log-output string 应该产生调试输出的组件的逗号分隔列表,可能的值为:
debugger 记录调试器命令
gdbwire 日志连接到gdbserial后端
lldbout 将输出从debugserver/lldb复制到标准输出
debuglineerr 读取.debug_line时日志可恢复错误
rpc 记录所有RPC消息
fncall 日志函数调用协议
minidump 日志minidump加载
使用--log启用日志时,默认为“debugger”.
--wd string 用于运行程序的工作目录. (default ".") 使用"dlv [command] --help"获取有关命令的详细信息.
支持的命令太多了,在这里我们主要介绍它的调试命令——debug
2.dlv debug
首先使用dlv debug --help 查看其的帮助信息:
userdeMBP:go-learning user$ dlv debug --help
编译禁用优化的程序,启动并附加到该程序。 默认情况下,没有参数,Delve将编译当前目录中的“main”包,并开始调试它。或者,您可以指定一个包名,Delve将编译该包,并开始一个新的调试会话。 Usage:
dlv debug [package] [flags] Flags:
--output string 二进制文件的输出路径. (default "debug") Global Flags:和上面的一样,这里省略
举例说明:
首先要进行调试的代码为:
package main import (
"fmt"
"time"
) func counting(c chan<- int){
for i := ; i < ; i++{
time.Sleep( * time.Second)
c <- i
}
close(c)
} func main() {
msg := "Starting main"
fmt.Println(msg)
bus := make(chan int)
msg = "starting a gofunc"
go counting(bus)
for count := range bus{
fmt.Println("count : ", count)
}
}
然后开启调试:
userdeMBP:go-learning user$ dlv debug test.go
Type 'help' for list of commands.
(dlv)
然后我们可以输入help来查看能够使用的debug命令有哪些:
The following commands are available:
args ------------------------ 打印函数参数.
break (alias: b) ------------ 设置断点.
breakpoints (alias: bp) ----- 输出活动断点的信息.
call ------------------------ 恢复进程,注入一个函数调用(还在实验阶段!!)
clear ----------------------- 删除断点.
clearall -------------------- 删除多个断点.
condition (alias: cond) ----- 设置断点条件.
config ---------------------- 修改配置参数.
continue (alias: c) --------- 运行到断点或程序终止.
deferred -------------------- 在延迟调用的上下文中执行命令.
disassemble (alias: disass) - 反汇编程序.
down ------------------------ 将当前帧向下移动.
edit (alias: ed) ------------ 在$DELVE_EDITOR或$EDITOR中打开你所在的位置
exit (alias: quit | q) ------ 退出调试器.
frame ----------------------- 设置当前帧,或在不同的帧上执行命令.
funcs ----------------------- 打印函数列表.
goroutine ------------------- 显示或更改当前goroutine
goroutines ------------------ 列举程序goroutines.
help (alias: h) ------------- 打印帮助信息.
list (alias: ls | l) -------- 显示源代码.
locals ---------------------- 打印局部变量.
next (alias: n) ------------- 转到下一个源行.
on -------------------------- 在命中断点时执行命令.
print (alias: p) ------------ 计算一个表达式.
regs ------------------------ 打印CPU寄存器的内容.
restart (alias: r) ---------- 重启进程.
set ------------------------- 更改变量的值.
source ---------------------- 执行包含delve命令列表的文件
sources --------------------- 打印源文件列表.
stack (alias: bt) ----------- 打印堆栈跟踪信息.
step (alias: s) ------------- 单步执行程序.
step-instruction (alias: si) 单步执行一条cpu指令.
stepout --------------------- 跳出当前函数.
thread (alias: tr) ---------- 切换到指定的线程.
threads --------------------- 打印每个跟踪线程的信息.
trace (alias: t) ------------ 设置跟踪点.
types ----------------------- 打印类型列表
up -------------------------- 向上移动当前帧.
vars ------------------------ 打印包变量.
whatis ---------------------- 打印表达式的类型.
在命令前键入help来获得命令的完整文档,如help goroutine
2)首先开始进行调试,这里先什么都不做直接输入continue,即c,运行到断点或程序终止
(dlv) c
Starting main
count :
count :
count :
count :
count :
count :
count :
count :
count :
count :
Process has exited with status
可见这个代码跑了起来
3)restart(缩写r)重启进程
(dlv) b main.main
Process has exited with status
上面进行添加断点的操作,但是并没有返回想要的信息
这是因为我们之前已经将程序运行结束了,这时候不能直接添加断点信息
所以要先调用restart来将进程重启
4)break(即b)添加断点
现在我们可以为其添加断点了
(dlv) b main.main //在main函数处添加断点
Breakpoint set at 0x10b123b for main.main() ./test.go:
(dlv) b main.counting //在counting函数处添加断点
Breakpoint set at 0x10b118f for main.counting() ./test.go:
我们也可以使用"文件:行号"的格式来添加断点,如:
b /Users/user/go-learning/test.go: //等价于在函数counting处添加断点
然后继续程序运行,可见会先停在main函数处:
(dlv) c
> main.main() ./test.go: (hits goroutine(): total:) (PC: 0x10b123b)
: c <- i
: }
: close(c)
: }
:
=> : func main() {
: msg := "Starting main"
: fmt.Println(msg)
: bus := make(chan int)
: msg = "starting a gofunc"
: go counting(bus)
5)breakpoints(缩写bp)输出活动断点信息
(dlv) bp
Breakpoint runtime-fatal-throw at 0x102b3e0 for runtime.fatalthrow() /usr/local/Cellar/go/1.11./libexec/src/runtime/panic.go: ()
Breakpoint unrecovered-panic at 0x102b450 for runtime.fatalpanic() /usr/local/Cellar/go/1.11./libexec/src/runtime/panic.go: ()
print runtime.curg._panic.arg
Breakpoint at 0x10b123b for main.main() ./test.go: ()
Breakpoint at 0x10b118f for main.counting() ./test.go: ()
可见上面的两个设置的断点
6)list 显示从停止行起前后5行的源代码
(dlv) list
> main.main() ./test.go: (hits goroutine(): total:) (PC: 0x10b123b)
: c <- i
: }
: close(c)
: }
:
=> : func main() {
: msg := "Starting main"
: fmt.Println(msg)
: bus := make(chan int)
: msg = "starting a gofunc"
: go counting(bus)
7)next (缩写n)转到下一个源行
可见现在的指针从16行转到了17行
如果想继续向下,可以直接回车,Delve会默认重复上一条命令
(dlv) n
> main.main() ./test.go: (PC: 0x10b1252)
: }
: close(c)
: }
:
: func main() {
=> : msg := "Starting main"
: fmt.Println(msg)
: bus := make(chan int)
: msg = "starting a gofunc"
: go counting(bus)
: for count := range bus{
8)print(缩写p)计算一个表达式
在这里用来打印一个变量的值,举例看下面
9)locals 打印局部变量
10)step(缩写s)单步执行程序
(dlv) p msg
"H�\a\x00�\x00\x00\x00��\x05\x01\x00\x00\x00\x00\x00\x03\x00\x00�\x00\x00\x00��\a\x00�\x00\x00\x00�Q\x00\x01\x00\x00\x00\x00X�\x06\x00�\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00�\x0f\v\x01\x00\x00\x00\x00...+16825344 more"
(dlv) locals
msg = "H�\a\x00�\x00\x00\x00��\x05\x01\x00\x00\x00\x00\x00\x03\x00\x00�\x00\x00\x00��\a\x00�\x00\x00\x00�Q\x00\x01\x00\x00\x00\x00X�\x06\x00�\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00�\x0f\v\x01\x00\x00\x00\x00...+16825344 more"
(dlv) p count
Command failed: could not find symbol value for count
(dlv) s
> main.main() ./test.go: (PC: 0x10b1267)
: close(c)
: }
:
: func main() {
: msg := "Starting main"
=> : fmt.Println(msg)
: bus := make(chan int)
: msg = "starting a gofunc"
: go counting(bus)
: for count := range bus{
: fmt.Println("count : ", count)
(dlv) p msg
"Starting main"
(dlv) locals
msg = "Starting main"
在执行s前,使用p打印了msg局部变量的值,locals打印了局部变量的值,因为还没有运行到给msg复制的代码,所以返回的结果不是期望的值
然后当我们运行s后,可见指针也从17变成了18,然后这时候再运行p和locals,可见返回了期望的值
print还可以用来判断一个表达式:
(dlv) p msg == "Starting main"
true
11)whatis打印表达式类型
(dlv) whatis msg
string
能够得到msg变量为string类型
然后我们接着运行到下一个断点,这时候开启了一个groutine
(dlv) c
Starting main
> main.counting() ./test.go: (hits goroutine(): total:) (PC: 0x10b118f)
: import (
: "fmt"
: "time"
: )
:
=> : func counting(c chan<- int){
: for i := ; i < ; i++{
: time.Sleep( * time.Second)
: c <- i
: }
: close(c)
12)goroutine 显示或更改当前goroutine
(dlv) help goroutine goroutine
goroutine <id>
goroutine <id> <command> 调用时不带参数,它将显示关于当前goroutine的信息。
使用单个参数调用时,它将切换到指定的goroutine。
使用更多参数调用时,它将在指定的goroutine上执行命令。
可见现在正在运行的goroutine
(dlv) goroutine
Thread at ./test.go:
Goroutine :
Runtime: ./test.go: main.counting (0x10b118f)
User: ./test.go: main.counting (0x10b118f)
Go: ./test.go: main.main (0x10b138a)
Start: ./test.go: main.counting (0x10b1180)
指定查看的是goroutine 1 :
(dlv) goroutine
Switched from to (thread )
(dlv) goroutine
Thread at ./test.go:
Goroutine :
Runtime: /usr/local/Cellar/go/1.11./libexec/src/runtime/proc.go: runtime.gopark (0x102d0f4)
User: ./test.go: main.main (0x10b13ae)
Go: /usr/local/Cellar/go/1.11./libexec/src/runtime/asm_amd64.s: runtime.rt0_go (0x10557fb)
Start: /usr/local/Cellar/go/1.11./libexec/src/runtime/proc.go: runtime.main (0x102cb60)
这时候如果运行locals,得到的是goroutine 1所在处的局部变量:
(dlv) locals
gp = (*runtime.g)(0xc000000300)
mp = (*runtime.m)(0x11904c0)
status = (unreadable read out of bounds)
要回去原来的goroutine就调用:
(dlv) goroutine
Switched from to (thread )
13)goroutines 列举程序goroutines
(dlv) help goroutines
列举程序goroutines. goroutines [-u (default: user location)|-r (runtime location)|-g (go statement location)|-s (start location)] [ -t (stack trace)] Print out info for every goroutine. The flag controls what information is shown along with each goroutine: -u 显示用户代码中最顶层堆栈帧的位置
-r 显示最顶层stackframe的位置(包括私有运行时函数中的帧)
-g 显示创建goroutine的go指令的位置
-s 显示起始函数的位置
-t 显示goroutine的堆栈跟踪 如果没有指定具体的标志,则默认使用-u.
(dlv) goroutines
Goroutine - User: ./test.go: main.main (0x10b13ae)
Goroutine - User: /usr/local/Cellar/go/1.11./libexec/src/runtime/proc.go: runtime.gopark (0x102d0f4)
Goroutine - User: /usr/local/Cellar/go/1.11./libexec/src/runtime/proc.go: runtime.gopark (0x102d0f4)
Goroutine - User: /usr/local/Cellar/go/1.11./libexec/src/runtime/proc.go: runtime.gopark (0x102d0f4)
* Goroutine - User: ./test.go: main.counting (0x10b118f) (thread )
[ goroutines]
(dlv) goroutines -s
Goroutine - Start: /usr/local/Cellar/go/1.11./libexec/src/runtime/proc.go: runtime.main (0x102cb60)
Goroutine - Start: /usr/local/Cellar/go/1.11./libexec/src/runtime/proc.go: runtime.forcegchelper (0x102ced0)
Goroutine - Start: /usr/local/Cellar/go/1.11./libexec/src/runtime/mgcsweep.go: runtime.bgsweep (0x1020170)
Goroutine - Start: /usr/local/Cellar/go/1.11./libexec/src/runtime/mfinal.go: runtime.runfinq (0x1017ae0)
* Goroutine - Start: ./test.go: main.counting (0x10b1180) (thread )
[ goroutines]
14)args 打印函数参数
得到counting函数的参数值:
(dlv) args
c = chan<- int /
因为之前只设置了两个断点,因此再次调用c则又将运行程序完毕:
(dlv) c
count :
count : 1
...
15)stepout 跳出当前函数
(dlv) restart
Process restarted with PID
(dlv) b main.main //之前的断点一直存在,即使程序结束,因此此时的counting函数中也有断点
Command failed: Breakpoint exists at /Users/user/go-learning/test.go: at 10b123b
(dlv) c
> main.main() ./test.go: (hits goroutine(): total:) (PC: 0x10b123b)
: c <- i
: }
: close(c)
: }
:
=> : func main() {
: msg := "Starting main"
: fmt.Println(msg)
: bus := make(chan int)
: msg = "starting a gofunc"
: go counting(bus)
(dlv) stepout
Starting main
> main.counting() ./test.go: (hits goroutine(): total:) (PC: 0x10b118f)
breakpoint hit during stepout, continuing...
count :
count :
count :
...
因为stepout跳过的是main()函数,可见直接也跳过了counting的断点,然后执行程序到结束
16)clear删除断点\clearall删除多个断点
因为如果不手动清除断点,即使程序结束并restart程序,断点信息也仍然在
(dlv) breakpoints //查看当前的断点信息
Breakpoint runtime-fatal-throw at 0x102b3e0 for runtime.fatalthrow() /usr/local/Cellar/go/1.11./libexec/src/runtime/panic.go: ()
Breakpoint unrecovered-panic at 0x102b450 for runtime.fatalpanic() /usr/local/Cellar/go/1.11./libexec/src/runtime/panic.go: ()
print runtime.curg._panic.arg
Breakpoint at 0x10b123b for main.main() ./test.go: ()
Breakpoint at 0x10b118f for main.counting() ./test.go: ()
(dlv) clear 1 //先删除第一个断点,即main函数上的断点
Breakpoint cleared at 0x10b123b for main.main() ./test.go:
(dlv) breakpoints //查看可见果然成功删除
Breakpoint runtime-fatal-throw at 0x102b3e0 for runtime.fatalthrow() /usr/local/Cellar/go/1.11./libexec/src/runtime/panic.go: ()
Breakpoint unrecovered-panic at 0x102b450 for runtime.fatalpanic() /usr/local/Cellar/go/1.11./libexec/src/runtime/panic.go: ()
print runtime.curg._panic.arg
Breakpoint at 0x10b118f for main.counting() ./test.go: ()
(dlv) clearall //然后将所有断点都删除
Breakpoint cleared at 0x10b118f for main.counting() ./test.go:
(dlv) breakpoints //查看可见断点信息为空
Breakpoint runtime-fatal-throw at 0x102b3e0 for runtime.fatalthrow() /usr/local/Cellar/go/1.11./libexec/src/runtime/panic.go: ()
Breakpoint unrecovered-panic at 0x102b450 for runtime.fatalpanic() /usr/local/Cellar/go/1.11./libexec/src/runtime/panic.go: ()
print runtime.curg._panic.arg
(dlv)
17)on 在命中断点时执行命令
(dlv) help on
Executes a command when a breakpoint is hit. on <breakpoint name or id> <command>. Supported commands: print, stack and goroutine)
(dlv) b /Users/user/go-learning/test.go: //添加断点
Breakpoint set at 0x10b1368 for main.main() ./test.go:
(dlv) on print msg == "starting a gofunc" //设置当到达该断点后运行的命令,即判断msg的值是否为"starting a gofunc"
(dlv) breakpoints //查看此时的断点信息
Breakpoint runtime-fatal-throw at 0x102b3e0 for runtime.fatalthrow() /usr/local/Cellar/go/1.11./libexec/src/runtime/panic.go: ()
Breakpoint unrecovered-panic at 0x102b450 for runtime.fatalpanic() /usr/local/Cellar/go/1.11./libexec/src/runtime/panic.go: ()
print runtime.curg._panic.arg
Breakpoint at 0x10b1368 for main.main() ./test.go: ()
print msg == "starting a gofunc"
(dlv) c
Starting main
> main.main() ./test.go: (hits goroutine(): total:) (PC: 0x10b1368)
msg == "starting a gofunc": true //可见打印出来了,并返回值为true
: func main() {
: msg := "Starting main"
: fmt.Println(msg)
: bus := make(chan int)
: msg = "starting a gofunc"
=> : go counting(bus)
: for count := range bus{
: fmt.Println("count : ", count)
: }
: }
18)set 更改变量的值
(dlv) help set
Changes the value of a variable. [goroutine <n>] [frame <m>] set <variable> = <value>
⚠️Only numerical variables and pointers can be changed.
因此更改string类型变量将报错:
(dlv) set msg = "change msg"
Command failed: can not set variables of type string (not implemented)
19)up向上移动当前帧/dowm向下移动当前帧
(dlv) up
> main.main() ./test.go: (hits goroutine(): total:) (PC: 0x10b1368)
Frame : /usr/local/Cellar/go/1.11./libexec/src/runtime/proc.go: (PC: 102cd25)
: // A program compiled with -buildmode=c-archive or c-shared
: // has a main, but it is not executed.
: return
: }
: fn = main_main // make an indirect call, as the linker doesn't know the address of the main package when laying down the runtime
=> : fn()
: if raceenabled {
: racefini()
: }
:
: // Make racy client program work: if panicking on
(dlv) down
> main.main() ./test.go: (hits goroutine(): total:) (PC: 0x10b1368)
Frame : ./test.go: (PC: 10b1368)
: func main() {
: msg := "Starting main"
: fmt.Println(msg)
: bus := make(chan int)
: msg = "starting a gofunc"
=> : go counting(bus)
: for count := range bus{
: fmt.Println("count : ", count)
: }
: }
20)exit (alias: quit | q)退出调试器
(dlv) exit
userdeMBP:go-learning user$
3.dlv attach
如果项目正在运行中,想要对正在运行的项目进行调试
1)首先使用"ps aux | grep 编译文件名"来查看运行程序的进程号pid
2)然后就能够使用"dlv attach pid"来连接该运行程序,然后就能使用之前debug中的命令来进行调试了
其他的命令之后如果用到了再添加
Go调试工具—— Delve的更多相关文章
- golang调试工具Delve
Devle是一个非常棒的golang 调试工具,支持多种调试方式,直接运行调试,或者attach到一个正在运行中的golang程序,进行调试. 线上golang服务出现问题时,Devle是必不少的在线 ...
- Go web编程学习笔记——未完待续
1. 1).GOPATH设置 先设置自己的GOPATH,可以在本机中运行$PATH进行查看: userdeMacBook-Pro:~ user$ $GOPATH -bash: /Users/user/ ...
- Golang介绍以及安装
Go语言 Google开源 编译形语言 21世纪的C语言 Go语言的特点 简单易并发 开发效率高 执行性能好 Go语言应用的领域 服务端开发 日志处理 文件系统 监控服务 容器虚拟化 Docker k ...
- (Go)02.go 安装delve调试工具测试
安装调试工具 go get github.com/derekparker/delve/cmd/dlv 增加断点调试 调试--->启动调试
- 发布:.NET开发人员必备的可视化调试工具(你值的拥有)
1:如何使用 1:点击下载:.NET可视化调试工具 (更新于2016-12-29 19:11:00) (终于彻底兼容了部分VS环境下无法使用的问题) 2:解压RAR后执行:CYQ.VisualierS ...
- 自定义可视化调试工具(Microsoft.VisualStudio.DebuggerVisualizers)
前言: 最近飞机失联的太多,明天要飞北京处理服务器双机热备的问题,航空保险已买,单号是:TF10122913. 至于我的银行卡密码,在我枕头下面的字条里,要是我之后没再更新文章,请通知我家人,哈哈哈哈 ...
- 微信小程序开发调试工具
为了帮助开发者简单和高效地开发微信小程序,我们推出了全新的 开发者工具 ,集成了开发调试.代码编辑及程序发布等功能. 扫码登录 启动工具时,开发者需要使用已在后台绑定成功的微信号扫描二维码登录,后续所 ...
- iOS 调试工具
仪表 xcode5 引入了调试仪表,通过仪表可以直观的看出应用的CPU和内存占用量.运行一个程序,点击仪表栏.可以发现当程序处于运行状态时,调试导航面板会以柱状图显示CPU和内存占用量,并随着应用实 ...
- 在php中使用strace、gdb、tcpdump调试工具
[转] http://www.syyong.com/php/Using-strace-GDB-and-tcpdump-debugging-tools-in-PHP.html 在php中我们最常使用调试 ...
随机推荐
- Session的原理,大型网站中Session方面应注意什么?
一.Session和Cookie的区别Session是在服务器端保持会话数据的一种方法(通常用于pc端网站保持登录状态,手机端通常会使用token方式实现),存储在服务端. Cookie是在客户端保持 ...
- Android 实现倒计时操作
new CountDownTimer(10000, 1000) { @Override public void onTick(long millisUntilFinished) { } @Overri ...
- 一年过去了,25万月薪的AI工程师还存在吗?
导读:2017 年的时候,AI 前线进行了一场有关人工智能领域薪资差异的专题策划,这篇名为<25 万年薪的你与 25 万月薪的他,猎头来谈你们之间的差别>的文章引起了读者们的热烈讨论.一年 ...
- redis 数据库安装和基本使用
Redis 介绍: Redis是一款开源的.高性能的键-值存储(key-value store).它常被称作是一款数据结构服务器(data structure server). Redis的键值可以包 ...
- Android开发学习之RecyclerView
1.在app/build.gradle中添加RecyclerView依赖 implementation 'com.android.support:recyclerview-v7:28.0.0' 注意依 ...
- Python 常用的正则表达式
校验数字的相关表达式: 功能 表达式 数字 ^[0-9]*$ n位的数字 ^\d{n}$ 至少n位的数字 ^\d{n,}$ m-n位的数字 ^\d{m,n}$ 零和非零开头的数字 ^(0|[1-9][ ...
- [20181031]12c 在线移动数据文件.txt
[20181031]12c 在线移动数据文件.txt --//12c以前,移动或者改名数据文件是一项比较麻烦的事情,至少要停一下业务.而12c支持在线移动或者改名数据文件,并且有点不可思议--//的是 ...
- 自动化测试的Selenium的python版安装与使用
Selenium是专做网页自动化测试的,即web drive,通过百度Selenium就能找到Selenium的官网 由图可见,selenium支持相当多的编程语言进行网页自动化测试,这里我们使用py ...
- AspNet MVC中使用Hangfire执行定时任务
Hangfire在Aspnet中执行定时任务: 第一步: NuGet中加入Hangfire包 第二步: 添加Owin的自启动 第三步.Hangfire的后台控制仪表盘默认情况下只能本地访问,外网访问需 ...
- 转:敏捷开发之Scrum扫盲篇
现在敏捷开发是越来越火了,人人都在谈敏捷,人人都在学习Scrum和XP... 为了不落后他人,于是我也开始学习Scrum,今天主要是对我最近阅读的相关资料,根据自己的理解,用自己的话来讲述Scrum中 ...