安装

首先你必须有等于或高于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.0
Build: $Id: 068e...57e34f0f08ce01466 $

使用

首先先使用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 1)--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

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 mainimport ("fmt""time"
)func counting(c chan<- int){for i := 0; i < 10; i++{time.Sleep(2 * 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)}
}

然后开启调试:

$ 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 ------------------- 显示或更改当前goroutinegoroutines ------------------ 列举程序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

首先开始进行调试,这里先什么都不做直接输入continue,即c,运行到断点或程序终止

(dlv) c
Starting main
count :  0
count :  1
count :  2
count :  3
count :  4
count :  5
count :  6
count :  7
count :  8
count :  9
Process 1324 has exited with status 0

可见这个代码跑了起来

restart(缩写r)重启进程

(dlv) b main.main
Process 1324 has exited with status 0

上面进行添加断点的操作,但是并没有返回想要的信息
这是因为我们之前已经将程序运行结束了,这时候不能直接添加断点信息
所以要先调用restart来将进程重启

(dlv) restart
Process restarted with PID 3018
(dlv) b main.main
Breakpoint 1 set at 0x10b123b for main.main() ./test.go:16

break(即b)添加断点
现在我们可以为其添加断点了

(dlv) b main.main //在main函数处添加断点
Breakpoint 1 set at 0x10b123b for main.main() ./test.go:16
(dlv) b main.counting //在counting函数处添加断点
Breakpoint 2 set at 0x10b118f for main.counting() ./test.go:8

我们也可以使用"文件:行号"的格式来添加断点,如:

b test.go:8 //等价于在函数counting处添加断点

然后继续程序运行,可见会先停在main函数处:

(dlv) c
> main.main() ./test.go:16 (hits goroutine(1):1 total:1) (PC: 0x10b123b)11:            c <- i12:        }13:        close(c)14:    }15:
=>  16:    func main() {17:        msg := "Starting main"18:        fmt.Println(msg)19:        bus := make(chan int)20:        msg = "starting a gofunc"21:        go counting(bus)

breakpoints(缩写bp)输出活动断点信息

(dlv) bp
Breakpoint runtime-fatal-throw at 0x102b3e0 for runtime.fatalthrow() /usr/local/Cellar/go/1.11.4/libexec/src/runtime/panic.go:654 (0)
Breakpoint unrecovered-panic at 0x102b450 for runtime.fatalpanic() /usr/local/Cellar/go/1.11.4/libexec/src/runtime/panic.go:681 (0)print runtime.curg._panic.arg
Breakpoint 1 at 0x10b123b for main.main() ./test.go:16 (1)
Breakpoint 2 at 0x10b118f for main.counting() ./test.go:8 (0)

可见上面的两个设置的断点

list 显示从停止行起前后5行的源代码

(dlv) list
> main.main() ./test.go:16 (hits goroutine(1):1 total:1) (PC: 0x10b123b)11:            c <- i12:        }13:        close(c)14:    }15:
=>  16:    func main() {17:        msg := "Starting main"18:        fmt.Println(msg)19:        bus := make(chan int)20:        msg = "starting a gofunc"21:        go counting(bus)

next (缩写n)转到下一个源行
可见现在的指针从16行转到了17行
如果想继续向下,可以直接回车,Delve会默认重复上一条命令
next不会进入当前函数

(dlv) n
> main.main() ./test.go:17 (PC: 0x10b1252)12:        }13:        close(c)14:    }15:    16:    func main() {
=>  17:        msg := "Starting main"18:        fmt.Println(msg)19:        bus := make(chan int)20:        msg = "starting a gofunc"21:        go counting(bus)22:        for count := range bus{

print(缩写p)计算一个表达式

(dlv)
> main.main() ./test_del.go:18 (PC: 0x4adf57)13:         close(c)14: }15:16: func main() {17:         msg := "Starting main"
=>  18:         fmt.Println(msg)19:         bus := make(chan int)20:         msg = "starting a gofunc"21:         go counting(bus)22:         for count := range bus{23:                 fmt.Println("count : ", count)
(dlv) print msg
"Starting main"

locals 打印局部变量

(dlv) locals
msg = "Starting main"

step(缩写s)单步执行程序

(dlv) p count
Command failed: could not find symbol value for count
(dlv) s
> main.main() ./test.go:18 (PC: 0x10b1267)13:        close(c)14:    }15:    16:    func main() {17:        msg := "Starting main"
=>  18:        fmt.Println(msg)19:        bus := make(chan int)20:        msg = "starting a gofunc"21:        go counting(bus)22:        for count := range bus{23:            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

whatis打印表达式类型

(dlv) whatis msg
string

能够得到msg变量为string类型

然后我们接着运行到下一个断点,这时候开启了一个groutine

(dlv) c
Starting main
> main.counting() ./test.go:8 (hits goroutine(18):1 total:1) (PC: 0x10b118f)3:    import (4:        "fmt"5:        "time"6:    )7:
=>   8:    func counting(c chan<- int){9:        for i := 0; i < 10; i++{10:            time.Sleep(2 * time.Second)11:            c <- i12:        }13:        close(c)

goroutine 显示或更改当前goroutine

(dlv) help goroutinegoroutinegoroutine <id>goroutine <id> <command>调用时不带参数,它将显示关于当前goroutine的信息。
使用单个参数调用时,它将切换到指定的goroutine。
使用更多参数调用时,它将在指定的goroutine上执行命令。

可见现在正在运行的goroutine

(dlv) goroutine
Thread 77795 at ./test.go:8
Goroutine 18:Runtime: ./test.go:8 main.counting (0x10b118f)User: ./test.go:8 main.counting (0x10b118f)Go: ./test.go:21 main.main (0x10b138a)Start: ./test.go:8 main.counting (0x10b1180)

指定查看的是goroutine 1 :

(dlv) goroutine 1
Switched from 18 to 1 (thread 77795)
(dlv) goroutine
Thread 77795 at ./test.go:8
Goroutine 1:Runtime: /usr/local/Cellar/go/1.11.4/libexec/src/runtime/proc.go:303 runtime.gopark (0x102d0f4)User: ./test.go:22 main.main (0x10b13ae)Go: /usr/local/Cellar/go/1.11.4/libexec/src/runtime/asm_amd64.s:201 runtime.rt0_go (0x10557fb)Start: /usr/local/Cellar/go/1.11.4/libexec/src/runtime/proc.go:110 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 18
Switched from 1 to 18 (thread 77795)

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) goroutinesGoroutine 1 - User: ./test.go:22 main.main (0x10b13ae)Goroutine 2 - User: /usr/local/Cellar/go/1.11.4/libexec/src/runtime/proc.go:303 runtime.gopark (0x102d0f4)Goroutine 3 - User: /usr/local/Cellar/go/1.11.4/libexec/src/runtime/proc.go:303 runtime.gopark (0x102d0f4)Goroutine 17 - User: /usr/local/Cellar/go/1.11.4/libexec/src/runtime/proc.go:303 runtime.gopark (0x102d0f4)
* Goroutine 18 - User: ./test.go:8 main.counting (0x10b118f) (thread 77795)
[5 goroutines]
(dlv) goroutines -sGoroutine 1 - Start: /usr/local/Cellar/go/1.11.4/libexec/src/runtime/proc.go:110 runtime.main (0x102cb60)Goroutine 2 - Start: /usr/local/Cellar/go/1.11.4/libexec/src/runtime/proc.go:243 runtime.forcegchelper (0x102ced0)Goroutine 3 - Start: /usr/local/Cellar/go/1.11.4/libexec/src/runtime/mgcsweep.go:46 runtime.bgsweep (0x1020170)Goroutine 17 - Start: /usr/local/Cellar/go/1.11.4/libexec/src/runtime/mfinal.go:161 runtime.runfinq (0x1017ae0)
* Goroutine 18 - Start: ./test.go:8 main.counting (0x10b1180) (thread 77795)
[5 goroutines]

args 打印函数参数

得到counting函数的参数值:

(dlv) args
c = chan<- int 0/0

因为之前只设置了两个断点,因此再次调用c则又将运行程序完毕:

(dlv) c
count :  0
count :  1
...

stepout 跳出当前函数

(dlv) restart
Process restarted with PID 1352
(dlv) b main.main //之前的断点一直存在,即使程序结束,因此此时的counting函数中也有断点
Command failed: Breakpoint exists at /Users/user/go-learning/test.go:16 at 10b123b
(dlv) c
> main.main() ./test.go:16 (hits goroutine(1):1 total:1) (PC: 0x10b123b)11:         c <- i12:     }13:     close(c)14: }15:
=>  16: func main() {17:     msg := "Starting main"18:     fmt.Println(msg)19:     bus := make(chan int)20:     msg = "starting a gofunc"21:     go counting(bus)
(dlv) stepout
Starting main
> main.counting() ./test.go:8 (hits goroutine(5):1 total:1) (PC: 0x10b118f)breakpoint hit during stepout, continuing...
count :  0
count :  1
count :  2
...

因为stepout跳过的是main()函数,可见直接也跳过了counting的断点,然后执行程序到结束

clear删除断点\clearall删除多个断点

因为如果不手动清除断点,即使程序结束并restart程序,断点信息也仍然在

(dlv) breakpoints //查看当前的断点信息
Breakpoint runtime-fatal-throw at 0x102b3e0 for runtime.fatalthrow() /usr/local/Cellar/go/1.11.4/libexec/src/runtime/panic.go:654 (0)
Breakpoint unrecovered-panic at 0x102b450 for runtime.fatalpanic() /usr/local/Cellar/go/1.11.4/libexec/src/runtime/panic.go:681 (0)print runtime.curg._panic.arg
Breakpoint 1 at 0x10b123b for main.main() ./test.go:16 (1)
Breakpoint 2 at 0x10b118f for main.counting() ./test.go:8 (0)
(dlv) clear 1 //先删除第一个断点,即main函数上的断点
Breakpoint 1 cleared at 0x10b123b for main.main() ./test.go:16
(dlv) breakpoints //查看可见果然成功删除
Breakpoint runtime-fatal-throw at 0x102b3e0 for runtime.fatalthrow() /usr/local/Cellar/go/1.11.4/libexec/src/runtime/panic.go:654 (0)
Breakpoint unrecovered-panic at 0x102b450 for runtime.fatalpanic() /usr/local/Cellar/go/1.11.4/libexec/src/runtime/panic.go:681 (0)print runtime.curg._panic.arg
Breakpoint 2 at 0x10b118f for main.counting() ./test.go:8 (0)
(dlv) clearall //然后将所有断点都删除
Breakpoint 2 cleared at 0x10b118f for main.counting() ./test.go:8
(dlv) breakpoints //查看可见断点信息为空
Breakpoint runtime-fatal-throw at 0x102b3e0 for runtime.fatalthrow() /usr/local/Cellar/go/1.11.4/libexec/src/runtime/panic.go:654 (0)
Breakpoint unrecovered-panic at 0x102b450 for runtime.fatalpanic() /usr/local/Cellar/go/1.11.4/libexec/src/runtime/panic.go:681 (0)print runtime.curg._panic.arg
(dlv)

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:21 //添加断点
Breakpoint 2 set at 0x10b1368 for main.main() ./test.go:21
(dlv) on 2 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.4/libexec/src/runtime/panic.go:654 (0)
Breakpoint unrecovered-panic at 0x102b450 for runtime.fatalpanic() /usr/local/Cellar/go/1.11.4/libexec/src/runtime/panic.go:681 (0)print runtime.curg._panic.arg
Breakpoint 2 at 0x10b1368 for main.main() ./test.go:21 (0)print msg == "starting a gofunc"
(dlv) c
Starting main
> main.main() ./test.go:21 (hits goroutine(1):1 total:1) (PC: 0x10b1368)msg == "starting a gofunc": true //可见打印出来了,并返回值为true16: func main() {17:     msg := "Starting main"18:     fmt.Println(msg)19:     bus := make(chan int)20:     msg = "starting a gofunc"
=>  21:     go counting(bus)22:     for count := range bus{23:         fmt.Println("count : ", count)24:     }25: }

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)

up向上移动当前帧/dowm向下移动当前帧

(dlv) up
> main.main() ./test.go:21 (hits goroutine(1):1 total:1) (PC: 0x10b1368)
Frame 1: /usr/local/Cellar/go/1.11.4/libexec/src/runtime/proc.go:201 (PC: 102cd25)196:            // A program compiled with -buildmode=c-archive or c-shared197:            // has a main, but it is not executed.198:            return199:        }200:        fn = main_main // make an indirect call, as the linker doesn't know the address of the main package when laying down the runtime
=> 201:        fn()202:        if raceenabled {203:            racefini()204:        }205:    206:        // Make racy client program work: if panicking on
(dlv) down
> main.main() ./test.go:21 (hits goroutine(1):1 total:1) (PC: 0x10b1368)
Frame 0: ./test.go:21 (PC: 10b1368)16:    func main() {17:        msg := "Starting main"18:        fmt.Println(msg)19:        bus := make(chan int)20:        msg = "starting a gofunc"
=>  21:        go counting(bus)22:        for count := range bus{23:            fmt.Println("count : ", count)24:        }25:    }

exit (alias: quit | q)退出调试器

(dlv) exit
userdeMBP:go-learning user$

dlv attach

如果项目正在运行中,想要对正在运行的项目进行调试

  1. 首先使用"ps aux | grep 编译文件名"来查看运行程序的进程号pid
  2. 然后就能够使用"dlv attach pid"来连接该运行程序,然后就能使用之前debug中的命令来进行调试了

Go调试工具Delve相关推荐

  1. golang调试工具Delve

    转自:http://www.cnblogs.com/li-peng/p/8522592.html Devle是一个非常棒的golang 调试工具,支持多种调试方式,直接运行调试,或者attach到一个 ...

  2. delve应该安装到哪_golang调试工具Delve

    转自:http://www.cnblogs.com/li-peng/p/8522592.html Devle是一个非常棒的golang 调试工具,支持多种调试方式,直接运行调试,或者attach到一个 ...

  3. Golang调试工具Delve使用简介

    Delve 是一款很不错的 Golang 调试工具,可以实现类似 Visual Studio 的断点调试功能,也可以用来在程序 Crash 的时候生成 Coredump 文件,此外 Delve 也适合 ...

  4. LiteIDE调试工具delve

    1.使用gdb调试报错"/usr/local/bin/gdb was found on system PATH(hint: is GDB installed?)"以及"L ...

  5. delve应该安装到哪_Go调试工具—— Delve

    参考https://github.com/go-delve/delve 安装 首先你必须有等于或高于1.8版本的Go,我的版本是: userdeMBP:go-learning user$ go ver ...

  6. delve应该安装到哪_Golang /Go调试工具delve

    安装 这里提供了支持的所有平台(操作系统)上安装Delve的说明. 请注意您必须安装Go 1.5或更高版本. 此外如果使用Go 1.5,您必须设置GO15VENDOREXPERIMENT = 1,然后 ...

  7. delve应该安装到哪_Golang调试工具Delve安装及使用

    一.安装 照着 github 上 delve 项目的安装说明操作, go mod 模式下推荐使用第二种方式. 1.拉取最新 delve 项目代码到本地,编译安装. # cd $GOPATH/src/ ...

  8. golang 开发笔记

    1.0Go环境配置 1.1 Go安装 1.2 目录结构 1.3 Go命令 1.4 开发工具 1.5 版本控制(dep) 1.6 Go 各版本特性 1.7 调试工具Delve 1.8 Go 性能测试工具 ...

  9. Golang相关面试题

    Golang (63条消息) go/golang面试中的高频八股文问题_光哥2020的博客-CSDN博客_golang八股文 1.go的profile工具? profile就是定时采样,收集cpu,内 ...

最新文章

  1. Several frontend roundtrip diagram - synchronous and Asynchronous
  2. 微信企业号开发:启用回调模式
  3. flask 知识点总结
  4. stack heap java_java中的Heap 和 Stack | 学步园
  5. 概率论与数理统计 (二)计算题和应用题
  6. 解决时间控件input不能选择的问题
  7. hadoop集群的搭建与配置(2)
  8. 2018年TI杯大学生电子设计竞赛题C-无线充电电动小车 题目加答案,非常详细
  9. python selenium Frefox使 用代理访问网页
  10. dnf手游登录不显示服务器,不要着急 DNF手游提示登录失败error7正确解决方法
  11. Win11未识别的网络无internet怎么办?
  12. java两个字符串相等却总返回false
  13. 透过“最牛”年报,聊聊优然牧业的“最佳养牛模式”
  14. 写作者网新浪微博通过认证加V
  15. php mail 垃圾邮件,如何避免我的邮件从PHP邮件()被标记为垃圾邮件? - 程序园
  16. 如何查看PHP得版本?
  17. 百度开发者中心聚力AI开放能力,打造新时代下开发者的“淘金矿”
  18. 读书笔记——《我们时代的神经症人格》
  19. FlexSlider 轮播图插件 的详细设置参数
  20. 干货来袭!java编程工具排行榜

热门文章

  1. 熟女姿态:完美女人必备的十个资本!
  2. 【ESD专题】3.ESD防护器件(TVS管的原理和选型)
  3. mysql 1677_mysql之数据库主从复制配置报错1677
  4. 设计模式:里氏替换原则(详解)
  5. 测试工程师也应该具备产品化思维吗?
  6. 【狂飙后遗症】你磕的CP是那一对,他们的后代长什么样?
  7. 新用户报到,以及门电路相关手游推荐
  8. 关于“小于/等于/大于”的英文缩写
  9. 嵌入式Linux(十三)RTC实时时钟
  10. (已解决)微信小程序调查问卷所有题放在一个页面上