perf report/script: Support percent and multiple range in --time option
v6 cover perf report/script: Support percent and multiple range in --time option
v6:
---
1. Create a new option "--timestamp-boundary" in perf record.
Currently '--buildid-all' is not enabled by default. So the walking on all samples is the default operation. There is no big overhead to calculate the timestamp boundary in process_sample_event handler once we already go through all samples. So the timestamp boundary calculation is enabled by default when '--buildid-all' is not enabled.
While if '--buildid-all' is enabled, we creates a new option "--timestamp-boundary" for user to decide if it enables the timestamp boundary calculation.
Impacted patch:
---------------
perf record: Get the first sample time and last sample time
2. Fix the merge issue with the latest perf/core branch. No functional changes.
Impacted patch:
---------------
perf report: support time percent and multiple time ranges perf script: support time percent and multiple time ranges
v5:
---
1. There is an issue that the sample walking can only work when '--buildid-all' is not enabled. So we need to let the walking be able to work even if '--buildid-all' is enabled and let the processing skips the dso hit marking for this case.
2. Check if first/last sample time is recorded in perf data file. If it's not recorded, return error message to user.
Patched modified in v5:
perf record: Get the first sample time and last sample time perf report: support time percent and multiple time ranges perf script: support time percent and multiple time ranges
v4:
---
1. Use perf script time style for timestamp printing. Also add with the printing of sample duration. For example:
perf report --header
time of first sample : 5276531.323099
time of last sample : 5276555.345625
sample duration : 24022.526 ms
2. Fix an invalid time string issue. For example,
perf script --time 10%/10x12321xsdfdasfdsafdsafdsa
Now in code, it uses strtol to replace atoi.
3. Remove perf_time__skip_sample, only uses perf_time__ranges_skip_sample in perf report/perf script.
v3:
---
1. Move the definitions of first_sample_time/last_sample_time from perf_session and struct record to perf_evlist and update the related code.
v2:
---
1. This patch creates a new header feature type HEADER_SAMPLE_TIME and related ops. Save the first sample time and the last sample time to the feature section in perf file header.
2. Add checking for last element in time range.
For example, select the first and second 10% time slices. perf report --time 10%/1,10%/2
Note that now it includes the last element in [10%, 20%] but it doesn't include the last element in [0, 10%). It's to avoid the overlap.
Following patches are changed:
perf header: Record first sample time and last sample time in perf file header perf record: Get the first sample time and last sample time
perf util: Create function to perform multiple time range checking
v1: initial post
----------------
Current perf report/script/... have a --time option to limit the time range of output. But it only supports the absolute time.
The patch series extend this option to let it support percent of time and support the multiple time ranges.
For example:
1. Select the second 10% time slice
perf report --time 10%/2
2. Select from 0% to 10% time slice
perf report --time 0%-10%
It also support the multiple time ranges.
3. Select the first and second 10% time slices
perf report --time 10%/1,10%/2
4. Select from 0% to 10% and 30% to 40% slices
perf report --time 0%-10%,30%-40%
Jin Yao (6):
perf header: Record first sample time and last sample time in perf
file header
perf record: Get the first sample time and last sample time perf util: Create function to parse time percent
perf util: Create function to perform multiple time range checking perf report: support time percent and multiple time ranges perf script: support time percent and multiple time ranges
tools/perf/Documentation/perf-record.txt | 3 + tools/perf/Documentation/perf-report.txt | 16 ++ tools/perf/Documentation/perf-script.txt | 16 ++ tools/perf/Documentation/perf.data-file-format.txt | 4 + tools/perf/builtin-record.c | 18 +- tools/perf/builtin-report.c | 31 ++- tools/perf/builtin-script.c | 34 ++- tools/perf/util/evlist.h | 2 + tools/perf/util/header.c | 60 ++++++ tools/perf/util/header.h | 1 + tools/perf/util/time-utils.c | 233 +++++++++++++++++++-- tools/perf/util/time-utils.h | 6 + 12 files changed, 398 insertions(+), 26 deletions(-)
Signature · 1 line
2.7.4
PATCH v6 1/6 perf header: Record first sample time and last sample time in perf file header
perf report/script/... have a --time option to limit the time range of output. That's very useful to slice large traces, e.g. when processing the output of perf script for some analysis.
But right now --time only supports absolute time. Also there is no fast way to get the start/end times of a given trace except for looking at it. This makes it hard to e.g. only decode the first half of the trace, which is useful for parallelization of scripts
Another problem is that perf records are variable size and there is no synchronization mechanism. So the only way to find the last sample reliably would be to walk all samples. But we want to avoid that in perf report/... because it is already quite expensive. That is why storing the first sample time and last sample time in perf record is better.
This patch creates a new header feature type HEADER_SAMPLE_TIME and related ops. Save the first sample time and the last sample time to the feature section in perf file header.
Change log:
-----------
v4: Use perf script time style for timestamp printing. Also add with
the printing of sample duration.
v3: Remove the definitions of first_sample_time/last_sample_time from
perf_session. Just define them in perf_evlist
--- tools/perf/Documentation/perf.data-file-format.txt | 4 ++ tools/perf/util/evlist.h | 2 + tools/perf/util/header.c | 60 ++++++++++++++++++++++ tools/perf/util/header.h | 1 + 4 files changed, 67 insertions(+)
tools/perf/Documentation/perf.data-file-format.txt +4 -0
@@ -238,6 +238,10 @@ struct auxtrace_index { struct auxtrace_index_entry entries[PERF_AUXTRACE_INDEX_ENTRY_COUNT]; }; + HEADER_SAMPLE_TIME = 21, + +Two uint64_t for the time of first sample and the time of last sample. + other bits are reserved and should ignored for now HEADER_FEAT_BITS = 256,
tools/perf/util/evlist.h +2 -0
@@ -50,6 +50,8 @@ struct perf_evlist {struct perf_evsel *selected;struct events_stats stats;struct perf_env *env; +u64 first_sample_time; +u64 last_sample_time; };struct perf_evsel_str_handler {
tools/perf/util/header.c +60 -0
96 lines not on this page · raw message
tools/perf/util/header.h +1 -0
@@ -33,6 +33,7 @@ enum {HEADER_AUXTRACE ,HEADER_STAT ,HEADER_CACHE , +HEADER_SAMPLE_TIME ,HEADER_LAST_FEATURE , HEADER_FEAT_BITS = 256, };
Signature · 1 line
2.7.4
Arnaldo Carvalho de Melo
maintainerRe: [PATCH v6 1/6] perf header: Record first sample time and last sample time in perf file header
Em Mon, Nov 06, 2017 at 08:21:06PM +0800, Jin Yao escreveu:
Quoted 15 lines
perf report/script/... have a --time option to limit the time range of output. That's very useful to slice large traces, e.g. when processing the output of perf script for some analysis.
But right now --time only supports absolute time. Also there is no fast way to get the start/end times of a given trace except for looking at it. This makes it hard to e.g. only decode the first half of the trace, which is useful for parallelization of scripts
Another problem is that perf records are variable size and there is no synchronization mechanism. So the only way to find the last sample reliably would be to walk all samples. But we want to avoid that in perf report/... because it is already quite expensive. That is why storing the first sample time and last sample time in perf record is better.
Quoted 3 lines
This patch creates a new header feature type
HEADER_SAMPLE_TIMEand related ops. Save the first sample time and the last sample time to the feature section in perf file header
I'll add a clarification here, right after your text:
". That will be done when, for instance, processing build-ids, where we already have to process all samples to create the build-id table, take advantage of that to further amortize that processing by storing HEADER_SAMPLE_TIME to make 'perf report/script' faster when using --time."
Quoted 165 lines
Change log:
-----------
v4: Use perf script time style for timestamp printing. Also add with
the printing of sample duration.v3: Remove the definitions of first_sample_time/last_sample_time from
perf_session. Just define them inperf_evlistSigned-off-by Jin Yao <[email protected]>--- tools/perf/Documentation/perf.data-file-format.txt | 4 ++ tools/perf/util/evlist.h | 2 + tools/perf/util/header.c | 60 ++++++++++++++++++++++ tools/perf/util/header.h | 1 + 4 files changed, 67 insertions(+)tools/perf/Documentation/perf.data-file-format.txt +4
-0@@ -238,6 +238,10 @@ struct auxtrace_index { struct auxtrace_index_entry entries[PERF_AUXTRACE_INDEX_ENTRY_COUNT]; }; + HEADER_SAMPLE_TIME = 21, + +Two uint64_t for the time of first sample and the time of last sample. + other bits are reserved and should ignored for now HEADER_FEAT_BITS = 256,tools/perf/util/evlist.h +2
-0@@ -50,6 +50,8 @@ struct perf_evlist {struct perf_evsel *selected;struct events_stats stats;struct perf_env *env; +u64 first_sample_time; +u64 last_sample_time; };struct perf_evsel_str_handler {tools/perf/util/header.c +60
-0@@ -17,6 +17,7 @@#include <sys/types.h> #include <sys/utsname.h> #include <unistd.h> +#include <linux/time64.h> #include "evlist.h" #include "evsel.h" @@ -36,6 +37,7 @@#include <api/fs/fs.h> #include "asm/bug.h" #include "tool.h" +#include "time-utils.h" #include "sane_ctype.h" @@ -1181,6 +1183,20 @@ static int write_stat(struct feat_fd *ff __maybe_unused,return 0 ; } +static int write_sample_time (struct feat_fd *ff, +struct perf_evlist *evlist) +{ +int ret; + + ret =do_write (ff, &evlist->first_sample_time, +sizeof (evlist->first_sample_time)); +if (ret <0 ) +return ret; + +return do_write (ff, &evlist->last_sample_time, +sizeof (evlist->last_sample_time)); +} +static void print_hostname (struct feat_fd *ff,FILE *fp) {fprintf (fp,"# hostname : %s\n" , ff->ph->env.hostname);@@ -1506,6 +1522,28 @@ static void print_group_desc(struct feat_fd *ff, FILE *fp) } } +static void print_sample_time (struct feat_fd *ff,FILE *fp) +{ +struct perf_session *session; +char time_buf[32 ]; +double d; + + session = container_of(ff->ph, struct perf_session, header); + +timestamp__scnprintf_usec (session->evlist->first_sample_time, + time_buf,sizeof (time_buf)); +fprintf (fp,"# time of first sample : %s\n" , time_buf); + +timestamp__scnprintf_usec (session->evlist->last_sample_time, + time_buf,sizeof (time_buf)); +fprintf (fp,"# time of last sample : %s\n" , time_buf); + + d = (double )(session->evlist->last_sample_time - + session->evlist->first_sample_time) /NSEC_PER_MSEC ; + +fprintf (fp,"# sample duration : %10.3f ms\n" , d); +} +static int __event_process_build_id (struct build_id_event *bev,char *filename,struct perf_session *session)@@ -2147,6 +2185,27 @@ static int process_cache(struct feat_fd *ff, void *data __maybe_unused)return -1 ; } +static int process_sample_time (struct feat_fd *ff,void *data__maybe_unused ) +{ +struct perf_session *session; +u64 first_sample_time, last_sample_time; +int ret; + + session = container_of(ff->ph, struct perf_session, header); + + ret =do_read_u64 (ff, &first_sample_time); +if (ret) +return -1 ; + + ret =do_read_u64 (ff, &last_sample_time); +if (ret) +return -1 ; + + session->evlist->first_sample_time = first_sample_time; + session->evlist->last_sample_time = last_sample_time; +return 0 ; +} +struct feature_ops {int (*write)(struct feat_fd *ff,struct perf_evlist *evlist);void (*print)(struct feat_fd *ff,FILE *fp);@@ -2204,6 +2263,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {FEAT_OPN (AUXTRACE , auxtrace, false),FEAT_OPN (STAT , stat, false),FEAT_OPN (CACHE , cache, true), + FEAT_OPR(SAMPLE_TIME, sample_time, false), }; struct header_print_data {tools/perf/util/header.h +1
-0@@ -33,6 +33,7 @@ enum {HEADER_AUXTRACE ,HEADER_STAT ,HEADER_CACHE , +HEADER_SAMPLE_TIME ,HEADER_LAST_FEATURE , HEADER_FEAT_BITS = 256, };Signature · 1 line
2.7.4
Re: [PATCH v6 1/6] perf header: Record first sample time and last sample time in perf file header
Quoted 20 lines · Arnaldo Carvalho de Melo
Em Mon, Nov 06, 2017 at 08:21:06PM +0800, Jin Yao escreveu:
Quoted 15 lines
perf report/script/... have a --time option to limit the time range of output. That's very useful to slice large traces, e.g. when processing the output of perf script for some analysis.
But right now --time only supports absolute time. Also there is no fast way to get the start/end times of a given trace except for looking at it. This makes it hard to e.g. only decode the first half of the trace, which is useful for parallelization of scripts
Another problem is that perf records are variable size and there is no synchronization mechanism. So the only way to find the last sample reliably would be to walk all samples. But we want to avoid that in perf report/... because it is already quite expensive. That is why storing the first sample time and last sample time in perf record is better.
Quoted 3 lines
This patch creates a new header feature type
HEADER_SAMPLE_TIMEand related ops. Save the first sample time and the last sample time to the feature section in perf file header
Quoted 8 lines
I'll add a clarification here, right after your text:
". That will be done when, for instance, processing build-ids, where we already have to process all samples to create the build-id table, take advantage of that to further amortize that processing by storing
HEADER_SAMPLE_TIMEto make 'perf report/script' faster when using --time."
Thanks Arnaldo! The clarification is good!
Thanks
Jin Yao
PATCH v6 2/6 perf record: Get the first sample time and last sample time
In reply to Jin YaoIn perf record, it's walked on all samples yet. So it's very easy to get the first/last samples and save the time to perf file header via the function write_sample_time().
In later, perf report/script will fetch the time from perf file header.
Change log:
-----------
v6: Currently '--buildid-all' is not enabled at default. So the walking
on all samples is the default operation. There is no big overhead to calculate the timestamp boundary in process_sample_event handler once we already go through all samples. So the timestamp boundary calculation is enabled by default when '--buildid-all' is not enabled.
While if '--buildid-all' is enabled, we creates a new option "--timestamp-boundary" for user to decide if it enables the timestamp boundary calculation.
v5: There is an issue that the sample walking can only work when
'--buildid-all' is not enabled. So we need to let the walking be able to work even if '--buildid-all' is enabled and let the processing skips the dso hit marking for this case.
At first, I want to provide a new option "--record-time-boundaries". While after consideration, I think a new option is not very necessary.
v3: Remove the definitions of first_sample_time and last_sample_time
from struct record and directly save them in perf_evlist.
--- tools/perf/Documentation/perf-record.txt | 3 +++ tools/perf/builtin-record.c | 18 +++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-)
tools/perf/Documentation/perf-record.txt +3 -0
@@ -430,6 +430,9 @@ Configure all used events to run in user space. --timestamp-filename Append timestamp to output file name. +--timestamp-boundary:: +Record timestamp boundary (time of first/last samples). + --switch-output[=mode]:: Generate multiple perf.data files, timestamp prefixed, switching to a new one based on 'mode' value:
tools/perf/builtin-record.c +15 -3
@@ -78,6 +78,7 @@ struct record {bool no_buildid_cache_set;bool buildid_all;bool timestamp_filename; +bool timestamp_boundary;struct switch_output switch_output;unsigned long long samples; };
@@ -391,8 +392,15 @@ static int process_sample_event(struct perf_tool *tool, { struct record *rec = container_of(tool, struct record, tool); - rec->samples++; +if (rec->evlist->first_sample_time ==0 ) + rec->evlist->first_sample_time = sample->time; + + rec->evlist->last_sample_time = sample->time; +if (rec->buildid_all) +return 0 ; + + rec->samples++;return build_id__mark_dso_hit (tool, event, sample, evsel, machine); }
@@ -417,9 +425,11 @@ static int process_buildids(struct record *rec)/* * If --buildid-all is given, it marks all DSO regardless of hits, -* so no need to process samples. +* so no need to process samples. But if timestamp_boundary is enabled, +* it still needs to walk on all samples to get the timestamps of +* first/last samples. */ -if (rec->buildid_all) +if (rec->buildid_all && !rec->timestamp_boundary) rec->tool.sample =NULL ;return perf_session__process_events (session);
@@ -1579,6 +1589,8 @@ static struct option __record_options[] = { "Record build-id of all DSOs regardless of hits"),OPT_BOOLEAN (0 ,"timestamp-filename" , &record.timestamp_filename,"append timestamp to output filename" ), +OPT_BOOLEAN (0 ,"timestamp-boundary" , &record.timestamp_boundary, +"Record timestamp boundary (time of first/last samples)" ), OPT_STRING_OPTARG_SET(0, "switch-output", &record.switch_output.str, &record.switch_output.set, "signal,size,time", "Switch output when receive SIGUSR2 or cross size,time threshold",
Signature · 1 line
2.7.4
Arnaldo Carvalho de Melo
maintainerRe: [PATCH v6 2/6] perf record: Get the first sample time and last sample time
Em Mon, Nov 06, 2017 at 08:21:07PM +0800, Jin Yao escreveu:
Quoted 1 line
In perf record, it's walked on all samples yet. So it's very easy to get
"In the default 'perf record' configuration, all samples are processed, to create the HEADER_BUILD_ID table. So..."
Quoted 4 lines
the first/last samples and save the time to perf file header via the function
write_sample_time().In later, perf report/script will fetch the time from perf file header.
Later, at post processing time, ...
Quoted 4 lines
Change log:
-----------
v6: Currently '--buildid-all' is not enabled at default. So the walking
--no-buildid-all, right?
Quoted 95 lines
on all samples is the default operation. There is no big overhead to calculate the timestamp boundary in
process_sample_eventhandler once we already go through all samples. So the timestamp boundary calculation is enabled by default when '--buildid-all' is not enabled.While if '--buildid-all' is enabled, we creates a new option "--timestamp-boundary" for user to decide if it enables the timestamp boundary calculation.
v5: There is an issue that the sample walking can only work when
'--buildid-all' is not enabled. So we need to let the walking be able to work even if '--buildid-all' is enabled and let the processing skips the dso hit marking for this case.At first, I want to provide a new option "--record-time-boundaries". While after consideration, I think a new option is not very necessary.
v3: Remove the definitions of
first_sample_timeandlast_sample_time
from struct record and directly save them inperf_evlist.Signed-off-by Jin Yao <[email protected]>--- tools/perf/Documentation/perf-record.txt | 3 +++ tools/perf/builtin-record.c | 18 +++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-)tools/perf/Documentation/perf-record.txt +3
-0@@ -430,6 +430,9 @@ Configure all used events to run in user space. --timestamp-filename Append timestamp to output file name. +--timestamp-boundary:: +Record timestamp boundary (time of first/last samples). + --switch-output[=mode]:: Generate multiple perf.data files, timestamp prefixed, switching to a new one based on 'mode' value:tools/perf/builtin-record.c +15
-3@@ -78,6 +78,7 @@ struct record {bool no_buildid_cache_set;bool buildid_all;bool timestamp_filename; +bool timestamp_boundary;struct switch_output switch_output;unsigned long long samples; };@@ -391,8 +392,15 @@ static int process_sample_event(struct perf_tool *tool, { struct record *rec = container_of(tool, struct record, tool); - rec->samples++; +if (rec->evlist->first_sample_time ==0 ) + rec->evlist->first_sample_time = sample->time; + + rec->evlist->last_sample_time = sample->time; +if (rec->buildid_all) +return 0 ; + + rec->samples++;return build_id__mark_dso_hit (tool, event, sample, evsel, machine); }@@ -417,9 +425,11 @@ static int process_buildids(struct record *rec)/* * If --buildid-all is given, it marks all DSO regardless of hits, -* so no need to process samples. +* so no need to process samples. But if timestamp_boundary is enabled, +* it still needs to walk on all samples to get the timestamps of +* first/last samples. */ -if (rec->buildid_all) +if (rec->buildid_all && !rec->timestamp_boundary) rec->tool.sample =NULL ;return perf_session__process_events (session);@@ -1579,6 +1589,8 @@ static struct option __record_options[] = { "Record build-id of all DSOs regardless of hits"),OPT_BOOLEAN (0 ,"timestamp-filename" , &record.timestamp_filename,"append timestamp to output filename" ), +OPT_BOOLEAN (0 ,"timestamp-boundary" , &record.timestamp_boundary, +"Record timestamp boundary (time of first/last samples)" ), OPT_STRING_OPTARG_SET(0, "switch-output", &record.switch_output.str, &record.switch_output.set, "signal,size,time", "Switch output when receive SIGUSR2 or cross size,time threshold",Signature · 1 line
2.7.4
Re: [PATCH v6 2/6] perf record: Get the first sample time and last sample time
Quoted 6 lines · Arnaldo Carvalho de Melo
Em Mon, Nov 06, 2017 at 08:21:07PM +0800, Jin Yao escreveu:
Quoted 1 line
In perf record, it's walked on all samples yet. So it's very easy to get
"In the default 'perf record' configuration, all samples are processed, to create the
HEADER_BUILD_IDtable. So..."
Quoted 7 lines
Quoted 4 lines
the first/last samples and save the time to perf file header via the function
write_sample_time().In later, perf report/script will fetch the time from perf file header.
Later, at post processing time, ...
Thanks Arnaldo! The update on patch description is good.
Quoted 7 lines
Quoted 4 lines
Change log:
-----------
v6: Currently '--buildid-all' is not enabled at default. So the walking--no-buildid-all, right?
No, it is '--buildid-all'. There is no '--no-buildid-all' available now.
See in __record_options[] (builtin-record.c),
OPT_BOOLEAN(0, "buildid-all", &record.buildid_all,
"Record build-id of all DSOs regardless of hits"),
So by default, buildid-all is set with false.
Thanks
Jin Yao
Quoted 95 lines
Quoted 95 lines
on all samples is the default operation. There is no big overhead to calculate the timestamp boundary in
process_sample_eventhandler once we already go through all samples. So the timestamp boundary calculation is enabled by default when '--buildid-all' is not enabled.While if '--buildid-all' is enabled, we creates a new option "--timestamp-boundary" for user to decide if it enables the timestamp boundary calculation.
v5: There is an issue that the sample walking can only work when
'--buildid-all' is not enabled. So we need to let the walking be able to work even if '--buildid-all' is enabled and let the processing skips the dso hit marking for this case.At first, I want to provide a new option "--record-time-boundaries". While after consideration, I think a new option is not very necessary.
v3: Remove the definitions of
first_sample_timeandlast_sample_time
from struct record and directly save them inperf_evlist.Signed-off-by Jin Yao <[email protected]>--- tools/perf/Documentation/perf-record.txt | 3 +++ tools/perf/builtin-record.c | 18 +++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-)tools/perf/Documentation/perf-record.txt +3
-0@@ -430,6 +430,9 @@ Configure all used events to run in user space. --timestamp-filename Append timestamp to output file name. +--timestamp-boundary:: +Record timestamp boundary (time of first/last samples). + --switch-output[=mode]:: Generate multiple perf.data files, timestamp prefixed, switching to a new one based on 'mode' value:tools/perf/builtin-record.c +15
-3@@ -78,6 +78,7 @@ struct record {bool no_buildid_cache_set;bool buildid_all;bool timestamp_filename; +bool timestamp_boundary;struct switch_output switch_output;unsigned long long samples; };@@ -391,8 +392,15 @@ static int process_sample_event(struct perf_tool *tool, { struct record *rec = container_of(tool, struct record, tool); - rec->samples++; +if (rec->evlist->first_sample_time ==0 ) + rec->evlist->first_sample_time = sample->time; + + rec->evlist->last_sample_time = sample->time; +if (rec->buildid_all) +return 0 ; + + rec->samples++;return build_id__mark_dso_hit (tool, event, sample, evsel, machine); }@@ -417,9 +425,11 @@ static int process_buildids(struct record *rec)/* * If --buildid-all is given, it marks all DSO regardless of hits, -* so no need to process samples. +* so no need to process samples. But if timestamp_boundary is enabled, +* it still needs to walk on all samples to get the timestamps of +* first/last samples. */ -if (rec->buildid_all) +if (rec->buildid_all && !rec->timestamp_boundary) rec->tool.sample =NULL ;return perf_session__process_events (session);@@ -1579,6 +1589,8 @@ static struct option __record_options[] = { "Record build-id of all DSOs regardless of hits"),OPT_BOOLEAN (0 ,"timestamp-filename" , &record.timestamp_filename,"append timestamp to output filename" ), +OPT_BOOLEAN (0 ,"timestamp-boundary" , &record.timestamp_boundary, +"Record timestamp boundary (time of first/last samples)" ), OPT_STRING_OPTARG_SET(0, "switch-output", &record.switch_output.str, &record.switch_output.set, "signal,size,time", "Switch output when receive SIGUSR2 or cross size,time threshold",Signature · 1 line
2.7.4
PATCH v6 3/6 perf util: Create function to parse time percent
In reply to Jin YaoCurrent perf report/script/... have a --time option to limit the time range of output. But right now it only supports absolute time.
For easy using, now it can support a percent of time usage.
For example:
1. Select the second 10% time slice
perf report --time 10%/2
2. Select from 0% to 10% time slice
perf report --time 0%-10%
It also support the multiple time ranges.
3. Select the first and second 10% time slices
perf report --time 10%/1,10%/2
4. Select from 0% to 10% and 30% to 40% slices
perf report --time 0%-10%,30%-40%
Change log:
-----------
v4: An issue is found. Following passes.
perf script --time 10%/10x12321xsdfdasfdsafdsafdsa
Now it uses strtol to replace atoi.
--- tools/perf/util/time-utils.c | 205 ++++++++++++++++++++++++++++++++++++++++--- tools/perf/util/time-utils.h | 3 + 2 files changed, 196 insertions(+), 12 deletions(-)
tools/perf/util/time-utils.c +193 -12
@@ -5,6 +5,7 @@#include <time.h> #include <errno.h> #include <inttypes.h> +#include <math.h> #include "perf.h" #include "debug.h"
@@ -59,11 +60,10 @@ static int parse_timestr_sec_nsec(struct perf_time_interval *ptime,return 0 ; } -int perf_time__parse_str (struct perf_time_interval *ptime,const char *ostr) +static int split_start_end (char **start,char **end,const char *ostr,char ch) {char *start_str, *end_str;char *d, *str; -int rc =0 ;if (ostr ==NULL || *ostr =='\0' )return 0 ;
@@ -73,25 +73,35 @@ int perf_time__parse_str(struct perf_time_interval *ptime, const char *ostr)if (str ==NULL )return -ENOMEM ; - ptime->start =0 ; - ptime->end =0 ; - -/* str has the format: <start>,<stop> -* variations: <start>, -* ,<stop> -* , -*/ start_str = str; - d =strchr (start_str,',' ); + d =strchr (start_str, ch);if (d) { *d ='\0' ; ++d; } end_str = d; + *start = start_str; + *end = end_str; + +return 0 ; +} + +int perf_time__parse_str (struct perf_time_interval *ptime,const char *ostr) +{ +char *start_str =NULL , *end_str; +int rc; + + rc =split_start_end (&start_str, &end_str, ostr,',' ); +if (rc || !start_str) +return rc; + + ptime->start =0 ; + ptime->end =0 ; + rc =parse_timestr_sec_nsec (ptime, start_str, end_str); -free (str); +free (start_str);/* make sure end time is after start time if it was given */ if (rc ==0 && ptime->end && ptime->end < ptime->start)
@@ -103,6 +113,177 @@ int perf_time__parse_str(struct perf_time_interval *ptime, const char *ostr)return rc; } +static int parse_percent (double *pcnt,char *str) +{ +char *c; + + c =strchr (str,'%' ); +if (c) + *c ='\0' ; +else +return -1 ; + + *pcnt =atof (str) /100.0 ; + +return 0 ; +} + +static int percent_slash_split (char *str,struct perf_time_interval *ptime, +u64 start,u64 end) +{ +char *p, *end_str; +double pcnt, start_pcnt, end_pcnt; +u64 total = end - start; +int i; + +/* +* Example: +* 10%/2: select the second 10% slice and the third 10% slice +*/ + +/* We can modify this string since the original one is copied */ + p =strchr (str,'/' ); +if (!p) +return -1 ; + + *p ='\0' ; +if (parse_percent (&pcnt, str) <0 ) +return -1 ; + + p++; + i = (int )strtol (p, &end_str,10 ); +if (*end_str) +return -1 ; + +if (pcnt <=0.0 ) +return -1 ; + + start_pcnt = pcnt * (i -1 ); + end_pcnt = pcnt * i; + +if (start_pcnt <0.0 || start_pcnt >1.0 || + end_pcnt <0.0 || end_pcnt >1.0 ) { +return -1 ; + } + + ptime->start = start +round (start_pcnt * total); + ptime->end = start +round (end_pcnt * total); + +return 0 ; +} + +static int percent_dash_split (char *str,struct perf_time_interval *ptime, +u64 start,u64 end) +{ +char *start_str =NULL , *end_str; +double start_pcnt, end_pcnt; +u64 total = end - start; +int ret; + +/* +* Example: 0%-10% +*/ + + ret =split_start_end (&start_str, &end_str, str,'-' ); +if (ret || !start_str) +return ret; + +if ((parse_percent (&start_pcnt, start_str) !=0 ) || + (parse_percent (&end_pcnt, end_str) !=0 )) { +free (start_str); +return -1 ; + } + +free (start_str); + +if (start_pcnt <0.0 || start_pcnt >1.0 || + end_pcnt <0.0 || end_pcnt >1.0 || + start_pcnt > end_pcnt) { +return -1 ; + } + + ptime->start = start +round (start_pcnt * total); + ptime->end = start +round (end_pcnt * total); + +return 0 ; +} + +typedef int (*time_pecent_split )(char *,struct perf_time_interval *, +u64 start,u64 end); + +static int percent_comma_split (struct perf_time_interval *ptime_buf,int num, +const char *ostr,u64 start,u64 end, +time_pecent_split func) +{ +char *str, *p1, *p2; +int len, ret, i =0 ; + + str =strdup (ostr); +if (str ==NULL ) +return -ENOMEM ; + + len =strlen (str); + p1 = str; + +while (p1 < str + len) { +if (i >= num) { +free (str); +return -1 ; + } + + p2 =strchr (p1,',' ); +if (p2) + *p2 ='\0' ; + + ret = (func)(p1, &ptime_buf[i], start, end); +if (ret <0 ) { +free (str); +return -1 ; + } + +pr_debug ("start time %d: %" PRIu64", " , i, ptime_buf[i].start); +pr_debug ("end time %d: %" PRIu64"\n" , i, ptime_buf[i].end); + + i++; + +if (p2) + p1 = p2 +1 ; +else +break ; + } + +free (str); +return i; +} + +int perf_time__percent_parse_str (struct perf_time_interval *ptime_buf,int num, +const char *ostr,u64 start,u64 end) +{ +char *c; + +/* +* ostr example: +* 10%/2,10%/3: select the second 10% slice and the third 10% slice +* 0%-10%,30%-40%: multiple time range +*/ + +memset (ptime_buf,0 ,sizeof (*ptime_buf) * num); + + c =strchr (ostr,'/' ); +if (c) { +return percent_comma_split (ptime_buf, num, ostr, start, + end, percent_slash_split); + } + + c =strchr (ostr,'-' ); +if (c) { +return percent_comma_split (ptime_buf, num, ostr, start, + end, percent_dash_split); + } + +return -1 ; +} +bool perf_time__skip_sample (struct perf_time_interval *ptime,u64 timestamp) {/* if time is not set don't drop sample */
tools/perf/util/time-utils.h +3 -0
@@ -12,6 +12,9 @@ int parse_nsec_time(const char *str, u64 *ptime);int perf_time__parse_str (struct perf_time_interval *ptime,const char *ostr); +int perf_time__percent_parse_str (struct perf_time_interval *ptime_buf,int num, +const char *ostr,u64 start,u64 end); +bool perf_time__skip_sample (struct perf_time_interval *ptime,u64 timestamp);int timestamp__scnprintf_usec (u64 timestamp,char *buf,size_t sz);
Signature · 1 line
2.7.4
PATCH v6 4/6 perf util: Create function to perform multiple time range checking
In reply to Jin YaoPrevious patch supports the multiple time range.
For example, select the first and second 10% time slices. perf report --time 10%/1,10%/2
We need a function to check if a timestamp is in the ranges of [0, 10%) and [10%, 20%].
Note that it includes the last element in [10%, 20%] but it doesn't include the last element in [0, 10%). It's to avoid the overlap.
This patch implments a new function perf_time__ranges_skip_sample for this checking.
Change log:
-----------
v4: Let perf_time__ranges_skip_sample be compatible with
perf_time__skip_sample when only one time range.
--- tools/perf/util/time-utils.c | 28 ++++++++++++++++++++++++++++ tools/perf/util/time-utils.h | 3 +++ 2 files changed, 31 insertions(+)
tools/perf/util/time-utils.c +28 -0
@@ -299,6 +299,34 @@ bool perf_time__skip_sample(struct perf_time_interval *ptime, u64 timestamp)return false; } +bool perf_time__ranges_skip_sample (struct perf_time_interval *ptime_buf, +int num,u64 timestamp) +{ +struct perf_time_interval *ptime; +int i; + +if ((timestamp ==0 ) || (num ==0 )) +return false; + +if (num ==1 ) +return perf_time__skip_sample (&ptime_buf[0 ], timestamp); + +/* +* start/end of multiple time ranges must be valid. +*/ +for (i =0 ; i < num; i++) { + ptime = &ptime_buf[i]; + +if (timestamp >= ptime->start && + ((timestamp < ptime->end && i < num -1 ) || + (timestamp <= ptime->end && i == num -1 ))) { +break ; + } + } + +return (i == num) ? true : false; +} +int timestamp__scnprintf_usec (u64 timestamp,char *buf,size_t sz) {u64 sec = timestamp /NSEC_PER_SEC ;
tools/perf/util/time-utils.h +3 -0
@@ -17,6 +17,9 @@ int perf_time__percent_parse_str(struct perf_time_interval *ptime_buf, int num,bool perf_time__skip_sample (struct perf_time_interval *ptime,u64 timestamp); +bool perf_time__ranges_skip_sample (struct perf_time_interval *ptime_buf, +int num,u64 timestamp); +int timestamp__scnprintf_usec (u64 timestamp,char *buf,size_t sz);int fetch_current_timestamp (char *buf,size_t sz);
Signature · 1 line
2.7.4
PATCH v6 5/6 perf report: support time percent and multiple time ranges
In reply to Jin Yaoperf report has a --time option to limit the time range of output. It only supports absolute time.
Now this option is extended to support multiple time ranges and support the percent of time.
For example:
1. Select the first and second 10% time slices
perf report --time 10%/1,10%/2
2. Select from 0% to 10% and 30% to 40% slices
perf report --time 0%-10%,30%-40%
Change log:
-----------
v6: Fix the merge issue with latest perf/core branch.
No functional changes.
v5: Add checking of first/last sample time to detect if it's recorded
in perf.data. If it's not recorded, returns error message to user.
v4: Remove perf_time__skip_sample, only uses perf_time__ranges_skip_sample
v3: Since the definitions of first_sample_time/last_sample_time
are moved from perf_session to perf_evlist so change the related code.
--- tools/perf/Documentation/perf-report.txt | 16 ++++++++++++++++ tools/perf/builtin-report.c | 31 ++++++++++++++++++++++++++----- 2 files changed, 42 insertions(+), 5 deletions(-)
tools/perf/Documentation/perf-report.txt +16 -0
@@ -402,6 +402,22 @@ OPTIONS stop time is not given (i.e, time string is 'x.y,') then analysis goes to end of file. + Also support time percent with multipe time range. Time string is + 'a%/n,b%/m,...' or 'a%-b%,c%-%d,...'. The maximum number of slices is 10. + + For example: + Select the second 10% time slice + perf report --time 10%/2 + + Select from 0% to 10% time slice + perf report --time 0%-10% + + Select the first and second 10% time slices + perf report --time 10%/1,10%/2 + + Select from 0% to 10% and 30% to 40% slices + perf report --time 0%-10%,30%-40% + --itrace:: Options for decoding instruction tracing data. The options are:
tools/perf/builtin-report.c +26 -5
@@ -51,6 +51,8 @@#include <sys/stat.h> #include <unistd.h> +#define PTIME_RANGE_MAX 10 +struct report {struct perf_tool tool;struct perf_session *session;
@@ -68,7 +70,8 @@ struct report {const char *cpu_list;const char *symbol_filter_str;const char *time_str; -struct perf_time_interval ptime; +struct perf_time_interval ptime_range[PTIME_RANGE_MAX ]; +int range_num;float min_percent;u64 nr_entries;u64 queue_size;
@@ -185,8 +188,10 @@ static int process_sample_event(struct perf_tool *tool, };int ret =0 ; -if (perf_time__skip_sample (&rep->ptime, sample->time)) +if (perf_time__ranges_skip_sample (rep->ptime_range, rep->range_num, + sample->time)) {return 0 ; + }if (machine__resolve (machine, &al, sample) <0 ) { pr_debug("problem processing %d event, skipping it.\n",
@@ -1073,9 +1078,25 @@ int cmd_report(int argc, const char **argv)if (symbol__init (&session->header.env) <0 ) goto error; -if (perf_time__parse_str (&report.ptime, report.time_str) !=0 ) { -pr_err ("Invalid time string\n" ); -return -EINVAL ; +if (perf_time__parse_str (report.ptime_range, report.time_str) !=0 ) { +if (session->evlist->first_sample_time ==0 && + session->evlist->last_sample_time ==0 ) { +pr_err ("No first/last sample time in perf data\n" ); +return -EINVAL ; + } + + report.range_num =perf_time__percent_parse_str ( + report.ptime_range,PTIME_RANGE_MAX , + report.time_str, + session->evlist->first_sample_time, + session->evlist->last_sample_time); + +if (report.range_num <0 ) { +pr_err ("Invalid time string\n" ); +return -EINVAL ; + } + }else { + report.range_num =1 ; }sort__setup_elide (stdout);
Signature · 1 line
2.7.4
PATCH v6 6/6 perf script: support time percent and multiple time ranges
In reply to Jin Yaoperf script has a --time option to limit the time range of output. It only supports absolute time.
Now this option is extended to support multiple time ranges and support the percent of time.
For example:
1. Select the first and second 10% time slices
perf script --time 10%/1,10%/2
2. Select from 0% to 10% and 30% to 40% slices
perf script --time 0%-10%,30%-40%
Change log:
-----------
v6: Fix the merge issue with latest perf/core branch.
No functional changes.
v5: Add checking of first/last sample time to detect if it's recorded
in perf.data. If it's not recorded, returns error message to user.
v4: Remove perf_time__skip_sample, only uses perf_time__ranges_skip_sample
v3: Since the definitions of first_sample_time/last_sample_time
are moved from perf_session to perf_evlist so change the related code.
--- tools/perf/Documentation/perf-script.txt | 16 +++++++++++++++ tools/perf/builtin-script.c | 34 ++++++++++++++++++++++++++------ 2 files changed, 44 insertions(+), 6 deletions(-)
tools/perf/Documentation/perf-script.txt +16 -0
@@ -321,6 +321,22 @@ include::itrace.txt[] stop time is not given (i.e, time string is 'x.y,') then analysis goes to end of file. + Also support time percent with multipe time range. Time string is + 'a%/n,b%/m,...' or 'a%-b%,c%-%d,...'. The maximum number of slices is 10. + + For example: + Select the second 10% time slice + perf script --time 10%/2 + + Select from 0% to 10% time slice + perf script --time 0%-10% + + Select the first and second 10% time slices + perf script --time 10%/1,10%/2 + + Select from 0% to 10% and 30% to 40% slices + perf script --time 0%-10%,30%-40% + --max-blocks:: Set the maximum number of program blocks to print with brstackasm for each sample.
tools/perf/builtin-script.c +28 -6
@@ -1429,6 +1429,8 @@ static int perf_sample__fprintf_synth(struct perf_sample *sample,return 0 ; } +#define PTIME_RANGE_MAX 10 +struct perf_script {struct perf_tool tool;struct perf_session *session;
@@ -1442,7 +1444,8 @@ struct perf_script {struct thread_map *threads;int name_width;const char *time_str; -struct perf_time_interval ptime; +struct perf_time_interval ptime_range[PTIME_RANGE_MAX ]; +int range_num; };static int perf_evlist__max_name_len (struct perf_evlist *evlist)
@@ -1642,8 +1645,10 @@ static int process_sample_event(struct perf_tool *tool, struct perf_script *scr = container_of(tool, struct perf_script, tool);struct addr_location al; -if (perf_time__skip_sample (&scr->ptime, sample->time)) +if (perf_time__ranges_skip_sample (scr->ptime_range, scr->range_num, + sample->time)) {return 0 ; + }if (debug_mode) {if (sample->time < last_timestamp) {
@@ -3252,10 +3257,27 @@ int cmd_script(int argc, const char **argv) goto out_delete;/* needs to be parsed after looking up reference time */ -if (perf_time__parse_str (&script.ptime, script.time_str) !=0 ) { -pr_err ("Invalid time string\n" ); - err = -EINVAL ; - goto out_delete; +if (perf_time__parse_str (script.ptime_range, script.time_str) !=0 ) { +if (session->evlist->first_sample_time ==0 && + session->evlist->last_sample_time ==0 ) { +pr_err ("No first/last sample time in perf data\n" ); + err = -EINVAL ; + goto out_delete; + } + + script.range_num =perf_time__percent_parse_str ( + script.ptime_range,PTIME_RANGE_MAX , + script.time_str, + session->evlist->first_sample_time, + session->evlist->last_sample_time); + +if (script.range_num <0 ) { +pr_err ("Invalid time string\n" ); + err = -EINVAL ; + goto out_delete; + } + }else { + script.range_num =1 ; } err =__cmd_script (&script);
Signature · 1 line
2.7.4
Quoted 18 lines · Jin Yao
v6:
---
1. Create a new option "--timestamp-boundary" in perf record.Currently '--buildid-all' is not enabled by default. So the walking on all samples is the default operation. There is no big overhead to calculate the timestamp boundary in
process_sample_eventhandler once we already go through all samples. So the timestamp boundary calculation is enabled by default when '--buildid-all' is not enabled.While if '--buildid-all' is enabled, we creates a new option "--timestamp-boundary" for user to decide if it enables the timestamp boundary calculation.
Impacted patch:
---------------
perf record: Get the first sample time and last sample time
Quoted 8 lines
2. Fix the merge issue with the latest perf/core branch. No functional changes.
Impacted patch:
---------------
perf report: support time percent and multiple time ranges perf script: support time percent and multiple time ranges
Hi Arnaldo,
Is the v6 acceptable or I need to continue improving?
I'd like to continue improving the patch if it's needed.
Thanks
Jin Yao