Commit 4330b439 authored by Ingo Molnar's avatar Ingo Molnar

Merge tag 'perf-core-for-mingo-20160621' of...

Merge tag 'perf-core-for-mingo-20160621' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core

Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:

New features:

 - Add --dry-run option to 'perf record' to check if command line options can be
   parsed, but not doing any recording (Wang Nan)

 - Allow dumping the object files generated by llvm when processing eBPF
   scriptlet events (Wang Nan)

 - Add stackcollapse.py script to help generating flame graphs (Paolo Bonzini)

Documentation changes:

 - Fix 'perf script'  documentation of '-f' when it should be '-F' (Adrian Hunter)

Infrastructure changes:

 - Fix write_backwards fallback when using a new tool on older kernels
   without support for this feature (Arnaldo Carvalho de Melo)

 - Remove some leftovers from the initial codebase copying from git
   (Arnaldo Carvalho de Melo)

 - List libelf-devel as an alternative, as this is how the libelf
   development package is called on OpenSuSE (Jean Delvare)

 - Rename __hists__add_entry to hists__add_entry (Jiri Olsa)
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 02469a95 6745d8ea
......@@ -1038,7 +1038,7 @@ ifdef CONFIG_STACK_VALIDATION
ifeq ($(has_libelf),1)
objtool_target := tools/objtool FORCE
else
$(warning "Cannot use CONFIG_STACK_VALIDATION, please install libelf-dev or elfutils-libelf-devel")
$(warning "Cannot use CONFIG_STACK_VALIDATION, please install libelf-dev, libelf-devel or elfutils-libelf-devel")
SKIP_STACK_VALIDATION := 1
export SKIP_STACK_VALIDATION
endif
......
......@@ -360,6 +360,13 @@ particular perf.data snapshot should be kept or not.
Implies --timestamp-filename, --no-buildid and --no-buildid-cache.
--dry-run::
Parse options then exit. --dry-run can be used to detect errors in cmdline
options.
'perf record --dry-run -e' can act as a BPF script compiler if llvm.dump-obj
in config file is set to true.
SEE ALSO
--------
linkperf:perf-stat[1], linkperf:perf-list[1]
......@@ -119,13 +119,13 @@ OPTIONS
srcline, period, iregs, brstack, brstacksym, flags.
Field list can be prepended with the type, trace, sw or hw,
to indicate to which event type the field list applies.
e.g., -f sw:comm,tid,time,ip,sym and -f trace:time,cpu,trace
e.g., -F sw:comm,tid,time,ip,sym and -F trace:time,cpu,trace
perf script -f <fields>
perf script -F <fields>
is equivalent to:
perf script -f trace:<fields> -f sw:<fields> -f hw:<fields>
perf script -F trace:<fields> -F sw:<fields> -F hw:<fields>
i.e., the specified fields apply to all event types if the type string
is not given.
......@@ -133,9 +133,9 @@ OPTIONS
The arguments are processed in the order received. A later usage can
reset a prior request. e.g.:
-f trace: -f comm,tid,time,ip,sym
-F trace: -F comm,tid,time,ip,sym
The first -f suppresses trace events (field list is ""), but then the
The first -F suppresses trace events (field list is ""), but then the
second invocation sets the fields to comm,tid,time,ip,sym. In this case a
warning is given to the user:
......@@ -143,9 +143,9 @@ OPTIONS
Alternatively, consider the order:
-f comm,tid,time,ip,sym -f trace:
-F comm,tid,time,ip,sym -F trace:
The first -f sets the fields for all events and the second -f
The first -F sets the fields for all events and the second -F
suppresses trace events. The user is given a warning message about
the override, and the result of the above is that only S/W and H/W
events are displayed with the given fields.
......@@ -154,14 +154,14 @@ OPTIONS
event type, a message is displayed to the user that the option is
ignored for that type. For example:
$ perf script -f comm,tid,trace
$ perf script -F comm,tid,trace
'trace' not valid for hardware events. Ignoring.
'trace' not valid for software events. Ignoring.
Alternatively, if the type is given an invalid field is specified it
is an error. For example:
perf script -v -f sw:comm,tid,trace
perf script -v -F sw:comm,tid,trace
'trace' not valid for software events.
At this point usage is displayed, and perf-script exits.
......@@ -173,7 +173,7 @@ OPTIONS
respectively.
Finally, a user may not set fields to none for all event types.
i.e., -f "" is not allowed.
i.e., -F "" is not allowed.
The brstack output includes branch related information with raw addresses using the
/v/v/v/v/ syntax in the following order:
......
......@@ -75,7 +75,7 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel,
sample->period = 1;
sample->weight = 1;
he = __hists__add_entry(hists, al, NULL, NULL, NULL, sample, true);
he = hists__add_entry(hists, al, NULL, NULL, NULL, sample, true);
if (he == NULL)
return -ENOMEM;
......
......@@ -310,16 +310,6 @@ static int formula_fprintf(struct hist_entry *he, struct hist_entry *pair,
return -1;
}
static int hists__add_entry(struct hists *hists,
struct addr_location *al,
struct perf_sample *sample)
{
if (__hists__add_entry(hists, al, NULL, NULL, NULL,
sample, true) != NULL)
return 0;
return -ENOMEM;
}
static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_sample *sample,
......@@ -336,7 +326,7 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,
return -1;
}
if (hists__add_entry(hists, &al, sample)) {
if (!hists__add_entry(hists, &al, NULL, NULL, NULL, sample, true)) {
pr_warning("problem incrementing symbol period, skipping event\n");
goto out_put;
}
......
......@@ -1274,6 +1274,8 @@ static struct record record = {
const char record_callchain_help[] = CALLCHAIN_RECORD_HELP
"\n\t\t\t\tDefault: fp";
static bool dry_run;
/*
* XXX Will stay a global variable till we fix builtin-script.c to stop messing
* with it and switch to use the library functions in perf_evlist that came
......@@ -1393,6 +1395,8 @@ struct option __record_options[] = {
"append timestamp to output filename"),
OPT_BOOLEAN(0, "switch-output", &record.switch_output,
"Switch output when receive SIGUSR2"),
OPT_BOOLEAN(0, "dry-run", &dry_run,
"Parse options then exit"),
OPT_END()
};
......@@ -1462,6 +1466,9 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
if (err)
return err;
if (dry_run)
return 0;
err = bpf__setup_stdout(rec->evlist);
if (err) {
bpf__strerror_setup_stdout(rec->evlist, err, errbuf, sizeof(errbuf));
......
......@@ -257,7 +257,7 @@ else
LIBC_SUPPORT := 1
endif
ifeq ($(LIBC_SUPPORT),1)
msg := $(warning No libelf found, disables 'probe' tool and BPF support in 'perf record', please install elfutils-libelf-devel/libelf-dev);
msg := $(warning No libelf found, disables 'probe' tool and BPF support in 'perf record', please install libelf-dev, libelf-devel or elfutils-libelf-devel);
NO_LIBELF := 1
NO_DWARF := 1
......
......@@ -139,8 +139,6 @@ struct option options[] = {
OPT_ARGUMENT("html-path", "html-path"),
OPT_ARGUMENT("paginate", "paginate"),
OPT_ARGUMENT("no-pager", "no-pager"),
OPT_ARGUMENT("perf-dir", "perf-dir"),
OPT_ARGUMENT("work-tree", "work-tree"),
OPT_ARGUMENT("debugfs-dir", "debugfs-dir"),
OPT_ARGUMENT("buildid-dir", "buildid-dir"),
OPT_ARGUMENT("list-cmds", "list-cmds"),
......@@ -200,35 +198,6 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
use_pager = 0;
if (envchanged)
*envchanged = 1;
} else if (!strcmp(cmd, "--perf-dir")) {
if (*argc < 2) {
fprintf(stderr, "No directory given for --perf-dir.\n");
usage(perf_usage_string);
}
setenv(PERF_DIR_ENVIRONMENT, (*argv)[1], 1);
if (envchanged)
*envchanged = 1;
(*argv)++;
(*argc)--;
handled++;
} else if (!prefixcmp(cmd, CMD_PERF_DIR)) {
setenv(PERF_DIR_ENVIRONMENT, cmd + strlen(CMD_PERF_DIR), 1);
if (envchanged)
*envchanged = 1;
} else if (!strcmp(cmd, "--work-tree")) {
if (*argc < 2) {
fprintf(stderr, "No directory given for --work-tree.\n");
usage(perf_usage_string);
}
setenv(PERF_WORK_TREE_ENVIRONMENT, (*argv)[1], 1);
if (envchanged)
*envchanged = 1;
(*argv)++;
(*argc)--;
} else if (!prefixcmp(cmd, CMD_WORK_TREE)) {
setenv(PERF_WORK_TREE_ENVIRONMENT, cmd + strlen(CMD_WORK_TREE), 1);
if (envchanged)
*envchanged = 1;
} else if (!strcmp(cmd, "--debugfs-dir")) {
if (*argc < 2) {
fprintf(stderr, "No directory given for --debugfs-dir.\n");
......@@ -363,11 +332,6 @@ const char perf_version_string[] = PERF_VERSION;
#define RUN_SETUP (1<<0)
#define USE_PAGER (1<<1)
/*
* require working tree to be present -- anything uses this needs
* RUN_SETUP for reading from the configuration file.
*/
#define NEED_WORK_TREE (1<<2)
static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
{
......
#!/bin/sh
#
# stackcollapse.py can cover all type of perf samples including
# the tracepoints, so no special record requirements, just record what
# you want to analyze.
#
perf record "$@"
#!/bin/sh
# description: produce callgraphs in short form for scripting use
perf script -s "$PERF_EXEC_PATH"/scripts/python/stackcollapse.py -- "$@"
#!/usr/bin/perl -w
#
# stackcollapse.py - format perf samples with one line per distinct call stack
#
# This script's output has two space-separated fields. The first is a semicolon
# separated stack including the program name (from the "comm" field) and the
# function names from the call stack. The second is a count:
#
# swapper;start_kernel;rest_init;cpu_idle;default_idle;native_safe_halt 2
#
# The file is sorted according to the first field.
#
# Input may be created and processed using:
#
# perf record -a -g -F 99 sleep 60
# perf script report stackcollapse > out.stacks-folded
#
# (perf script record stackcollapse works too).
#
# Written by Paolo Bonzini <pbonzini@redhat.com>
# Based on Brendan Gregg's stackcollapse-perf.pl script.
import os
import sys
from collections import defaultdict
from optparse import OptionParser, make_option
sys.path.append(os.environ['PERF_EXEC_PATH'] + \
'/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
from perf_trace_context import *
from Core import *
from EventClass import *
# command line parsing
option_list = [
# formatting options for the bottom entry of the stack
make_option("--include-tid", dest="include_tid",
action="store_true", default=False,
help="include thread id in stack"),
make_option("--include-pid", dest="include_pid",
action="store_true", default=False,
help="include process id in stack"),
make_option("--no-comm", dest="include_comm",
action="store_false", default=True,
help="do not separate stacks according to comm"),
make_option("--tidy-java", dest="tidy_java",
action="store_true", default=False,
help="beautify Java signatures"),
make_option("--kernel", dest="annotate_kernel",
action="store_true", default=False,
help="annotate kernel functions with _[k]")
]
parser = OptionParser(option_list=option_list)
(opts, args) = parser.parse_args()
if len(args) != 0:
parser.error("unexpected command line argument")
if opts.include_tid and not opts.include_comm:
parser.error("requesting tid but not comm is invalid")
if opts.include_pid and not opts.include_comm:
parser.error("requesting pid but not comm is invalid")
# event handlers
lines = defaultdict(lambda: 0)
def process_event(param_dict):
def tidy_function_name(sym, dso):
if sym is None:
sym = '[unknown]'
sym = sym.replace(';', ':')
if opts.tidy_java:
# the original stackcollapse-perf.pl script gives the
# example of converting this:
# Lorg/mozilla/javascript/MemberBox;.<init>(Ljava/lang/reflect/Method;)V
# to this:
# org/mozilla/javascript/MemberBox:.init
sym = sym.replace('<', '')
sym = sym.replace('>', '')
if sym[0] == 'L' and sym.find('/'):
sym = sym[1:]
try:
sym = sym[:sym.index('(')]
except ValueError:
pass
if opts.annotate_kernel and dso == '[kernel.kallsyms]':
return sym + '_[k]'
else:
return sym
stack = list()
if 'callchain' in param_dict:
for entry in param_dict['callchain']:
entry.setdefault('sym', dict())
entry['sym'].setdefault('name', None)
entry.setdefault('dso', None)
stack.append(tidy_function_name(entry['sym']['name'],
entry['dso']))
else:
param_dict.setdefault('symbol', None)
param_dict.setdefault('dso', None)
stack.append(tidy_function_name(param_dict['symbol'],
param_dict['dso']))
if opts.include_comm:
comm = param_dict["comm"].replace(' ', '_')
sep = "-"
if opts.include_pid:
comm = comm + sep + str(param_dict['sample']['pid'])
sep = "/"
if opts.include_tid:
comm = comm + sep + str(param_dict['sample']['tid'])
stack.append(comm)
stack_string = ';'.join(reversed(stack))
lines[stack_string] = lines[stack_string] + 1
def trace_end():
list = lines.keys()
list.sort()
for stack in list:
print "%s %d" % (stack, lines[stack])
......@@ -84,7 +84,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
if (machine__resolve(machine, &al, &sample) < 0)
goto out;
he = __hists__add_entry(hists, &al, NULL,
he = hists__add_entry(hists, &al, NULL,
NULL, NULL, &sample, true);
if (he == NULL) {
addr_location__put(&al);
......@@ -103,7 +103,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
if (machine__resolve(machine, &al, &sample) < 0)
goto out;
he = __hists__add_entry(hists, &al, NULL,
he = hists__add_entry(hists, &al, NULL,
NULL, NULL, &sample, true);
if (he == NULL) {
addr_location__put(&al);
......
......@@ -11,14 +11,9 @@
#include <linux/string.h>
#define CMD_EXEC_PATH "--exec-path"
#define CMD_PERF_DIR "--perf-dir="
#define CMD_WORK_TREE "--work-tree="
#define CMD_DEBUGFS_DIR "--debugfs-dir="
#define PERF_DIR_ENVIRONMENT "PERF_DIR"
#define PERF_WORK_TREE_ENVIRONMENT "PERF_WORK_TREE"
#define EXEC_PATH_ENVIRONMENT "PERF_EXEC_PATH"
#define DEFAULT_PERF_DIR_ENVIRONMENT ".perf"
#define PERF_DEBUGFS_ENVIRONMENT "PERF_DEBUGFS_DIR"
#define PERF_TRACEFS_ENVIRONMENT "PERF_TRACEFS_DIR"
#define PERF_PAGER_ENVIRONMENT "PERF_PAGER"
......@@ -32,7 +27,6 @@ int perf_config_int(const char *, const char *);
u64 perf_config_u64(const char *, const char *);
int perf_config_bool(const char *, const char *);
int config_error_nonbool(const char *);
const char *perf_config_dirname(const char *, const char *);
const char *perf_etc_perfconfig(void);
char *alias_lookup(const char *alias);
......@@ -45,9 +39,6 @@ static inline int is_absolute_path(const char *path)
return path[0] == '/';
}
char *strip_path_suffix(const char *path, const char *suffix);
char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
char *perf_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
#endif /* __PERF_CACHE_H */
......@@ -372,7 +372,7 @@ int perf_config_bool(const char *name, const char *value)
return !!perf_config_bool_or_int(name, value, &discard);
}
const char *perf_config_dirname(const char *name, const char *value)
static const char *perf_config_dirname(const char *name, const char *value)
{
if (!name)
return NULL;
......
......@@ -1389,8 +1389,11 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
if (perf_missing_features.lbr_flags)
evsel->attr.branch_sample_type &= ~(PERF_SAMPLE_BRANCH_NO_FLAGS |
PERF_SAMPLE_BRANCH_NO_CYCLES);
if (perf_missing_features.write_backward)
if (perf_missing_features.write_backward) {
if (evsel->overwrite)
return -EINVAL;
evsel->attr.write_backward = false;
}
retry_sample_id:
if (perf_missing_features.sample_id_all)
evsel->attr.sample_id_all = 0;
......@@ -1453,12 +1456,6 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
err = -EINVAL;
goto out_close;
}
if (evsel->overwrite &&
perf_missing_features.write_backward) {
err = -EINVAL;
goto out_close;
}
}
}
......@@ -1496,7 +1493,10 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
* Must probe features in the order they were added to the
* perf_event_attr interface.
*/
if (!perf_missing_features.clockid_wrong && evsel->attr.use_clockid) {
if (!perf_missing_features.write_backward && evsel->attr.write_backward) {
perf_missing_features.write_backward = true;
goto fallback_missing_features;
} else if (!perf_missing_features.clockid_wrong && evsel->attr.use_clockid) {
perf_missing_features.clockid_wrong = true;
goto fallback_missing_features;
} else if (!perf_missing_features.clockid && evsel->attr.use_clockid) {
......@@ -1521,12 +1521,7 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
PERF_SAMPLE_BRANCH_NO_FLAGS))) {
perf_missing_features.lbr_flags = true;
goto fallback_missing_features;
} else if (!perf_missing_features.write_backward &&
evsel->attr.write_backward) {
perf_missing_features.write_backward = true;
goto fallback_missing_features;
}
out_close:
do {
while (--thread >= 0) {
......@@ -2409,6 +2404,8 @@ int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target,
"We found oprofile daemon running, please stop it and try again.");
break;
case EINVAL:
if (evsel->overwrite && perf_missing_features.write_backward)
return scnprintf(msg, size, "Reading from overwrite event is not supported by this kernel.");
if (perf_missing_features.clockid)
return scnprintf(msg, size, "clockid feature not supported.");
if (perf_missing_features.clockid_wrong)
......
......@@ -531,7 +531,7 @@ static struct hist_entry *hists__findnew_entry(struct hists *hists,
return he;
}
struct hist_entry *__hists__add_entry(struct hists *hists,
struct hist_entry *hists__add_entry(struct hists *hists,
struct addr_location *al,
struct symbol *sym_parent,
struct branch_info *bi,
......@@ -622,7 +622,7 @@ iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al
*/
sample->period = cost;
he = __hists__add_entry(hists, al, iter->parent, NULL, mi,
he = hists__add_entry(hists, al, iter->parent, NULL, mi,
sample, true);
if (!he)
return -ENOMEM;
......@@ -727,7 +727,7 @@ iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *a
sample->period = 1;
sample->weight = bi->flags.cycles ? bi->flags.cycles : 1;
he = __hists__add_entry(hists, al, iter->parent, &bi[i], NULL,
he = hists__add_entry(hists, al, iter->parent, &bi[i], NULL,
sample, true);
if (he == NULL)
return -ENOMEM;
......@@ -764,7 +764,7 @@ iter_add_single_normal_entry(struct hist_entry_iter *iter, struct addr_location
struct perf_sample *sample = iter->sample;
struct hist_entry *he;
he = __hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL,
he = hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL,
sample, true);
if (he == NULL)
return -ENOMEM;
......@@ -825,7 +825,7 @@ iter_add_single_cumulative_entry(struct hist_entry_iter *iter,
struct hist_entry *he;
int err = 0;
he = __hists__add_entry(hists, al, iter->parent, NULL, NULL,
he = hists__add_entry(hists, al, iter->parent, NULL, NULL,
sample, true);
if (he == NULL)
return -ENOMEM;
......@@ -900,7 +900,7 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter,
}
}
he = __hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL,
he = hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL,
sample, false);
if (he == NULL)
return -ENOMEM;
......
......@@ -120,7 +120,7 @@ extern const struct hist_iter_ops hist_iter_branch;
extern const struct hist_iter_ops hist_iter_mem;
extern const struct hist_iter_ops hist_iter_cumulative;
struct hist_entry *__hists__add_entry(struct hists *hists,
struct hist_entry *hists__add_entry(struct hists *hists,
struct addr_location *al,
struct symbol *parent,
struct branch_info *bi,
......
......@@ -42,6 +42,8 @@ int perf_llvm_config(const char *var, const char *value)
llvm_param.kbuild_dir = strdup(value);
else if (!strcmp(var, "kbuild-opts"))
llvm_param.kbuild_opts = strdup(value);
else if (!strcmp(var, "dump-obj"))
llvm_param.dump_obj = !!perf_config_bool(var, value);
else
return -1;
llvm_param.user_set_param = true;
......@@ -326,6 +328,42 @@ get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts)
pr_debug("include option is set to %s\n", *kbuild_include_opts);
}
static void
dump_obj(const char *path, void *obj_buf, size_t size)
{
char *obj_path = strdup(path);
FILE *fp;
char *p;
if (!obj_path) {
pr_warning("WARNING: No enough memory, skip object dumping\n");
return;
}
p = strrchr(obj_path, '.');
if (!p || (strcmp(p, ".c") != 0)) {
pr_warning("WARNING: invalid llvm source path: '%s', skip object dumping\n",
obj_path);
goto out;
}
p[1] = 'o';
fp = fopen(obj_path, "wb");
if (!fp) {
pr_warning("WARNING: failed to open '%s': %s, skip object dumping\n",
obj_path, strerror(errno));
goto out;
}
pr_info("LLVM: dumping %s\n", obj_path);
if (fwrite(obj_buf, size, 1, fp) != 1)
pr_warning("WARNING: failed to write to file '%s': %s, skip object dumping\n",
obj_path, strerror(errno));
fclose(fp);
out:
free(obj_path);
}
int llvm__compile_bpf(const char *path, void **p_obj_buf,
size_t *p_obj_buf_sz)
{
......@@ -411,6 +449,10 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
free(kbuild_dir);
free(kbuild_include_opts);
if (llvm_param.dump_obj)
dump_obj(path, obj_buf, obj_buf_sz);
if (!p_obj_buf)
free(obj_buf);
else
......
......@@ -29,6 +29,11 @@ struct llvm_param {
* compiling. Should not be used for dynamic compiling.
*/
const char *kbuild_opts;
/*
* Default is false. If set to true, write compiling result
* to object file.
*/
bool dump_obj;
/*
* Default is false. If one of the above fields is set by user
* explicitly then user_set_llvm is set to true. This is used
......
......@@ -14,14 +14,8 @@
static char bad_path[] = "/bad-path/";
/*
* Two hacks:
* One hack:
*/
static const char *get_perf_dir(void)
{
return ".";
}
static char *get_pathname(void)
{
static char pathname_array[4][PATH_MAX];
......@@ -54,60 +48,3 @@ char *mkpath(const char *fmt, ...)
return bad_path;
return cleanup_path(pathname);
}
char *perf_path(const char *fmt, ...)
{
const char *perf_dir = get_perf_dir();
char *pathname = get_pathname();
va_list args;
unsigned len;
len = strlen(perf_dir);
if (len > PATH_MAX-100)
return bad_path;
memcpy(pathname, perf_dir, len);
if (len && perf_dir[len-1] != '/')
pathname[len++] = '/';
va_start(args, fmt);
len += vsnprintf(pathname + len, PATH_MAX - len, fmt, args);
va_end(args);
if (len >= PATH_MAX)
return bad_path;
return cleanup_path(pathname);
}
/* strip arbitrary amount of directory separators at end of path */
static inline int chomp_trailing_dir_sep(const char *path, int len)
{
while (len && is_dir_sep(path[len - 1]))
len--;
return len;
}
/*
* If path ends with suffix (complete path components), returns the
* part before suffix (sans trailing directory separators).
* Otherwise returns NULL.
*/
char *strip_path_suffix(const char *path, const char *suffix)
{
int path_len = strlen(path), suffix_len = strlen(suffix);
while (suffix_len) {
if (!path_len)
return NULL;
if (is_dir_sep(path[path_len - 1])) {
if (!is_dir_sep(suffix[suffix_len - 1]))
return NULL;
path_len = chomp_trailing_dir_sep(path, path_len);
suffix_len = chomp_trailing_dir_sep(suffix, suffix_len);
}
else if (path[--path_len] != suffix[--suffix_len])
return NULL;
}
if (path_len && !is_dir_sep(path[path_len - 1]))
return NULL;
return strndup(path, chomp_trailing_dir_sep(path, path_len));
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment