perf record 的默认行为是什么?
What is the default behavior of perf record?
我很清楚perf
总是记录一个或多个事件,采样可以基于计数器或基于时间。但是当 -e
和 -F
开关没有给出时,perf record
的默认行为是什么? perf-record
的联机帮助页没有告诉您它在这种情况下的作用。
默认事件为cycles
,perf record
后运行perf script
可见。在那里,您还可以看到默认的采样行为是基于时间的,因为循环数不是常数。默认频率为 4000 Hz,可以在 source code 中看到,并通过将文件大小或样本数与指定 -F 4000
的记录进行比较来检查。
perf wiki 表示频率为 1000 Hz,但对于 3.4 及更高版本的内核,这不再适用。
perf record
中的默认事件 selection 在 user-space perf 工具中完成,该工具通常作为 linux 内核的一部分分发。使用 linux 内核源目录中的 make perf-src-tar-gz
,我们可以制作 tar gz 以快速重建或从 https://mirrors.edge.kernel.org/pub/linux/kernel/tools/perf 下载这样的 tar。还有几个 linux 内核源代码的在线“LXR”交叉引用查看器,可以像 grep 一样使用它来了解 perf 内部结构。
有 select 性能记录的默认事件列表 (evlist) 的功能:__perf_evlist__add_default
of tools/perf/util/evlist.c file:
int __perf_evlist__add_default(struct evlist *evlist, bool precise)
{
struct evsel *evsel = perf_evsel__new_cycles(precise);
evlist__add(evlist, evsel);
return 0;
}
在从选项解析的事件为零的情况下从 perf 记录实现调用:tools/perf/builtin-record.c: int cmd_record()
rec->evlist->core.nr_entries == 0 &&
__perf_evlist__add_default(rec->evlist, !record.opts.no_samples)
和perf_evsel__new_cycles
will ask for hardware event cycles (PERF_TYPE_HARDWARE + PERF_COUNT_HW_CPU_CYCLES) with optional kernel sampling, and max precise (check modifiers in man perf-list,是EIP sampling skid workarounds using PEBS or IBS):
struct evsel *perf_evsel__new_cycles(bool precise)
{
struct perf_event_attr attr = {
.type = PERF_TYPE_HARDWARE,
.config = PERF_COUNT_HW_CPU_CYCLES,
.exclude_kernel = !perf_event_can_profile_kernel(),
};
struct evsel *evsel;
/*
* Now let the usual logic to set up the perf_event_attr defaults
* to kick in when we return and before perf_evsel__open() is called.
*/
evsel = evsel__new(&attr);
evsel->precise_max = true;
/* use asprintf() because free(evsel) assumes name is allocated */
if (asprintf(&evsel->name, "cycles%s%s%.*s",
(attr.precise_ip || attr.exclude_kernel) ? ":" : "",
attr.exclude_kernel ? "u" : "",
attr.precise_ip ? attr.precise_ip + 1 : 0, "ppp") < 0)
return evsel;
}
如果失败 perf_event_open(无法访问硬件周期采样,例如在没有虚拟化 PMU 的虚拟化环境中),则会故障回复到 软件 cpu-clock 在 tools/perf/builtin-record.c: int record__open()
which calls perf_evsel__fallback()
of tools/perf/util/evsel.c 中采样:
bool perf_evsel__fallback(struct evsel *evsel, int err,
char *msg, size_t msgsize)
{
if ((err == ENOENT || err == ENXIO || err == ENODEV) &&
evsel->core.attr.type == PERF_TYPE_HARDWARE &&
evsel->core.attr.config == PERF_COUNT_HW_CPU_CYCLES) {
/*
* If it's cycles then fall back to hrtimer based
* cpu-clock-tick sw counter, which is always available even if
* no PMU support.
*/
scnprintf(msg, msgsize, "%s", "The cycles event is not supported, trying to fall back to cpu-clock-ticks");
evsel->core.attr.type = PERF_TYPE_SOFTWARE;
evsel->core.attr.config = PERF_COUNT_SW_CPU_CLOCK;
return true;
} ...
}
我很清楚perf
总是记录一个或多个事件,采样可以基于计数器或基于时间。但是当 -e
和 -F
开关没有给出时,perf record
的默认行为是什么? perf-record
的联机帮助页没有告诉您它在这种情况下的作用。
默认事件为cycles
,perf record
后运行perf script
可见。在那里,您还可以看到默认的采样行为是基于时间的,因为循环数不是常数。默认频率为 4000 Hz,可以在 source code 中看到,并通过将文件大小或样本数与指定 -F 4000
的记录进行比较来检查。
perf wiki 表示频率为 1000 Hz,但对于 3.4 及更高版本的内核,这不再适用。
perf record
中的默认事件 selection 在 user-space perf 工具中完成,该工具通常作为 linux 内核的一部分分发。使用 linux 内核源目录中的 make perf-src-tar-gz
,我们可以制作 tar gz 以快速重建或从 https://mirrors.edge.kernel.org/pub/linux/kernel/tools/perf 下载这样的 tar。还有几个 linux 内核源代码的在线“LXR”交叉引用查看器,可以像 grep 一样使用它来了解 perf 内部结构。
有 select 性能记录的默认事件列表 (evlist) 的功能:__perf_evlist__add_default
of tools/perf/util/evlist.c file:
int __perf_evlist__add_default(struct evlist *evlist, bool precise)
{
struct evsel *evsel = perf_evsel__new_cycles(precise);
evlist__add(evlist, evsel);
return 0;
}
在从选项解析的事件为零的情况下从 perf 记录实现调用:tools/perf/builtin-record.c: int cmd_record()
rec->evlist->core.nr_entries == 0 &&
__perf_evlist__add_default(rec->evlist, !record.opts.no_samples)
和perf_evsel__new_cycles
will ask for hardware event cycles (PERF_TYPE_HARDWARE + PERF_COUNT_HW_CPU_CYCLES) with optional kernel sampling, and max precise (check modifiers in man perf-list,是EIP sampling skid workarounds using PEBS or IBS):
struct evsel *perf_evsel__new_cycles(bool precise)
{
struct perf_event_attr attr = {
.type = PERF_TYPE_HARDWARE,
.config = PERF_COUNT_HW_CPU_CYCLES,
.exclude_kernel = !perf_event_can_profile_kernel(),
};
struct evsel *evsel;
/*
* Now let the usual logic to set up the perf_event_attr defaults
* to kick in when we return and before perf_evsel__open() is called.
*/
evsel = evsel__new(&attr);
evsel->precise_max = true;
/* use asprintf() because free(evsel) assumes name is allocated */
if (asprintf(&evsel->name, "cycles%s%s%.*s",
(attr.precise_ip || attr.exclude_kernel) ? ":" : "",
attr.exclude_kernel ? "u" : "",
attr.precise_ip ? attr.precise_ip + 1 : 0, "ppp") < 0)
return evsel;
}
如果失败 perf_event_open(无法访问硬件周期采样,例如在没有虚拟化 PMU 的虚拟化环境中),则会故障回复到 软件 cpu-clock 在 tools/perf/builtin-record.c: int record__open()
which calls perf_evsel__fallback()
of tools/perf/util/evsel.c 中采样:
bool perf_evsel__fallback(struct evsel *evsel, int err,
char *msg, size_t msgsize)
{
if ((err == ENOENT || err == ENXIO || err == ENODEV) &&
evsel->core.attr.type == PERF_TYPE_HARDWARE &&
evsel->core.attr.config == PERF_COUNT_HW_CPU_CYCLES) {
/*
* If it's cycles then fall back to hrtimer based
* cpu-clock-tick sw counter, which is always available even if
* no PMU support.
*/
scnprintf(msg, msgsize, "%s", "The cycles event is not supported, trying to fall back to cpu-clock-ticks");
evsel->core.attr.type = PERF_TYPE_SOFTWARE;
evsel->core.attr.config = PERF_COUNT_SW_CPU_CLOCK;
return true;
} ...
}