设置和配置
获取和创建项目
基本快照
分支与合并
共享和更新项目
检查和比较
打补丁
调试
电子邮件
外部系统
服务器管理
指南
管理
底层命令
- 2.47.1 → 2.50.1 无更改
-
2.47.0
2024-10-06
- 2.44.1 → 2.46.4 无更改
-
2.44.0
2024-02-23
- 2.39.1 → 2.43.7 无更改
-
2.39.0
2022-12-12
- 2.38.1 → 2.38.5 无更改
-
2.38.0
2022-10-02
- 2.37.1 → 2.37.7 无更改
-
2.37.0
2022-06-27
- 2.34.1 → 2.36.6 无更改
-
2.34.0
2021-11-15
- 2.33.1 → 2.33.8 无更改
-
2.33.0
2021-08-16
- 2.32.1 → 2.32.7 无变更
-
2.32.0
2021-06-06
- 2.30.1 → 2.31.8 无更改
-
2.30.0
2020-12-27
- 2.27.1 → 2.29.3 无更改
-
2.27.0
2020-06-01
- 2.25.1 → 2.26.3 无更改
- 2.25.0 无更改
- 2.24.1 → 2.24.4 无更改
-
2.24.0
2019-11-04
- 2.23.1 → 2.23.4 无更改
-
2.23.0
2019-08-16
- 2.22.2 → 2.22.5 无更改
-
2.22.1
2019-08-11
-
2.22.0
2019-06-07
Trace2 API可用于将调试、性能和遥测信息打印到标准错误或文件。除非通过启用一个或多个Trace2目标明确启用Trace2功能,否则该功能处于非活动状态。
Trace2 API旨在取代现有(Trace1)由GIT_TRACE
和GIT_TRACE_PERFORMANCE
设施提供的printf
()式追踪。在初始实现期间,Trace2和Trace1可以并行运行。
Trace2 API定义了一组带有已知字段的高级消息,例如(start
: argv
)和(exit
: {exit-code
, elapsed-time
})。
Git代码库中的Trace2检测将Trace2消息发送到已启用的Trace2目标。目标将这些消息内容转换为特定用途的格式,并将事件写入其数据流。通过这种方式,Trace2 API可以驱动多种不同类型的分析。
目标是使用VTable定义的,允许未来轻松扩展到其他格式。例如,这可以用于定义二进制格式。
Trace2通过系统和全局配置文件中的trace2.*
配置值以及GIT_TRACE2*
环境变量进行控制。Trace2不读取仓库本地或工作区配置文件,也不遵守-c
命令行配置设置。
Trace2 目标
Trace2定义了以下Trace2目标集。格式详情将在后续章节中给出。
普通格式目标
普通格式目标是传统的printf
()格式,类似于GIT_TRACE
格式。此格式通过GIT_TRACE2
环境变量或trace2.normalTarget
系统或全局配置设置启用。
例如
$ export GIT_TRACE2=~/log.normal $ git version git version 2.20.1.155.g426c96fcdb
或
$ git config --global trace2.normalTarget ~/log.normal $ git version git version 2.20.1.155.g426c96fcdb
产生
$ cat ~/log.normal 12:28:42.620009 common-main.c:38 version 2.20.1.155.g426c96fcdb 12:28:42.620989 common-main.c:39 start git version 12:28:42.621101 git.c:432 cmd_name version (version) 12:28:42.621215 git.c:662 exit elapsed:0.001227 code:0 12:28:42.621250 trace2/tr2_tgt_normal.c:124 atexit elapsed:0.001265 code:0
性能格式目标
性能格式目标(PERF)是一种基于列的格式,用于取代GIT_TRACE_PERFORMANCE
,适用于开发和测试,可能作为gprof
等工具的补充。此格式通过GIT_TRACE2_PERF
环境变量或trace2.perfTarget
系统或全局配置设置启用。
例如
$ export GIT_TRACE2_PERF=~/log.perf $ git version git version 2.20.1.155.g426c96fcdb
或
$ git config --global trace2.perfTarget ~/log.perf $ git version git version 2.20.1.155.g426c96fcdb
产生
$ cat ~/log.perf 12:28:42.620675 common-main.c:38 | d0 | main | version | | | | | 2.20.1.155.g426c96fcdb 12:28:42.621001 common-main.c:39 | d0 | main | start | | 0.001173 | | | git version 12:28:42.621111 git.c:432 | d0 | main | cmd_name | | | | | version (version) 12:28:42.621225 git.c:662 | d0 | main | exit | | 0.001227 | | | code:0 12:28:42.621259 trace2/tr2_tgt_perf.c:211 | d0 | main | atexit | | 0.001265 | | | code:0
事件格式目标
事件格式目标是一种基于JSON的事件数据格式,适用于遥测分析。此格式通过GIT_TRACE2_EVENT
环境变量或trace2.eventTarget
系统或全局配置设置启用。
例如
$ export GIT_TRACE2_EVENT=~/log.event $ git version git version 2.20.1.155.g426c96fcdb
或
$ git config --global trace2.eventTarget ~/log.event $ git version git version 2.20.1.155.g426c96fcdb
产生
$ cat ~/log.event {"event":"version","sid":"20190408T191610.507018Z-H9b68c35f-P000059a8","thread":"main","time":"2019-01-16T17:28:42.620713Z","file":"common-main.c","line":38,"evt":"4","exe":"2.20.1.155.g426c96fcdb"} {"event":"start","sid":"20190408T191610.507018Z-H9b68c35f-P000059a8","thread":"main","time":"2019-01-16T17:28:42.621027Z","file":"common-main.c","line":39,"t_abs":0.001173,"argv":["git","version"]} {"event":"cmd_name","sid":"20190408T191610.507018Z-H9b68c35f-P000059a8","thread":"main","time":"2019-01-16T17:28:42.621122Z","file":"git.c","line":432,"name":"version","hierarchy":"version"} {"event":"exit","sid":"20190408T191610.507018Z-H9b68c35f-P000059a8","thread":"main","time":"2019-01-16T17:28:42.621236Z","file":"git.c","line":662,"t_abs":0.001227,"code":0} {"event":"atexit","sid":"20190408T191610.507018Z-H9b68c35f-P000059a8","thread":"main","time":"2019-01-16T17:28:42.621268Z","file":"trace2/tr2_tgt_event.c","line":163,"t_abs":0.001265,"code":0}
Trace2 API
Trace2公共API在trace2.h
中定义并记录;有关更多信息,请参阅该文件。所有公共函数和宏都以trace2_
为前缀,并在trace2.c
中实现。
没有公共的Trace2数据结构。
Trace2代码还在trace2/
目录中定义了一组私有函数和数据类型。这些符号以tr2_
为前缀,并且只能由trace2.c
中的函数(或trace2/
中的其他私有源文件)使用。
Trace2 目标格式
NORMAL 格式
事件以以下形式的行写入:
[<time> SP <filename>:<line> SP+] <event-name> [[SP] <event-message>] LF
如果GIT_TRACE2_BRIEF
或trace2.normalBrief
为真,则time
、filename
和line
字段将被省略。
此目标旨在提供更多摘要(如GIT_TRACE),并且比其他目标细节更少。例如,它忽略线程、区域和数据消息。
PERF 格式
事件以以下形式的行写入:
[<time> SP <filename>:<line> SP+ BAR SP] d<depth> SP BAR SP <thread-name> SP+ BAR SP <event-name> SP+ BAR SP [r<repo-id>] SP+ BAR SP [<t_abs>] SP+ BAR SP [<t_rel>] SP+ BAR SP [<category>] SP+ BAR SP DOTS* <perf-event-message> LF
- <深度>
-
是git进程的深度。这是父git进程的数量。顶层git命令的深度值为“d0”。其子进程的深度值为“d1”。二级子进程的深度值为“d2”,依此类推。
- <线程名>
-
是线程的唯一名称。主线程称为“main”。其他线程名称的形式为“th%d:%s”,包括一个唯一的数字和线程过程的名称。
- <事件名>
-
是事件名。
- <仓库ID>
-
如果存在,是一个表示正在使用的仓库的数字。当打开仓库时,会发出
def_repo
事件。这定义了仓库ID和关联的工作区。随后的仓库特定事件将引用此仓库ID。目前,主仓库始终是“r1”。此字段是为未来进程内子模块预期的。
- <绝对时间>
-
如果存在,是程序启动以来的绝对时间(秒)。
- <相对时间>
-
如果存在,是相对于当前区域开始的时间(秒)。对于线程退出事件,它是线程的已用时间。
- <类别>
-
存在于区域和数据事件上,用于指示一个广泛的类别,例如“index”或“status”。
- <性能事件消息>
-
是自由形式的
printf
()消息,旨在供人类阅读。
15:33:33.532712 wt-status.c:2310 | d0 | main | region_enter | r1 | 0.126064 | | status | label:print 15:33:33.532712 wt-status.c:2331 | d0 | main | region_leave | r1 | 0.127568 | 0.001504 | status | label:print
如果GIT_TRACE2_PERF_BRIEF
或trace2.perfBrief
为真,则time
、file
和line
字段将被省略。
d0 | main | region_leave | r1 | 0.011717 | 0.009122 | index | label:preload
PERF目标用于开发期间的交互式性能分析,并且相当“嘈杂”。
EVENT 格式
每个事件都是一个JSON对象,包含多个键/值对,以单行写入,后跟一个换行符(LF)。
'{' <key> ':' <value> [',' <key> ':' <value>]* '}' LF
一些键/值对是所有事件通用的,有些是事件特定的。
通用键/值对
以下键/值对是所有事件通用的
{ "event":"version", "sid":"20190408T191827.272759Z-H9b68c35f-P00003510", "thread":"main", "time":"2019-04-08T19:18:27.282761Z", "file":"common-main.c", "line":42, ... }
如果GIT_TRACE2_EVENT_BRIEF
或trace2.eventBrief
为真,则所有事件中都省略file
和line
字段,并且time
字段仅存在于“start”和“atexit”事件上。
事件特定键/值对
"version"
-
此事件提供可执行文件的版本和EVENT格式。它应该始终是追踪会话中的第一个事件。如果添加了新的事件类型,移除了现有字段,或现有事件或字段的解释发生重大变化,则EVENT格式版本将增加。较小的更改,例如向现有事件添加新字段,不需要增加EVENT格式版本。
{ "event":"version", ... "evt":"4", # EVENT format version "exe":"2.20.1.155.g426c96fcdb" # git version }
"too_many_files"
-
如果目标追踪目录中的文件过多(请参阅trace2.maxFiles配置选项),此事件将写入到git-trace2-discard哨兵文件。
{ "event":"too_many_files", ... }
"start"
-
此事件包含main()接收到的完整argv。
{ "event":"start", ... "t_abs":0.001227, # elapsed time in seconds "argv":["git","version"] }
"exit"
-
当git调用
exit
()时,会发出此事件。{ "event":"exit", ... "t_abs":0.001227, # elapsed time in seconds "code":0 # exit code }
"atexit"
-
此事件由Trace2的
atexit
例程在最终关闭期间发出。它应该是进程发出的最后一个事件。(此处报告的已用时间比“exit”事件中报告的时间要长,因为它在所有其他atexit任务完成后运行。)
{ "event":"atexit", ... "t_abs":0.001227, # elapsed time in seconds "code":0 # exit code }
"signal"
-
当程序被用户信号终止时,会发出此事件。根据平台的不同,信号事件可能会阻止“atexit”事件的生成。
{ "event":"signal", ... "t_abs":0.001227, # elapsed time in seconds "signo":13 # SIGTERM, SIGINT, etc. }
"error"
-
当调用
BUG
()、bug
()、error
()、die
()、warning
()或usage
()函数时,会发出此事件。{ "event":"error", ... "msg":"invalid option: --cahced", # formatted error message "fmt":"invalid option: %s" # error format string }
错误事件可能会多次发出。格式字符串允许后处理器根据类型对错误进行分组,而无需担心特定的错误参数。
"cmd_path"
-
此事件包含已发现的git可执行文件的完整路径(在配置为解析此路径的平台上)。
{ "event":"cmd_path", ... "path":"C:/work/gfw/git.exe" }
"cmd_ancestry"
-
此事件包含当前进程的父级(以及更早世代的父级)的文本命令名称,以数组形式按从最近的父级到最远的曾祖父的顺序排列。它可能未在所有平台上实现。
{ "event":"cmd_ancestry", ... "ancestry":["bash","tmux: server","systemd"] }
"cmd_name"
-
此事件包含此git进程的命令名称以及来自父git进程的命令层次结构。
{ "event":"cmd_name", ... "name":"pack-objects", "hierarchy":"push/pack-objects" }
通常,“name”字段包含命令的规范名称。当规范名称不可用时,使用以下特殊值之一:
"_query_" # "git --html-path" "_run_dashed_" # when "git foo" tries to run "git-foo" "_run_shell_alias_" # alias expansion to a shell command "_run_git_alias_" # alias expansion to a git command "_usage_" # usage error
"cmd_mode"
-
此事件(如果存在)描述命令变体。此事件可能会多次发出。
{ "event":"cmd_mode", ... "name":"branch" }
“name”字段是一个任意字符串,用于描述命令模式。例如,checkout可以检出一个分支或一个单独的文件。这些变体通常具有不同的性能特征,不可比较。
"alias"
-
当别名被展开时,此事件会存在。
{ "event":"alias", ... "alias":"l", # registered alias "argv":["log","--graph"] # alias expansion }
"child_start"
-
此事件描述即将派生的子进程。
{ "event":"child_start", ... "child_id":2, "child_class":"?", "use_shell":false, "argv":["git","rev-list","--objects","--stdin","--not","--all","--quiet"] "hook_name":"<hook_name>" # present when child_class is "hook" "cd":"<path>" # present when cd is required }
“child_id”字段可用于将此child_start与相应的child_exit事件匹配。
“child_class”字段是一个粗略的分类,例如“editor”、“pager”、“transport/*”和“hook”。未分类的子级归类为“?”。
"child_exit"
-
此事件在当前进程从
waitpid
()返回并收集到子进程的退出信息后生成。{ "event":"child_exit", ... "child_id":2, "pid":14708, # child PID "code":0, # child exit-code "t_rel":0.110605 # observed run-time of child process }
请注意,子进程的会话ID对于当前/生成进程不可用,因此此处报告子进程的PID作为后处理的提示。(但这只是一个提示,因为子进程可能是没有会话ID的shell脚本。)
请注意,
t_rel
字段包含子进程观察到的运行时间(秒)(从fork/exec/spawn之前开始,在waitpid
()之后停止,并包括操作系统进程创建开销)。因此,此时间将略大于子进程本身报告的atexit时间。 "child_ready"
-
此事件在当前进程启动后台进程并释放其所有句柄后生成。
{ "event":"child_ready", ... "child_id":2, "pid":14708, # child PID "ready":"ready", # child ready state "t_rel":0.110605 # observed run-time of child process }
请注意,子进程的会话ID对于当前/生成进程不可用,因此此处报告子进程的PID作为后处理的提示。(但这只是一个提示,因为子进程可能是没有会话ID的shell脚本。)
此事件在子进程在后台启动并获得少量时间启动和开始工作后生成。如果子进程正常启动而父进程仍在等待,“ready”字段将具有值“ready”。如果子进程启动太慢且父进程超时,该字段将具有值“timeout”。如果子进程启动但父进程无法探测它,该字段将具有值“error”。
在父进程发出此事件后,它将释放其所有对子进程的句柄,并将子进程视为后台守护进程。因此,即使子进程最终完成启动,父进程也不会发出更新的事件。
请注意,
t_rel
字段包含父进程将子进程释放到后台时的观察到的运行时间(秒)。子进程被假定为长时间运行的守护进程,并且可能比父进程存活更长时间。因此,父进程的子事件时间不应与子进程的atexit时间进行比较。 "exec"
-
此事件在git尝试
exec
()另一个命令而不是启动子进程之前生成。{ "event":"exec", ... "exec_id":0, "exe":"git", "argv":["foo", "bar"] }
“exec_id”字段是命令唯一的ID,仅在
exec
()失败并生成相应的exec_result事件时有用。 "exec_result"
-
如果
exec
()失败且控制返回到当前git命令,则会生成此事件。{ "event":"exec_result", ... "exec_id":0, "code":1 # error code (errno) from exec() }
"thread_start"
-
此事件在线程启动时生成。它从新线程的线程过程中内部生成(因为它需要访问线程的线程本地存储中的数据)。
{ "event":"thread_start", ... "thread":"th02:preload_thread" # thread name }
"thread_exit"
-
此事件在线程退出时生成。它从线程的线程过程中内部生成。
{ "event":"thread_exit", ... "thread":"th02:preload_thread", # thread name "t_rel":0.007328 # thread elapsed time }
"def_param"
-
生成此事件以记录全局参数,例如配置设置、命令行标志或环境变量。
{ "event":"def_param", ... "scope":"global", "param":"core.abbrev", "value":"7" }
"def_repo"
-
此事件定义了一个仓库ID并将其与工作树的根目录关联。
{ "event":"def_repo", ... "repo":1, "worktree":"/Users/jeffhost/work/gfw" }
如前所述,仓库ID目前始终为1,因此只会有一个def_repo事件。之后,如果支持进程内子模块,则每个访问的子模块都应发出一个def_repo事件。
"region_enter"
-
此事件在进入区域时生成。
{ "event":"region_enter", ... "repo":1, # optional "nesting":1, # current region stack depth "category":"index", # optional "label":"do_read_index", # optional "msg":".git/index" # optional }
category
字段可在未来增强中用于基于类别的过滤。GIT_TRACE2_EVENT_NESTING
或trace2.eventNesting
可用于过滤深度嵌套的区域和数据事件。它默认为“2”。 "region_leave"
-
此事件在离开区域时生成。
{ "event":"region_leave", ... "repo":1, # optional "t_rel":0.002876, # time spent in region in seconds "nesting":1, # region stack depth "category":"index", # optional "label":"do_read_index", # optional "msg":".git/index" # optional }
"data"
-
此事件用于记录线程和区域本地的键/值对。
{ "event":"data", ... "repo":1, # optional "t_abs":0.024107, # absolute elapsed time "t_rel":0.001031, # elapsed time in region/thread "nesting":2, # region stack depth "category":"index", "key":"read/cache_nr", "value":"3552" }
“value”字段可以是整数或字符串。
"data-json"
-
此事件用于记录包含结构化数据的预格式化JSON字符串。
{ "event":"data_json", ... "repo":1, # optional "t_abs":0.015905, "t_rel":0.015905, "nesting":1, "category":"process", "key":"windows/ancestry", "value":["bash.exe","bash.exe"] }
"th_timer"
-
此事件记录秒表计时器在线程中运行的时间量。当线程退出时,会为请求每线程事件的计时器生成此事件。
{ "event":"th_timer", ... "category":"my_category", "name":"my_timer", "intervals":5, # number of time it was started/stopped "t_total":0.052741, # total time in seconds it was running "t_min":0.010061, # shortest interval "t_max":0.011648 # longest interval }
"timer"
-
此事件记录秒表计时器在所有线程中聚合运行的时间量。此事件在进程退出时生成。
{ "event":"timer", ... "category":"my_category", "name":"my_timer", "intervals":5, # number of time it was started/stopped "t_total":0.052741, # total time in seconds it was running "t_min":0.010061, # shortest interval "t_max":0.011648 # longest interval }
"th_counter"
-
此事件记录线程中计数器变量的值。当线程退出时,会为请求每线程事件的计数器生成此事件。
{ "event":"th_counter", ... "category":"my_category", "name":"my_counter", "count":23 }
"counter"
-
此事件记录所有线程中计数器变量的值。此事件在进程退出时生成。此处报告的总值是所有线程的总和。
{ "event":"counter", ... "category":"my_category", "name":"my_counter", "count":23 }
"printf"
-
此事件记录人类可读的消息,没有特定的格式指南。
{ "event":"printf", ... "t_abs":0.015905, # elapsed time in seconds "msg":"Hello world" # optional }
Trace2 API 使用示例
以下是一个假想的Trace2 API使用示例,展示了预期的用法(不涉及实际的Git细节)。
- 初始化
-
初始化发生在
main
()中。在幕后,会注册一个atexit
和signal
处理程序。int main(int argc, const char **argv) { int exit_code; trace2_initialize(); trace2_cmd_start(argv); exit_code = cmd_main(argc, argv); trace2_cmd_exit(exit_code); return exit_code; }
- 命令详情
-
在基本信息建立后,可以将被发现的额外命令信息发送到Trace2。
int cmd_checkout(int argc, const char **argv) { trace2_cmd_name("checkout"); trace2_cmd_mode("branch"); trace2_def_repo(the_repository); // emit "def_param" messages for "interesting" config settings. trace2_cmd_list_config(); if (do_something()) trace2_cmd_error("Path '%s': cannot do something", path); return 0; }
- 子进程
-
包装生成子进程的代码。
void run_child(...) { int child_exit_code; struct child_process cmd = CHILD_PROCESS_INIT; ... cmd.trace2_child_class = "editor"; trace2_child_start(&cmd); child_exit_code = spawn_child_and_wait_for_it(); trace2_child_exit(&cmd, child_exit_code); }
例如,以下fetch命令生成了ssh、index-pack、rev-list和gc。此示例还显示fetch花费了5.199秒,其中4.932秒在ssh中。
$ export GIT_TRACE2_BRIEF=1 $ export GIT_TRACE2=~/log.normal $ git fetch origin ...
$ cat ~/log.normal version 2.20.1.vfs.1.1.47.g534dbe1ad1 start git fetch origin worktree /Users/jeffhost/work/gfw cmd_name fetch (fetch) child_start[0] ssh git@github.com ... child_start[1] git index-pack ... ... (Trace2 events from child processes omitted) child_exit[1] pid:14707 code:0 elapsed:0.076353 child_exit[0] pid:14706 code:0 elapsed:4.931869 child_start[2] git rev-list ... ... (Trace2 events from child process omitted) child_exit[2] pid:14708 code:0 elapsed:0.110605 child_start[3] git gc --auto ... (Trace2 events from child process omitted) child_exit[3] pid:14709 code:0 elapsed:0.006240 exit elapsed:5.198503 code:0 atexit elapsed:5.198541 code:0
当一个git进程是另一个git进程的(直接或间接)子进程时,它会继承Trace2上下文信息。这允许子进程打印命令层次结构。此示例显示gc是fetch的子进程[3]。当gc进程将其名称报告为“gc”时,它还会将层次结构报告为“fetch/gc”。(在此示例中,为了清晰起见,子进程的trace2消息已缩进。)
$ export GIT_TRACE2_BRIEF=1 $ export GIT_TRACE2=~/log.normal $ git fetch origin ...
$ cat ~/log.normal version 2.20.1.160.g5676107ecd.dirty start git fetch official worktree /Users/jeffhost/work/gfw cmd_name fetch (fetch) ... child_start[3] git gc --auto version 2.20.1.160.g5676107ecd.dirty start /Users/jeffhost/work/gfw/git gc --auto worktree /Users/jeffhost/work/gfw cmd_name gc (fetch/gc) exit elapsed:0.001959 code:0 atexit elapsed:0.001997 code:0 child_exit[3] pid:20303 code:0 elapsed:0.007564 exit elapsed:3.868938 code:0 atexit elapsed:3.868970 code:0
- 区域
-
区域可用于计时代码的有趣部分。
void wt_status_collect(struct wt_status *s) { trace2_region_enter("status", "worktrees", s->repo); wt_status_collect_changes_worktree(s); trace2_region_leave("status", "worktrees", s->repo); trace2_region_enter("status", "index", s->repo); wt_status_collect_changes_index(s); trace2_region_leave("status", "index", s->repo); trace2_region_enter("status", "untracked", s->repo); wt_status_collect_untracked(s); trace2_region_leave("status", "untracked", s->repo); } void wt_status_print(struct wt_status *s) { trace2_region_enter("status", "print", s->repo); switch (s->status_format) { ... } trace2_region_leave("status", "print", s->repo); }
在此示例中,扫描未跟踪文件从+0.012568运行到+0.027149(自进程启动以来),耗时0.014581秒。
$ export GIT_TRACE2_PERF_BRIEF=1 $ export GIT_TRACE2_PERF=~/log.perf $ git status ... $ cat ~/log.perf d0 | main | version | | | | | 2.20.1.160.g5676107ecd.dirty d0 | main | start | | 0.001173 | | | git status d0 | main | def_repo | r1 | | | | worktree:/Users/jeffhost/work/gfw d0 | main | cmd_name | | | | | status (status) ... d0 | main | region_enter | r1 | 0.010988 | | status | label:worktrees d0 | main | region_leave | r1 | 0.011236 | 0.000248 | status | label:worktrees d0 | main | region_enter | r1 | 0.011260 | | status | label:index d0 | main | region_leave | r1 | 0.012542 | 0.001282 | status | label:index d0 | main | region_enter | r1 | 0.012568 | | status | label:untracked d0 | main | region_leave | r1 | 0.027149 | 0.014581 | status | label:untracked d0 | main | region_enter | r1 | 0.027411 | | status | label:print d0 | main | region_leave | r1 | 0.028741 | 0.001330 | status | label:print d0 | main | exit | | 0.028778 | | | code:0 d0 | main | atexit | | 0.028809 | | | code:0
区域可以嵌套。这会导致消息在PERF目标中缩进,例如。已用时间相对于相应嵌套级别的开始时间,正如预期。例如,如果我们向以下内容添加区域消息:
static enum path_treatment read_directory_recursive(struct dir_struct *dir, struct index_state *istate, const char *base, int baselen, struct untracked_cache_dir *untracked, int check_only, int stop_at_first_file, const struct pathspec *pathspec) { enum path_treatment state, subdir_state, dir_state = path_none; trace2_region_enter_printf("dir", "read_recursive", NULL, "%.*s", baselen, base); ... trace2_region_leave_printf("dir", "read_recursive", NULL, "%.*s", baselen, base); return dir_state; }
我们可以进一步调查扫描未跟踪文件所花费的时间。
$ export GIT_TRACE2_PERF_BRIEF=1 $ export GIT_TRACE2_PERF=~/log.perf $ git status ... $ cat ~/log.perf d0 | main | version | | | | | 2.20.1.162.gb4ccea44db.dirty d0 | main | start | | 0.001173 | | | git status d0 | main | def_repo | r1 | | | | worktree:/Users/jeffhost/work/gfw d0 | main | cmd_name | | | | | status (status) ... d0 | main | region_enter | r1 | 0.015047 | | status | label:untracked d0 | main | region_enter | | 0.015132 | | dir | ..label:read_recursive d0 | main | region_enter | | 0.016341 | | dir | ....label:read_recursive vcs-svn/ d0 | main | region_leave | | 0.016422 | 0.000081 | dir | ....label:read_recursive vcs-svn/ d0 | main | region_enter | | 0.016446 | | dir | ....label:read_recursive xdiff/ d0 | main | region_leave | | 0.016522 | 0.000076 | dir | ....label:read_recursive xdiff/ d0 | main | region_enter | | 0.016612 | | dir | ....label:read_recursive git-gui/ d0 | main | region_enter | | 0.016698 | | dir | ......label:read_recursive git-gui/po/ d0 | main | region_enter | | 0.016810 | | dir | ........label:read_recursive git-gui/po/glossary/ d0 | main | region_leave | | 0.016863 | 0.000053 | dir | ........label:read_recursive git-gui/po/glossary/ ... d0 | main | region_enter | | 0.031876 | | dir | ....label:read_recursive builtin/ d0 | main | region_leave | | 0.032270 | 0.000394 | dir | ....label:read_recursive builtin/ d0 | main | region_leave | | 0.032414 | 0.017282 | dir | ..label:read_recursive d0 | main | region_leave | r1 | 0.032454 | 0.017407 | status | label:untracked ... d0 | main | exit | | 0.034279 | | | code:0 d0 | main | atexit | | 0.034322 | | | code:0
Trace2区域类似于现有的trace_performance_enter()和trace_performance_leave()例程,但它们是线程安全的并维护每线程的计时器堆栈。
- 数据消息
-
添加到区域的数据消息。
int read_index_from(struct index_state *istate, const char *path, const char *gitdir) { trace2_region_enter_printf("index", "do_read_index", the_repository, "%s", path); ... trace2_data_intmax("index", the_repository, "read/version", istate->version); trace2_data_intmax("index", the_repository, "read/cache_nr", istate->cache_nr); trace2_region_leave_printf("index", "do_read_index", the_repository, "%s", path); }
此示例显示索引包含3552个条目。
$ export GIT_TRACE2_PERF_BRIEF=1 $ export GIT_TRACE2_PERF=~/log.perf $ git status ... $ cat ~/log.perf d0 | main | version | | | | | 2.20.1.156.gf9916ae094.dirty d0 | main | start | | 0.001173 | | | git status d0 | main | def_repo | r1 | | | | worktree:/Users/jeffhost/work/gfw d0 | main | cmd_name | | | | | status (status) d0 | main | region_enter | r1 | 0.001791 | | index | label:do_read_index .git/index d0 | main | data | r1 | 0.002494 | 0.000703 | index | ..read/version:2 d0 | main | data | r1 | 0.002520 | 0.000729 | index | ..read/cache_nr:3552 d0 | main | region_leave | r1 | 0.002539 | 0.000748 | index | label:do_read_index .git/index ...
- 线程事件
-
添加到线程过程的线程消息。
例如,多线程的preload-index代码可以通过围绕线程池的区域进行检测,然后在线程过程中进行每线程的启动和退出事件。
static void *preload_thread(void *_data) { // start the per-thread clock and emit a message. trace2_thread_start("preload_thread"); // report which chunk of the array this thread was assigned. trace2_data_intmax("index", the_repository, "offset", p->offset); trace2_data_intmax("index", the_repository, "count", nr); do { ... } while (--nr > 0); ... // report elapsed time taken by this thread. trace2_thread_exit(); return NULL; } void preload_index(struct index_state *index, const struct pathspec *pathspec, unsigned int refresh_flags) { trace2_region_enter("index", "preload", the_repository); for (i = 0; i < threads; i++) { ... /* create thread */ } for (i = 0; i < threads; i++) { ... /* join thread */ } trace2_region_leave("index", "preload", the_repository); }
在此示例中,
main
线程执行了preload_index()并启动了preload
区域。启动了七个线程,命名为th01:preload_thread
到th07:preload_thread
。来自每个线程的事件在发生时会自动追加到共享目标流中,因此它们可能以随机顺序与其他线程一起出现。最后,主线程等待线程完成并离开区域。数据事件使用活动线程名称进行标记。它们用于报告每线程参数。
$ export GIT_TRACE2_PERF_BRIEF=1 $ export GIT_TRACE2_PERF=~/log.perf $ git status ... $ cat ~/log.perf ... d0 | main | region_enter | r1 | 0.002595 | | index | label:preload d0 | th01:preload_thread | thread_start | | 0.002699 | | | d0 | th02:preload_thread | thread_start | | 0.002721 | | | d0 | th01:preload_thread | data | r1 | 0.002736 | 0.000037 | index | offset:0 d0 | th02:preload_thread | data | r1 | 0.002751 | 0.000030 | index | offset:2032 d0 | th03:preload_thread | thread_start | | 0.002711 | | | d0 | th06:preload_thread | thread_start | | 0.002739 | | | d0 | th01:preload_thread | data | r1 | 0.002766 | 0.000067 | index | count:508 d0 | th06:preload_thread | data | r1 | 0.002856 | 0.000117 | index | offset:2540 d0 | th03:preload_thread | data | r1 | 0.002824 | 0.000113 | index | offset:1016 d0 | th04:preload_thread | thread_start | | 0.002710 | | | d0 | th02:preload_thread | data | r1 | 0.002779 | 0.000058 | index | count:508 d0 | th06:preload_thread | data | r1 | 0.002966 | 0.000227 | index | count:508 d0 | th07:preload_thread | thread_start | | 0.002741 | | | d0 | th07:preload_thread | data | r1 | 0.003017 | 0.000276 | index | offset:3048 d0 | th05:preload_thread | thread_start | | 0.002712 | | | d0 | th05:preload_thread | data | r1 | 0.003067 | 0.000355 | index | offset:1524 d0 | th05:preload_thread | data | r1 | 0.003090 | 0.000378 | index | count:508 d0 | th07:preload_thread | data | r1 | 0.003037 | 0.000296 | index | count:504 d0 | th03:preload_thread | data | r1 | 0.002971 | 0.000260 | index | count:508 d0 | th04:preload_thread | data | r1 | 0.002983 | 0.000273 | index | offset:508 d0 | th04:preload_thread | data | r1 | 0.007311 | 0.004601 | index | count:508 d0 | th05:preload_thread | thread_exit | | 0.008781 | 0.006069 | | d0 | th01:preload_thread | thread_exit | | 0.009561 | 0.006862 | | d0 | th03:preload_thread | thread_exit | | 0.009742 | 0.007031 | | d0 | th06:preload_thread | thread_exit | | 0.009820 | 0.007081 | | d0 | th02:preload_thread | thread_exit | | 0.010274 | 0.007553 | | d0 | th07:preload_thread | thread_exit | | 0.010477 | 0.007736 | | d0 | th04:preload_thread | thread_exit | | 0.011657 | 0.008947 | | d0 | main | region_leave | r1 | 0.011717 | 0.009122 | index | label:preload ... d0 | main | exit | | 0.029996 | | | code:0 d0 | main | atexit | | 0.030027 | | | code:0
在此示例中,预加载区域耗时0.009122秒。这7个线程在索引的各自部分上花费了0.006069到0.008947秒。线程“th01”处理了从偏移量0开始的508个项目。线程“th02”处理了从偏移量2032开始的508个项目。线程“th04”处理了从偏移量508开始的508个项目。
此示例还显示,线程名称在每个线程启动时以竞争方式分配。
- 配置(def param)事件
-
将“有趣的”配置值转储到trace2日志中。
我们可以选择性地发出配置事件,请参阅git-config[1]中的
trace2.configparams
了解如何启用它。$ git config --system color.ui never $ git config --global color.ui always $ git config --local color.ui auto $ git config --list --show-scope | grep 'color.ui' system color.ui=never global color.ui=always local color.ui=auto
然后,使用
GIT_TRACE2_CONFIG_PARAMS
将配置color.ui
标记为“有趣的”配置$ export GIT_TRACE2_PERF_BRIEF=1 $ export GIT_TRACE2_PERF=~/log.perf $ export GIT_TRACE2_CONFIG_PARAMS=color.ui $ git version ... $ cat ~/log.perf d0 | main | version | | | | | ... d0 | main | start | | 0.001642 | | | /usr/local/bin/git version d0 | main | cmd_name | | | | | version (version) d0 | main | def_param | | | | scope:system | color.ui:never d0 | main | def_param | | | | scope:global | color.ui:always d0 | main | def_param | | | | scope:local | color.ui:auto d0 | main | data | r0 | 0.002100 | 0.002100 | fsync | fsync/writeout-only:0 d0 | main | data | r0 | 0.002126 | 0.002126 | fsync | fsync/hardware-flush:0 d0 | main | exit | | 0.000470 | | | code:0 d0 | main | atexit | | 0.000477 | | | code:0
- 秒表计时器事件
-
测量函数调用或代码段中花费的时间量,这些代码段可能在进程的整个生命周期中从代码中的许多地方调用。
static void expensive_function(void) { trace2_timer_start(TRACE2_TIMER_ID_TEST1); ... sleep_millisec(1000); // Do something expensive ... trace2_timer_stop(TRACE2_TIMER_ID_TEST1); } static int ut_100timer(int argc, const char **argv) { ... expensive_function(); // Do something else 1... expensive_function(); // Do something else 2... expensive_function(); return 0; }
在此示例中,我们测量了
expensive_function
()中花费的总时间,无论它在程序整体流程中的何时被调用。$ export GIT_TRACE2_PERF_BRIEF=1 $ export GIT_TRACE2_PERF=~/log.perf $ t/helper/test-tool trace2 100timer 3 1000 ... $ cat ~/log.perf d0 | main | version | | | | | ... d0 | main | start | | 0.001453 | | | t/helper/test-tool trace2 100timer 3 1000 d0 | main | cmd_name | | | | | trace2 (trace2) d0 | main | exit | | 3.003667 | | | code:0 d0 | main | timer | | | | test | name:test1 intervals:3 total:3.001686 min:1.000254 max:1.000929 d0 | main | atexit | | 3.003796 | | | code:0