3每个 Yocto 跟踪工具的基本用法(附示例)

目录

3每个 Yocto 跟踪工具的基本用法(附示例)

3.1 PERF

3.1.1性能设置

3.1.2基本性能使用

3.1.3性能文档

3.2跟踪

3.2.1 ftrace 设置

3.2.2 ftrace 基本用法

3.2.3 “跟踪事件”子系统

3.2.4 trace-cmd/kernelshark

3.2.5 ftrace 文档

3.3 systemtap

3.3.1 systemtap 设置

3.3.2在目标上运行脚本

3.3.3 systemtap 文档

3.4 系统配置

3.4.1 Sysprof 设置

3.4.2 Sysprof 基本使用

3.4.3 Sysprof 文档

3.5 LTTng(Linux 跟踪工具包,下一代)

3.5.1 LTTng 设置

3.5.2收集和查看跟踪

3.5.3 LTTng 文档

3.6跟踪

3.6.1 blktrace 设置

3.6.2基本 blktrace 用法

3.6.3 blktrace 文档


本章介绍了每个跟踪工具的基本使用示例。

3.1 PERF

“perf”工具是与 Linux 内核捆绑在一起的分析和跟踪工具。

不要让它是内核的一部分这一事实使您误以为它仅用于跟踪和分析内核 - 您确实可以使用它来跟踪和分析内核,但您也可以使用它来单独分析特定的应用程序(有或没有内核上下文),您还可以使用它同时跟踪和分析系统上的内核和所有应用程序,以获得系统范围内正在发生的事情的视图。

在许多方面,perf 旨在成为当今 Linux 中可用的所有跟踪和分析工具的超集,包括本 HOWTO 中涵盖的所有其他工具。在过去的几年里,perf 包含了许多其他工具的功能,同时,这些其他工具删除了大部分以前的功能,并用对现在由 perf 实现的等效功能的调用取而代之子系统。推断表明,在某些时候,其他工具将变得完全多余并消失;在那之前,我们将在这些页面中介绍那些其他工具,并在许多情况下展示如何在 perf 和其他工具中完成相同的事情,当这样做似乎有用时。

下面的内容详细介绍了您可能希望应用该工具的一些最常见方式;可以在工具本身或perf(1)的手册页中找到完整的文档 。

3.1.1性能设置

在本节中,我们假设您已经执行了“常规设置”部分中概述的基本设置。

特别是,如果您在local.conf文件中分析使用以下内容构建的图像,您将从 perf 中获得最大收益:

INHIBIT_PACKAGE_STRIP = "1"

perf 大部分运行在目标系统上。您可以存档配置文件数据并将其复制到主机进行分析,但对于本文档的其余部分,我们假设您已通过 ssh 连接到主机并将在目标上运行 perf 命令。

3.1.2基本性能使用

perf 工具几乎是自我记录的。要提醒自己可用的命令,只需键入“perf”,它将显示基本用法以及可用的 perf 子命令:

root@crownbay:~# perfusage: perf [--version] [--help] COMMAND [ARGS]The most commonly used perf commands are:annotate        Read perf.data (created by perf record) and display annotated codearchive         Create archive with object files with build-ids found in perf.data filebench           General framework for benchmark suitesbuildid-cache   Manage build-id cache.buildid-list    List the buildids in a perf.data filediff            Read two perf.data files and display the differential profileevlist          List the event names in a perf.data fileinject          Filter to augment the events stream with additional informationkmem            Tool to trace/measure kernel memory(slab) propertieskvm             Tool to trace/measure kvm guest oslist            List all symbolic event typeslock            Analyze lock eventsprobe           Define new dynamic tracepointsrecord          Run a command and record its profile into perf.datareport          Read perf.data (created by perf record) and display the profilesched           Tool to trace/measure scheduler properties (latencies)script          Read perf.data (created by perf record) and display trace outputstat            Run a command and gather performance counter statisticstest            Runs sanity tests.timechart       Tool to visualize total system behavior during a workloadtop             System profiling tool.See 'perf help COMMAND' for more information on a specific command.

3.1.2.1使用 perf 进行基本分析

作为一个简单的测试案例,我们将分析一个相当大的文件的“wget”,这是一个最有趣的案例,因为它同时具有文件和网络 I/O 方面,至少在标准 Yocto 图像的情况下,它是作为 BusyBox 的一部分实现,因此我们用来分析它的方法可以以与 Yocto 中支持的整个 BusyBox 小程序非常相似的方式使用。

root@crownbay:~# rm linux-2.6.19.2.tar.bz2; \wget http://downloads.yoctoproject.org/mirror/sources/linux-2.6.19.2.tar.bz2

获取有关特定工作负载正在发生的事情的一些基本整体数据的最快和最简单的方法是使用“性能统计”对其进行分析。'perf stat' 基本上使用一些默认计数器进行分析,并在运行结束时显示总计数:

root@crownbay:~# perf stat wget http://downloads.yoctoproject.org/mirror/sources/linux-2.6.19.2.tar.bz2
Connecting to downloads.yoctoproject.org (140.211.169.59:80)
linux-2.6.19.2.tar.b 100% |***************************************************| 41727k  0:00:00 ETAPerformance counter stats for 'wget http://downloads.yoctoproject.org/mirror/sources/linux-2.6.19.2.tar.bz2':4597.223902 task-clock                #    0.077 CPUs utilized23568 context-switches          #    0.005 M/sec68 CPU-migrations            #    0.015 K/sec241 page-faults               #    0.052 K/sec3045817293 cycles                    #    0.663 GHz<not supported> stalled-cycles-frontend<not supported> stalled-cycles-backend858909167 instructions              #    0.28  insns per cycle165441165 branches                  #   35.987 M/sec19550329 branch-misses             #   11.82% of all branches59.836627620 seconds time elapsed

很多时候,这样一个头脑简单的测试不会产生太大的兴趣,但有时它会产生(请参阅真实世界的 Yocto 错误(慢循环安装写入速度))。

另外,请注意,'perf stat' 不限于一组固定的计数器 - 基本上,'perf list' 输出中列出的任何事件都可以通过 'perf stat' 进行统计。例如,假设我们想查看与内核内存分配/释放以及缓存命中和未命中相关的所有事件的摘要:

root@crownbay:~# perf stat -e kmem:* -e cache-references -e cache-misses wget http://downloads.yoctoproject.org/mirror/sources/linux-2.6.19.2.tar.bz2
Connecting to downloads.yoctoproject.org (140.211.169.59:80)
linux-2.6.19.2.tar.b 100% |***************************************************| 41727k  0:00:00 ETAPerformance counter stats for 'wget http://downloads.yoctoproject.org/mirror/sources/linux-2.6.19.2.tar.bz2':5566 kmem:kmalloc125517 kmem:kmem_cache_alloc0 kmem:kmalloc_node0 kmem:kmem_cache_alloc_node34401 kmem:kfree69920 kmem:kmem_cache_free133 kmem:mm_page_free41 kmem:mm_page_free_batched11502 kmem:mm_page_alloc11375 kmem:mm_page_alloc_zone_locked0 kmem:mm_page_pcpu_drain0 kmem:mm_page_alloc_extfrag66848602 cache-references2917740 cache-misses              #    4.365 % of all cache refs44.831023415 seconds time elapsed

因此,'perf stat' 为我们提供了一种很好的简单方法来快速了解一组事件可能发生的情况,但通常我们需要更多细节才能以我们可以的方式了解正在发生的事情以有用的方式采取行动。

为了深入研究下一个细节,我们可以使用“perf record”/“perf report”,它会收集分析数据并使用基于文本的交互式 UI(或者如果我们指定 –stdio 则简单地作为文本) '性能报告')。

作为我们第一次尝试分析此工作负载时,我们将简单地运行“perf record”,将我们想要分析的工作负载交给它(在“perf record”之后的所有内容以及我们提供给它的任何 perf 选项 - 这里没有 - 将在一个新外壳)。perf 收集样本直到进程退出并将它们记录在当前工作目录中名为“perf.data”的文件中。

root@crownbay:~# perf record wget http://downloads.yoctoproject.org/mirror/sources/linux-2.6.19.2.tar.bz2Connecting to downloads.yoctoproject.org (140.211.169.59:80)
linux-2.6.19.2.tar.b 100% |************************************************| 41727k  0:00:00 ETA
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.176 MB perf.data (~7700 samples) ]

要在“基于文本的 UI” (tui) 中查看结果,只需运行“perf report”,它将读取当前工作目录中的 perf.data 文件并在交互式 UI 中显示结果:

root@crownbay:~# perf report

上面的屏幕截图显示了一个“平面”配置文件,每个“存储桶”对应于在分析运行期间分析的功能的一个条目,从最流行到最不常用(perf 也有按各种顺序和键排序的选项)作为仅高于特定阈值的显示条目等 - 有关详细信息,请参阅 perf 文档)。请注意,这包括用户空间函数(包含 [.] 的条目)和进程的内核函数(包含 [k] 的条目)。(perf 具有命令行修饰符,可用于将分析限制为内核或用户空间等)。

还请注意,上面的报告显示了“busybox”的条目,它是在 Yocto 中实现“wget”的可执行文件,但该条目中不是有用的函数名称,而是显示一个不太友好的十六进制值。以下步骤将展示如何解决该问题。

然而,在我们这样做之前,让我们尝试运行一个不同的配置文件,它显示一些更有趣的东西。新配置文件和前一个配置文件之间的唯一区别是我们将添加 -g 选项,它不仅会记录采样函数的地址,还会记录采样函数的整个调用链:

root@crownbay:~# perf record -g wget http://downloads.yoctoproject.org/mirror/sources/linux-2.6.19.2.tar.bz2
Connecting to downloads.yoctoproject.org (140.211.169.59:80)
linux-2.6.19.2.tar.b 100% |************************************************| 41727k  0:00:00 ETA
[ perf record: Woken up 3 times to write data ]
[ perf record: Captured and wrote 0.652 MB perf.data (~28476 samples) ]root@crownbay:~# perf report

使用调用图视图,我们实际上不仅可以看到哪些函数花费了最多的时间,而且我们还可以看到这些函数如何被调用的摘要,并了解程序如何在进程中与内核交互。

请注意,上面屏幕截图中的每个条目现在都在左侧包含一个“+”。这意味着我们可以扩展条目并深入到进入该条目的调用链。在其中任何一个上按 'enter' 将展开调用链(您也可以按 'E' 同时展开它们或按 'C' 将它们全部折叠)。

在上面的屏幕截图中,我们一直__copy_to_user_ll()向下切换条目和几个子节点。这让我们可以看到哪些调用链对分析__copy_to_user_ll()函数做出了贡献,该函数占总分析的 1.77%。

作为对这些调用链的一些背景解释,请考虑在运行 wget 以在网络上获取文件时在高层会发生什么。基本上发生的事情是数据通过网络连接(套接字)进入内核并传递给用户空间程序“wget”(它实际上是 BusyBox 的一部分,但现在这并不重要),它使用缓冲区内核传递给它并将其写入磁盘文件以保存它。

我们在上面的调用堆栈中看到的这个过程的部分是内核将从套接字读取的数据向下传递到 wget 的部分,即复制到用户的部分。

还要注意,这里还有一种情况,十六进制值显示在调用堆栈中,这里是展开的sys_clock_gettime() 函数中。稍后我们将看到它解析为 busybox 中的用户空间函数调用。

上面的屏幕截图显示了数据旅程的另一半 - 从 wget 程序的用户空间缓冲区到磁盘。为了将缓冲区写入磁盘,wget 程序发出 a write(2),它对copy-from-user内核执行 a ,然后通过一些迂回路径(可能也存在于配置文件数据中的某处)进行处理,以将其安全地发送到磁盘。

现在我们已经看到了配置文件数据的基本布局以及如何从中提取有用信息的基础知识,让我们回到手头的任务,看看我们是否可以对时间花在哪些地方有一些基本的了解我们正在分析的程序,wget。请记住,wget 实际上是作为 BusyBox 中的小程序实现的,因此虽然进程名称是“wget”,但我们真正感兴趣的可执行文件是 BusyBox。因此,让我们展开包含 BusyBox 的第一个条目:

同样,在我们展开之前,我们看到函数被标记为十六进制值,而不是像大多数内核条目一样的符号。扩展 BusyBox 条目并没有让它变得更好。

问题是 perf 找不到 busybox 二进制文件的符号信息,这实际上是被 Yocto 构建系统剥离的。

一种解决方法是在local.conf构建映像时将以下内容放入文件中:

INHIBIT_PACKAGE_STRIP = "1"

但是,我们已经有一个二进制文件被剥离的图像,那么我们可以做些什么来获得 perf 来解析符号呢?基本上我们需要为 BusyBox 包安装调试信息。

要生成图像中的包的调试信息,我们可以添加 dbg-pkgs到EXTRA_IMAGE_FEATURES中local.conf。例如:

EXTRA_IMAGE_FEATURES = "debug-tweaks tools-profile dbg-pkgs"

此外,为了生成 perf 理解的 debuginfo 类型,我们还需要 在文件中设置 PACKAGE_DEBUG_SPLIT_STYLElocal.conf

PACKAGE_DEBUG_SPLIT_STYLE = 'debug-file-directory'

完成后,我们可以为 BusyBox 安装调试信息。构建后的调试包可以 build/tmp/deploy/rpm/*在主机系统中找到。找到busybox-dbg-...rpm 文件并将其复制到目标。例如:

[trz@empanada core2]$ scp /home/trz/yocto/crownbay-tracing-dbg/build/tmp/deploy/rpm/core2_32/busybox-dbg-1.20.2-r2.core2_32.rpm root@192.168.1.31:
busybox-dbg-1.20.2-r2.core2_32.rpm                     100% 1826KB   1.8MB/s   00:01

现在在目标上安装调试 rpm:

root@crownbay:~# rpm -i busybox-dbg-1.20.2-r2.core2_32.rpm

现在安装了 debuginfo,我们看到 BusyBox 条目现在象征性地显示它们的功能:

如果我们展开其中一个条目并在叶节点上按“回车”,我们会看到一个操作菜单,我们可以采取这些操作来获取与该条目相关的更多信息:

这些操作之一允许我们显示一个视图,该视图显示分析函数的以 busybox 为中心的视图(在这种情况下,我们还使用“E”键扩展了所有节点):

最后,我们可以看到,现在安装了 BusyBox debuginfo,前面sys_clock_gettime()提到的条目中以前未解析的符号现在已解析,并且显示 sys_clock_gettime 系统调用是 6.75% 的复制到用户开销的来源由handle_input()BusyBox 函数发起:

在最低级别的细节上,我们可以深入到汇编级别,看看哪些指令导致函数中的开销最大。在“udhcpc_main”函数上按“enter”,我们再次看到一个菜单:

选择“Annotate udhcpc_main”,我们可以通过 udhcpc_main 函数的指令获得详细的百分比列表。从显示中,我们可以看到在此函数中花费的时间超过 50% 被几个测试和常量 (1) 移动到寄存器占用:

作为跟踪的继续,让我们尝试使用不同计数器的另一个配置文件,而不是默认的“周期”。

Linux 中的跟踪和分析基础设施已经变得统一,允许我们使用具有完全不同的计数器集的相同工具,而不仅仅是传统工具必须限制自己使用的标准硬件计数器(当然传统工具也可以利用他们现在可用的扩展可能性,并且在某些情况下,如前所述)。

我们可以通过“perf list”获取可用于分析工作负载的可用事件列表:

root@crownbay:~# perf listList of pre-defined events (to be used in -e):cpu-cycles OR cycles                               [Hardware event]stalled-cycles-frontend OR idle-cycles-frontend    [Hardware event]stalled-cycles-backend OR idle-cycles-backend      [Hardware event]instructions                                       [Hardware event]cache-references                                   [Hardware event]cache-misses                                       [Hardware event]branch-instructions OR branches                    [Hardware event]branch-misses                                      [Hardware event]bus-cycles                                         [Hardware event]ref-cycles                                         [Hardware event]cpu-clock                                          [Software event]task-clock                                         [Software event]page-faults OR faults                              [Software event]minor-faults                                       [Software event]major-faults                                       [Software event]context-switches OR cs                             [Software event]cpu-migrations OR migrations                       [Software event]alignment-faults                                   [Software event]emulation-faults                                   [Software event]L1-dcache-loads                                    [Hardware cache event]L1-dcache-load-misses                              [Hardware cache event]L1-dcache-prefetch-misses                          [Hardware cache event]L1-icache-loads                                    [Hardware cache event]L1-icache-load-misses                              [Hardware cache event]...rNNN                                               [Raw hardware event descriptor]cpu/t1=v1[,t2=v2,t3 ...]/modifier                  [Raw hardware event descriptor](see 'perf list --help' on how to encode it)mem:<addr>[:access]                                [Hardware breakpoint]sunrpc:rpc_call_status                             [Tracepoint event]sunrpc:rpc_bind_status                             [Tracepoint event]sunrpc:rpc_connect_status                          [Tracepoint event]sunrpc:rpc_task_begin                              [Tracepoint event]skb:kfree_skb                                      [Tracepoint event]skb:consume_skb                                    [Tracepoint event]skb:skb_copy_datagram_iovec                        [Tracepoint event]net:net_dev_xmit                                   [Tracepoint event]net:net_dev_queue                                  [Tracepoint event]net:netif_receive_skb                              [Tracepoint event]net:netif_rx                                       [Tracepoint event]napi:napi_poll                                     [Tracepoint event]sock:sock_rcvqueue_full                            [Tracepoint event]sock:sock_exceed_buf_limit                         [Tracepoint event]udp:udp_fail_queue_rcv_skb                         [Tracepoint event]hda:hda_send_cmd                                   [Tracepoint event]hda:hda_get_response                               [Tracepoint event]hda:hda_bus_reset                                  [Tracepoint event]scsi:scsi_dispatch_cmd_start                       [Tracepoint event]scsi:scsi_dispatch_cmd_error                       [Tracepoint event]scsi:scsi_eh_wakeup                                [Tracepoint event]drm:drm_vblank_event                               [Tracepoint event]drm:drm_vblank_event_queued                        [Tracepoint event]drm:drm_vblank_event_delivered                     [Tracepoint event]random:mix_pool_bytes                              [Tracepoint event]random:mix_pool_bytes_nolock                       [Tracepoint event]random:credit_entropy_bits                         [Tracepoint event]gpio:gpio_direction                                [Tracepoint event]gpio:gpio_value                                    [Tracepoint event]block:block_rq_abort                               [Tracepoint event]block:block_rq_requeue                             [Tracepoint event]block:block_rq_issue                               [Tracepoint event]block:block_bio_bounce                             [Tracepoint event]block:block_bio_complete                           [Tracepoint event]block:block_bio_backmerge                          [Tracepoint event]..writeback:writeback_wake_thread                    [Tracepoint event]writeback:writeback_wake_forker_thread             [Tracepoint event]writeback:writeback_bdi_register                   [Tracepoint event]..writeback:writeback_single_inode_requeue           [Tracepoint event]writeback:writeback_single_inode                   [Tracepoint event]kmem:kmalloc                                       [Tracepoint event]kmem:kmem_cache_alloc                              [Tracepoint event]kmem:mm_page_alloc                                 [Tracepoint event]kmem:mm_page_alloc_zone_locked                     [Tracepoint event]kmem:mm_page_pcpu_drain                            [Tracepoint event]kmem:mm_page_alloc_extfrag                         [Tracepoint event]vmscan:mm_vmscan_kswapd_sleep                      [Tracepoint event]vmscan:mm_vmscan_kswapd_wake                       [Tracepoint event]vmscan:mm_vmscan_wakeup_kswapd                     [Tracepoint event]vmscan:mm_vmscan_direct_reclaim_begin              [Tracepoint event]..module:module_get                                  [Tracepoint event]module:module_put                                  [Tracepoint event]module:module_request                              [Tracepoint event]sched:sched_kthread_stop                           [Tracepoint event]sched:sched_wakeup                                 [Tracepoint event]sched:sched_wakeup_new                             [Tracepoint event]sched:sched_process_fork                           [Tracepoint event]sched:sched_process_exec                           [Tracepoint event]sched:sched_stat_runtime                           [Tracepoint event]rcu:rcu_utilization                                [Tracepoint event]workqueue:workqueue_queue_work                     [Tracepoint event]workqueue:workqueue_execute_end                    [Tracepoint event]signal:signal_generate                             [Tracepoint event]signal:signal_deliver                              [Tracepoint event]timer:timer_init                                   [Tracepoint event]timer:timer_start                                  [Tracepoint event]timer:hrtimer_cancel                               [Tracepoint event]timer:itimer_state                                 [Tracepoint event]timer:itimer_expire                                [Tracepoint event]irq:irq_handler_entry                              [Tracepoint event]irq:irq_handler_exit                               [Tracepoint event]irq:softirq_entry                                  [Tracepoint event]irq:softirq_exit                                   [Tracepoint event]irq:softirq_raise                                  [Tracepoint event]printk:console                                     [Tracepoint event]task:task_newtask                                  [Tracepoint event]task:task_rename                                   [Tracepoint event]syscalls:sys_enter_socketcall                      [Tracepoint event]syscalls:sys_exit_socketcall                       [Tracepoint event]...syscalls:sys_enter_unshare                         [Tracepoint event]syscalls:sys_exit_unshare                          [Tracepoint event]raw_syscalls:sys_enter                             [Tracepoint event]raw_syscalls:sys_exit                              [Tracepoint event]

捆绑在一起

这些与跟踪事件子系统定义的事件集完全相同,由 ftrace/tracecmd/kernelshark 作为 /sys/kernel/debug/tracing/events 中的文件公开,由 SystemTap 作为 kernel.trace(“tracepoint_name”) 和(部分) 由 LTTng 访问。

在查看此工作负载时,我们只会对其中的一个子集感兴趣,因此让我们选择最有可能的子系统(由 Tracepoint 事件中冒号前的字符串标识)并仅使用那些通配符子系统执行“perf stat”运行:

root@crownbay:~# perf stat -e skb:* -e net:* -e napi:* -e sched:* -e workqueue:* -e irq:* -e syscalls:* wget http://downloads.yoctoproject.org/mirror/sources/linux-2.6.19.2.tar.bz2
Performance counter stats for 'wget http://downloads.yoctoproject.org/mirror/sources/linux-2.6.19.2.tar.bz2':23323 skb:kfree_skb0 skb:consume_skb49897 skb:skb_copy_datagram_iovec6217 net:net_dev_xmit6217 net:net_dev_queue7962 net:netif_receive_skb2 net:netif_rx8340 napi:napi_poll0 sched:sched_kthread_stop0 sched:sched_kthread_stop_ret3749 sched:sched_wakeup0 sched:sched_wakeup_new0 sched:sched_switch29 sched:sched_migrate_task0 sched:sched_process_free1 sched:sched_process_exit0 sched:sched_wait_task0 sched:sched_process_wait0 sched:sched_process_fork1 sched:sched_process_exec0 sched:sched_stat_wait2106519415641 sched:sched_stat_sleep0 sched:sched_stat_iowait147453613 sched:sched_stat_blocked12903026955 sched:sched_stat_runtime0 sched:sched_pi_setprio3574 workqueue:workqueue_queue_work3574 workqueue:workqueue_activate_work0 workqueue:workqueue_execute_start0 workqueue:workqueue_execute_end16631 irq:irq_handler_entry16631 irq:irq_handler_exit28521 irq:softirq_entry28521 irq:softirq_exit28728 irq:softirq_raise1 syscalls:sys_enter_sendmmsg1 syscalls:sys_exit_sendmmsg0 syscalls:sys_enter_recvmmsg0 syscalls:sys_exit_recvmmsg14 syscalls:sys_enter_socketcall14 syscalls:sys_exit_socketcall...16965 syscalls:sys_enter_read16965 syscalls:sys_exit_read12854 syscalls:sys_enter_write12854 syscalls:sys_exit_write...58.029710972 seconds time elapsed

让我们选择这些跟踪点之一并告诉 perf 使用它作为采样事件做一个配置文件:

root@crownbay:~# perf record -g -e sched:sched_wakeup wget http://downloads.yoctoproject.org/mirror/sources/linux-2.6.19.2.tar.bz2

上面的截图显示了使用 sched:sched_switch tracepoint 运行配置文件的结果,它显示了到 sched_wakeup 的各种路径的相对成本(注意 sched_wakeup 是跟踪点的名称——它实际上是在 ttwu_do_wakeup() 中定义的,它说明了配置文件中实际显示的函数名称:

/*
 * Mark the task runnable and perform wakeup-preemption.
 */
static void
ttwu_do_wakeup(struct rq *rq, struct task_struct *p, int wake_flags)
{trace_sched_wakeup(p, true);...
}

上面展开并显示了几个更有趣的调用链,基本上是一些网络接收路径,当网络数据准备好时,它们可能最终会唤醒 wget(busybox)。

请注意,因为跟踪点通常用于跟踪,所以跟踪点的默认采样周期为 1,即对于跟踪点 perf 将在每次事件发生时进行采样(这可以使用 -c 选项进行更改)。这与硬件计数器形成对比,例如用于正常分析的默认“周期”硬件计数器,其中采样周期要高得多(以千计),因为分析应该具有尽可能低的开销,并且每个周期的采样将贵得令人望而却步。

3.1.2.2使用 perf 进行基本跟踪

分析是一个很好的工具,可用于解决许多问题或获取工作负载或整个系统中正在发生的事情的高级视图。然而,根据定义,它是一个近似值,正如与之相关的最突出的词“采样”所暗示的那样。一方面,它允许廉价地拍摄系统中正在发生的事情的代表性图片,但另一方面,当数据表明需要更深入地“深入研究”以发现真正的东西时,这种廉价性限制了其效用继续。在这种情况下,查看真实情况的唯一方法是能够查看(或更智能地总结)进入由粗粒度分析数据公开的更高级别行为的各个步骤。

作为一个具体的例子,我们可以跟踪我们认为可能适用于我们的工作负载的所有事件:

root@crownbay:~# perf record -g -e skb:* -e net:* -e napi:* -e sched:sched_switch -e sched:sched_wakeup -e irq:*-e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_writewget http://downloads.yoctoproject.org/mirror/sources/linux-2.6.19.2.tar.bz2

我们可以使用不带参数的“perf script”查看原始跟踪输出:

root@crownbay:~# perf scriptperf  1262 [000] 11624.857082: sys_exit_read: 0x0perf  1262 [000] 11624.857193: sched_wakeup: comm=migration/0 pid=6 prio=0 success=1 target_cpu=000wget  1262 [001] 11624.858021: softirq_raise: vec=1 [action=TIMER]wget  1262 [001] 11624.858074: softirq_entry: vec=1 [action=TIMER]wget  1262 [001] 11624.858081: softirq_exit: vec=1 [action=TIMER]wget  1262 [001] 11624.858166: sys_enter_read: fd: 0x0003, buf: 0xbf82c940, count: 0x0200wget  1262 [001] 11624.858177: sys_exit_read: 0x200wget  1262 [001] 11624.858878: kfree_skb: skbaddr=0xeb248d80 protocol=0 location=0xc15a5308wget  1262 [001] 11624.858945: kfree_skb: skbaddr=0xeb248000 protocol=0 location=0xc15a5308wget  1262 [001] 11624.859020: softirq_raise: vec=1 [action=TIMER]wget  1262 [001] 11624.859076: softirq_entry: vec=1 [action=TIMER]wget  1262 [001] 11624.859083: softirq_exit: vec=1 [action=TIMER]wget  1262 [001] 11624.859167: sys_enter_read: fd: 0x0003, buf: 0xb7720000, count: 0x0400wget  1262 [001] 11624.859192: sys_exit_read: 0x1d7wget  1262 [001] 11624.859228: sys_enter_read: fd: 0x0003, buf: 0xb7720000, count: 0x0400wget  1262 [001] 11624.859233: sys_exit_read: 0x0wget  1262 [001] 11624.859573: sys_enter_read: fd: 0x0003, buf: 0xbf82c580, count: 0x0200wget  1262 [001] 11624.859584: sys_exit_read: 0x200wget  1262 [001] 11624.859864: sys_enter_read: fd: 0x0003, buf: 0xb7720000, count: 0x0400wget  1262 [001] 11624.859888: sys_exit_read: 0x400wget  1262 [001] 11624.859935: sys_enter_read: fd: 0x0003, buf: 0xb7720000, count: 0x0400wget  1262 [001] 11624.859944: sys_exit_read: 0x400

这为我们提供了在工作负载中发生的与这些事件相关的带时间戳的详细事件序列。

在许多方面,分析可以被视为跟踪的一个子集——理论上,如果您有一组足以捕获工作负载的所有重要方面的跟踪事件,您可以得出分析运行可以得到的任何结果或视图.

传统分析的另一个方面是,虽然在很多方面都很强大,但它受到底层数据粒度的限制。分析工具提供了各种对样本数据进行排序和呈现的方式,这使得它更有用,更适合用户实验,但最终它不能以开放式方式用于提取不存在的数据由于从概念上讲,其中大部分已被丢弃。

然而,完整的详细跟踪数据确实提供了以无限多种方式操作和呈现在跟踪运行期间收集的信息的机会。

另一种看待它的方式是,“原始”计数器可以单独使用以生成有趣的输出的方式只有这么多;要获得比简单计数更复杂的东西,需要一些额外的逻辑,这些逻辑通常非常针对手头的问题。例如,如果我们想使用一个“计数器”来映射进程在处理器上计划运行的时间和它实际运行的时间之间的时间差值,我们不会期望这样的计数器单独存在,但我们可以派生出一个叫做“wakeup_latency”的方法,并使用它从跟踪数据中提取该指标的有用视图。同样,我们真的无法从标准分析工具中计算出系统上每个进程读取和写入的数据量,以及这些读取和写入中完全失败的次数。

幸运的是,有一种通用方法可以处理此类需求,称为“编程语言”。在给定数据的特定格式的情况下,使编程语言易于应用于此类问题被称为该数据和语言的“编程语言绑定”。Perf 支持两种编程语言绑定,一种用于 Python,一种用于 Perl。

捆绑在一起

用于操作和聚合跟踪数据的语言绑定当然不是一个新想法。第一个这样做的项目是 IBM 的 DProbes dpcc 编译器,这是一个 ANSI C 编译器,它针对在目标系统上的内核解释器上运行的低级汇编语言。这与 Sun 的 DTrace 所做的完全类似,只是 DTrace 为此目的发明了自己的语言。Systemtap 深受 DTrace 的启发,也创建了自己的一次性语言,但不是在内核解释器上运行该产品,而是创建了一个精心设计的基于编译器的机制,将其语言翻译成用 C 编写的内核模块。

现在我们在 perf.data 中有跟踪数据,我们可以使用 'perf script -g' 生成一个骨架脚本,其中包含我们记录的读/写进入/退出事件的处理程序:

root@crownbay:~# perf script -g python
generated Python script: perf-script.py

骨架脚本只是为 perf.data 文件中的每个事件类型创建一个 python 函数。每个函数的主体只是打印事件名称及其参数。例如:

def net__netif_rx(event_name, context, common_cpu,common_secs, common_nsecs, common_pid, common_comm,skbaddr, len, name):print_header(event_name, common_cpu, common_secs, common_nsecs,common_pid, common_comm)print "skbaddr=%u, len=%u, name=%s\n" % (skbaddr, len, name),

我们可以直接运行该脚本来打印 perf.data 文件中包含的所有事件:

root@crownbay:~# perf script -s perf-script.pyin trace_begin
syscalls__sys_exit_read     0 11624.857082795     1262 perf                  nr=3, ret=0
sched__sched_wakeup      0 11624.857193498     1262 perf                  comm=migration/0, pid=6, prio=0,      success=1, target_cpu=0
irq__softirq_raise       1 11624.858021635     1262 wget                  vec=TIMER
irq__softirq_entry       1 11624.858074075     1262 wget                  vec=TIMER
irq__softirq_exit        1 11624.858081389     1262 wget                  vec=TIMER
syscalls__sys_enter_read     1 11624.858166434     1262 wget                  nr=3, fd=3, buf=3213019456,      count=512
syscalls__sys_exit_read     1 11624.858177924     1262 wget                  nr=3, ret=512
skb__kfree_skb           1 11624.858878188     1262 wget                  skbaddr=3945041280,           location=3243922184, protocol=0
skb__kfree_skb           1 11624.858945608     1262 wget                  skbaddr=3945037824,      location=3243922184, protocol=0
irq__softirq_raise       1 11624.859020942     1262 wget                  vec=TIMER
irq__softirq_entry       1 11624.859076935     1262 wget                  vec=TIMER
irq__softirq_exit        1 11624.859083469     1262 wget                  vec=TIMER
syscalls__sys_enter_read     1 11624.859167565     1262 wget                  nr=3, fd=3, buf=3077701632,      count=1024
syscalls__sys_exit_read     1 11624.859192533     1262 wget                  nr=3, ret=471
syscalls__sys_enter_read     1 11624.859228072     1262 wget                  nr=3, fd=3, buf=3077701632,      count=1024
syscalls__sys_exit_read     1 11624.859233707     1262 wget                  nr=3, ret=0
syscalls__sys_enter_read     1 11624.859573008     1262 wget                  nr=3, fd=3, buf=3213018496,      count=512
syscalls__sys_exit_read     1 11624.859584818     1262 wget                  nr=3, ret=512
syscalls__sys_enter_read     1 11624.859864562     1262 wget                  nr=3, fd=3, buf=3077701632,      count=1024
syscalls__sys_exit_read     1 11624.859888770     1262 wget                  nr=3, ret=1024
syscalls__sys_enter_read     1 11624.859935140     1262 wget                  nr=3, fd=3, buf=3077701632,      count=1024
syscalls__sys_exit_read     1 11624.859944032     1262 wget                  nr=3, ret=1024

这本身并不是很有用。毕竟,我们可以通过在与 perf.data 文件相同的目录中简单地运行不带参数的“perf 脚本”来完成几乎相同的事情。

然而,我们可以用我们想要的任何东西替换生成的函数体中的打印语句,从而使其更加有用。

作为一个简单的例子,让我们用一个简单的函数替换函数体中的打印语句,该函数除了增加每个事件的计数外什么都不做。当程序针对 perf.data 文件运行时,每次遇到特定事件时,该事件的计数都会增加。例如:

def net__netif_rx(event_name, context, common_cpu,common_secs, common_nsecs, common_pid, common_comm,skbaddr, len, name):inc_counts(event_name)

生成的代码中的每个事件处理函数都经过修改以执行此操作。为方便起见,我们定义了一个名为 inc_counts() 的通用函数,每个处理程序都会调用它;inc_counts() 简单地使用“counts”散列计算每个事件的计数,这是一个专门的散列函数,可以进行类似 Perl 的自动激活,这种功能对于处理跟踪中常用的各种多级聚合非常有用(请参阅 perf 的有关详细信息的 Python 语言绑定文档):

counts = autodict()def inc_counts(event_name):try:counts[event_name] += 1except TypeError:counts[event_name] = 1

最后,在跟踪处理运行结束时,我们希望打印所有事件计数的结果。为此,我们使用特殊的“trace_end()”函数:

def trace_end():for event_name, count in counts.iteritems():print "%-40s %10s\n" % (event_name, count)

最终结果是跟踪中记录的所有事件的摘要:

skb__skb_copy_datagram_iovec                  13148
irq__softirq_entry                             4796
irq__irq_handler_exit                          3805
irq__softirq_exit                              4795
syscalls__sys_enter_write                      8990
net__net_dev_xmit                               652
skb__kfree_skb                                 4047
sched__sched_wakeup                            1155
irq__irq_handler_entry                         3804
irq__softirq_raise                             4799
net__net_dev_queue                              652
syscalls__sys_enter_read                      17599
net__netif_receive_skb                         1743
syscalls__sys_exit_read                       17598
net__netif_rx                                     2
napi__napi_poll                                1877
syscalls__sys_exit_write                       8990

请注意,这与我们从“perf stat”获得的信息几乎完全相同,这在一定程度上支持了前面提到的想法,即给定正确类型的跟踪数据,可以从中导出更高级别的分析类型摘要.

有关使用“perf 脚本”python 绑定的文档。

3.1.2.3 全系统跟踪和分析

到目前为止的示例都集中在跟踪特定的程序或工作负载上——换句话说,每次分析运行都在命令行中指定了要分析的程序,例如“perf record wget ...”。

在许多情况下,在单独的 shell 中运行工作负载的同时运行系统范围的配置文件或跟踪也是可能的,而且在许多情况下更有趣。

要进行系统范围的分析或跟踪,您通常使用 -a 标志来“perf record”。

为了演示这一点,打开一个窗口并使用 -a 标志启动配置文件(按 Ctrl-C 停止跟踪):

root@crownbay:~# perf record -g -a
^C[ perf record: Woken up 6 times to write data ]
[ perf record: Captured and wrote 1.400 MB perf.data (~61172 samples) ]

在另一个窗口中,运行 wget 测试:

root@crownbay:~# wget http://downloads.yoctoproject.org/mirror/sources/linux-2.6.19.2.tar.bz2
Connecting to downloads.yoctoproject.org (140.211.169.59:80)
linux-2.6.19.2.tar.b 100% \|*******************************\| 41727k 0:00:00 ETA

在这里,我们不仅可以看到 wget 负载的条目,还可以看到系统上运行的其他进程的条目:

在上面的快照中,我们可以看到源自 libc 的调用链和来自 Xorg 的调用链,它表明我们在用户空间中使用了专有的 X 驱动程序(注意扩展的 Xorg 调用链中存在“PVR”和其他一些无法解析的符号) )。

还要注意,我们在上面的快照中有内核和用户空间条目。我们还可以告诉 perf 关注用户空间,但在我们记录配置文件时为“周期”硬件计数器提供一个修饰符,在本例中为“u”:

root@crownbay:~# perf record -g -a -e cycles:u
^C[ perf record: Woken up 2 times to write data ]
[ perf record: Captured and wrote 0.376 MB perf.data (~16443 samples) ]

请注意,在上面的屏幕截图中,我们只看到用户空间条目 ([.])

最后,我们可以在叶节点上按“回车”并选择“放大 DSO”菜单项以仅显示与特定 DSO 关联的条目。在下面的屏幕截图中,我们放大了“libc”DSO,其中显示了与 libc-xxx.so DSO 关联的所有条目。

我们还可以使用系统范围的 -a 开关来进行系统范围的跟踪。在这里,我们将跟踪几个调度程序事件:

root@crownbay:~# perf record -a -e sched:sched_switch -e sched:sched_wakeup
^C[ perf record: Woken up 38 times to write data ]
[ perf record: Captured and wrote 9.780 MB perf.data (~427299 samples) ]

我们可以使用不带参数的“perf script”查看原始输出:

root@crownbay:~# perf scriptperf  1383 [001]  6171.460045: sched_wakeup: comm=kworker/1:1 pid=21 prio=120 success=1 target_cpu=001perf  1383 [001]  6171.460066: sched_switch: prev_comm=perf prev_pid=1383 prev_prio=120 prev_state=R+ ==> next_comm=kworker/1:1 next_pid=21 next_prio=120kworker/1:1    21 [001]  6171.460093: sched_switch: prev_comm=kworker/1:1 prev_pid=21 prev_prio=120 prev_state=S ==> next_comm=perf next_pid=1383 next_prio=120swapper     0 [000]  6171.468063: sched_wakeup: comm=kworker/0:3 pid=1209 prio=120 success=1 target_cpu=000swapper     0 [000]  6171.468107: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=kworker/0:3 next_pid=1209 next_prio=120kworker/0:3  1209 [000]  6171.468143: sched_switch: prev_comm=kworker/0:3 prev_pid=1209 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120perf  1383 [001]  6171.470039: sched_wakeup: comm=kworker/1:1 pid=21 prio=120 success=1 target_cpu=001perf  1383 [001]  6171.470058: sched_switch: prev_comm=perf prev_pid=1383 prev_prio=120 prev_state=R+ ==> next_comm=kworker/1:1 next_pid=21 next_prio=120kworker/1:1    21 [001]  6171.470082: sched_switch: prev_comm=kworker/1:1 prev_pid=21 prev_prio=120 prev_state=S ==> next_comm=perf next_pid=1383 next_prio=120perf  1383 [001]  6171.480035: sched_wakeup: comm=kworker/1:1 pid=21 prio=120 success=1 target_cpu=001

3.1.2.3.1过滤

请注意,有很多事件与我们感兴趣的内容实际上没有任何关系,即安排 'perf' 自身进出或唤醒 perf 的事件。我们可以通过使用 '-filter' 选项来摆脱这些 - 对于我们使用 -e 指定的每个事件,我们可以在此之后添加一个 –filter 以过滤掉包含具有特定值的字段的跟踪事件:

root@crownbay:~# perf record -a -e sched:sched_switch --filter 'next_comm != perf && prev_comm != perf' -e sched:sched_wakeup --filter 'comm != perf'
^C[ perf record: Woken up 38 times to write data ]
[ perf record: Captured and wrote 9.688 MB perf.data (~423279 samples) ]root@crownbay:~# perf scriptswapper     0 [000]  7932.162180: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=kworker/0:3 next_pid=1209 next_prio=120kworker/0:3  1209 [000]  7932.162236: sched_switch: prev_comm=kworker/0:3 prev_pid=1209 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120perf  1407 [001]  7932.170048: sched_wakeup: comm=kworker/1:1 pid=21 prio=120 success=1 target_cpu=001perf  1407 [001]  7932.180044: sched_wakeup: comm=kworker/1:1 pid=21 prio=120 success=1 target_cpu=001perf  1407 [001]  7932.190038: sched_wakeup: comm=kworker/1:1 pid=21 prio=120 success=1 target_cpu=001perf  1407 [001]  7932.200044: sched_wakeup: comm=kworker/1:1 pid=21 prio=120 success=1 target_cpu=001perf  1407 [001]  7932.210044: sched_wakeup: comm=kworker/1:1 pid=21 prio=120 success=1 target_cpu=001perf  1407 [001]  7932.220044: sched_wakeup: comm=kworker/1:1 pid=21 prio=120 success=1 target_cpu=001swapper     0 [001]  7932.230111: sched_wakeup: comm=kworker/1:1 pid=21 prio=120 success=1 target_cpu=001swapper     0 [001]  7932.230146: sched_switch: prev_comm=swapper/1 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=kworker/1:1 next_pid=21 next_prio=120kworker/1:1    21 [001]  7932.230205: sched_switch: prev_comm=kworker/1:1 prev_pid=21 prev_prio=120 prev_state=S ==> next_comm=swapper/1 next_pid=0 next_prio=120swapper     0 [000]  7932.326109: sched_wakeup: comm=kworker/0:3 pid=1209 prio=120 success=1 target_cpu=000swapper     0 [000]  7932.326171: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=kworker/0:3 next_pid=1209 next_prio=120kworker/0:3  1209 [000]  7932.326214: sched_switch: prev_comm=kworker/0:3 prev_pid=1209 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120

在这种情况下,我们过滤掉了所有在其“comm”或“comm_prev”或“comm_next”字段中包含“perf”的事件。请注意,仍然为 perf 记录了事件,但请注意,这些事件对于过滤字段没有 'perf' 值。要从 perf 中完全过滤掉任何东西需要做更多的工作,但为了演示如何使用过滤器,它已经足够接近了。

捆绑在一起

这些与跟踪事件子系统定义的事件过滤器集完全相同。有关这些事件过滤器的更多讨论,请参阅 ftrace/tracecmd/kernelshark 部分。

捆绑在一起

这些事件过滤器由内核中的专用伪解释器实现,并且是性能设计中不可或缺的一部分,因为它与跟踪相关。基于内核的事件过滤器提供了一种机制来精确地限制出现在用户空间中的事件流,在用户空间中提供与真实编程语言的绑定以对事件流进行后处理是有意义的。这种架构允许在内核和用户空间之间进行智能和灵活的处理分区。将此与 SystemTap 等其他工具进行对比,后者在内核中进行所有处理,因此需要一种特殊的项目定义语言以适应该设计或 LTTng,一切都发送到用户空间,因此需要超高效的内核到用户空间传输机制才能正常运行。虽然 perf 当然可以受益于例如传输设计的进步,但它从根本上并不依赖于它们。基本上,如果您发现性能跟踪应用程序导致缓冲区 I/O 溢出,则可能意味着您没有充分利用内核过滤引擎。

3.1.2.4使用动态跟踪点

perf 不限于“perf list”列出的一组固定的静态跟踪点。用户还可以在内核中的任何位置添加他们自己的“动态”跟踪点。例如,假设我们想在 do_fork() 上定义我们自己的跟踪点。我们可以使用“perf probe” perf 子命令来做到这一点:

root@crownbay:~# perf probe do_fork
Added new event:probe:do_fork        (on do_fork)You can now use it in all perf tools, such as:perf record -e probe:do_fork -aR sleep 1

通过“perf probe”添加新的跟踪点会产生一个事件,其中包含 /sys/kernel/debug/tracing/events 中的所有预期文件和格式,与静态跟踪点相同(如跟踪事件子系统中更详细的讨论)部分:

root@crownbay:/sys/kernel/debug/tracing/events/probe/do_fork# ls -al
drwxr-xr-x    2 root     root             0 Oct 28 11:42 .
drwxr-xr-x    3 root     root             0 Oct 28 11:42 ..
-rw-r--r--    1 root     root             0 Oct 28 11:42 enable
-rw-r--r--    1 root     root             0 Oct 28 11:42 filter
-r--r--r--    1 root     root             0 Oct 28 11:42 format
-r--r--r--    1 root     root             0 Oct 28 11:42 idroot@crownbay:/sys/kernel/debug/tracing/events/probe/do_fork# cat format
name: do_fork
ID: 944
format:field:unsigned short common_type;    offset:0;       size:2; signed:0;field:unsigned char common_flags;    offset:2;       size:1; signed:0;field:unsigned char common_preempt_count;    offset:3;       size:1; signed:0;field:int common_pid;        offset:4;       size:4; signed:1;field:int common_padding;    offset:8;       size:4; signed:1;field:unsigned long __probe_ip;      offset:12;      size:4; signed:0;print fmt: "(%lx)", REC->__probe_ip

我们可以列出当前存在的所有动态跟踪点:

root@crownbay:~# perf probe -lprobe:do_fork (on do_fork)probe:schedule (on schedule)

让我们记录系统范围('sleep 30' 是记录系统范围的技巧,但基本上什么都不做,然后在 30 秒后唤醒):

root@crownbay:~# perf record -g -a -e probe:do_fork sleep 30
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.087 MB perf.data (~3812 samples) ]

使用 'perf script' 我们可以看到触发的每个 do_fork 事件:

root@crownbay:~# perf script# ========
# captured on: Sun Oct 28 11:55:18 2012
# hostname : crownbay
# os release : 3.4.11-yocto-standard
# perf version : 3.4.11
# arch : i686
# nrcpus online : 2
# nrcpus avail : 2
# cpudesc : Intel(R) Atom(TM) CPU E660 @ 1.30GHz
# cpuid : GenuineIntel,6,38,1
# total memory : 1017184 kB
# cmdline : /usr/bin/perf record -g -a -e probe:do_fork sleep 30
# event : name = probe:do_fork, type = 2, config = 0x3b0, config1 = 0x0, config2 = 0x0, excl_usr = 0, excl_kern= 0, id = { 5, 6 }
# HEADER_CPU_TOPOLOGY info available, use -I to display
# ========
#matchbox-deskto  1197 [001] 34211.378318: do_fork: (c1028460)matchbox-deskto  1295 [001] 34211.380388: do_fork: (c1028460)pcmanfm  1296 [000] 34211.632350: do_fork: (c1028460)pcmanfm  1296 [000] 34211.639917: do_fork: (c1028460)matchbox-deskto  1197 [001] 34217.541603: do_fork: (c1028460)matchbox-deskto  1299 [001] 34217.543584: do_fork: (c1028460)gthumb  1300 [001] 34217.697451: do_fork: (c1028460)gthumb  1300 [001] 34219.085734: do_fork: (c1028460)gthumb  1300 [000] 34219.121351: do_fork: (c1028460)gthumb  1300 [001] 34219.264551: do_fork: (c1028460)pcmanfm  1296 [000] 34219.590380: do_fork: (c1028460)matchbox-deskto  1197 [001] 34224.955965: do_fork: (c1028460)matchbox-deskto  1306 [001] 34224.957972: do_fork: (c1028460)matchbox-termin  1307 [000] 34225.038214: do_fork: (c1028460)matchbox-termin  1307 [001] 34225.044218: do_fork: (c1028460)matchbox-termin  1307 [000] 34225.046442: do_fork: (c1028460)matchbox-deskto  1197 [001] 34237.112138: do_fork: (c1028460)matchbox-deskto  1311 [001] 34237.114106: do_fork: (c1028460)gaku  1312 [000] 34237.202388: do_fork: (c1028460)

在同一个文件上使用“性能报告”,我们可以看到在这 30 秒内启动几个程序的调用图:

捆绑在一起

跟踪事件子系统以完全相同的方式容纳静态和动态跟踪点 - 就基础设施而言,没有区别。有关跟踪事件子系统的更多详细信息,请参阅 ftrace 部分。

捆绑在一起

动态跟踪点由 kprobes 和 uprobes 在幕后实现。kprobes 和 uprobes 也被 SystemTap 使用并且实际上是 SystemTap 的主要焦点。

3.1.3性能文档

本节中讨论的命令的联机版本可以在此处找到:

  • 该“PERF统计”手册页。

  • 该“PERF记录”手册页。

  • 该“报告PERF”手册页。

  • 该“PERF探头”手册页。

  • 该“PERF脚本”手册页。

  • 有关使用“perf 脚本”python 绑定的文档。

  • 顶级perf(1) 联机帮助页。

通常,您应该能够通过 perf 本身调用手册页,例如“perf help”或“perf help record”。

要在目标上安装 perf 联机帮助页,请按如下方式修改配置:

IMAGE_INSTALL:append = " perf perf-doc"
DISTRO_FEATURES:append = " api-documentation"

文本形式的手册页以及一些其他文件(例如一组示例)也可以在内核树的“perf”目录中找到:

tools/perf/Documentation

在 perf wiki 上还有一个很好的 perf 教程,在某些方面比我们在这里做的更详细:Perf Tutorial

3.2跟踪

“ftrace”字面意思是“ftrace 函数跟踪器”,但实际上这包括许多相关的跟踪器以及它们都使用的基础设施。

3.2.1 ftrace 设置

在本节中,我们假设您已经执行了“常规设置”部分中概述的基本设置。

ftrace、trace-cmd 和 kernelshark 在目标系统上运行,并且可以开箱即用 - 无需额外设置。对于本节的其余部分,我们假设您已通过 ssh 连接到主机并将在目标上运行 ftrace。kernelshark 是一个 GUI 应用程序,如果您使用 '-X' 选项进行 ssh,您可以让 kernelshark GUI 在目标上运行,但如果需要,可以在主机上远程显示。

3.2.2 ftrace 基本用法

'ftrace' 本质上是指已挂载的 debugfs 文件系统的 /tracing 目录中包含的所有内容(Yocto 遵循标准约定并将其挂载在 /sys/kernel/debug 中)。下面是在 Yocto 系统上的 /sys/kernel/debug/tracing 中找到的所有文件的列表:

root@sugarbay:/sys/kernel/debug/tracing# ls
README                      kprobe_events               trace
available_events            kprobe_profile              trace_clock
available_filter_functions  options                     trace_marker
available_tracers           per_cpu                     trace_options
buffer_size_kb              printk_formats              trace_pipe
buffer_total_size_kb        saved_cmdlines              tracing_cpumask
current_tracer              set_event                   tracing_enabled
dyn_ftrace_total_info       set_ftrace_filter           tracing_on
enabled_functions           set_ftrace_notrace          tracing_thresh
events                      set_ftrace_pid
free_buffer                 set_graph_function

上面列出的文件用于各种目的——一些直接与跟踪器本身相关,其他用于设置跟踪选项,还有一些实际上包含跟踪器有效时的跟踪输出。有的功能从名字就可以猜到,有的需要解释;无论如何,我们将介绍我们在下面看到的一些文件,但要了解其他文件,请参阅 ftrace 文档。

我们将首先查看一些可用的内置跟踪器。

cat'ing the 'available_tracers' 文件列出了可用的跟踪器集:

root@sugarbay:/sys/kernel/debug/tracing# cat available_tracers
blk function_graph function nop

“current_tracer”文件包含当前有效的跟踪器:

root@sugarbay:/sys/kernel/debug/tracing# cat current_tracer
nop

上面的 current_tracer 列表显示“nop”跟踪器有效,这只是另一种说法,即当前实际上没有跟踪器有效。

将 available_tracer 之一回显到 current_tracer 中使指定的跟踪器成为当前的跟踪器:

root@sugarbay:/sys/kernel/debug/tracing# echo function > current_tracer
root@sugarbay:/sys/kernel/debug/tracing# cat current_tracer
function

以上将当前跟踪器设置为“函数跟踪器”。这个跟踪器跟踪内核中的每个函数调用,并使其作为“跟踪”文件的内容可用。读取“跟踪”文件列出了函数跟踪器跟踪的当前缓冲的函数调用:

root@sugarbay:/sys/kernel/debug/tracing# cat trace | less# tracer: function
#
# entries-in-buffer/entries-written: 310629/766471   #P:8
#
#                              _-----=> irqs-off
#                             / _----=> need-resched
#                            | / _---=> hardirq/softirq
#                            || / _--=> preempt-depth
#                            ||| /     delay
#           TASK-PID   CPU#  ||||    TIMESTAMP  FUNCTION
#              | |       |   ||||       |         |<idle>-0     [004] d..1   470.867169: ktime_get_real <-intel_idle<idle>-0     [004] d..1   470.867170: getnstimeofday <-ktime_get_real<idle>-0     [004] d..1   470.867171: ns_to_timeval <-intel_idle<idle>-0     [004] d..1   470.867171: ns_to_timespec <-ns_to_timeval<idle>-0     [004] d..1   470.867172: smp_apic_timer_interrupt <-apic_timer_interrupt<idle>-0     [004] d..1   470.867172: native_apic_mem_write <-smp_apic_timer_interrupt<idle>-0     [004] d..1   470.867172: irq_enter <-smp_apic_timer_interrupt<idle>-0     [004] d..1   470.867172: rcu_irq_enter <-irq_enter<idle>-0     [004] d..1   470.867173: rcu_idle_exit_common.isra.33 <-rcu_irq_enter<idle>-0     [004] d..1   470.867173: local_bh_disable <-irq_enter<idle>-0     [004] d..1   470.867173: add_preempt_count <-local_bh_disable<idle>-0     [004] d.s1   470.867174: tick_check_idle <-irq_enter<idle>-0     [004] d.s1   470.867174: tick_check_oneshot_broadcast <-tick_check_idle<idle>-0     [004] d.s1   470.867174: ktime_get <-tick_check_idle<idle>-0     [004] d.s1   470.867174: tick_nohz_stop_idle <-tick_check_idle<idle>-0     [004] d.s1   470.867175: update_ts_time_stats <-tick_nohz_stop_idle<idle>-0     [004] d.s1   470.867175: nr_iowait_cpu <-update_ts_time_stats<idle>-0     [004] d.s1   470.867175: tick_do_update_jiffies64 <-tick_check_idle<idle>-0     [004] d.s1   470.867175: _raw_spin_lock <-tick_do_update_jiffies64<idle>-0     [004] d.s1   470.867176: add_preempt_count <-_raw_spin_lock<idle>-0     [004] d.s2   470.867176: do_timer <-tick_do_update_jiffies64<idle>-0     [004] d.s2   470.867176: _raw_spin_lock <-do_timer<idle>-0     [004] d.s2   470.867176: add_preempt_count <-_raw_spin_lock<idle>-0     [004] d.s3   470.867177: ntp_tick_length <-do_timer<idle>-0     [004] d.s3   470.867177: _raw_spin_lock_irqsave <-ntp_tick_length...

上面跟踪中的每一行都显示了给定 cpu 上内核中发生的事情,以及函数调用的详细级别。每个条目显示被调用的函数,后跟它的调用者(在箭头之后)。

函数跟踪器让您非常详细地了解内核在进行跟踪时正在做什么,并且是了解内核代码如何在动态意义上工作的好方法。

捆绑在一起

ftrace 函数跟踪器也可以从 perf 中获得,作为 ftrace:function tracepoint。

跟踪调用链比它需要的要困难一些 - 幸运的是,有一个函数跟踪器的变体可以显式显示调用链,称为“function_graph”跟踪器:

root@sugarbay:/sys/kernel/debug/tracing# echo function_graph > current_tracer
root@sugarbay:/sys/kernel/debug/tracing# cat trace | lesstracer: function_graphCPU  DURATION                  FUNCTION CALLS|     |   |                     |   |   |   |
7)   0.046 us    |      pick_next_task_fair();
7)   0.043 us    |      pick_next_task_stop();
7)   0.042 us    |      pick_next_task_rt();
7)   0.032 us    |      pick_next_task_fair();
7)   0.030 us    |      pick_next_task_idle();
7)               |      _raw_spin_unlock_irq() {7)   0.033 us    |        sub_preempt_count();
7)   0.258 us    |      }
7)   0.032 us    |      sub_preempt_count();
7) + 13.341 us   |    } /* __schedule */
7)   0.095 us    |  } /* sub_preempt_count */
7)               |  schedule() {7)               |    __schedule() {7)   0.060 us    |      add_preempt_count();
7)   0.044 us    |      rcu_note_context_switch();
7)               |      _raw_spin_lock_irq() {7)   0.033 us    |        add_preempt_count();
7)   0.247 us    |      }
7)               |      idle_balance() {7)               |        _raw_spin_unlock() {7)   0.031 us    |          sub_preempt_count();
7)   0.246 us    |        }
7)               |        update_shares() {7)   0.030 us    |          __rcu_read_lock();
7)   0.029 us    |          __rcu_read_unlock();
7)   0.484 us    |        }
7)   0.030 us    |        __rcu_read_lock();
7)               |        load_balance() {7)               |          find_busiest_group() {7)   0.031 us    |            idle_cpu();
7)   0.029 us    |            idle_cpu();
7)   0.035 us    |            idle_cpu();
7)   0.906 us    |          }
7)   1.141 us    |        }
7)   0.022 us    |        msecs_to_jiffies();
7)               |        load_balance() {7)               |          find_busiest_group() {7)   0.031 us    |            idle_cpu();
.
.
.
4)   0.062 us    |        msecs_to_jiffies();
4)   0.062 us    |        __rcu_read_unlock();
4)               |        _raw_spin_lock() {4)   0.073 us    |          add_preempt_count();
4)   0.562 us    |        }
4) + 17.452 us   |      }
4)   0.108 us    |      put_prev_task_fair();
4)   0.102 us    |      pick_next_task_fair();
4)   0.084 us    |      pick_next_task_stop();
4)   0.075 us    |      pick_next_task_rt();
4)   0.062 us    |      pick_next_task_fair();
4)   0.066 us    |      pick_next_task_idle();
------------------------------------------
4)   kworker-74   =>    <idle>-0
------------------------------------------4)               |      finish_task_switch() {4)               |        _raw_spin_unlock_irq() {4)   0.100 us    |          sub_preempt_count();
4)   0.582 us    |        }
4)   1.105 us    |      }
4)   0.088 us    |      sub_preempt_count();
4) ! 100.066 us  |    }
.
.
.
3)               |  sys_ioctl() {3)   0.083 us    |    fget_light();
3)               |    security_file_ioctl() {3)   0.066 us    |      cap_file_ioctl();
3)   0.562 us    |    }
3)               |    do_vfs_ioctl() {3)               |      drm_ioctl() {3)   0.075 us    |        drm_ut_debug_printk();
3)               |        i915_gem_pwrite_ioctl() {3)               |          i915_mutex_lock_interruptible() {3)   0.070 us    |            mutex_lock_interruptible();
3)   0.570 us    |          }
3)               |          drm_gem_object_lookup() {3)               |            _raw_spin_lock() {3)   0.080 us    |              add_preempt_count();
3)   0.620 us    |            }
3)               |            _raw_spin_unlock() {3)   0.085 us    |              sub_preempt_count();
3)   0.562 us    |            }
3)   2.149 us    |          }
3)   0.133 us    |          i915_gem_object_pin();
3)               |          i915_gem_object_set_to_gtt_domain() {3)   0.065 us    |            i915_gem_object_flush_gpu_write_domain();
3)   0.065 us    |            i915_gem_object_wait_rendering();
3)   0.062 us    |            i915_gem_object_flush_cpu_write_domain();
3)   1.612 us    |          }
3)               |          i915_gem_object_put_fence() {3)   0.097 us    |            i915_gem_object_flush_fence.constprop.36();
3)   0.645 us    |          }
3)   0.070 us    |          add_preempt_count();
3)   0.070 us    |          sub_preempt_count();
3)   0.073 us    |          i915_gem_object_unpin();
3)   0.068 us    |          mutex_unlock();
3)   9.924 us    |        }
3) + 11.236 us   |      }
3) + 11.770 us   |    }
3) + 13.784 us   |  }
3)               |  sys_ioctl() {

如您所见,function_graph 显示更容易理解。另请注意,除了函数调用和相关的大括号外,其他事件(例如调度程序事件)也显示在上下文中。事实上,通过简单地启用这些事件,您可以自由地包含下一节中描述的跟踪事件子系统中可用的任何跟踪点,它们将出现在函数图显示的上下文中。了解内核动态的强大工具。

另请注意,显示屏左侧有各种注释。例如,如果执行给定函数所花费的总时间超过某个阈值,则左侧会出现一个感叹号或加号。有关所有这些字段的详细信息,请参阅 ftrace 文档。

3.2.3 “跟踪事件”子系统

/sys/kernel/debug/tracing 目录中包含的一个特别重要的目录是“events”子目录,它包含系统中每个跟踪点的表示。列出“events”子目录的内容,我们主要看到另一组子目录:

root@sugarbay:/sys/kernel/debug/tracing# cd events
root@sugarbay:/sys/kernel/debug/tracing/events# ls -al
drwxr-xr-x   38 root     root             0 Nov 14 23:19 .
drwxr-xr-x    5 root     root             0 Nov 14 23:19 ..
drwxr-xr-x   19 root     root             0 Nov 14 23:19 block
drwxr-xr-x   32 root     root             0 Nov 14 23:19 btrfs
drwxr-xr-x    5 root     root             0 Nov 14 23:19 drm
-rw-r--r--    1 root     root             0 Nov 14 23:19 enable
drwxr-xr-x   40 root     root             0 Nov 14 23:19 ext3
drwxr-xr-x   79 root     root             0 Nov 14 23:19 ext4
drwxr-xr-x   14 root     root             0 Nov 14 23:19 ftrace
drwxr-xr-x    8 root     root             0 Nov 14 23:19 hda
-r--r--r--    1 root     root             0 Nov 14 23:19 header_event
-r--r--r--    1 root     root             0 Nov 14 23:19 header_page
drwxr-xr-x   25 root     root             0 Nov 14 23:19 i915
drwxr-xr-x    7 root     root             0 Nov 14 23:19 irq
drwxr-xr-x   12 root     root             0 Nov 14 23:19 jbd
drwxr-xr-x   14 root     root             0 Nov 14 23:19 jbd2
drwxr-xr-x   14 root     root             0 Nov 14 23:19 kmem
drwxr-xr-x    7 root     root             0 Nov 14 23:19 module
drwxr-xr-x    3 root     root             0 Nov 14 23:19 napi
drwxr-xr-x    6 root     root             0 Nov 14 23:19 net
drwxr-xr-x    3 root     root             0 Nov 14 23:19 oom
drwxr-xr-x   12 root     root             0 Nov 14 23:19 power
drwxr-xr-x    3 root     root             0 Nov 14 23:19 printk
drwxr-xr-x    8 root     root             0 Nov 14 23:19 random
drwxr-xr-x    4 root     root             0 Nov 14 23:19 raw_syscalls
drwxr-xr-x    3 root     root             0 Nov 14 23:19 rcu
drwxr-xr-x    6 root     root             0 Nov 14 23:19 rpm
drwxr-xr-x   20 root     root             0 Nov 14 23:19 sched
drwxr-xr-x    7 root     root             0 Nov 14 23:19 scsi
drwxr-xr-x    4 root     root             0 Nov 14 23:19 signal
drwxr-xr-x    5 root     root             0 Nov 14 23:19 skb
drwxr-xr-x    4 root     root             0 Nov 14 23:19 sock
drwxr-xr-x   10 root     root             0 Nov 14 23:19 sunrpc
drwxr-xr-x  538 root     root             0 Nov 14 23:19 syscalls
drwxr-xr-x    4 root     root             0 Nov 14 23:19 task
drwxr-xr-x   14 root     root             0 Nov 14 23:19 timer
drwxr-xr-x    3 root     root             0 Nov 14 23:19 udp
drwxr-xr-x   21 root     root             0 Nov 14 23:19 vmscan
drwxr-xr-x    3 root     root             0 Nov 14 23:19 vsyscall
drwxr-xr-x    6 root     root             0 Nov 14 23:19 workqueue
drwxr-xr-x   26 root     root             0 Nov 14 23:19 writeback

这些子目录中的每一个都对应于一个“子系统”,并且又包含更多子目录,每个子目录最终都对应一个跟踪点。例如,这里是“kmem”子系统的内容:

root@sugarbay:/sys/kernel/debug/tracing/events# cd kmem
root@sugarbay:/sys/kernel/debug/tracing/events/kmem# ls -al
drwxr-xr-x   14 root     root             0 Nov 14 23:19 .
drwxr-xr-x   38 root     root             0 Nov 14 23:19 ..
-rw-r--r--    1 root     root             0 Nov 14 23:19 enable
-rw-r--r--    1 root     root             0 Nov 14 23:19 filter
drwxr-xr-x    2 root     root             0 Nov 14 23:19 kfree
drwxr-xr-x    2 root     root             0 Nov 14 23:19 kmalloc
drwxr-xr-x    2 root     root             0 Nov 14 23:19 kmalloc_node
drwxr-xr-x    2 root     root             0 Nov 14 23:19 kmem_cache_alloc
drwxr-xr-x    2 root     root             0 Nov 14 23:19 kmem_cache_alloc_node
drwxr-xr-x    2 root     root             0 Nov 14 23:19 kmem_cache_free
drwxr-xr-x    2 root     root             0 Nov 14 23:19 mm_page_alloc
drwxr-xr-x    2 root     root             0 Nov 14 23:19 mm_page_alloc_extfrag
drwxr-xr-x    2 root     root             0 Nov 14 23:19 mm_page_alloc_zone_locked
drwxr-xr-x    2 root     root             0 Nov 14 23:19 mm_page_free
drwxr-xr-x    2 root     root             0 Nov 14 23:19 mm_page_free_batched
drwxr-xr-x    2 root     root             0 Nov 14 23:19 mm_page_pcpu_drain

让我们看看特定跟踪点的子目录中有什么,在本例中是 kmalloc:

root@sugarbay:/sys/kernel/debug/tracing/events/kmem# cd kmalloc
root@sugarbay:/sys/kernel/debug/tracing/events/kmem/kmalloc# ls -al
drwxr-xr-x    2 root     root             0 Nov 14 23:19 .
drwxr-xr-x   14 root     root             0 Nov 14 23:19 ..
-rw-r--r--    1 root     root             0 Nov 14 23:19 enable
-rw-r--r--    1 root     root             0 Nov 14 23:19 filter
-r--r--r--    1 root     root             0 Nov 14 23:19 format
-r--r--r--    1 root     root             0 Nov 14 23:19 id

跟踪点的“格式”文件描述了内存中的事件,它被各种跟踪工具使用,这些工​​具现在利用这些跟踪点来解析事件并理解它,还有一个“打印 fmt”字段,允许工具像 ftrace 将事件显示为文本。kmalloc 事件的格式如下所示:

root@sugarbay:/sys/kernel/debug/tracing/events/kmem/kmalloc# cat format
name: kmalloc
ID: 313
format:field:unsigned short common_type;    offset:0;       size:2; signed:0;field:unsigned char common_flags;    offset:2;       size:1; signed:0;field:unsigned char common_preempt_count;    offset:3;       size:1; signed:0;field:int common_pid;        offset:4;       size:4; signed:1;field:int common_padding;    offset:8;       size:4; signed:1;field:unsigned long call_site;       offset:16;      size:8; signed:0;field:const void * ptr;      offset:24;      size:8; signed:0;field:size_t bytes_req;      offset:32;      size:8; signed:0;field:size_t bytes_alloc;    offset:40;      size:8; signed:0;field:gfp_t gfp_flags;       offset:48;      size:4; signed:0;print fmt: "call_site=%lx ptr=%p bytes_req=%zu bytes_alloc=%zu gfp_flags=%s", REC->call_site, REC->ptr, REC->bytes_req, REC->bytes_alloc,
(REC->gfp_flags) ? __print_flags(REC->gfp_flags, "|", {(unsigned long)(((( gfp_t)0x10u) | (( gfp_t)0x40u) | (( gfp_t)0x80u) | ((
gfp_t)0x20000u) | (( gfp_t)0x02u) | (( gfp_t)0x08u)) | (( gfp_t)0x4000u) | (( gfp_t)0x10000u) | (( gfp_t)0x1000u) | (( gfp_t)0x200u) | ((
gfp_t)0x400000u)), "GFP_TRANSHUGE"}, {(unsigned long)((( gfp_t)0x10u) | (( gfp_t)0x40u) | (( gfp_t)0x80u) | (( gfp_t)0x20000u) | ((
gfp_t)0x02u) | (( gfp_t)0x08u)), "GFP_HIGHUSER_MOVABLE"}, {(unsigned long)((( gfp_t)0x10u) | (( gfp_t)0x40u) | (( gfp_t)0x80u) | ((
gfp_t)0x20000u) | (( gfp_t)0x02u)), "GFP_HIGHUSER"}, {(unsigned long)((( gfp_t)0x10u) | (( gfp_t)0x40u) | (( gfp_t)0x80u) | ((
gfp_t)0x20000u)), "GFP_USER"}, {(unsigned long)((( gfp_t)0x10u) | (( gfp_t)0x40u) | (( gfp_t)0x80u) | (( gfp_t)0x80000u)), GFP_TEMPORARY"},
{(unsigned long)((( gfp_t)0x10u) | (( gfp_t)0x40u) | (( gfp_t)0x80u)), "GFP_KERNEL"}, {(unsigned long)((( gfp_t)0x10u) | (( gfp_t)0x40u)),
"GFP_NOFS"}, {(unsigned long)((( gfp_t)0x20u)), "GFP_ATOMIC"}, {(unsigned long)((( gfp_t)0x10u)), "GFP_NOIO"}, {(unsigned long)((
gfp_t)0x20u), "GFP_HIGH"}, {(unsigned long)(( gfp_t)0x10u), "GFP_WAIT"}, {(unsigned long)(( gfp_t)0x40u), "GFP_IO"}, {(unsigned long)((
gfp_t)0x100u), "GFP_COLD"}, {(unsigned long)(( gfp_t)0x200u), "GFP_NOWARN"}, {(unsigned long)(( gfp_t)0x400u), "GFP_REPEAT"}, {(unsigned
long)(( gfp_t)0x800u), "GFP_NOFAIL"}, {(unsigned long)(( gfp_t)0x1000u), "GFP_NORETRY"},      {(unsigned long)(( gfp_t)0x4000u), "GFP_COMP"},
{(unsigned long)(( gfp_t)0x8000u), "GFP_ZERO"}, {(unsigned long)(( gfp_t)0x10000u), "GFP_NOMEMALLOC"}, {(unsigned long)(( gfp_t)0x20000u),
"GFP_HARDWALL"}, {(unsigned long)(( gfp_t)0x40000u), "GFP_THISNODE"}, {(unsigned long)(( gfp_t)0x80000u), "GFP_RECLAIMABLE"}, {(unsigned
long)(( gfp_t)0x08u), "GFP_MOVABLE"}, {(unsigned long)(( gfp_t)0), "GFP_NOTRACK"}, {(unsigned long)(( gfp_t)0x400000u), "GFP_NO_KSWAPD"},
{(unsigned long)(( gfp_t)0x800000u), "GFP_OTHER_NODE"} ) : "GFP_NOWAIT"

跟踪点目录中的“启用”文件允许用户(或诸如 trace-cmd 之类的工具)实际打开和关闭跟踪点。启用后,相应的跟踪点将开始出现在前面描述的 ftrace 'trace' 文件中。例如,这将打开 kmalloc 跟踪点:

root@sugarbay:/sys/kernel/debug/tracing/events/kmem/kmalloc# echo 1 > enable

目前,我们对函数跟踪器或其他可能有效的跟踪器不感兴趣,所以我们首先将其关闭,但如果我们这样做,我们仍然需要打开跟踪以查看事件输出缓冲区:

root@sugarbay:/sys/kernel/debug/tracing# echo nop > current_tracer
root@sugarbay:/sys/kernel/debug/tracing# echo 1 > tracing_on

现在,如果我们查看 'trace' 文件,除了刚刚打开的 kmalloc 事件外,我们什么也看不到:

root@sugarbay:/sys/kernel/debug/tracing# cat trace | less
# tracer: nop
#
# entries-in-buffer/entries-written: 1897/1897   #P:8
#
#                              _-----=> irqs-off
#                             / _----=> need-resched
#                            | / _---=> hardirq/softirq
#                            || / _--=> preempt-depth
#                            ||| /     delay
#           TASK-PID   CPU#  ||||    TIMESTAMP  FUNCTION
#              | |       |   ||||       |         |dropbear-1465  [000] ...1 18154.620753: kmalloc: call_site=ffffffff816650d4 ptr=ffff8800729c3000 bytes_req=2048 bytes_alloc=2048 gfp_flags=GFP_KERNEL<idle>-0     [000] ..s3 18154.621640: kmalloc: call_site=ffffffff81619b36 ptr=ffff88006d555800 bytes_req=512 bytes_alloc=512 gfp_flags=GFP_ATOMIC<idle>-0     [000] ..s3 18154.621656: kmalloc: call_site=ffffffff81619b36 ptr=ffff88006d555800 bytes_req=512 bytes_alloc=512 gfp_flags=GFP_ATOMIC
matchbox-termin-1361  [001] ...1 18154.755472: kmalloc: call_site=ffffffff81614050 ptr=ffff88006d5f0e00 bytes_req=512 bytes_alloc=512 gfp_flags=GFP_KERNEL|GFP_REPEATXorg-1264  [002] ...1 18154.755581: kmalloc: call_site=ffffffff8141abe8 ptr=ffff8800734f4cc0 bytes_req=168 bytes_alloc=192 gfp_flags=GFP_KERNEL|GFP_NOWARN|GFP_NORETRYXorg-1264  [002] ...1 18154.755583: kmalloc: call_site=ffffffff814192a3 ptr=ffff88001f822520 bytes_req=24 bytes_alloc=32 gfp_flags=GFP_KERNEL|GFP_ZEROXorg-1264  [002] ...1 18154.755589: kmalloc: call_site=ffffffff81419edb ptr=ffff8800721a2f00 bytes_req=64 bytes_alloc=64 gfp_flags=GFP_KERNEL|GFP_ZERO
matchbox-termin-1361  [001] ...1 18155.354594: kmalloc: call_site=ffffffff81614050 ptr=ffff88006db35400 bytes_req=576 bytes_alloc=1024 gfp_flags=GFP_KERNEL|GFP_REPEATXorg-1264  [002] ...1 18155.354703: kmalloc: call_site=ffffffff8141abe8 ptr=ffff8800734f4cc0 bytes_req=168 bytes_alloc=192 gfp_flags=GFP_KERNEL|GFP_NOWARN|GFP_NORETRYXorg-1264  [002] ...1 18155.354705: kmalloc: call_site=ffffffff814192a3 ptr=ffff88001f822520 bytes_req=24 bytes_alloc=32 gfp_flags=GFP_KERNEL|GFP_ZEROXorg-1264  [002] ...1 18155.354711: kmalloc: call_site=ffffffff81419edb ptr=ffff8800721a2f00 bytes_req=64 bytes_alloc=64 gfp_flags=GFP_KERNEL|GFP_ZERO<idle>-0     [000] ..s3 18155.673319: kmalloc: call_site=ffffffff81619b36 ptr=ffff88006d555800 bytes_req=512 bytes_alloc=512 gfp_flags=GFP_ATOMICdropbear-1465  [000] ...1 18155.673525: kmalloc: call_site=ffffffff816650d4 ptr=ffff8800729c3000 bytes_req=2048 bytes_alloc=2048 gfp_flags=GFP_KERNEL<idle>-0     [000] ..s3 18155.674821: kmalloc: call_site=ffffffff81619b36 ptr=ffff88006d554800 bytes_req=512 bytes_alloc=512 gfp_flags=GFP_ATOMIC<idle>-0     [000] ..s3 18155.793014: kmalloc: call_site=ffffffff81619b36 ptr=ffff88006d554800 bytes_req=512 bytes_alloc=512 gfp_flags=GFP_ATOMICdropbear-1465  [000] ...1 18155.793219: kmalloc: call_site=ffffffff816650d4 ptr=ffff8800729c3000 bytes_req=2048 bytes_alloc=2048 gfp_flags=GFP_KERNEL<idle>-0     [000] ..s3 18155.794147: kmalloc: call_site=ffffffff81619b36 ptr=ffff88006d555800 bytes_req=512 bytes_alloc=512 gfp_flags=GFP_ATOMIC<idle>-0     [000] ..s3 18155.936705: kmalloc: call_site=ffffffff81619b36 ptr=ffff88006d555800 bytes_req=512 bytes_alloc=512 gfp_flags=GFP_ATOMICdropbear-1465  [000] ...1 18155.936910: kmalloc: call_site=ffffffff816650d4 ptr=ffff8800729c3000 bytes_req=2048 bytes_alloc=2048 gfp_flags=GFP_KERNEL<idle>-0     [000] ..s3 18155.937869: kmalloc: call_site=ffffffff81619b36 ptr=ffff88006d554800 bytes_req=512 bytes_alloc=512 gfp_flags=GFP_ATOMIC
matchbox-termin-1361  [001] ...1 18155.953667: kmalloc: call_site=ffffffff81614050 ptr=ffff88006d5f2000 bytes_req=512 bytes_alloc=512 gfp_flags=GFP_KERNEL|GFP_REPEATXorg-1264  [002] ...1 18155.953775: kmalloc: call_site=ffffffff8141abe8 ptr=ffff8800734f4cc0 bytes_req=168 bytes_alloc=192 gfp_flags=GFP_KERNEL|GFP_NOWARN|GFP_NORETRYXorg-1264  [002] ...1 18155.953777: kmalloc: call_site=ffffffff814192a3 ptr=ffff88001f822520 bytes_req=24 bytes_alloc=32 gfp_flags=GFP_KERNEL|GFP_ZEROXorg-1264  [002] ...1 18155.953783: kmalloc: call_site=ffffffff81419edb ptr=ffff8800721a2f00 bytes_req=64 bytes_alloc=64 gfp_flags=GFP_KERNEL|GFP_ZERO<idle>-0     [000] ..s3 18156.176053: kmalloc: call_site=ffffffff81619b36 ptr=ffff88006d554800 bytes_req=512 bytes_alloc=512 gfp_flags=GFP_ATOMICdropbear-1465  [000] ...1 18156.176257: kmalloc: call_site=ffffffff816650d4 ptr=ffff8800729c3000 bytes_req=2048 bytes_alloc=2048 gfp_flags=GFP_KERNEL<idle>-0     [000] ..s3 18156.177717: kmalloc: call_site=ffffffff81619b36 ptr=ffff88006d555800 bytes_req=512 bytes_alloc=512 gfp_flags=GFP_ATOMIC<idle>-0     [000] ..s3 18156.399229: kmalloc: call_site=ffffffff81619b36 ptr=ffff88006d555800 bytes_req=512 bytes_alloc=512 gfp_flags=GFP_ATOMICdropbear-1465  [000] ...1 18156.399434: kmalloc: call_site=ffffffff816650d4 ptr=ffff8800729c3000 bytes_http://rostedt.homelinux.com/kernelshark/req=2048 bytes_alloc=2048 gfp_flags=GFP_KERNEL<idle>-0     [000] ..s3 18156.400660: kmalloc: call_site=ffffffff81619b36 ptr=ffff88006d554800 bytes_req=512 bytes_alloc=512 gfp_flags=GFP_ATOMIC
matchbox-termin-1361  [001] ...1 18156.552800: kmalloc: call_site=ffffffff81614050 ptr=ffff88006db34800 bytes_req=576 bytes_alloc=1024 gfp_flags=GFP_KERNEL|GFP_REPEAT

要再次禁用 kmalloc 事件,我们需要向启用文件发送 0:

root@sugarbay:/sys/kernel/debug/tracing/events/kmem/kmalloc# echo 0 > enable

您可以启用任意数量的事件或完整的子系统(通过使用子系统目录中的“启用”文件),并通过启用尽可能多的适当跟踪点来了解系统中正在发生的事情。

本 HOWTO 中描述的许多工具就是这样做的,包括下一节中的 trace-cmd 和 kernelshark。

捆绑在一起

这些跟踪点及其表示不仅被 ftrace 使用,而且被本文档中涵盖的许多其他工具使用,它们构成了 Linux 中各种可用跟踪器的中心集成点。它们构成了以下工具检测的核心部分:perf、lttng、ftrace、blktrace 和 SystemTap

捆绑在一起

最终,/sys/kernel/debug/tracing 中当前可用的所有专用跟踪器都将被删除并替换为基于“跟踪事件”子系统的等效跟踪器。

3.2.4 trace-cmd/kernelshark

trace-cmd 本质上是一个扩展的命令行“包装器”界面,它隐藏了 /sys/kernel/debug/tracing 中所有单个文件的详细信息,允许用户在 /sys/kernel/debug/tracing 中指定特定的特定事件/events/ 子目录并收集跟踪信息并避免直接处理这些细节。

作为最重要的另一层,kernelshark 提供了一个 GUI,允许用户使用直观的界面启动和停止跟踪并指定事件集,并将输出查看为跟踪事件和每个 CPU 的图形显示。它直接使用“trace-cmd”作为管道来完成所有隐藏工作(并且实际上显示它使用的 trace-cmd 命令,我们将看到)。

要使用 kernelshark 开始跟踪,首先启动 kernelshark:

root@sugarbay:~# kernelshark

然后通过从 kernelshark 菜单中选择来调出“捕获”对话框:

Capture | Record

这将显示以下对话框,允许您选择一个或多个事件(甚至一个或多个完整子系统)进行跟踪:

请注意,这些与之前的跟踪事件子系统部分中描述的事件集完全相同,实际上是 trace-cmd 为 kernelshark 获取它们的地方。

在上面的屏幕截图中,我们决定稍微探索一下图形子系统,因此选择跟踪“i915”和“drm”子系统中包含的所有跟踪点。

完成后,我们可以使用对话框右下角的“运行”和“停止”按钮启动和停止跟踪(跟踪开始后,同一按钮将变成“停止”按钮):

请注意,右侧窗格显示了用于运行跟踪的确切 trace-cmd 命令行,以及 trace-cmd 运行的结果。

一旦按下“停止”按钮,图形视图就会神奇地充满跟踪数据的彩色 per-cpu 显示,以及下面的详细事件列表:

这是另一个示例,这次是由跟踪“所有事件”产生的显示:

该工具一目了然,但有关在数据中导航的更多详细信息,请参阅kernelshark 网站。

3.2.5 ftrace 文档

ftrace 的文档可以在内核文档目录中找到:

Documentation/trace/ftrace.txt

跟踪事件子系统的文档也可以在内核文档目录中找到:

Documentation/trace/events.txt

在 LWN 上有一系列关于使用 ftrace 和 trace-cmd 的不错的文章:

  • 使用 Ftrace 调试内核 - 第 1 部分

  • 使用 Ftrace 调试内核 - 第 2 部分

  • Ftrace 函数追踪器的秘密

  • trace-cmd:Ftrace 的前端

这里有更详细的文档 kernelshark 用法: KernelShark

可以在 /sys/kernel/debug/tracing/README.

3.3 systemtap

SystemTap 是一个系统范围的基于脚本的跟踪和分析工具。

SystemTap 脚本是在内核中执行的类 C 程序,用于收集/打印/聚合从它们最终被调用的上下文中提取的数据。

例如,SystemTap 教程中的这个探针在每次系统 open()sa 文件上的任何进程时简单地打印一行。对于每一行,它打印打开文件的程序的可执行文件名,连同它的 PID,以及它打开(或试图打开)的文件名,它从打开的系统调用的 argstr 中提取。

probe syscall.open
{printf ("%s(%d) open (%s)\n", execname(), pid(), argstr)
}probe timer.ms(4000) # after 4 seconds
{exit ()
}

通常,要执行此探测器,您只需在要探测的系统上安装 systemtap,然后直接在该系统上运行探测器,例如假设包含上述文本的文件名为 trace_open.stp:

# stap trace_open.stp

systemtap 在幕后运行此探针的操作是 1) 解析探针并将其转换为等效的“C”形式,2) 将“C”形式编译为内核模块,3) 将模块插入内核,从而支持4) 收集探针产生的数据并显示给用户。

为了完成第 1 步和第 2 步,“stap”程序需要访问内核构建系统,该系统生成了被探测系统正在运行的内核。在典型的嵌入式系统(“目标”)的情况下,不幸的是,内核构建系统通常不是在目标上运行的映像的一部分。然而,它通常在生成目标图像的“主机”系统上可用;在这种情况下,步骤 1 和 2 在主机系统上执行,步骤 3 和 4 在目标系统上执行,仅使用 systemtap 'runtime'。

Yocto 中的 systemtap 支持假设在目标上只运行第 3 步和第 4 步;可以在目标上执行所有操作,但本节仅假设典型的嵌入式用例。

所以基本上,为了在目标上运行 systemtap 脚本,你需要做的是 1) 在主机系统上,将探测器编译成对目标有意义的内核模块,2) 将模块复制到目标系统上,然后3) 将模块插入到目标内核中,并为其配备武器,以及 4) 收集探测器生成的数据并将其显示给用户。

3.3.1 systemtap 设置

这些是很多步骤和很多细节,但幸运的是,Yocto 包含一个名为“crosstap”的脚本,它将处理这些细节,允许您在远程目标上简单地执行 systemtap 脚本,并在必要时使用参数。

但是,为了从远程主机执行此操作,您需要有权访问您启动的映像的构建。“crosstap”脚本提供了有关如何在未完成构建的情况下在主机上运行脚本时执行此操作的详细信息:

$ crosstap root@192.168.1.88 trace_open.stpError: No target kernel build found.
Did you forget to create a local build of your image?'crosstap' requires a local sdk build of the target system
(or a build that includes 'tools-profile') in order to build
kernel modules that can probe the target system.Practically speaking, that means you need to do the following:- If you're running a pre-built image, download the release
   and/or BSP tarballs used to build the image.
 - If you're working from git sources, just clone the metadataand BSP layers needed to build the image you'll be booting.
 - Make sure you're properly set up to build a new image (seethe BSP README and/or the widely available basic documentationthat discusses how to build images).- Build an -sdk version of the image e.g.:$ bitbake core-image-sato-sdkOR- Build a non-sdk image but include the profiling tools:[ edit local.conf and add 'tools-profile' to the end ofthe EXTRA_IMAGE_FEATURES variable ]$ bitbake core-image-satoOnce you've build the image on the host system, you're ready to
boot it (or the equivalent pre-built image) and use 'crosstap'
to probe it (you need to source the environment as usual first):$ source oe-init-build-env$ cd ~/my/systemtap/scripts$ crosstap root@192.168.1.xxx myscript.stp

注意

SystemTap 使用“crosstap”,假设您可以建立到远程目标的 ssh 连接。有关验证 ssh 连接的详细信息,请参阅 crosstap wiki 页面。此外,默认情况下,*-minimal 图像中未启用 ssh 进入目标系统的功能。

所以基本上你需要做的是构建一个 SDK 映像或带有“工具配置文件”的映像,如本手册“常规设置”部分所述,并启动生成的目标映像。

注意

如果您有一个包含多台机器的构建目录,您需要在 local.conf 中选择您要连接的 MACHINE,并且该机器构建目录中的内核必须与引导系统上的内核完全匹配,否则您将获得当您尝试调用脚本时,上面的“crosstap”消息。

3.3.2在目标上运行脚本

完成后,您应该能够在目标上运行 systemtap 脚本:

$ cd /path/to/yocto
$ source oe-init-build-env### Shell environment set up for builds. ###You can now run 'bitbake <target>'Common targets are:core-image-minimalcore-image-satometa-toolchainmeta-ide-supportYou can also run generated QEMU images with a command like 'runqemu qemux86-64'

完成后,您可以 cd 到包含脚本的任何目录并使用“crosstap”运行脚本:

$ cd /path/to/my/systemap/script
$ crosstap root@192.168.7.2 trace_open.stp

如果连接到目标时出现错误,例如:

$ crosstap root@192.168.7.2 trace_open.stp
error establishing ssh connection on remote 'root@192.168.7.2'

尝试通过 ssh 连接到目标,看看会发生什么:

$ ssh root@192.168.7.2

很多时候,连接问题是由于指定了错误的 IP 地址或出现“主机密钥验证错误”。

如果一切按计划进行,您应该会看到如下内容(出现提示时输入密码,如果设置为不使用密码,请按 Enter):

$ crosstap root@192.168.7.2 trace_open.stp
root@192.168.7.2's password:
matchbox-termin(1036) open ("/tmp/vte3FS2LW", O_RDWR|O_CREAT|O_EXCL|O_LARGEFILE, 0600)
matchbox-termin(1036) open ("/tmp/vteJMC7LW", O_RDWR|O_CREAT|O_EXCL|O_LARGEFILE, 0600)

3.3.3 systemtap 文档

SystemTap 语言参考可以在这里找到:SystemTap 语言参考

可以在此处找到指向其他 SystemTap 文档、教程和示例的链接:SystemTap 文档页面

3.4 系统配置

Sysprof 是一个非常易于使用的系统级分析器,它由一个带有三个窗格和几个按钮的窗口组成,这些按钮允许您从一个位置开始、停止和查看配置文件。

3.4.1 Sysprof 设置

在本节中,我们假设您已经执行了“常规设置”部分中概述的基本设置。

Sysprof 是在目标系统上运行的基于 GUI 的应用程序。对于本文档的其余部分,我们假设您已通过 ssh 连接到主机并将在目标上运行 Sysprof(您可以使用“-X”选项进行 ssh 并让 Sysprof GUI 在目标上运行,但在远程显示如果你愿意的话,主持人)。

3.4.2 Sysprof 基本使用

要开始分析系统,您只需按“开始”按钮。要停止配置文件并通过一个简单的步骤开始查看配置文件数据,请按“配置文件”按钮。

按下配置文件按钮后,三个窗格将填充配置文件数据:

左窗格显示功能和进程列表。选择其中之一将在右窗格中展开该功能,显示其所有被调用者。请注意,这种面向调用者的显示本质上与 perf 的默认面向调用者的调用链显示相反。

在上面的屏幕截图中,我们关注__copy_to_user_ll()并查找调用链,我们可以看到调用者之一 __copy_to_user_ll是 sys_read() 以及它们之间的完整调用路径。请注意,这实质上是我们在本页 perf 部分中显示的 perf 显示中看到的相同信息的一部分。

类似地,上面是从用户复制调用链的 Sysprof 显示的快照。

最后,查看左下角的第三个 Sysprof 窗格,我们可以看到左上角窗格中选定的特定函数的所有调用者的列表。在这种情况下,下方窗格显示 的所有调用者 __mark_inode_dirty

双击这些功能之一将依次将焦点更改为所选功能,依此类推。

捆绑在一起

如果您喜欢 sysprof 的“面向呼叫者”的显示,您也可以在其他工具中对其进行近似处理。例如,'perf report' 有 -g (–call-graph) 选项,您可以试验;选项之一是“调用者”,用于基于调用者的反向调用图显示。

3.4.3 Sysprof 文档

似乎没有关于 Sysprof 的任何文档,但也许这是因为它非常不言自明。然而,Sysprof 网站在这里:Sysprof,Linux 的系统范围性能分析器

3.5 LTTng(Linux 跟踪工具包,下一代)

3.5.1 LTTng 设置

在本节中,我们假设您已经执行了“常规设置”部分中概述的基本设置。LTTng 通过 ssh 连接到目标系统上运行。

3.5.2收集和查看跟踪

一旦您应用了上述提交并构建并启动了您的镜像(您需要构建 core-image-sato-sdk 镜像或使用“常规设置”部分中描述的其他方法之一),您就可以开始追踪。

3.5.2.1收集和查看目标上的跟踪(外壳内)

首先,从主机,ssh 到目标:

$ ssh -l root 192.168.1.47
The authenticity of host '192.168.1.47 (192.168.1.47)' can't be established.
RSA key fingerprint is 23:bd:c8:b1:a8:71:52:00:ee:00:4f:64:9e:10:b9:7e.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.1.47' (RSA) to the list of known hosts.
root@192.168.1.47's password:

到达目标后,使用以下步骤创建跟踪:

root@crownbay:~# lttng create
Spawning a session daemon
Session auto-20121015-232120 created.
Traces will be written in /home/root/lttng-traces/auto-20121015-232120

启用要跟踪的事件(在本例中为所有内核事件):

root@crownbay:~# lttng enable-event --kernel --all
All kernel events are enabled in channel channel0

开始跟踪:

root@crownbay:~# lttng start
Tracing started for session auto-20121015-232120

然后在一段时间后或在运行要跟踪的特定工作负载后停止跟踪:

root@crownbay:~# lttng stop
Tracing stopped for session auto-20121015-232120

您现在可以在目标上以文本形式查看跟踪:

root@crownbay:~# lttng view
[23:21:56.989270399] (+?.?????????) sys_geteuid: { 1 }, { }
[23:21:56.989278081] (+0.000007682) exit_syscall: { 1 }, { ret = 0 }
[23:21:56.989286043] (+0.000007962) sys_pipe: { 1 }, { fildes = 0xB77B9E8C }
[23:21:56.989321802] (+0.000035759) exit_syscall: { 1 }, { ret = 0 }
[23:21:56.989329345] (+0.000007543) sys_mmap_pgoff: { 1 }, { addr = 0x0, len = 10485760, prot = 3, flags = 131362, fd = 4294967295, pgoff = 0 }
[23:21:56.989351694] (+0.000022349) exit_syscall: { 1 }, { ret = -1247805440 }
[23:21:56.989432989] (+0.000081295) sys_clone: { 1 }, { clone_flags = 0x411, newsp = 0xB5EFFFE4, parent_tid = 0xFFFFFFFF, child_tid = 0x0 }
[23:21:56.989477129] (+0.000044140) sched_stat_runtime: { 1 }, { comm = "lttng-consumerd", tid = 1193, runtime = 681660, vruntime = 43367983388 }
[23:21:56.989486697] (+0.000009568) sched_migrate_task: { 1 }, { comm = "lttng-consumerd", tid = 1193, prio = 20, orig_cpu = 1, dest_cpu = 1 }
[23:21:56.989508418] (+0.000021721) hrtimer_init: { 1 }, { hrtimer = 3970832076, clockid = 1, mode = 1 }
[23:21:56.989770462] (+0.000262044) hrtimer_cancel: { 1 }, { hrtimer = 3993865440 }
[23:21:56.989771580] (+0.000001118) hrtimer_cancel: { 0 }, { hrtimer = 3993812192 }
[23:21:56.989776957] (+0.000005377) hrtimer_expire_entry: { 1 }, { hrtimer = 3993865440, now = 79815980007057, function = 3238465232 }
[23:21:56.989778145] (+0.000001188) hrtimer_expire_entry: { 0 }, { hrtimer = 3993812192, now = 79815980008174, function = 3238465232 }
[23:21:56.989791695] (+0.000013550) softirq_raise: { 1 }, { vec = 1 }
[23:21:56.989795396] (+0.000003701) softirq_raise: { 0 }, { vec = 1 }
[23:21:56.989800635] (+0.000005239) softirq_raise: { 0 }, { vec = 9 }
[23:21:56.989807130] (+0.000006495) sched_stat_runtime: { 1 }, { comm = "lttng-consumerd", tid = 1193, runtime = 330710, vruntime = 43368314098 }
[23:21:56.989809993] (+0.000002863) sched_stat_runtime: { 0 }, { comm = "lttng-sessiond", tid = 1181, runtime = 1015313, vruntime = 36976733240 }
[23:21:56.989818514] (+0.000008521) hrtimer_expire_exit: { 0 }, { hrtimer = 3993812192 }
[23:21:56.989819631] (+0.000001117) hrtimer_expire_exit: { 1 }, { hrtimer = 3993865440 }
[23:21:56.989821866] (+0.000002235) hrtimer_start: { 0 }, { hrtimer = 3993812192, function = 3238465232, expires = 79815981000000, softexpires = 79815981000000 }
[23:21:56.989822984] (+0.000001118) hrtimer_start: { 1 }, { hrtimer = 3993865440, function = 3238465232, expires = 79815981000000, softexpires = 79815981000000 }
[23:21:56.989832762] (+0.000009778) softirq_entry: { 1 }, { vec = 1 }
[23:21:56.989833879] (+0.000001117) softirq_entry: { 0 }, { vec = 1 }
[23:21:56.989838069] (+0.000004190) timer_cancel: { 1 }, { timer = 3993871956 }
[23:21:56.989839187] (+0.000001118) timer_cancel: { 0 }, { timer = 3993818708 }
[23:21:56.989841492] (+0.000002305) timer_expire_entry: { 1 }, { timer = 3993871956, now = 79515980, function = 3238277552 }
[23:21:56.989842819] (+0.000001327) timer_expire_entry: { 0 }, { timer = 3993818708, now = 79515980, function = 3238277552 }
[23:21:56.989854831] (+0.000012012) sched_stat_runtime: { 1 }, { comm = "lttng-consumerd", tid = 1193, runtime = 49237, vruntime = 43368363335 }
[23:21:56.989855949] (+0.000001118) sched_stat_runtime: { 0 }, { comm = "lttng-sessiond", tid = 1181, runtime = 45121, vruntime = 36976778361 }
[23:21:56.989861257] (+0.000005308) sched_stat_sleep: { 1 }, { comm = "kworker/1:1", tid = 21, delay = 9451318 }
[23:21:56.989862374] (+0.000001117) sched_stat_sleep: { 0 }, { comm = "kworker/0:0", tid = 4, delay = 9958820 }
[23:21:56.989868241] (+0.000005867) sched_wakeup: { 0 }, { comm = "kworker/0:0", tid = 4, prio = 120, success = 1, target_cpu = 0 }
[23:21:56.989869358] (+0.000001117) sched_wakeup: { 1 }, { comm = "kworker/1:1", tid = 21, prio = 120, success = 1, target_cpu = 1 }
[23:21:56.989877460] (+0.000008102) timer_expire_exit: { 1 }, { timer = 3993871956 }
[23:21:56.989878577] (+0.000001117) timer_expire_exit: { 0 }, { timer = 3993818708 }
.
.
.

您现在可以安全地销毁跟踪会话(请注意,这不会删除跟踪 - 它仍然存在于 ~/lttng-traces 中):

root@crownbay:~# lttng destroy
Session auto-20121015-232120 destroyed at /home/root

请注意,跟踪保存在与 'lttng create' 返回的同名目录中,位于 ~/lttng-traces 目录下(请注意,您可以通过将自己的名称提供给 'lttng create' 来更改此名称):

root@crownbay:~# ls -al ~/lttng-traces
drwxrwx---    3 root     root          1024 Oct 15 23:21 .
drwxr-xr-x    5 root     root          1024 Oct 15 23:57 ..
drwxrwx---    3 root     root          1024 Oct 15 23:21 auto-20121015-232120

3.5.2.2收集和查看目标上的用户空间跟踪(在 shell 内)

对于 LTTng 用户空间跟踪,您需要有一个正确检测的用户空间程序。对于此示例,我们将使用 lttng-ust 构建生成的“hello”测试程序。

'hello' 测试程序没有通过 lttng-ust 构建安装在 rootfs 上,所以我们需要手动复制它。首先 cd 进入包含 hello 可执行文件的构建目录:

$ cd build/tmp/work/core2_32-poky-linux/lttng-ust/2.0.5-r0/git/tests/hello/.libs

将其复制到目标机器上:

$ scp hello root@192.168.1.20:

您现在已经在目标上安装了已检测的 lttng 'hello world' 测试程序,可以进行测试了。

首先,从主机,ssh 到目标:

$ ssh -l root 192.168.1.47
The authenticity of host '192.168.1.47 (192.168.1.47)' can't be established.
RSA key fingerprint is 23:bd:c8:b1:a8:71:52:00:ee:00:4f:64:9e:10:b9:7e.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.1.47' (RSA) to the list of known hosts.
root@192.168.1.47's password:

到达目标后,使用以下步骤创建跟踪:

root@crownbay:~# lttng create
Session auto-20190303-021943 created.
Traces will be written in /home/root/lttng-traces/auto-20190303-021943

启用要跟踪的事件(在本例中为所有用户空间事件):

root@crownbay:~# lttng enable-event --userspace --all
All UST events are enabled in channel channel0

开始跟踪:

root@crownbay:~# lttng start
Tracing started for session auto-20190303-021943

运行检测的 hello world 程序:

root@crownbay:~# ./hello
Hello, World!
Tracing... done.

然后在一段时间后或在运行要跟踪的特定工作负载后停止跟踪:

root@crownbay:~# lttng stop
Tracing stopped for session auto-20190303-021943

您现在可以在目标上以文本形式查看跟踪:

root@crownbay:~# lttng view
[02:31:14.906146544] (+?.?????????) hello:1424 ust_tests_hello:tptest: { cpu_id = 1 }, { intfield = 0, intfield2 = 0x0, longfield = 0, netintfield = 0, netintfieldhex = 0x0, arrfield1 = [ [0] = 1, [1] = 2, [2] = 3 ], arrfield2 = "test", _seqfield1_length = 4, seqfield1 = [ [0] = 116, [1] = 101, [2] = 115, [3] = 116 ], _seqfield2_length = 4,  seqfield2 = "test", stringfield = "test", floatfield = 2222, doublefield = 2, boolfield = 1 }
[02:31:14.906170360] (+0.000023816) hello:1424 ust_tests_hello:tptest: { cpu_id = 1 }, { intfield = 1, intfield2 = 0x1, longfield = 1, netintfield = 1, netintfieldhex = 0x1, arrfield1 = [ [0] = 1, [1] = 2, [2] = 3 ], arrfield2 = "test", _seqfield1_length = 4, seqfield1 = [ [0] = 116, [1] = 101, [2] = 115, [3] = 116 ], _seqfield2_length = 4, seqfield2 = "test", stringfield = "test", floatfield = 2222, doublefield = 2, boolfield = 1 }
[02:31:14.906183140] (+0.000012780) hello:1424 ust_tests_hello:tptest: { cpu_id = 1 }, { intfield = 2, intfield2 = 0x2, longfield = 2, netintfield = 2, netintfieldhex = 0x2, arrfield1 = [ [0] = 1, [1] = 2, [2] = 3 ], arrfield2 = "test", _seqfield1_length = 4, seqfield1 = [ [0] = 116, [1] = 101, [2] = 115, [3] = 116 ], _seqfield2_length = 4, seqfield2 = "test", stringfield = "test", floatfield = 2222, doublefield = 2, boolfield = 1 }
[02:31:14.906194385] (+0.000011245) hello:1424 ust_tests_hello:tptest: { cpu_id = 1 }, { intfield = 3, intfield2 = 0x3, longfield = 3, netintfield = 3, netintfieldhex = 0x3, arrfield1 = [ [0] = 1, [1] = 2, [2] = 3 ], arrfield2 = "test", _seqfield1_length = 4, seqfield1 = [ [0] = 116, [1] = 101, [2] = 115, [3] = 116 ], _seqfield2_length = 4, seqfield2 = "test", stringfield = "test", floatfield = 2222, doublefield = 2, boolfield = 1 }
.
.
.

您现在可以安全地销毁跟踪会话(请注意,这不会删除跟踪 - 它仍然存在于 ~/lttng-traces 中):

root@crownbay:~# lttng destroy
Session auto-20190303-021943 destroyed at /home/root

3.5.3 LTTng 文档

您可以在LTTng 文档站点上找到主要的LTTng 文档。此站点上的文档适用于在 Linux 环境中工作并对高效软件跟踪感兴趣的中高级软件开发人员。

有关 LTTng 的一般信息,请访问LTTng 项目站点。您可以在此站点上找到“入门”链接,该链接会将您带到 LTTng 快速入门。

3.6跟踪

blktrace 是一个用于跟踪和报告低级磁盘 I/O 的工具。blktrace 提供了等式的一半跟踪;它的输出可以通过管道传输到 blkparse 程序,该程序以人类可读的形式呈现数据并进行一些基本分析:

3.6.1 blktrace 设置

在本节中,我们假设您已经执行了“常规设置”部分中概述的基本设置。

blktrace 是在目标系统上运行的应用程序。您可以在目标上运行整个 blktrace 和 blkparse 管道,或者您可以在目标上以“监听”模式运行 blktrace 并让 blktrace 和 blkparse 在主机上收集和分析数据(请参阅下面的“远程使用 blktrace ”部分) . 对于本节的其余部分,我们假设您已通过 ssh 连接到主机并将在目标上运行 blkrace。

3.6.2基本 blktrace 用法

要记录跟踪,只需运行“blktrace”命令,为其指定要跟踪活动的块设备的名称:

root@crownbay:~# blktrace /dev/sdc

在另一个 shell 中,执行要跟踪的工作负载。

root@crownbay:/media/sdc# rm linux-2.6.19.2.tar.bz2; wget http://downloads.yoctoproject.org/mirror/sources/linux-2.6.19.2.tar.bz2; sync
Connecting to downloads.yoctoproject.org (140.211.169.59:80)
linux-2.6.19.2.tar.b 100% \|*******************************\| 41727k 0:00:00 ETA

在 blktrace shell 中按 Ctrl-C 停止跟踪。它将显示记录了多少事件,以及每个 cpu 的文件大小(blktrace 在每个 cpu 内核缓冲区中记录跟踪,并简单地将它们转储到用户空间,以便 blkparse 稍后进行合并和排序)。

^C=== sdc ===CPU  0:                 7082 events,      332 KiB dataCPU  1:                 1578 events,       74 KiB dataTotal:                  8660 events (dropped 0),      406 KiB data

如果检查保存到磁盘的文件,您会看到多个文件,每个 CPU 一个,文件名的第一部分是设备名称:

root@crownbay:~# ls -al
drwxr-xr-x    6 root     root          1024 Oct 27 22:39 .
drwxr-sr-x    4 root     root          1024 Oct 26 18:24 ..
-rw-r--r--    1 root     root        339938 Oct 27 22:40 sdc.blktrace.0
-rw-r--r--    1 root     root         75753 Oct 27 22:40 sdc.blktrace.1

要查看跟踪事件,只需在包含跟踪文件的目录中调用“blkparse”,为其提供构成文件名第一部分的设备名称:

root@crownbay:~# blkparse sdc8,32   1        1     0.000000000  1225  Q  WS 3417048 + 8 [jbd2/sdc-8]8,32   1        2     0.000025213  1225  G  WS 3417048 + 8 [jbd2/sdc-8]8,32   1        3     0.000033384  1225  P   N [jbd2/sdc-8]8,32   1        4     0.000043301  1225  I  WS 3417048 + 8 [jbd2/sdc-8]8,32   1        0     0.000057270     0  m   N cfq1225 insert_request8,32   1        0     0.000064813     0  m   N cfq1225 add_to_rr8,32   1        5     0.000076336  1225  U   N [jbd2/sdc-8] 18,32   1        0     0.000088559     0  m   N cfq workload slice:1508,32   1        0     0.000097359     0  m   N cfq1225 set_active wl_prio:0 wl_type:18,32   1        0     0.000104063     0  m   N cfq1225 Not idling. st->count:18,32   1        0     0.000112584     0  m   N cfq1225 fifo=  (null)8,32   1        0     0.000118730     0  m   N cfq1225 dispatch_insert8,32   1        0     0.000127390     0  m   N cfq1225 dispatched a request8,32   1        0     0.000133536     0  m   N cfq1225 activate rq, drv=18,32   1        6     0.000136889  1225  D  WS 3417048 + 8 [jbd2/sdc-8]8,32   1        7     0.000360381  1225  Q  WS 3417056 + 8 [jbd2/sdc-8]8,32   1        8     0.000377422  1225  G  WS 3417056 + 8 [jbd2/sdc-8]8,32   1        9     0.000388876  1225  P   N [jbd2/sdc-8]8,32   1       10     0.000397886  1225  Q  WS 3417064 + 8 [jbd2/sdc-8]8,32   1       11     0.000404800  1225  M  WS 3417064 + 8 [jbd2/sdc-8]8,32   1       12     0.000412343  1225  Q  WS 3417072 + 8 [jbd2/sdc-8]8,32   1       13     0.000416533  1225  M  WS 3417072 + 8 [jbd2/sdc-8]8,32   1       14     0.000422121  1225  Q  WS 3417080 + 8 [jbd2/sdc-8]8,32   1       15     0.000425194  1225  M  WS 3417080 + 8 [jbd2/sdc-8]8,32   1       16     0.000431968  1225  Q  WS 3417088 + 8 [jbd2/sdc-8]8,32   1       17     0.000435251  1225  M  WS 3417088 + 8 [jbd2/sdc-8]8,32   1       18     0.000440279  1225  Q  WS 3417096 + 8 [jbd2/sdc-8]8,32   1       19     0.000443911  1225  M  WS 3417096 + 8 [jbd2/sdc-8]8,32   1       20     0.000450336  1225  Q  WS 3417104 + 8 [jbd2/sdc-8]8,32   1       21     0.000454038  1225  M  WS 3417104 + 8 [jbd2/sdc-8]8,32   1       22     0.000462070  1225  Q  WS 3417112 + 8 [jbd2/sdc-8]8,32   1       23     0.000465422  1225  M  WS 3417112 + 8 [jbd2/sdc-8]8,32   1       24     0.000474222  1225  I  WS 3417056 + 64 [jbd2/sdc-8]8,32   1        0     0.000483022     0  m   N cfq1225 insert_request8,32   1       25     0.000489727  1225  U   N [jbd2/sdc-8] 18,32   1        0     0.000498457     0  m   N cfq1225 Not idling. st->count:18,32   1        0     0.000503765     0  m   N cfq1225 dispatch_insert8,32   1        0     0.000512914     0  m   N cfq1225 dispatched a request8,32   1        0     0.000518851     0  m   N cfq1225 activate rq, drv=2...8,32   0        0    58.515006138     0  m   N cfq3551 complete rqnoidle 18,32   0     2024    58.516603269     3  C  WS 3156992 + 16 [0]8,32   0        0    58.516626736     0  m   N cfq3551 complete rqnoidle 18,32   0        0    58.516634558     0  m   N cfq3551 arm_idle: 8 group_idle: 08,32   0        0    58.516636933     0  m   N cfq schedule dispatch8,32   1        0    58.516971613     0  m   N cfq3551 slice expired t=08,32   1        0    58.516982089     0  m   N cfq3551 sl_used=13 disp=6 charge=13 iops=0 sect=808,32   1        0    58.516985511     0  m   N cfq3551 del_from_rr8,32   1        0    58.516990819     0  m   N cfq3551 put_queueCPU0 (sdc):Reads Queued:           0,        0KiB       Writes Queued:         331,   26,284KiBRead Dispatches:        0,        0KiB       Write Dispatches:      485,   40,484KiBReads Requeued:         0            Writes Requeued:         0Reads Completed:        0,        0KiB       Writes Completed:      511,   41,000KiBRead Merges:            0,        0KiB       Write Merges:           13,      160KiBRead depth:             0            Write depth:             2IO unplugs:            23            Timer unplugs:           0
CPU1 (sdc):Reads Queued:           0,        0KiB       Writes Queued:         249,   15,800KiBRead Dispatches:        0,        0KiB       Write Dispatches:       42,    1,600KiBReads Requeued:         0            Writes Requeued:         0Reads Completed:        0,        0KiB       Writes Completed:       16,    1,084KiBRead Merges:            0,        0KiB       Write Merges:           40,      276KiBRead depth:             0            Write depth:             2IO unplugs:            30            Timer unplugs:           1Total (sdc):Reads Queued:           0,        0KiB       Writes Queued:         580,   42,084KiBRead Dispatches:        0,        0KiB       Write Dispatches:      527,   42,084KiBReads Requeued:         0            Writes Requeued:         0Reads Completed:        0,        0KiB       Writes Completed:      527,   42,084KiBRead Merges:            0,        0KiB       Write Merges:           53,      436KiBIO unplugs:            53            Timer unplugs:           1Throughput (R/W): 0KiB/s / 719KiB/s
Events (sdc): 6,592 entries
Skips: 0 forward (0 -   0.0%)
Input file sdc.blktrace.0 added
Input file sdc.blktrace.1 added

该报告显示了在 blktrace 数据中发现的每个事件,以及运行期间整体块 I/O 流量的摘要。您可以查看 blkparse联机帮助页以了解跟踪列表中显示的每个字段的含义。

3.6.2.1直播模式

blktrace 和 blkparse 是从头开始设计的,能够在“管道模式”下一起运行,其中 blktrace 的标准输出可以直接输入 blkparse 的标准输入:

root@crownbay:~# blktrace /dev/sdc -o - | blkparse -i -

这使长期跟踪会话能够运行而无需向磁盘写入任何内容,并允许用户通过在屏幕上滚动或传递跟踪输出时查看跟踪输出来“实时”查找跟踪数据中的某些条件以及管道中的另一个程序,例如 grep,可用于识别和捕获感兴趣的条件。

实际上还有另一个 blktrace 命令将上述管道实现为单个命令,因此用户不必费心输入上述命令序列:

root@crownbay:~# btrace /dev/sdc

3.6.2.2远程使用 blktrace

因为 blktrace 跟踪块 I/O 并同时通常将其跟踪数据写入块设备,并且通常因为使被跟踪的设备与跟踪器写入的设备相同并不是一个好主意,所以 blktrace 提供通过提供对通过网络发送所有跟踪数据的本机支持,一种在完全不干扰被跟踪设备的情况下进行跟踪的方法。

要让 blktrace 在此模式下运行,请在使用 -l 选项跟踪的目标系统以及要跟踪的设备上启动 blktrace:

root@crownbay:~# blktrace -l /dev/sdc
server: waiting for connections...

在主机系统上,使用 -h 选项连接到目标系统,并将设备传递给它以进行跟踪:

$ blktrace -d /dev/sdc -h 192.168.1.43
blktrace: connecting to 192.168.1.43
blktrace: connected!

在目标系统上,您应该看到:

server: connection from 192.168.1.43

在另一个 shell 中,执行要跟踪的工作负载。

root@crownbay:/media/sdc# rm linux-2.6.19.2.tar.bz2; wget http://downloads.yoctoproject.org/mirror/sources/linux-2.6.19.2.tar.bz2; sync
Connecting to downloads.yoctoproject.org (140.211.169.59:80)
linux-2.6.19.2.tar.b 100% \|*******************************\| 41727k 0:00:00 ETA

完成后,在主机系统上执行 Ctrl-C 以停止跟踪:

^C=== sdc ===CPU  0:                 7691 events,      361 KiB dataCPU  1:                 4109 events,      193 KiB dataTotal:                 11800 events (dropped 0),      554 KiB data

在目标系统上,您还应该看到刚刚结束的跟踪的跟踪摘要:

server: end of run for 192.168.1.43:sdc
=== sdc ===CPU  0:                 7691 events,      361 KiB dataCPU  1:                 4109 events,      193 KiB dataTotal:                 11800 events (dropped 0),      554 KiB data

主机上的 blktrace 实例会将目标输出保存在主机名时间戳目录中:

$ ls -al
drwxr-xr-x   10 root     root          1024 Oct 28 02:40 .
drwxr-sr-x    4 root     root          1024 Oct 26 18:24 ..
drwxr-xr-x    2 root     root          1024 Oct 28 02:40 192.168.1.43-2012-10-28-02:40:56

cd 进入该目录以查看输出文件:

$ ls -l
-rw-r--r--    1 root     root        369193 Oct 28 02:44 sdc.blktrace.0
-rw-r--r--    1 root     root        197278 Oct 28 02:44 sdc.blktrace.1

并使用设备名称在主机系统上运行 blkparse:

$ blkparse sdc8,32   1        1     0.000000000  1263  Q  RM 6016 + 8 [ls]8,32   1        0     0.000036038     0  m   N cfq1263 alloced8,32   1        2     0.000039390  1263  G  RM 6016 + 8 [ls]8,32   1        3     0.000049168  1263  I  RM 6016 + 8 [ls]8,32   1        0     0.000056152     0  m   N cfq1263 insert_request8,32   1        0     0.000061600     0  m   N cfq1263 add_to_rr8,32   1        0     0.000075498     0  m   N cfq workload slice:300...8,32   0        0   177.266385696     0  m   N cfq1267 arm_idle: 8 group_idle: 08,32   0        0   177.266388140     0  m   N cfq schedule dispatch8,32   1        0   177.266679239     0  m   N cfq1267 slice expired t=08,32   1        0   177.266689297     0  m   N cfq1267 sl_used=9 disp=6 charge=9 iops=0 sect=568,32   1        0   177.266692649     0  m   N cfq1267 del_from_rr8,32   1        0   177.266696560     0  m   N cfq1267 put_queueCPU0 (sdc):Reads Queued:           0,        0KiB       Writes Queued:         270,   21,708KiBRead Dispatches:       59,    2,628KiB       Write Dispatches:      495,   39,964KiBReads Requeued:         0            Writes Requeued:         0Reads Completed:       90,    2,752KiB       Writes Completed:      543,   41,596KiBRead Merges:            0,        0KiB       Write Merges:            9,      344KiBRead depth:             2            Write depth:             2IO unplugs:            20            Timer unplugs:           1
CPU1 (sdc):Reads Queued:         688,    2,752KiB       Writes Queued:         381,   20,652KiBRead Dispatches:       31,      124KiB       Write Dispatches:       59,    2,396KiBReads Requeued:         0            Writes Requeued:         0Reads Completed:        0,        0KiB       Writes Completed:       11,      764KiBRead Merges:          598,    2,392KiB       Write Merges:           88,      448KiBRead depth:             2            Write depth:             2IO unplugs:            52            Timer unplugs:           0Total (sdc):Reads Queued:         688,    2,752KiB       Writes Queued:         651,   42,360KiBRead Dispatches:       90,    2,752KiB       Write Dispatches:      554,   42,360KiBReads Requeued:         0            Writes Requeued:         0Reads Completed:       90,    2,752KiB       Writes Completed:      554,   42,360KiBRead Merges:          598,    2,392KiB       Write Merges:           97,      792KiBIO unplugs:            72            Timer unplugs:           1Throughput (R/W): 15KiB/s / 238KiB/s
Events (sdc): 9,301 entries
Skips: 0 forward (0 -   0.0%)

您应该看到跟踪事件和摘要,就像在目标上运行相同的命令一样。

3.6.2.3通过“ftrace”跟踪块 I/O

也可以仅使用“跟踪事件”子系统来跟踪块 I/O ,如果您不想费心处理用户空间工具,这对于临时跟踪非常有用。

要为给定设备启用跟踪,请使用 /sys/block/xxx/trace/enable,其中 xxx 是设备名称。例如,这将启用对 /dev/sdc 的跟踪:

root@crownbay:/sys/kernel/debug/tracing# echo 1 > /sys/block/sdc/trace/enable

选择要跟踪的设备后,选择“blk”跟踪器将打开 blk 跟踪器:

root@crownbay:/sys/kernel/debug/tracing# cat available_tracers
blk function_graph function noproot@crownbay:/sys/kernel/debug/tracing# echo blk > current_tracer

执行您感兴趣的工作负载:

root@crownbay:/sys/kernel/debug/tracing# cat /media/sdc/testfile.txt

并查看输出(请注意,此处我们使用“trace_pipe”而不是 trace 来捕获此跟踪 - 这允许我们在管道上等待数据出现):

root@crownbay:/sys/kernel/debug/tracing# cat trace_pipecat-3587  [001] d..1  3023.276361:   8,32   Q   R 1699848 + 8 [cat]cat-3587  [001] d..1  3023.276410:   8,32   m   N cfq3587 allocedcat-3587  [001] d..1  3023.276415:   8,32   G   R 1699848 + 8 [cat]cat-3587  [001] d..1  3023.276424:   8,32   P   N [cat]cat-3587  [001] d..2  3023.276432:   8,32   I   R 1699848 + 8 [cat]cat-3587  [001] d..1  3023.276439:   8,32   m   N cfq3587 insert_requestcat-3587  [001] d..1  3023.276445:   8,32   m   N cfq3587 add_to_rrcat-3587  [001] d..2  3023.276454:   8,32   U   N [cat] 1cat-3587  [001] d..1  3023.276464:   8,32   m   N cfq workload slice:150cat-3587  [001] d..1  3023.276471:   8,32   m   N cfq3587 set_active wl_prio:0 wl_type:2cat-3587  [001] d..1  3023.276478:   8,32   m   N cfq3587 fifo=  (null)cat-3587  [001] d..1  3023.276483:   8,32   m   N cfq3587 dispatch_insertcat-3587  [001] d..1  3023.276490:   8,32   m   N cfq3587 dispatched a requestcat-3587  [001] d..1  3023.276497:   8,32   m   N cfq3587 activate rq, drv=1cat-3587  [001] d..2  3023.276500:   8,32   D   R 1699848 + 8 [cat]

这将关闭对指定设备的跟踪:

root@crownbay:/sys/kernel/debug/tracing# echo 0 > /sys/block/sdc/trace/enable

3.6.3 blktrace 文档

本节中讨论的命令的联机版本可以在此处找到:

  • https://linux.die.net/man/8/blktrace

  • https://linux.die.net/man/1/blkparse

  • https://linux.die.net/man/8/btrace

上述手册页以及其他 blktrace 实用程序(btt、blkiomon 等)的手册页可以在 blktrace 工具 git repo 的 /doc 目录中找到:

$ git clone git://git.kernel.dk/blktrace.git

3每个 Yocto 跟踪工具的基本用法(附示例)相关推荐

  1. SQLTracker跟踪工具用法

    下载地址:SQLTrackerOracle跟踪工具(支持64位)_oracle跟踪工具,oracle跟踪工具-Oracle工具类资源-CSDN下载 1.解压.D:\SQLTracker 2.运行D:\ ...

  2. 项目管理和缺陷跟踪工具Redmine

    官网: http://www.redmine.org/ http://demo.redmine.org/ 下载: http://www.redmine.org/projects/redmine/wik ...

  3. 反编译工具jad简单用法

    反编译工具jad简单用法 下载地址: [url]http://58.251.57.206/down1?cid=B99584EFA6154A13E5C0B273C3876BD4CC8CE672& ...

  4. TCP流嗅探和连接跟踪工具tcpick

    TCP流嗅探和连接跟踪工具tcpick 由于网络通信协议众多,TCP连接状态众多,所以TCP分析较为复杂.Kali Linux提供一款专用工具tcpick.该工具支持在线实时嗅探和离线文件嗅探.它可以 ...

  5. 路由跟踪工具0trace

    路由跟踪工具0trace 0trace是Kali Linuz自带的一个Shell脚本工具.该工具基于已建立的TCP连接,进行路由探测,实现侦查和防火墙穿透功能.使用时候,用户首先使用Telnet之类工 ...

  6. 被动路由跟踪工具InTrace

    被动路由跟踪工具InTrace InTrace是一款类似于Traceroute的路由跟踪工具.但它不同的是,他不主动发送数据包,而是通过监听当前主机和目标主机的数据包,进行分析,从而获取路由信息.这样 ...

  7. 消灭Bug!推荐7款优秀的开源Bug跟踪工具

    摘要: 在这个充满 bug 的世界里,最遥远的距离不是生与死,而是你亲手制造的 bug 就在你眼前,你却怎么都找不到她. 在过去几年里,程序员改变着世界,然而,我们手中诞生的每个新网站或 APP 都面 ...

  8. jsonutil java_Java实现的JSONUtil工具类与用法示例

    本文实例讲述了Java实现的JSONUtil工具类与用法.分享给大家供大家参考,具体如下: import java.util.HashMap; import java.util.Map; import ...

  9. 测试跟踪工具Bugzilla介绍

    测试跟踪工具Bugzilla介绍 也许你还没有看到一个错误管理系统所具有的价值:也许你正被大量的测试数据所淹没,而迫切的需要一个产品缺陷的记录及跟踪的好帮手:也许你正在通过如:电子表格.数据库等各种方 ...

  10. 开发工具:收集12 个顶级 Bug 跟踪工具,值得收藏!

    作者 | Eugene Stepnov 译者 | 张健欣 策划 | Tina 来源丨架构头条(ID:ArchFront) 在如今的在线世界,几乎所有的公司都面临它们产品中的 bugs,并且考虑如何管理 ...

最新文章

  1. NLP开源 CMU Sphinx
  2. C++:new 和 delete
  3. python文本分析的开源工具_重磅开源:TN文本分析语言
  4. java界面编辑教程_java程序设计基础教程第六章图形用户界面编辑.docx
  5. Spring框架----代理的分析
  6. go 中recover捕获异常
  7. 在anaconda环境下搭建python3.5 + jupyter sparkR,scala,pyspark
  8. web通用组件+Axure原型+Axure元件库+Axure后台管理系统框架模板+大屏数据可视化元件库+智慧社区管理系统大屏+图表组件+表单组合+智慧数据看板+通用大屏图表原件库+电脑端常用组件
  9. 微信小程序如何使用视频组件
  10. Sphinx入门操作
  11. IdentityHashMap 源代码
  12. uni-app ios 添加测试新设备,只需下载描述文件
  13. Build过程(objc)
  14. 安装应用提示 该文件包与具有同一名称的现有文件包存在冲突。 无法安装
  15. 桌面文件夹不见了怎么恢复?4招教你找回消失的文件夹
  16. java飘落的雪花_[Java教程]树叶飘落、雪花飘落等同时多个图片飘落
  17. P3396 哈希冲突 (根号算法)
  18. CSS3选择器及优先级
  19. 关于拦截器与过滤器使用场景、拦截器与过滤器的区别整理
  20. 叶筱静受邀主持北大“2019数字新金融领袖峰会”圆桌论坛

热门文章

  1. 解决BIEE中地图FOI数据过多
  2. RK3399外设驱动之RTC驱动(一)
  3. 自己制做python3.6精简绿色版
  4. 五一,读孔明《诫子书》有感
  5. 动词过去式和过去分词不规则变化
  6. 中国城市云计算首站现场会成都隆重举行
  7. mysql临键锁_innodb临键锁锁定范围
  8. 大学生计算机应用大赛广告设计,计算机应用工程系第四届“经纬在线杯”网页广告设计大赛圆满结束...
  9. SpringBoot整合Magic-Api
  10. 右下角出现“测试模式 win7 内部版本7601”如何去掉?