diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | tools/perf/Documentation/perf-record.txt | 7 | ||||
-rw-r--r-- | tools/perf/Documentation/perf-script.txt | 20 | ||||
-rw-r--r-- | tools/perf/builtin-annotate.c | 2 | ||||
-rw-r--r-- | tools/perf/builtin-diff.c | 12 | ||||
-rw-r--r-- | tools/perf/builtin-record.c | 7 | ||||
-rw-r--r-- | tools/perf/config/Makefile | 2 | ||||
-rw-r--r-- | tools/perf/perf.c | 36 | ||||
-rwxr-xr-x | tools/perf/scripts/python/bin/stackcollapse-record | 8 | ||||
-rwxr-xr-x | tools/perf/scripts/python/bin/stackcollapse-report | 3 | ||||
-rwxr-xr-x | tools/perf/scripts/python/stackcollapse.py | 127 | ||||
-rw-r--r-- | tools/perf/tests/hists_link.c | 4 | ||||
-rw-r--r-- | tools/perf/util/cache.h | 9 | ||||
-rw-r--r-- | tools/perf/util/config.c | 2 | ||||
-rw-r--r-- | tools/perf/util/evsel.c | 23 | ||||
-rw-r--r-- | tools/perf/util/hist.c | 34 | ||||
-rw-r--r-- | tools/perf/util/hist.h | 14 | ||||
-rw-r--r-- | tools/perf/util/llvm-utils.c | 42 | ||||
-rw-r--r-- | tools/perf/util/llvm-utils.h | 5 | ||||
-rw-r--r-- | tools/perf/util/path.c | 65 |
20 files changed, 251 insertions, 173 deletions
@@ -1038,7 +1038,7 @@ ifdef CONFIG_STACK_VALIDATION | |||
1038 | ifeq ($(has_libelf),1) | 1038 | ifeq ($(has_libelf),1) |
1039 | objtool_target := tools/objtool FORCE | 1039 | objtool_target := tools/objtool FORCE |
1040 | else | 1040 | else |
1041 | $(warning "Cannot use CONFIG_STACK_VALIDATION, please install libelf-dev or elfutils-libelf-devel") | 1041 | $(warning "Cannot use CONFIG_STACK_VALIDATION, please install libelf-dev, libelf-devel or elfutils-libelf-devel") |
1042 | SKIP_STACK_VALIDATION := 1 | 1042 | SKIP_STACK_VALIDATION := 1 |
1043 | export SKIP_STACK_VALIDATION | 1043 | export SKIP_STACK_VALIDATION |
1044 | endif | 1044 | endif |
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 8dbee832abd9..5b46b1d1a37c 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt | |||
@@ -360,6 +360,13 @@ particular perf.data snapshot should be kept or not. | |||
360 | 360 | ||
361 | Implies --timestamp-filename, --no-buildid and --no-buildid-cache. | 361 | Implies --timestamp-filename, --no-buildid and --no-buildid-cache. |
362 | 362 | ||
363 | --dry-run:: | ||
364 | Parse options then exit. --dry-run can be used to detect errors in cmdline | ||
365 | options. | ||
366 | |||
367 | 'perf record --dry-run -e' can act as a BPF script compiler if llvm.dump-obj | ||
368 | in config file is set to true. | ||
369 | |||
363 | SEE ALSO | 370 | SEE ALSO |
364 | -------- | 371 | -------- |
365 | linkperf:perf-stat[1], linkperf:perf-list[1] | 372 | linkperf:perf-stat[1], linkperf:perf-list[1] |
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index 4fc44c75263f..4f34379ebd77 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt | |||
@@ -119,13 +119,13 @@ OPTIONS | |||
119 | srcline, period, iregs, brstack, brstacksym, flags. | 119 | srcline, period, iregs, brstack, brstacksym, flags. |
120 | Field list can be prepended with the type, trace, sw or hw, | 120 | Field list can be prepended with the type, trace, sw or hw, |
121 | to indicate to which event type the field list applies. | 121 | to indicate to which event type the field list applies. |
122 | e.g., -f sw:comm,tid,time,ip,sym and -f trace:time,cpu,trace | 122 | e.g., -F sw:comm,tid,time,ip,sym and -F trace:time,cpu,trace |
123 | 123 | ||
124 | perf script -f <fields> | 124 | perf script -F <fields> |
125 | 125 | ||
126 | is equivalent to: | 126 | is equivalent to: |
127 | 127 | ||
128 | perf script -f trace:<fields> -f sw:<fields> -f hw:<fields> | 128 | perf script -F trace:<fields> -F sw:<fields> -F hw:<fields> |
129 | 129 | ||
130 | i.e., the specified fields apply to all event types if the type string | 130 | i.e., the specified fields apply to all event types if the type string |
131 | is not given. | 131 | is not given. |
@@ -133,9 +133,9 @@ OPTIONS | |||
133 | The arguments are processed in the order received. A later usage can | 133 | The arguments are processed in the order received. A later usage can |
134 | reset a prior request. e.g.: | 134 | reset a prior request. e.g.: |
135 | 135 | ||
136 | -f trace: -f comm,tid,time,ip,sym | 136 | -F trace: -F comm,tid,time,ip,sym |
137 | 137 | ||
138 | The first -f suppresses trace events (field list is ""), but then the | 138 | The first -F suppresses trace events (field list is ""), but then the |
139 | second invocation sets the fields to comm,tid,time,ip,sym. In this case a | 139 | second invocation sets the fields to comm,tid,time,ip,sym. In this case a |
140 | warning is given to the user: | 140 | warning is given to the user: |
141 | 141 | ||
@@ -143,9 +143,9 @@ OPTIONS | |||
143 | 143 | ||
144 | Alternatively, consider the order: | 144 | Alternatively, consider the order: |
145 | 145 | ||
146 | -f comm,tid,time,ip,sym -f trace: | 146 | -F comm,tid,time,ip,sym -F trace: |
147 | 147 | ||
148 | The first -f sets the fields for all events and the second -f | 148 | The first -F sets the fields for all events and the second -F |
149 | suppresses trace events. The user is given a warning message about | 149 | suppresses trace events. The user is given a warning message about |
150 | the override, and the result of the above is that only S/W and H/W | 150 | the override, and the result of the above is that only S/W and H/W |
151 | events are displayed with the given fields. | 151 | events are displayed with the given fields. |
@@ -154,14 +154,14 @@ OPTIONS | |||
154 | event type, a message is displayed to the user that the option is | 154 | event type, a message is displayed to the user that the option is |
155 | ignored for that type. For example: | 155 | ignored for that type. For example: |
156 | 156 | ||
157 | $ perf script -f comm,tid,trace | 157 | $ perf script -F comm,tid,trace |
158 | 'trace' not valid for hardware events. Ignoring. | 158 | 'trace' not valid for hardware events. Ignoring. |
159 | 'trace' not valid for software events. Ignoring. | 159 | 'trace' not valid for software events. Ignoring. |
160 | 160 | ||
161 | Alternatively, if the type is given an invalid field is specified it | 161 | Alternatively, if the type is given an invalid field is specified it |
162 | is an error. For example: | 162 | is an error. For example: |
163 | 163 | ||
164 | perf script -v -f sw:comm,tid,trace | 164 | perf script -v -F sw:comm,tid,trace |
165 | 'trace' not valid for software events. | 165 | 'trace' not valid for software events. |
166 | 166 | ||
167 | At this point usage is displayed, and perf-script exits. | 167 | At this point usage is displayed, and perf-script exits. |
@@ -173,7 +173,7 @@ OPTIONS | |||
173 | respectively. | 173 | respectively. |
174 | 174 | ||
175 | Finally, a user may not set fields to none for all event types. | 175 | Finally, a user may not set fields to none for all event types. |
176 | i.e., -f "" is not allowed. | 176 | i.e., -F "" is not allowed. |
177 | 177 | ||
178 | The brstack output includes branch related information with raw addresses using the | 178 | The brstack output includes branch related information with raw addresses using the |
179 | /v/v/v/v/ syntax in the following order: | 179 | /v/v/v/v/ syntax in the following order: |
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 25c81734a950..a2324e1892aa 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
@@ -75,7 +75,7 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel, | |||
75 | sample->period = 1; | 75 | sample->period = 1; |
76 | sample->weight = 1; | 76 | sample->weight = 1; |
77 | 77 | ||
78 | he = __hists__add_entry(hists, al, NULL, NULL, NULL, sample, true); | 78 | he = hists__add_entry(hists, al, NULL, NULL, NULL, sample, true); |
79 | if (he == NULL) | 79 | if (he == NULL) |
80 | return -ENOMEM; | 80 | return -ENOMEM; |
81 | 81 | ||
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 7f628f9c2fb4..8b6735f35179 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
@@ -310,16 +310,6 @@ static int formula_fprintf(struct hist_entry *he, struct hist_entry *pair, | |||
310 | return -1; | 310 | return -1; |
311 | } | 311 | } |
312 | 312 | ||
313 | static int hists__add_entry(struct hists *hists, | ||
314 | struct addr_location *al, | ||
315 | struct perf_sample *sample) | ||
316 | { | ||
317 | if (__hists__add_entry(hists, al, NULL, NULL, NULL, | ||
318 | sample, true) != NULL) | ||
319 | return 0; | ||
320 | return -ENOMEM; | ||
321 | } | ||
322 | |||
323 | static int diff__process_sample_event(struct perf_tool *tool __maybe_unused, | 313 | static int diff__process_sample_event(struct perf_tool *tool __maybe_unused, |
324 | union perf_event *event, | 314 | union perf_event *event, |
325 | struct perf_sample *sample, | 315 | struct perf_sample *sample, |
@@ -336,7 +326,7 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused, | |||
336 | return -1; | 326 | return -1; |
337 | } | 327 | } |
338 | 328 | ||
339 | if (hists__add_entry(hists, &al, sample)) { | 329 | if (!hists__add_entry(hists, &al, NULL, NULL, NULL, sample, true)) { |
340 | pr_warning("problem incrementing symbol period, skipping event\n"); | 330 | pr_warning("problem incrementing symbol period, skipping event\n"); |
341 | goto out_put; | 331 | goto out_put; |
342 | } | 332 | } |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index d4cf1b0c88f9..b1304ebc8779 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -1274,6 +1274,8 @@ static struct record record = { | |||
1274 | const char record_callchain_help[] = CALLCHAIN_RECORD_HELP | 1274 | const char record_callchain_help[] = CALLCHAIN_RECORD_HELP |
1275 | "\n\t\t\t\tDefault: fp"; | 1275 | "\n\t\t\t\tDefault: fp"; |
1276 | 1276 | ||
1277 | static bool dry_run; | ||
1278 | |||
1277 | /* | 1279 | /* |
1278 | * XXX Will stay a global variable till we fix builtin-script.c to stop messing | 1280 | * XXX Will stay a global variable till we fix builtin-script.c to stop messing |
1279 | * with it and switch to use the library functions in perf_evlist that came | 1281 | * with it and switch to use the library functions in perf_evlist that came |
@@ -1393,6 +1395,8 @@ struct option __record_options[] = { | |||
1393 | "append timestamp to output filename"), | 1395 | "append timestamp to output filename"), |
1394 | OPT_BOOLEAN(0, "switch-output", &record.switch_output, | 1396 | OPT_BOOLEAN(0, "switch-output", &record.switch_output, |
1395 | "Switch output when receive SIGUSR2"), | 1397 | "Switch output when receive SIGUSR2"), |
1398 | OPT_BOOLEAN(0, "dry-run", &dry_run, | ||
1399 | "Parse options then exit"), | ||
1396 | OPT_END() | 1400 | OPT_END() |
1397 | }; | 1401 | }; |
1398 | 1402 | ||
@@ -1462,6 +1466,9 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1462 | if (err) | 1466 | if (err) |
1463 | return err; | 1467 | return err; |
1464 | 1468 | ||
1469 | if (dry_run) | ||
1470 | return 0; | ||
1471 | |||
1465 | err = bpf__setup_stdout(rec->evlist); | 1472 | err = bpf__setup_stdout(rec->evlist); |
1466 | if (err) { | 1473 | if (err) { |
1467 | bpf__strerror_setup_stdout(rec->evlist, err, errbuf, sizeof(errbuf)); | 1474 | bpf__strerror_setup_stdout(rec->evlist, err, errbuf, sizeof(errbuf)); |
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 80018feb99c0..534c81176f6c 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile | |||
@@ -257,7 +257,7 @@ else | |||
257 | LIBC_SUPPORT := 1 | 257 | LIBC_SUPPORT := 1 |
258 | endif | 258 | endif |
259 | ifeq ($(LIBC_SUPPORT),1) | 259 | ifeq ($(LIBC_SUPPORT),1) |
260 | msg := $(warning No libelf found, disables 'probe' tool and BPF support in 'perf record', please install elfutils-libelf-devel/libelf-dev); | 260 | msg := $(warning No libelf found, disables 'probe' tool and BPF support in 'perf record', please install libelf-dev, libelf-devel or elfutils-libelf-devel); |
261 | 261 | ||
262 | NO_LIBELF := 1 | 262 | NO_LIBELF := 1 |
263 | NO_DWARF := 1 | 263 | NO_DWARF := 1 |
diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 15982cee5ef3..634bf7c6c477 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c | |||
@@ -139,8 +139,6 @@ struct option options[] = { | |||
139 | OPT_ARGUMENT("html-path", "html-path"), | 139 | OPT_ARGUMENT("html-path", "html-path"), |
140 | OPT_ARGUMENT("paginate", "paginate"), | 140 | OPT_ARGUMENT("paginate", "paginate"), |
141 | OPT_ARGUMENT("no-pager", "no-pager"), | 141 | OPT_ARGUMENT("no-pager", "no-pager"), |
142 | OPT_ARGUMENT("perf-dir", "perf-dir"), | ||
143 | OPT_ARGUMENT("work-tree", "work-tree"), | ||
144 | OPT_ARGUMENT("debugfs-dir", "debugfs-dir"), | 142 | OPT_ARGUMENT("debugfs-dir", "debugfs-dir"), |
145 | OPT_ARGUMENT("buildid-dir", "buildid-dir"), | 143 | OPT_ARGUMENT("buildid-dir", "buildid-dir"), |
146 | OPT_ARGUMENT("list-cmds", "list-cmds"), | 144 | OPT_ARGUMENT("list-cmds", "list-cmds"), |
@@ -200,35 +198,6 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) | |||
200 | use_pager = 0; | 198 | use_pager = 0; |
201 | if (envchanged) | 199 | if (envchanged) |
202 | *envchanged = 1; | 200 | *envchanged = 1; |
203 | } else if (!strcmp(cmd, "--perf-dir")) { | ||
204 | if (*argc < 2) { | ||
205 | fprintf(stderr, "No directory given for --perf-dir.\n"); | ||
206 | usage(perf_usage_string); | ||
207 | } | ||
208 | setenv(PERF_DIR_ENVIRONMENT, (*argv)[1], 1); | ||
209 | if (envchanged) | ||
210 | *envchanged = 1; | ||
211 | (*argv)++; | ||
212 | (*argc)--; | ||
213 | handled++; | ||
214 | } else if (!prefixcmp(cmd, CMD_PERF_DIR)) { | ||
215 | setenv(PERF_DIR_ENVIRONMENT, cmd + strlen(CMD_PERF_DIR), 1); | ||
216 | if (envchanged) | ||
217 | *envchanged = 1; | ||
218 | } else if (!strcmp(cmd, "--work-tree")) { | ||
219 | if (*argc < 2) { | ||
220 | fprintf(stderr, "No directory given for --work-tree.\n"); | ||
221 | usage(perf_usage_string); | ||
222 | } | ||
223 | setenv(PERF_WORK_TREE_ENVIRONMENT, (*argv)[1], 1); | ||
224 | if (envchanged) | ||
225 | *envchanged = 1; | ||
226 | (*argv)++; | ||
227 | (*argc)--; | ||
228 | } else if (!prefixcmp(cmd, CMD_WORK_TREE)) { | ||
229 | setenv(PERF_WORK_TREE_ENVIRONMENT, cmd + strlen(CMD_WORK_TREE), 1); | ||
230 | if (envchanged) | ||
231 | *envchanged = 1; | ||
232 | } else if (!strcmp(cmd, "--debugfs-dir")) { | 201 | } else if (!strcmp(cmd, "--debugfs-dir")) { |
233 | if (*argc < 2) { | 202 | if (*argc < 2) { |
234 | fprintf(stderr, "No directory given for --debugfs-dir.\n"); | 203 | fprintf(stderr, "No directory given for --debugfs-dir.\n"); |
@@ -363,11 +332,6 @@ const char perf_version_string[] = PERF_VERSION; | |||
363 | 332 | ||
364 | #define RUN_SETUP (1<<0) | 333 | #define RUN_SETUP (1<<0) |
365 | #define USE_PAGER (1<<1) | 334 | #define USE_PAGER (1<<1) |
366 | /* | ||
367 | * require working tree to be present -- anything uses this needs | ||
368 | * RUN_SETUP for reading from the configuration file. | ||
369 | */ | ||
370 | #define NEED_WORK_TREE (1<<2) | ||
371 | 335 | ||
372 | static int run_builtin(struct cmd_struct *p, int argc, const char **argv) | 336 | static int run_builtin(struct cmd_struct *p, int argc, const char **argv) |
373 | { | 337 | { |
diff --git a/tools/perf/scripts/python/bin/stackcollapse-record b/tools/perf/scripts/python/bin/stackcollapse-record new file mode 100755 index 000000000000..9d8f9f0f3a17 --- /dev/null +++ b/tools/perf/scripts/python/bin/stackcollapse-record | |||
@@ -0,0 +1,8 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | # | ||
4 | # stackcollapse.py can cover all type of perf samples including | ||
5 | # the tracepoints, so no special record requirements, just record what | ||
6 | # you want to analyze. | ||
7 | # | ||
8 | perf record "$@" | ||
diff --git a/tools/perf/scripts/python/bin/stackcollapse-report b/tools/perf/scripts/python/bin/stackcollapse-report new file mode 100755 index 000000000000..356b9656393d --- /dev/null +++ b/tools/perf/scripts/python/bin/stackcollapse-report | |||
@@ -0,0 +1,3 @@ | |||
1 | #!/bin/sh | ||
2 | # description: produce callgraphs in short form for scripting use | ||
3 | perf script -s "$PERF_EXEC_PATH"/scripts/python/stackcollapse.py -- "$@" | ||
diff --git a/tools/perf/scripts/python/stackcollapse.py b/tools/perf/scripts/python/stackcollapse.py new file mode 100755 index 000000000000..a2dfcda41ae6 --- /dev/null +++ b/tools/perf/scripts/python/stackcollapse.py | |||
@@ -0,0 +1,127 @@ | |||
1 | #!/usr/bin/perl -w | ||
2 | # | ||
3 | # stackcollapse.py - format perf samples with one line per distinct call stack | ||
4 | # | ||
5 | # This script's output has two space-separated fields. The first is a semicolon | ||
6 | # separated stack including the program name (from the "comm" field) and the | ||
7 | # function names from the call stack. The second is a count: | ||
8 | # | ||
9 | # swapper;start_kernel;rest_init;cpu_idle;default_idle;native_safe_halt 2 | ||
10 | # | ||
11 | # The file is sorted according to the first field. | ||
12 | # | ||
13 | # Input may be created and processed using: | ||
14 | # | ||
15 | # perf record -a -g -F 99 sleep 60 | ||
16 | # perf script report stackcollapse > out.stacks-folded | ||
17 | # | ||
18 | # (perf script record stackcollapse works too). | ||
19 | # | ||
20 | # Written by Paolo Bonzini <pbonzini@redhat.com> | ||
21 | # Based on Brendan Gregg's stackcollapse-perf.pl script. | ||
22 | |||
23 | import os | ||
24 | import sys | ||
25 | from collections import defaultdict | ||
26 | from optparse import OptionParser, make_option | ||
27 | |||
28 | sys.path.append(os.environ['PERF_EXEC_PATH'] + \ | ||
29 | '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') | ||
30 | |||
31 | from perf_trace_context import * | ||
32 | from Core import * | ||
33 | from EventClass import * | ||
34 | |||
35 | # command line parsing | ||
36 | |||
37 | option_list = [ | ||
38 | # formatting options for the bottom entry of the stack | ||
39 | make_option("--include-tid", dest="include_tid", | ||
40 | action="store_true", default=False, | ||
41 | help="include thread id in stack"), | ||
42 | make_option("--include-pid", dest="include_pid", | ||
43 | action="store_true", default=False, | ||
44 | help="include process id in stack"), | ||
45 | make_option("--no-comm", dest="include_comm", | ||
46 | action="store_false", default=True, | ||
47 | help="do not separate stacks according to comm"), | ||
48 | make_option("--tidy-java", dest="tidy_java", | ||
49 | action="store_true", default=False, | ||
50 | help="beautify Java signatures"), | ||
51 | make_option("--kernel", dest="annotate_kernel", | ||
52 | action="store_true", default=False, | ||
53 | help="annotate kernel functions with _[k]") | ||
54 | ] | ||
55 | |||
56 | parser = OptionParser(option_list=option_list) | ||
57 | (opts, args) = parser.parse_args() | ||
58 | |||
59 | if len(args) != 0: | ||
60 | parser.error("unexpected command line argument") | ||
61 | if opts.include_tid and not opts.include_comm: | ||
62 | parser.error("requesting tid but not comm is invalid") | ||
63 | if opts.include_pid and not opts.include_comm: | ||
64 | parser.error("requesting pid but not comm is invalid") | ||
65 | |||
66 | # event handlers | ||
67 | |||
68 | lines = defaultdict(lambda: 0) | ||
69 | |||
70 | def process_event(param_dict): | ||
71 | def tidy_function_name(sym, dso): | ||
72 | if sym is None: | ||
73 | sym = '[unknown]' | ||
74 | |||
75 | sym = sym.replace(';', ':') | ||
76 | if opts.tidy_java: | ||
77 | # the original stackcollapse-perf.pl script gives the | ||
78 | # example of converting this: | ||
79 | # Lorg/mozilla/javascript/MemberBox;.<init>(Ljava/lang/reflect/Method;)V | ||
80 | # to this: | ||
81 | # org/mozilla/javascript/MemberBox:.init | ||
82 | sym = sym.replace('<', '') | ||
83 | sym = sym.replace('>', '') | ||
84 | if sym[0] == 'L' and sym.find('/'): | ||
85 | sym = sym[1:] | ||
86 | try: | ||
87 | sym = sym[:sym.index('(')] | ||
88 | except ValueError: | ||
89 | pass | ||
90 | |||
91 | if opts.annotate_kernel and dso == '[kernel.kallsyms]': | ||
92 | return sym + '_[k]' | ||
93 | else: | ||
94 | return sym | ||
95 | |||
96 | stack = list() | ||
97 | if 'callchain' in param_dict: | ||
98 | for entry in param_dict['callchain']: | ||
99 | entry.setdefault('sym', dict()) | ||
100 | entry['sym'].setdefault('name', None) | ||
101 | entry.setdefault('dso', None) | ||
102 | stack.append(tidy_function_name(entry['sym']['name'], | ||
103 | entry['dso'])) | ||
104 | else: | ||
105 | param_dict.setdefault('symbol', None) | ||
106 | param_dict.setdefault('dso', None) | ||
107 | stack.append(tidy_function_name(param_dict['symbol'], | ||
108 | param_dict['dso'])) | ||
109 | |||
110 | if opts.include_comm: | ||
111 | comm = param_dict["comm"].replace(' ', '_') | ||
112 | sep = "-" | ||
113 | if opts.include_pid: | ||
114 | comm = comm + sep + str(param_dict['sample']['pid']) | ||
115 | sep = "/" | ||
116 | if opts.include_tid: | ||
117 | comm = comm + sep + str(param_dict['sample']['tid']) | ||
118 | stack.append(comm) | ||
119 | |||
120 | stack_string = ';'.join(reversed(stack)) | ||
121 | lines[stack_string] = lines[stack_string] + 1 | ||
122 | |||
123 | def trace_end(): | ||
124 | list = lines.keys() | ||
125 | list.sort() | ||
126 | for stack in list: | ||
127 | print "%s %d" % (stack, lines[stack]) | ||
diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index acf5a1301c07..6f96ca4d4fc0 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c | |||
@@ -84,7 +84,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) | |||
84 | if (machine__resolve(machine, &al, &sample) < 0) | 84 | if (machine__resolve(machine, &al, &sample) < 0) |
85 | goto out; | 85 | goto out; |
86 | 86 | ||
87 | he = __hists__add_entry(hists, &al, NULL, | 87 | he = hists__add_entry(hists, &al, NULL, |
88 | NULL, NULL, &sample, true); | 88 | NULL, NULL, &sample, true); |
89 | if (he == NULL) { | 89 | if (he == NULL) { |
90 | addr_location__put(&al); | 90 | addr_location__put(&al); |
@@ -103,7 +103,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) | |||
103 | if (machine__resolve(machine, &al, &sample) < 0) | 103 | if (machine__resolve(machine, &al, &sample) < 0) |
104 | goto out; | 104 | goto out; |
105 | 105 | ||
106 | he = __hists__add_entry(hists, &al, NULL, | 106 | he = hists__add_entry(hists, &al, NULL, |
107 | NULL, NULL, &sample, true); | 107 | NULL, NULL, &sample, true); |
108 | if (he == NULL) { | 108 | if (he == NULL) { |
109 | addr_location__put(&al); | 109 | addr_location__put(&al); |
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 0d814bb74661..369f382eedb6 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h | |||
@@ -11,14 +11,9 @@ | |||
11 | #include <linux/string.h> | 11 | #include <linux/string.h> |
12 | 12 | ||
13 | #define CMD_EXEC_PATH "--exec-path" | 13 | #define CMD_EXEC_PATH "--exec-path" |
14 | #define CMD_PERF_DIR "--perf-dir=" | ||
15 | #define CMD_WORK_TREE "--work-tree=" | ||
16 | #define CMD_DEBUGFS_DIR "--debugfs-dir=" | 14 | #define CMD_DEBUGFS_DIR "--debugfs-dir=" |
17 | 15 | ||
18 | #define PERF_DIR_ENVIRONMENT "PERF_DIR" | ||
19 | #define PERF_WORK_TREE_ENVIRONMENT "PERF_WORK_TREE" | ||
20 | #define EXEC_PATH_ENVIRONMENT "PERF_EXEC_PATH" | 16 | #define EXEC_PATH_ENVIRONMENT "PERF_EXEC_PATH" |
21 | #define DEFAULT_PERF_DIR_ENVIRONMENT ".perf" | ||
22 | #define PERF_DEBUGFS_ENVIRONMENT "PERF_DEBUGFS_DIR" | 17 | #define PERF_DEBUGFS_ENVIRONMENT "PERF_DEBUGFS_DIR" |
23 | #define PERF_TRACEFS_ENVIRONMENT "PERF_TRACEFS_DIR" | 18 | #define PERF_TRACEFS_ENVIRONMENT "PERF_TRACEFS_DIR" |
24 | #define PERF_PAGER_ENVIRONMENT "PERF_PAGER" | 19 | #define PERF_PAGER_ENVIRONMENT "PERF_PAGER" |
@@ -32,7 +27,6 @@ int perf_config_int(const char *, const char *); | |||
32 | u64 perf_config_u64(const char *, const char *); | 27 | u64 perf_config_u64(const char *, const char *); |
33 | int perf_config_bool(const char *, const char *); | 28 | int perf_config_bool(const char *, const char *); |
34 | int config_error_nonbool(const char *); | 29 | int config_error_nonbool(const char *); |
35 | const char *perf_config_dirname(const char *, const char *); | ||
36 | const char *perf_etc_perfconfig(void); | 30 | const char *perf_etc_perfconfig(void); |
37 | 31 | ||
38 | char *alias_lookup(const char *alias); | 32 | char *alias_lookup(const char *alias); |
@@ -45,9 +39,6 @@ static inline int is_absolute_path(const char *path) | |||
45 | return path[0] == '/'; | 39 | return path[0] == '/'; |
46 | } | 40 | } |
47 | 41 | ||
48 | char *strip_path_suffix(const char *path, const char *suffix); | ||
49 | |||
50 | char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2))); | 42 | char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2))); |
51 | char *perf_path(const char *fmt, ...) __attribute__((format (printf, 1, 2))); | ||
52 | 43 | ||
53 | #endif /* __PERF_CACHE_H */ | 44 | #endif /* __PERF_CACHE_H */ |
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index 31e09a4e8862..d15c59267644 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c | |||
@@ -372,7 +372,7 @@ int perf_config_bool(const char *name, const char *value) | |||
372 | return !!perf_config_bool_or_int(name, value, &discard); | 372 | return !!perf_config_bool_or_int(name, value, &discard); |
373 | } | 373 | } |
374 | 374 | ||
375 | const char *perf_config_dirname(const char *name, const char *value) | 375 | static const char *perf_config_dirname(const char *name, const char *value) |
376 | { | 376 | { |
377 | if (!name) | 377 | if (!name) |
378 | return NULL; | 378 | return NULL; |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 9b2e3e624efe..1d8f2bbd38a7 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -1389,8 +1389,11 @@ fallback_missing_features: | |||
1389 | if (perf_missing_features.lbr_flags) | 1389 | if (perf_missing_features.lbr_flags) |
1390 | evsel->attr.branch_sample_type &= ~(PERF_SAMPLE_BRANCH_NO_FLAGS | | 1390 | evsel->attr.branch_sample_type &= ~(PERF_SAMPLE_BRANCH_NO_FLAGS | |
1391 | PERF_SAMPLE_BRANCH_NO_CYCLES); | 1391 | PERF_SAMPLE_BRANCH_NO_CYCLES); |
1392 | if (perf_missing_features.write_backward) | 1392 | if (perf_missing_features.write_backward) { |
1393 | if (evsel->overwrite) | ||
1394 | return -EINVAL; | ||
1393 | evsel->attr.write_backward = false; | 1395 | evsel->attr.write_backward = false; |
1396 | } | ||
1394 | retry_sample_id: | 1397 | retry_sample_id: |
1395 | if (perf_missing_features.sample_id_all) | 1398 | if (perf_missing_features.sample_id_all) |
1396 | evsel->attr.sample_id_all = 0; | 1399 | evsel->attr.sample_id_all = 0; |
@@ -1453,12 +1456,6 @@ retry_open: | |||
1453 | err = -EINVAL; | 1456 | err = -EINVAL; |
1454 | goto out_close; | 1457 | goto out_close; |
1455 | } | 1458 | } |
1456 | |||
1457 | if (evsel->overwrite && | ||
1458 | perf_missing_features.write_backward) { | ||
1459 | err = -EINVAL; | ||
1460 | goto out_close; | ||
1461 | } | ||
1462 | } | 1459 | } |
1463 | } | 1460 | } |
1464 | 1461 | ||
@@ -1496,7 +1493,10 @@ try_fallback: | |||
1496 | * Must probe features in the order they were added to the | 1493 | * Must probe features in the order they were added to the |
1497 | * perf_event_attr interface. | 1494 | * perf_event_attr interface. |
1498 | */ | 1495 | */ |
1499 | if (!perf_missing_features.clockid_wrong && evsel->attr.use_clockid) { | 1496 | if (!perf_missing_features.write_backward && evsel->attr.write_backward) { |
1497 | perf_missing_features.write_backward = true; | ||
1498 | goto fallback_missing_features; | ||
1499 | } else if (!perf_missing_features.clockid_wrong && evsel->attr.use_clockid) { | ||
1500 | perf_missing_features.clockid_wrong = true; | 1500 | perf_missing_features.clockid_wrong = true; |
1501 | goto fallback_missing_features; | 1501 | goto fallback_missing_features; |
1502 | } else if (!perf_missing_features.clockid && evsel->attr.use_clockid) { | 1502 | } else if (!perf_missing_features.clockid && evsel->attr.use_clockid) { |
@@ -1521,12 +1521,7 @@ try_fallback: | |||
1521 | PERF_SAMPLE_BRANCH_NO_FLAGS))) { | 1521 | PERF_SAMPLE_BRANCH_NO_FLAGS))) { |
1522 | perf_missing_features.lbr_flags = true; | 1522 | perf_missing_features.lbr_flags = true; |
1523 | goto fallback_missing_features; | 1523 | goto fallback_missing_features; |
1524 | } else if (!perf_missing_features.write_backward && | ||
1525 | evsel->attr.write_backward) { | ||
1526 | perf_missing_features.write_backward = true; | ||
1527 | goto fallback_missing_features; | ||
1528 | } | 1524 | } |
1529 | |||
1530 | out_close: | 1525 | out_close: |
1531 | do { | 1526 | do { |
1532 | while (--thread >= 0) { | 1527 | while (--thread >= 0) { |
@@ -2409,6 +2404,8 @@ int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target, | |||
2409 | "We found oprofile daemon running, please stop it and try again."); | 2404 | "We found oprofile daemon running, please stop it and try again."); |
2410 | break; | 2405 | break; |
2411 | case EINVAL: | 2406 | case EINVAL: |
2407 | if (evsel->overwrite && perf_missing_features.write_backward) | ||
2408 | return scnprintf(msg, size, "Reading from overwrite event is not supported by this kernel."); | ||
2412 | if (perf_missing_features.clockid) | 2409 | if (perf_missing_features.clockid) |
2413 | return scnprintf(msg, size, "clockid feature not supported."); | 2410 | return scnprintf(msg, size, "clockid feature not supported."); |
2414 | if (perf_missing_features.clockid_wrong) | 2411 | if (perf_missing_features.clockid_wrong) |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 2515cfdb7365..d2647b1d82c0 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -531,13 +531,13 @@ out: | |||
531 | return he; | 531 | return he; |
532 | } | 532 | } |
533 | 533 | ||
534 | struct hist_entry *__hists__add_entry(struct hists *hists, | 534 | struct hist_entry *hists__add_entry(struct hists *hists, |
535 | struct addr_location *al, | 535 | struct addr_location *al, |
536 | struct symbol *sym_parent, | 536 | struct symbol *sym_parent, |
537 | struct branch_info *bi, | 537 | struct branch_info *bi, |
538 | struct mem_info *mi, | 538 | struct mem_info *mi, |
539 | struct perf_sample *sample, | 539 | struct perf_sample *sample, |
540 | bool sample_self) | 540 | bool sample_self) |
541 | { | 541 | { |
542 | struct hist_entry entry = { | 542 | struct hist_entry entry = { |
543 | .thread = al->thread, | 543 | .thread = al->thread, |
@@ -622,8 +622,8 @@ iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al | |||
622 | */ | 622 | */ |
623 | sample->period = cost; | 623 | sample->period = cost; |
624 | 624 | ||
625 | he = __hists__add_entry(hists, al, iter->parent, NULL, mi, | 625 | he = hists__add_entry(hists, al, iter->parent, NULL, mi, |
626 | sample, true); | 626 | sample, true); |
627 | if (!he) | 627 | if (!he) |
628 | return -ENOMEM; | 628 | return -ENOMEM; |
629 | 629 | ||
@@ -727,8 +727,8 @@ iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *a | |||
727 | sample->period = 1; | 727 | sample->period = 1; |
728 | sample->weight = bi->flags.cycles ? bi->flags.cycles : 1; | 728 | sample->weight = bi->flags.cycles ? bi->flags.cycles : 1; |
729 | 729 | ||
730 | he = __hists__add_entry(hists, al, iter->parent, &bi[i], NULL, | 730 | he = hists__add_entry(hists, al, iter->parent, &bi[i], NULL, |
731 | sample, true); | 731 | sample, true); |
732 | if (he == NULL) | 732 | if (he == NULL) |
733 | return -ENOMEM; | 733 | return -ENOMEM; |
734 | 734 | ||
@@ -764,8 +764,8 @@ iter_add_single_normal_entry(struct hist_entry_iter *iter, struct addr_location | |||
764 | struct perf_sample *sample = iter->sample; | 764 | struct perf_sample *sample = iter->sample; |
765 | struct hist_entry *he; | 765 | struct hist_entry *he; |
766 | 766 | ||
767 | he = __hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL, | 767 | he = hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL, |
768 | sample, true); | 768 | sample, true); |
769 | if (he == NULL) | 769 | if (he == NULL) |
770 | return -ENOMEM; | 770 | return -ENOMEM; |
771 | 771 | ||
@@ -825,8 +825,8 @@ iter_add_single_cumulative_entry(struct hist_entry_iter *iter, | |||
825 | struct hist_entry *he; | 825 | struct hist_entry *he; |
826 | int err = 0; | 826 | int err = 0; |
827 | 827 | ||
828 | he = __hists__add_entry(hists, al, iter->parent, NULL, NULL, | 828 | he = hists__add_entry(hists, al, iter->parent, NULL, NULL, |
829 | sample, true); | 829 | sample, true); |
830 | if (he == NULL) | 830 | if (he == NULL) |
831 | return -ENOMEM; | 831 | return -ENOMEM; |
832 | 832 | ||
@@ -900,8 +900,8 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter, | |||
900 | } | 900 | } |
901 | } | 901 | } |
902 | 902 | ||
903 | he = __hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL, | 903 | he = hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL, |
904 | sample, false); | 904 | sample, false); |
905 | if (he == NULL) | 905 | if (he == NULL) |
906 | return -ENOMEM; | 906 | return -ENOMEM; |
907 | 907 | ||
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index a19112872ff9..0a03e08be503 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -120,13 +120,13 @@ extern const struct hist_iter_ops hist_iter_branch; | |||
120 | extern const struct hist_iter_ops hist_iter_mem; | 120 | extern const struct hist_iter_ops hist_iter_mem; |
121 | extern const struct hist_iter_ops hist_iter_cumulative; | 121 | extern const struct hist_iter_ops hist_iter_cumulative; |
122 | 122 | ||
123 | struct hist_entry *__hists__add_entry(struct hists *hists, | 123 | struct hist_entry *hists__add_entry(struct hists *hists, |
124 | struct addr_location *al, | 124 | struct addr_location *al, |
125 | struct symbol *parent, | 125 | struct symbol *parent, |
126 | struct branch_info *bi, | 126 | struct branch_info *bi, |
127 | struct mem_info *mi, | 127 | struct mem_info *mi, |
128 | struct perf_sample *sample, | 128 | struct perf_sample *sample, |
129 | bool sample_self); | 129 | bool sample_self); |
130 | int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al, | 130 | int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al, |
131 | int max_stack_depth, void *arg); | 131 | int max_stack_depth, void *arg); |
132 | 132 | ||
diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c index 33071d6159bc..878a566763c3 100644 --- a/tools/perf/util/llvm-utils.c +++ b/tools/perf/util/llvm-utils.c | |||
@@ -42,6 +42,8 @@ int perf_llvm_config(const char *var, const char *value) | |||
42 | llvm_param.kbuild_dir = strdup(value); | 42 | llvm_param.kbuild_dir = strdup(value); |
43 | else if (!strcmp(var, "kbuild-opts")) | 43 | else if (!strcmp(var, "kbuild-opts")) |
44 | llvm_param.kbuild_opts = strdup(value); | 44 | llvm_param.kbuild_opts = strdup(value); |
45 | else if (!strcmp(var, "dump-obj")) | ||
46 | llvm_param.dump_obj = !!perf_config_bool(var, value); | ||
45 | else | 47 | else |
46 | return -1; | 48 | return -1; |
47 | llvm_param.user_set_param = true; | 49 | llvm_param.user_set_param = true; |
@@ -326,6 +328,42 @@ get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts) | |||
326 | pr_debug("include option is set to %s\n", *kbuild_include_opts); | 328 | pr_debug("include option is set to %s\n", *kbuild_include_opts); |
327 | } | 329 | } |
328 | 330 | ||
331 | static void | ||
332 | dump_obj(const char *path, void *obj_buf, size_t size) | ||
333 | { | ||
334 | char *obj_path = strdup(path); | ||
335 | FILE *fp; | ||
336 | char *p; | ||
337 | |||
338 | if (!obj_path) { | ||
339 | pr_warning("WARNING: No enough memory, skip object dumping\n"); | ||
340 | return; | ||
341 | } | ||
342 | |||
343 | p = strrchr(obj_path, '.'); | ||
344 | if (!p || (strcmp(p, ".c") != 0)) { | ||
345 | pr_warning("WARNING: invalid llvm source path: '%s', skip object dumping\n", | ||
346 | obj_path); | ||
347 | goto out; | ||
348 | } | ||
349 | |||
350 | p[1] = 'o'; | ||
351 | fp = fopen(obj_path, "wb"); | ||
352 | if (!fp) { | ||
353 | pr_warning("WARNING: failed to open '%s': %s, skip object dumping\n", | ||
354 | obj_path, strerror(errno)); | ||
355 | goto out; | ||
356 | } | ||
357 | |||
358 | pr_info("LLVM: dumping %s\n", obj_path); | ||
359 | if (fwrite(obj_buf, size, 1, fp) != 1) | ||
360 | pr_warning("WARNING: failed to write to file '%s': %s, skip object dumping\n", | ||
361 | obj_path, strerror(errno)); | ||
362 | fclose(fp); | ||
363 | out: | ||
364 | free(obj_path); | ||
365 | } | ||
366 | |||
329 | int llvm__compile_bpf(const char *path, void **p_obj_buf, | 367 | int llvm__compile_bpf(const char *path, void **p_obj_buf, |
330 | size_t *p_obj_buf_sz) | 368 | size_t *p_obj_buf_sz) |
331 | { | 369 | { |
@@ -411,6 +449,10 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf, | |||
411 | 449 | ||
412 | free(kbuild_dir); | 450 | free(kbuild_dir); |
413 | free(kbuild_include_opts); | 451 | free(kbuild_include_opts); |
452 | |||
453 | if (llvm_param.dump_obj) | ||
454 | dump_obj(path, obj_buf, obj_buf_sz); | ||
455 | |||
414 | if (!p_obj_buf) | 456 | if (!p_obj_buf) |
415 | free(obj_buf); | 457 | free(obj_buf); |
416 | else | 458 | else |
diff --git a/tools/perf/util/llvm-utils.h b/tools/perf/util/llvm-utils.h index 23b9a743fe72..9f501cef06a1 100644 --- a/tools/perf/util/llvm-utils.h +++ b/tools/perf/util/llvm-utils.h | |||
@@ -30,6 +30,11 @@ struct llvm_param { | |||
30 | */ | 30 | */ |
31 | const char *kbuild_opts; | 31 | const char *kbuild_opts; |
32 | /* | 32 | /* |
33 | * Default is false. If set to true, write compiling result | ||
34 | * to object file. | ||
35 | */ | ||
36 | bool dump_obj; | ||
37 | /* | ||
33 | * Default is false. If one of the above fields is set by user | 38 | * Default is false. If one of the above fields is set by user |
34 | * explicitly then user_set_llvm is set to true. This is used | 39 | * explicitly then user_set_llvm is set to true. This is used |
35 | * for perf test. If user doesn't set anything in .perfconfig | 40 | * for perf test. If user doesn't set anything in .perfconfig |
diff --git a/tools/perf/util/path.c b/tools/perf/util/path.c index 3bf6bf82ff2d..cff8bf0f87e8 100644 --- a/tools/perf/util/path.c +++ b/tools/perf/util/path.c | |||
@@ -14,14 +14,8 @@ | |||
14 | 14 | ||
15 | static char bad_path[] = "/bad-path/"; | 15 | static char bad_path[] = "/bad-path/"; |
16 | /* | 16 | /* |
17 | * Two hacks: | 17 | * One hack: |
18 | */ | 18 | */ |
19 | |||
20 | static const char *get_perf_dir(void) | ||
21 | { | ||
22 | return "."; | ||
23 | } | ||
24 | |||
25 | static char *get_pathname(void) | 19 | static char *get_pathname(void) |
26 | { | 20 | { |
27 | static char pathname_array[4][PATH_MAX]; | 21 | static char pathname_array[4][PATH_MAX]; |
@@ -54,60 +48,3 @@ char *mkpath(const char *fmt, ...) | |||
54 | return bad_path; | 48 | return bad_path; |
55 | return cleanup_path(pathname); | 49 | return cleanup_path(pathname); |
56 | } | 50 | } |
57 | |||
58 | char *perf_path(const char *fmt, ...) | ||
59 | { | ||
60 | const char *perf_dir = get_perf_dir(); | ||
61 | char *pathname = get_pathname(); | ||
62 | va_list args; | ||
63 | unsigned len; | ||
64 | |||
65 | len = strlen(perf_dir); | ||
66 | if (len > PATH_MAX-100) | ||
67 | return bad_path; | ||
68 | memcpy(pathname, perf_dir, len); | ||
69 | if (len && perf_dir[len-1] != '/') | ||
70 | pathname[len++] = '/'; | ||
71 | va_start(args, fmt); | ||
72 | len += vsnprintf(pathname + len, PATH_MAX - len, fmt, args); | ||
73 | va_end(args); | ||
74 | if (len >= PATH_MAX) | ||
75 | return bad_path; | ||
76 | return cleanup_path(pathname); | ||
77 | } | ||
78 | |||
79 | /* strip arbitrary amount of directory separators at end of path */ | ||
80 | static inline int chomp_trailing_dir_sep(const char *path, int len) | ||
81 | { | ||
82 | while (len && is_dir_sep(path[len - 1])) | ||
83 | len--; | ||
84 | return len; | ||
85 | } | ||
86 | |||
87 | /* | ||
88 | * If path ends with suffix (complete path components), returns the | ||
89 | * part before suffix (sans trailing directory separators). | ||
90 | * Otherwise returns NULL. | ||
91 | */ | ||
92 | char *strip_path_suffix(const char *path, const char *suffix) | ||
93 | { | ||
94 | int path_len = strlen(path), suffix_len = strlen(suffix); | ||
95 | |||
96 | while (suffix_len) { | ||
97 | if (!path_len) | ||
98 | return NULL; | ||
99 | |||
100 | if (is_dir_sep(path[path_len - 1])) { | ||
101 | if (!is_dir_sep(suffix[suffix_len - 1])) | ||
102 | return NULL; | ||
103 | path_len = chomp_trailing_dir_sep(path, path_len); | ||
104 | suffix_len = chomp_trailing_dir_sep(suffix, suffix_len); | ||
105 | } | ||
106 | else if (path[--path_len] != suffix[--suffix_len]) | ||
107 | return NULL; | ||
108 | } | ||
109 | |||
110 | if (path_len && !is_dir_sep(path[path_len - 1])) | ||
111 | return NULL; | ||
112 | return strndup(path, chomp_trailing_dir_sep(path, path_len)); | ||
113 | } | ||