Commit 963a70b8 authored by Ingo Molnar's avatar Ingo Molnar

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

Merge tag 'perf-core-for-mingo-2' 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:

  - Handle legacy syscalls tracepoints (David Ahern, Arnaldo Carvalho de Melo)

  - Indicate which callchain entries are annotated in the
    TUI hists browser (report/top) (Arnaldo Carvalho de Melo)

  - Fix failure to add multiple probes without debuginfo (He Kuang)

  - Fix 'trace' summary_only option (David Ahern)

  - Fix race in build_id_cache__add_s() in 'buildid-cache' (Milos Vyletel)

  - Don't allow empty argument for field-separator, fixing segfault (Wang Nan)

Infrastructure:

  - Add destructor for format_field in libtraceevent (David Ahern)

  - Prep work for support lzma compressed kernel modules (Jiri Olsa)

  - Update .gitignore with recently added/renamed feature detection files (Yunlong Song)
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 08b3f913 ca33380a
feature_dir := $(srctree)/tools/build/feature
ifneq ($(OUTPUT),)
OUTPUT_FEATURES = $(OUTPUT)feature/
$(shell mkdir -p $(OUTPUT_FEATURES))
endif
feature_check = $(eval $(feature_check_code))
define feature_check_code
feature-$(1) := $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS) $(FEATURE_CHECK_CFLAGS-$(1))" LDFLAGS="$(LDFLAGS) $(FEATURE_CHECK_LDFLAGS-$(1))" -C $(feature_dir) test-$1.bin >/dev/null 2>/dev/null && echo 1 || echo 0)
endef
feature_set = $(eval $(feature_set_code))
define feature_set_code
feature-$(1) := 1
endef
#
# Build the feature check binaries in parallel, ignore errors, ignore return value and suppress output:
#
#
# Note that this is not a complete list of all feature tests, just
# those that are typically built on a fully configured system.
#
# [ Feature tests not mentioned here have to be built explicitly in
# the rule that uses them - an example for that is the 'bionic'
# feature check. ]
#
FEATURE_TESTS = \
backtrace \
dwarf \
fortify-source \
sync-compare-and-swap \
glibc \
gtk2 \
gtk2-infobar \
libaudit \
libbfd \
libelf \
libelf-getphdrnum \
libelf-mmap \
libnuma \
libperl \
libpython \
libpython-version \
libslang \
libunwind \
pthread-attr-setaffinity-np \
stackprotector-all \
timerfd \
libdw-dwarf-unwind \
zlib \
lzma
FEATURE_DISPLAY = \
dwarf \
glibc \
gtk2 \
libaudit \
libbfd \
libelf \
libnuma \
libperl \
libpython \
libslang \
libunwind \
libdw-dwarf-unwind \
zlib \
lzma
# Set FEATURE_CHECK_(C|LD)FLAGS-all for all FEATURE_TESTS features.
# If in the future we need per-feature checks/flags for features not
# mentioned in this list we need to refactor this ;-).
set_test_all_flags = $(eval $(set_test_all_flags_code))
define set_test_all_flags_code
FEATURE_CHECK_CFLAGS-all += $(FEATURE_CHECK_CFLAGS-$(1))
FEATURE_CHECK_LDFLAGS-all += $(FEATURE_CHECK_LDFLAGS-$(1))
endef
$(foreach feat,$(FEATURE_TESTS),$(call set_test_all_flags,$(feat)))
#
# Special fast-path for the 'all features are available' case:
#
$(call feature_check,all,$(MSG))
#
# Just in case the build freshly failed, make sure we print the
# feature matrix:
#
ifeq ($(feature-all), 1)
#
# test-all.c passed - just set all the core feature flags to 1:
#
$(foreach feat,$(FEATURE_TESTS),$(call feature_set,$(feat)))
else
$(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS)" LDFLAGS=$(LDFLAGS) -i -j -C $(feature_dir) $(addsuffix .bin,$(FEATURE_TESTS)) >/dev/null 2>&1)
$(foreach feat,$(FEATURE_TESTS),$(call feature_check,$(feat)))
endif
#
# Print the result of the feature test:
#
feature_print_status = $(eval $(feature_print_status_code)) $(info $(MSG))
define feature_print_status_code
ifeq ($(feature-$(1)), 1)
MSG = $(shell printf '...%30s: [ \033[32mon\033[m ]' $(1))
else
MSG = $(shell printf '...%30s: [ \033[31mOFF\033[m ]' $(1))
endif
endef
feature_print_text = $(eval $(feature_print_text_code)) $(info $(MSG))
define feature_print_text_code
MSG = $(shell printf '...%30s: %s' $(1) $(2))
endef
FEATURE_DUMP := $(foreach feat,$(FEATURE_DISPLAY),feature-$(feat)($(feature-$(feat))))
FEATURE_DUMP_FILE := $(shell touch $(OUTPUT)FEATURE-DUMP; cat $(OUTPUT)FEATURE-DUMP)
ifeq ($(dwarf-post-unwind),1)
FEATURE_DUMP += dwarf-post-unwind($(dwarf-post-unwind-text))
endif
# The $(feature_display) controls the default detection message
# output. It's set if:
# - detected features differes from stored features from
# last build (in FEATURE-DUMP file)
# - one of the $(FEATURE_DISPLAY) is not detected
# - VF is enabled
ifneq ("$(FEATURE_DUMP)","$(FEATURE_DUMP_FILE)")
$(shell echo "$(FEATURE_DUMP)" > $(OUTPUT)FEATURE-DUMP)
feature_display := 1
endif
feature_display_check = $(eval $(feature_check_code))
define feature_display_check_code
ifneq ($(feature-$(1)), 1)
feature_display := 1
endif
endef
$(foreach feat,$(FEATURE_DISPLAY),$(call feature_display_check,$(feat)))
ifeq ($(VF),1)
feature_display := 1
feature_verbose := 1
endif
ifeq ($(feature_display),1)
$(info )
$(info Auto-detecting system features:)
$(foreach feat,$(FEATURE_DISPLAY),$(call feature_print_status,$(feat),))
ifeq ($(dwarf-post-unwind),1)
$(call feature_print_text,"DWARF post unwind library", $(dwarf-post-unwind-text))
endif
ifneq ($(feature_verbose),1)
$(info )
endif
endif
ifeq ($(feature_verbose),1)
TMP := $(filter-out $(FEATURE_DISPLAY),$(FEATURE_TESTS))
$(foreach feat,$(TMP),$(call feature_print_status,$(feat),))
$(info )
endif
...@@ -32,7 +32,8 @@ FILES= \ ...@@ -32,7 +32,8 @@ FILES= \
test-libbabeltrace.bin \ test-libbabeltrace.bin \
test-compile-32.bin \ test-compile-32.bin \
test-compile-x32.bin \ test-compile-x32.bin \
test-zlib.bin test-zlib.bin \
test-lzma.bin
CC := $(CROSS_COMPILE)gcc -MD CC := $(CROSS_COMPILE)gcc -MD
PKG_CONFIG := $(CROSS_COMPILE)pkg-config PKG_CONFIG := $(CROSS_COMPILE)pkg-config
...@@ -45,7 +46,7 @@ __BUILD = $(CC) $(CFLAGS) -Wall -Werror -o $(OUTPUT)$@ $(patsubst %.bin,%.c,$@) ...@@ -45,7 +46,7 @@ __BUILD = $(CC) $(CFLAGS) -Wall -Werror -o $(OUTPUT)$@ $(patsubst %.bin,%.c,$@)
############################### ###############################
test-all.bin: test-all.bin:
$(BUILD) -fstack-protector-all -O2 -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -laudit -I/usr/include/slang -lslang $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl -lz $(BUILD) -fstack-protector-all -O2 -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -laudit -I/usr/include/slang -lslang $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl -lz -llzma
test-hello.bin: test-hello.bin:
$(BUILD) $(BUILD)
...@@ -152,6 +153,9 @@ test-compile-x32.bin: ...@@ -152,6 +153,9 @@ test-compile-x32.bin:
test-zlib.bin: test-zlib.bin:
$(BUILD) -lz $(BUILD) -lz
test-lzma.bin:
$(BUILD) -llzma
-include *.d -include *.d
############################### ###############################
......
...@@ -113,6 +113,10 @@ ...@@ -113,6 +113,10 @@
#undef main #undef main
#endif #endif
#define main main_test_lzma
# include "test-lzma.c"
#undef main
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
main_test_libpython(); main_test_libpython();
...@@ -138,6 +142,7 @@ int main(int argc, char *argv[]) ...@@ -138,6 +142,7 @@ int main(int argc, char *argv[])
main_test_sync_compare_and_swap(argc, argv); main_test_sync_compare_and_swap(argc, argv);
main_test_zlib(); main_test_zlib();
main_test_pthread_attr_setaffinity_np(); main_test_pthread_attr_setaffinity_np();
main_test_lzma();
return 0; return 0;
} }
#include <lzma.h>
int main(void)
{
lzma_stream strm = LZMA_STREAM_INIT;
int ret;
ret = lzma_stream_decoder(&strm, UINT64_MAX, LZMA_CONCATENATED);
return ret ? -1 : 0;
}
...@@ -6228,15 +6228,20 @@ void pevent_ref(struct pevent *pevent) ...@@ -6228,15 +6228,20 @@ void pevent_ref(struct pevent *pevent)
pevent->ref_count++; pevent->ref_count++;
} }
void pevent_free_format_field(struct format_field *field)
{
free(field->type);
free(field->name);
free(field);
}
static void free_format_fields(struct format_field *field) static void free_format_fields(struct format_field *field)
{ {
struct format_field *next; struct format_field *next;
while (field) { while (field) {
next = field->next; next = field->next;
free(field->type); pevent_free_format_field(field);
free(field->name);
free(field);
field = next; field = next;
} }
} }
......
...@@ -619,6 +619,7 @@ enum pevent_errno pevent_parse_format(struct pevent *pevent, ...@@ -619,6 +619,7 @@ enum pevent_errno pevent_parse_format(struct pevent *pevent,
const char *buf, const char *buf,
unsigned long size, const char *sys); unsigned long size, const char *sys);
void pevent_free_format(struct event_format *event); void pevent_free_format(struct event_format *event);
void pevent_free_format_field(struct format_field *field);
void *pevent_get_field_raw(struct trace_seq *s, struct event_format *event, void *pevent_get_field_raw(struct trace_seq *s, struct event_format *event,
const char *name, struct pevent_record *record, const char *name, struct pevent_record *record,
......
PERF-CFLAGS PERF-CFLAGS
PERF-GUI-VARS PERF-GUI-VARS
PERF-VERSION-FILE PERF-VERSION-FILE
PERF-FEATURES FEATURE-DUMP
perf perf
perf-read-vdso32 perf-read-vdso32
perf-read-vdsox32 perf-read-vdsox32
......
...@@ -71,6 +71,8 @@ include config/utilities.mak ...@@ -71,6 +71,8 @@ include config/utilities.mak
# #
# Define NO_LIBBABELTRACE if you do not want libbabeltrace support # Define NO_LIBBABELTRACE if you do not want libbabeltrace support
# for CTF data format. # for CTF data format.
#
# Define NO_LZMA if you do not want to support compressed (xz) kernel modules
ifeq ($(srctree),) ifeq ($(srctree),)
srctree := $(patsubst %/,%,$(dir $(shell pwd))) srctree := $(patsubst %/,%,$(dir $(shell pwd)))
...@@ -521,7 +523,7 @@ $(INSTALL_DOC_TARGETS): ...@@ -521,7 +523,7 @@ $(INSTALL_DOC_TARGETS):
# #
config-clean: config-clean:
$(call QUIET_CLEAN, config) $(call QUIET_CLEAN, config)
$(Q)$(MAKE) -C config/feature-checks clean >/dev/null $(Q)$(MAKE) -C $(srctree)/tools/build/feature/ clean >/dev/null
clean: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean config-clean clean: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean config-clean
$(call QUIET_CLEAN, core-objs) $(RM) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(LANG_BINDINGS) $(call QUIET_CLEAN, core-objs) $(RM) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(LANG_BINDINGS)
......
...@@ -802,7 +802,7 @@ static const struct option options[] = { ...@@ -802,7 +802,7 @@ static const struct option options[] = {
OPT_STRING('s', "sort", &sort_order, "key[,key2...]", OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
"sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..." "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..."
" Please refer the man page for the complete list."), " Please refer the man page for the complete list."),
OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator", OPT_STRING_NOEMPTY('t', "field-separator", &symbol_conf.field_sep, "separator",
"separator for columns, no spaces will be added between " "separator for columns, no spaces will be added between "
"columns '.' is reserved."), "columns '.' is reserved."),
OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
......
...@@ -286,7 +286,7 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -286,7 +286,7 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused)
"input file name"), "input file name"),
OPT_STRING('C', "cpu", &mem.cpu_list, "cpu", OPT_STRING('C', "cpu", &mem.cpu_list, "cpu",
"list of cpus to profile"), "list of cpus to profile"),
OPT_STRING('x', "field-separator", &symbol_conf.field_sep, OPT_STRING_NOEMPTY('x', "field-separator", &symbol_conf.field_sep,
"separator", "separator",
"separator for columns, no spaces will be added" "separator for columns, no spaces will be added"
" between columns '.' is reserved."), " between columns '.' is reserved."),
......
...@@ -1135,6 +1135,8 @@ static struct syscall_fmt *syscall_fmt__find(const char *name) ...@@ -1135,6 +1135,8 @@ static struct syscall_fmt *syscall_fmt__find(const char *name)
struct syscall { struct syscall {
struct event_format *tp_format; struct event_format *tp_format;
int nr_args;
struct format_field *args;
const char *name; const char *name;
bool filtered; bool filtered;
bool is_exit; bool is_exit;
...@@ -1442,14 +1444,14 @@ static int syscall__set_arg_fmts(struct syscall *sc) ...@@ -1442,14 +1444,14 @@ static int syscall__set_arg_fmts(struct syscall *sc)
struct format_field *field; struct format_field *field;
int idx = 0; int idx = 0;
sc->arg_scnprintf = calloc(sc->tp_format->format.nr_fields - 1, sizeof(void *)); sc->arg_scnprintf = calloc(sc->nr_args, sizeof(void *));
if (sc->arg_scnprintf == NULL) if (sc->arg_scnprintf == NULL)
return -1; return -1;
if (sc->fmt) if (sc->fmt)
sc->arg_parm = sc->fmt->arg_parm; sc->arg_parm = sc->fmt->arg_parm;
for (field = sc->tp_format->format.fields->next; field; field = field->next) { for (field = sc->args; field; field = field->next) {
if (sc->fmt && sc->fmt->arg_scnprintf[idx]) if (sc->fmt && sc->fmt->arg_scnprintf[idx])
sc->arg_scnprintf[idx] = sc->fmt->arg_scnprintf[idx]; sc->arg_scnprintf[idx] = sc->fmt->arg_scnprintf[idx];
else if (field->flags & FIELD_IS_POINTER) else if (field->flags & FIELD_IS_POINTER)
...@@ -1515,6 +1517,14 @@ static int trace__read_syscall_info(struct trace *trace, int id) ...@@ -1515,6 +1517,14 @@ static int trace__read_syscall_info(struct trace *trace, int id)
if (sc->tp_format == NULL) if (sc->tp_format == NULL)
return -1; return -1;
sc->args = sc->tp_format->format.fields;
sc->nr_args = sc->tp_format->format.nr_fields;
/* drop nr field - not relevant here; does not exist on older kernels */
if (sc->args && strcmp(sc->args->name, "nr") == 0) {
sc->args = sc->args->next;
--sc->nr_args;
}
sc->is_exit = !strcmp(name, "exit_group") || !strcmp(name, "exit"); sc->is_exit = !strcmp(name, "exit_group") || !strcmp(name, "exit");
return syscall__set_arg_fmts(sc); return syscall__set_arg_fmts(sc);
...@@ -1537,7 +1547,7 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, ...@@ -1537,7 +1547,7 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size,
unsigned char *p; unsigned char *p;
unsigned long val; unsigned long val;
if (sc->tp_format != NULL) { if (sc->args != NULL) {
struct format_field *field; struct format_field *field;
u8 bit = 1; u8 bit = 1;
struct syscall_arg arg = { struct syscall_arg arg = {
...@@ -1547,7 +1557,7 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, ...@@ -1547,7 +1557,7 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size,
.thread = thread, .thread = thread,
}; };
for (field = sc->tp_format->format.fields->next; field; for (field = sc->args; field;
field = field->next, ++arg.idx, bit <<= 1) { field = field->next, ++arg.idx, bit <<= 1) {
if (arg.mask & bit) if (arg.mask & bit)
continue; continue;
...@@ -1724,7 +1734,8 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, ...@@ -1724,7 +1734,8 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
return -1; return -1;
} }
printed += trace__printf_interrupted_entry(trace, sample); if (!trace->summary_only)
printed += trace__printf_interrupted_entry(trace, sample);
ttrace->entry_time = sample->time; ttrace->entry_time = sample->time;
msg = ttrace->entry_str; msg = ttrace->entry_str;
......
...@@ -176,102 +176,7 @@ LDFLAGS += -Wl,-z,noexecstack ...@@ -176,102 +176,7 @@ LDFLAGS += -Wl,-z,noexecstack
EXTLIBS = -lpthread -lrt -lm -ldl EXTLIBS = -lpthread -lrt -lm -ldl
ifneq ($(OUTPUT),) include $(srctree)/tools/build/Makefile.feature
OUTPUT_FEATURES = $(OUTPUT)config/feature-checks/
$(shell mkdir -p $(OUTPUT_FEATURES))
endif
feature_check = $(eval $(feature_check_code))
define feature_check_code
feature-$(1) := $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS) $(FEATURE_CHECK_CFLAGS-$(1))" LDFLAGS="$(LDFLAGS) $(FEATURE_CHECK_LDFLAGS-$(1))" -C config/feature-checks test-$1.bin >/dev/null 2>/dev/null && echo 1 || echo 0)
endef
feature_set = $(eval $(feature_set_code))
define feature_set_code
feature-$(1) := 1
endef
#
# Build the feature check binaries in parallel, ignore errors, ignore return value and suppress output:
#
#
# Note that this is not a complete list of all feature tests, just
# those that are typically built on a fully configured system.
#
# [ Feature tests not mentioned here have to be built explicitly in
# the rule that uses them - an example for that is the 'bionic'
# feature check. ]
#
FEATURE_TESTS = \
backtrace \
dwarf \
fortify-source \
sync-compare-and-swap \
glibc \
gtk2 \
gtk2-infobar \
libaudit \
libbfd \
libelf \
libelf-getphdrnum \
libelf-mmap \
libnuma \
libperl \
libpython \
libpython-version \
libslang \
libunwind \
pthread-attr-setaffinity-np \
stackprotector-all \
timerfd \
libdw-dwarf-unwind \
zlib
FEATURE_DISPLAY = \
dwarf \
glibc \
gtk2 \
libaudit \
libbfd \
libelf \
libnuma \
libperl \
libpython \
libslang \
libunwind \
libdw-dwarf-unwind \
zlib
# Set FEATURE_CHECK_(C|LD)FLAGS-all for all FEATURE_TESTS features.
# If in the future we need per-feature checks/flags for features not
# mentioned in this list we need to refactor this ;-).
set_test_all_flags = $(eval $(set_test_all_flags_code))
define set_test_all_flags_code
FEATURE_CHECK_CFLAGS-all += $(FEATURE_CHECK_CFLAGS-$(1))
FEATURE_CHECK_LDFLAGS-all += $(FEATURE_CHECK_LDFLAGS-$(1))
endef
$(foreach feat,$(FEATURE_TESTS),$(call set_test_all_flags,$(feat)))
#
# Special fast-path for the 'all features are available' case:
#
$(call feature_check,all,$(MSG))
#
# Just in case the build freshly failed, make sure we print the
# feature matrix:
#
ifeq ($(feature-all), 1)
#
# test-all.c passed - just set all the core feature flags to 1:
#
$(foreach feat,$(FEATURE_TESTS),$(call feature_set,$(feat)))
else
$(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS)" LDFLAGS=$(LDFLAGS) -i -j -C config/feature-checks $(addsuffix .bin,$(FEATURE_TESTS)) >/dev/null 2>&1)
$(foreach feat,$(FEATURE_TESTS),$(call feature_check,$(feat)))
endif
ifeq ($(feature-stackprotector-all), 1) ifeq ($(feature-stackprotector-all), 1)
CFLAGS += -fstack-protector-all CFLAGS += -fstack-protector-all
...@@ -636,6 +541,17 @@ ifndef NO_ZLIB ...@@ -636,6 +541,17 @@ ifndef NO_ZLIB
endif endif
endif endif
ifndef NO_LZMA
ifeq ($(feature-lzma), 1)
CFLAGS += -DHAVE_LZMA_SUPPORT
EXTLIBS += -llzma
$(call detected,CONFIG_LZMA)
else
msg := $(warning No liblzma found, disables xz kernel module decompression, please install xz-devel/liblzma-dev);
NO_LZMA := 1
endif
endif
ifndef NO_BACKTRACE ifndef NO_BACKTRACE
ifeq ($(feature-backtrace), 1) ifeq ($(feature-backtrace), 1)
CFLAGS += -DHAVE_BACKTRACE_SUPPORT CFLAGS += -DHAVE_BACKTRACE_SUPPORT
...@@ -763,80 +679,12 @@ plugindir=$(libdir)/traceevent/plugins ...@@ -763,80 +679,12 @@ plugindir=$(libdir)/traceevent/plugins
plugindir_SQ= $(subst ','\'',$(plugindir)) plugindir_SQ= $(subst ','\'',$(plugindir))
endif endif
#
# Print the result of the feature test:
#
feature_print_status = $(eval $(feature_print_status_code)) $(info $(MSG))
define feature_print_status_code
ifeq ($(feature-$(1)), 1)
MSG = $(shell printf '...%30s: [ \033[32mon\033[m ]' $(1))
else
MSG = $(shell printf '...%30s: [ \033[31mOFF\033[m ]' $(1))
endif
endef
print_var = $(eval $(print_var_code)) $(info $(MSG)) print_var = $(eval $(print_var_code)) $(info $(MSG))
define print_var_code define print_var_code
MSG = $(shell printf '...%30s: %s' $(1) $($(1))) MSG = $(shell printf '...%30s: %s' $(1) $($(1)))
endef endef
feature_print_text = $(eval $(feature_print_text_code)) $(info $(MSG))
define feature_print_text_code
MSG = $(shell printf '...%30s: %s' $(1) $(2))
endef
FEATURE_DUMP := $(foreach feat,$(FEATURE_DISPLAY),feature-$(feat)($(feature-$(feat))))
FEATURE_DUMP_FILE := $(shell touch $(OUTPUT)FEATURE-DUMP; cat $(OUTPUT)FEATURE-DUMP)
ifeq ($(dwarf-post-unwind),1)
FEATURE_DUMP += dwarf-post-unwind($(dwarf-post-unwind-text))
endif
# The $(feature_display) controls the default detection message
# output. It's set if:
# - detected features differes from stored features from
# last build (in FEATURE-DUMP file)
# - one of the $(FEATURE_DISPLAY) is not detected
# - VF is enabled
ifneq ("$(FEATURE_DUMP)","$(FEATURE_DUMP_FILE)")
$(shell echo "$(FEATURE_DUMP)" > $(OUTPUT)FEATURE-DUMP)
feature_display := 1
endif
feature_check = $(eval $(feature_check_code))
define feature_check_code
ifneq ($(feature-$(1)), 1)
feature_display := 1
endif
endef
$(foreach feat,$(FEATURE_DISPLAY),$(call feature_check,$(feat)))
ifeq ($(VF),1) ifeq ($(VF),1)
feature_display := 1
feature_verbose := 1
endif
ifeq ($(feature_display),1)
$(info )
$(info Auto-detecting system features:)
$(foreach feat,$(FEATURE_DISPLAY),$(call feature_print_status,$(feat),))
ifeq ($(dwarf-post-unwind),1)
$(call feature_print_text,"DWARF post unwind library", $(dwarf-post-unwind-text))
endif
ifneq ($(feature_verbose),1)
$(info )
endif
endif
ifeq ($(feature_verbose),1)
TMP := $(filter-out $(FEATURE_DISPLAY),$(FEATURE_TESTS))
$(foreach feat,$(TMP),$(call feature_print_status,$(feat),))
$(info )
$(call print_var,prefix) $(call print_var,prefix)
$(call print_var,bindir) $(call print_var,bindir)
$(call print_var,libdir) $(call print_var,libdir)
......
...@@ -30,6 +30,7 @@ perf-y += keep-tracking.o ...@@ -30,6 +30,7 @@ perf-y += keep-tracking.o
perf-y += code-reading.o perf-y += code-reading.o
perf-y += sample-parsing.o perf-y += sample-parsing.o
perf-y += parse-no-sample-id-all.o perf-y += parse-no-sample-id-all.o
perf-y += kmod-path.o
perf-$(CONFIG_X86) += perf-time-to-tsc.o perf-$(CONFIG_X86) += perf-time-to-tsc.o
......
...@@ -166,6 +166,10 @@ static struct test { ...@@ -166,6 +166,10 @@ static struct test {
.desc = "Add fd to a fdarray, making it autogrow", .desc = "Add fd to a fdarray, making it autogrow",
.func = test__fdarray__add, .func = test__fdarray__add,
}, },
{
.desc = "Test kmod_path__parse function",
.func = test__kmod_path__parse,
},
{ {
.func = NULL, .func = NULL,
}, },
......
#include <stdbool.h>
#include "tests.h"
#include "dso.h"
#include "debug.h"
static int test(const char *path, bool alloc_name, bool alloc_ext,
bool kmod, bool comp, const char *name, const char *ext)
{
struct kmod_path m;
memset(&m, 0x0, sizeof(m));
TEST_ASSERT_VAL("kmod_path__parse",
!__kmod_path__parse(&m, path, alloc_name, alloc_ext));
pr_debug("%s - alloc name %d, alloc ext %d, kmod %d, comp %d, name '%s', ext '%s'\n",
path, alloc_name, alloc_ext, m.kmod, m.comp, m.name, m.ext);
TEST_ASSERT_VAL("wrong kmod", m.kmod == kmod);
TEST_ASSERT_VAL("wrong comp", m.comp == comp);
if (ext)
TEST_ASSERT_VAL("wrong ext", m.ext && !strcmp(ext, m.ext));
else
TEST_ASSERT_VAL("wrong ext", !m.ext);
if (name)
TEST_ASSERT_VAL("wrong name", m.name && !strcmp(name, m.name));
else
TEST_ASSERT_VAL("wrong name", !m.name);
free(m.name);
free(m.ext);
return 0;
}
#define T(path, an, ae, k, c, n, e) \
TEST_ASSERT_VAL("failed", !test(path, an, ae, k, c, n, e))
int test__kmod_path__parse(void)
{
/* path alloc_name alloc_ext kmod comp name ext */
T("/xxxx/xxxx/x-x.ko", true , true , true, false, "[x_x]", NULL);
T("/xxxx/xxxx/x-x.ko", false , true , true, false, NULL , NULL);
T("/xxxx/xxxx/x-x.ko", true , false , true, false, "[x_x]", NULL);
T("/xxxx/xxxx/x-x.ko", false , false , true, false, NULL , NULL);
/* path alloc_name alloc_ext kmod comp name ext */
T("/xxxx/xxxx/x.ko.gz", true , true , true, true, "[x]", "gz");
T("/xxxx/xxxx/x.ko.gz", false , true , true, true, NULL , "gz");
T("/xxxx/xxxx/x.ko.gz", true , false , true, true, "[x]", NULL);
T("/xxxx/xxxx/x.ko.gz", false , false , true, true, NULL , NULL);
/* path alloc_name alloc_ext kmod comp name ext */
T("/xxxx/xxxx/x.gz", true , true , false, true, "x.gz" ,"gz");
T("/xxxx/xxxx/x.gz", false , true , false, true, NULL ,"gz");
T("/xxxx/xxxx/x.gz", true , false , false, true, "x.gz" , NULL);
T("/xxxx/xxxx/x.gz", false , false , false, true, NULL , NULL);
/* path alloc_name alloc_ext kmod comp name ext */
T("x.gz", true , true , false, true, "x.gz", "gz");
T("x.gz", false , true , false, true, NULL , "gz");
T("x.gz", true , false , false, true, "x.gz", NULL);
T("x.gz", false , false , false, true, NULL , NULL);
/* path alloc_name alloc_ext kmod comp name ext */
T("x.ko.gz", true , true , true, true, "[x]", "gz");
T("x.ko.gz", false , true , true, true, NULL , "gz");
T("x.ko.gz", true , false , true, true, "[x]", NULL);
T("x.ko.gz", false , false , true, true, NULL , NULL);
return 0;
}
...@@ -51,6 +51,7 @@ int test__hists_cumulate(void); ...@@ -51,6 +51,7 @@ int test__hists_cumulate(void);
int test__switch_tracking(void); int test__switch_tracking(void);
int test__fdarray__filter(void); int test__fdarray__filter(void);
int test__fdarray__add(void); int test__fdarray__add(void);
int test__kmod_path__parse(void);
#if defined(__x86_64__) || defined(__i386__) || defined(__arm__) #if defined(__x86_64__) || defined(__i386__) || defined(__arm__)
#ifdef HAVE_DWARF_UNWIND_SUPPORT #ifdef HAVE_DWARF_UNWIND_SUPPORT
......
...@@ -511,6 +511,7 @@ static void hist_browser__show_callchain_entry(struct hist_browser *browser, ...@@ -511,6 +511,7 @@ static void hist_browser__show_callchain_entry(struct hist_browser *browser,
{ {
int color, width; int color, width;
char folded_sign = callchain_list__folded(chain); char folded_sign = callchain_list__folded(chain);
bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
color = HE_COLORSET_NORMAL; color = HE_COLORSET_NORMAL;
width = browser->b.width - (offset + 2); width = browser->b.width - (offset + 2);
...@@ -523,7 +524,8 @@ static void hist_browser__show_callchain_entry(struct hist_browser *browser, ...@@ -523,7 +524,8 @@ static void hist_browser__show_callchain_entry(struct hist_browser *browser,
ui_browser__set_color(&browser->b, color); ui_browser__set_color(&browser->b, color);
hist_browser__gotorc(browser, row, 0); hist_browser__gotorc(browser, row, 0);
slsmg_write_nstring(" ", offset); slsmg_write_nstring(" ", offset);
slsmg_printf("%c ", folded_sign); slsmg_printf("%c", folded_sign);
ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
slsmg_write_nstring(str, width); slsmg_write_nstring(str, width);
} }
......
...@@ -94,6 +94,7 @@ libperf-y += scripting-engines/ ...@@ -94,6 +94,7 @@ libperf-y += scripting-engines/
libperf-$(CONFIG_PERF_REGS) += perf_regs.o libperf-$(CONFIG_PERF_REGS) += perf_regs.o
libperf-$(CONFIG_ZLIB) += zlib.o libperf-$(CONFIG_ZLIB) += zlib.o
libperf-$(CONFIG_LZMA) += lzma.o
CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
CFLAGS_exec_cmd.o += -DPERF_EXEC_PATH="BUILD_STR($(perfexecdir_SQ))" -DPREFIX="BUILD_STR($(prefix_SQ))" CFLAGS_exec_cmd.o += -DPERF_EXEC_PATH="BUILD_STR($(perfexecdir_SQ))" -DPREFIX="BUILD_STR($(prefix_SQ))"
......
...@@ -374,7 +374,8 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name, ...@@ -374,7 +374,8 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name,
if (is_kallsyms) { if (is_kallsyms) {
if (copyfile("/proc/kallsyms", filename)) if (copyfile("/proc/kallsyms", filename))
goto out_free; goto out_free;
} else if (link(realname, filename) && copyfile(name, filename)) } else if (link(realname, filename) && errno != EEXIST &&
copyfile(name, filename))
goto out_free; goto out_free;
} }
......
...@@ -147,6 +147,9 @@ static const struct { ...@@ -147,6 +147,9 @@ static const struct {
} compressions[] = { } compressions[] = {
#ifdef HAVE_ZLIB_SUPPORT #ifdef HAVE_ZLIB_SUPPORT
{ "gz", gzip_decompress_to_file }, { "gz", gzip_decompress_to_file },
#endif
#ifdef HAVE_LZMA_SUPPORT
{ "xz", lzma_decompress_to_file },
#endif #endif
{ NULL, NULL }, { NULL, NULL },
}; };
...@@ -208,6 +211,72 @@ bool dso__needs_decompress(struct dso *dso) ...@@ -208,6 +211,72 @@ bool dso__needs_decompress(struct dso *dso)
dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE_COMP; dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE_COMP;
} }
/*
* Parses kernel module specified in @path and updates
* @m argument like:
*
* @comp - true if @path contains supported compression suffix,
* false otherwise
* @kmod - true if @path contains '.ko' suffix in right position,
* false otherwise
* @name - if (@alloc_name && @kmod) is true, it contains strdup-ed base name
* of the kernel module without suffixes, otherwise strudup-ed
* base name of @path
* @ext - if (@alloc_ext && @comp) is true, it contains strdup-ed string
* the compression suffix
*
* Returns 0 if there's no strdup error, -ENOMEM otherwise.
*/
int __kmod_path__parse(struct kmod_path *m, const char *path,
bool alloc_name, bool alloc_ext)
{
const char *name = strrchr(path, '/');
const char *ext = strrchr(path, '.');
memset(m, 0x0, sizeof(*m));
name = name ? name + 1 : path;
/* No extension, just return name. */
if (ext == NULL) {
if (alloc_name) {
m->name = strdup(name);
return m->name ? 0 : -ENOMEM;
}
return 0;
}
if (is_supported_compression(ext + 1)) {
m->comp = true;
ext -= 3;
}
/* Check .ko extension only if there's enough name left. */
if (ext > name)
m->kmod = !strncmp(ext, ".ko", 3);
if (alloc_name) {
if (m->kmod) {
if (asprintf(&m->name, "[%.*s]", (int) (ext - name), name) == -1)
return -ENOMEM;
} else {
if (asprintf(&m->name, "%s", name) == -1)
return -ENOMEM;
}
strxfrchar(m->name, '-', '_');
}
if (alloc_ext && m->comp) {
m->ext = strdup(ext + 4);
if (!m->ext) {
free((void *) m->name);
return -ENOMEM;
}
}
return 0;
}
/* /*
* Global list of open DSOs and the counter. * Global list of open DSOs and the counter.
*/ */
...@@ -1002,21 +1071,24 @@ struct dso *dsos__find(const struct dsos *dsos, const char *name, ...@@ -1002,21 +1071,24 @@ struct dso *dsos__find(const struct dsos *dsos, const char *name,
return dso__find_by_longname(&dsos->root, name); return dso__find_by_longname(&dsos->root, name);
} }
struct dso *__dsos__findnew(struct dsos *dsos, const char *name) struct dso *dsos__addnew(struct dsos *dsos, const char *name)
{ {
struct dso *dso = dsos__find(dsos, name, false); struct dso *dso = dso__new(name);
if (!dso) { if (dso != NULL) {
dso = dso__new(name); dsos__add(dsos, dso);
if (dso != NULL) { dso__set_basename(dso);
dsos__add(dsos, dso);
dso__set_basename(dso);
}
} }
return dso; return dso;
} }
struct dso *__dsos__findnew(struct dsos *dsos, const char *name)
{
struct dso *dso = dsos__find(dsos, name, false);
return dso ? dso : dsos__addnew(dsos, name);
}
size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp,
bool (skip)(struct dso *dso, int parm), int parm) bool (skip)(struct dso *dso, int parm), int parm)
{ {
......
...@@ -195,6 +195,20 @@ bool is_kernel_module(const char *pathname, bool *compressed); ...@@ -195,6 +195,20 @@ bool is_kernel_module(const char *pathname, bool *compressed);
bool decompress_to_file(const char *ext, const char *filename, int output_fd); bool decompress_to_file(const char *ext, const char *filename, int output_fd);
bool dso__needs_decompress(struct dso *dso); bool dso__needs_decompress(struct dso *dso);
struct kmod_path {
char *name;
char *ext;
bool comp;
bool kmod;
};
int __kmod_path__parse(struct kmod_path *m, const char *path,
bool alloc_name, bool alloc_ext);
#define kmod_path__parse(__m, __p) __kmod_path__parse(__m, __p, false, false)
#define kmod_path__parse_name(__m, __p) __kmod_path__parse(__m, __p, true , false)
#define kmod_path__parse_ext(__m, __p) __kmod_path__parse(__m, __p, false, true)
/* /*
* The dso__data_* external interface provides following functions: * The dso__data_* external interface provides following functions:
* dso__data_fd * dso__data_fd
...@@ -250,6 +264,7 @@ struct dso *dso__kernel_findnew(struct machine *machine, const char *name, ...@@ -250,6 +264,7 @@ struct dso *dso__kernel_findnew(struct machine *machine, const char *name,
const char *short_name, int dso_type); const char *short_name, int dso_type);
void dsos__add(struct dsos *dsos, struct dso *dso); void dsos__add(struct dsos *dsos, struct dso *dso);
struct dso *dsos__addnew(struct dsos *dsos, const char *name);
struct dso *dsos__find(const struct dsos *dsos, const char *name, struct dso *dsos__find(const struct dsos *dsos, const char *name,
bool cmp_short); bool cmp_short);
struct dso *__dsos__findnew(struct dsos *dsos, const char *name); struct dso *__dsos__findnew(struct dsos *dsos, const char *name);
......
#include <lzma.h>
#include <stdio.h>
#include <linux/compiler.h>
#include "util.h"
#include "debug.h"
#define BUFSIZE 8192
static const char *lzma_strerror(lzma_ret ret)
{
switch ((int) ret) {
case LZMA_MEM_ERROR:
return "Memory allocation failed";
case LZMA_OPTIONS_ERROR:
return "Unsupported decompressor flags";
case LZMA_FORMAT_ERROR:
return "The input is not in the .xz format";
case LZMA_DATA_ERROR:
return "Compressed file is corrupt";
case LZMA_BUF_ERROR:
return "Compressed file is truncated or otherwise corrupt";
default:
return "Unknown error, possibly a bug";
}
}
int lzma_decompress_to_file(const char *input, int output_fd)
{
lzma_action action = LZMA_RUN;
lzma_stream strm = LZMA_STREAM_INIT;
lzma_ret ret;
u8 buf_in[BUFSIZE];
u8 buf_out[BUFSIZE];
FILE *infile;
infile = fopen(input, "rb");
if (!infile) {
pr_err("lzma: fopen failed on %s: '%s'\n",
input, strerror(errno));
return -1;
}
ret = lzma_stream_decoder(&strm, UINT64_MAX, LZMA_CONCATENATED);
if (ret != LZMA_OK) {
pr_err("lzma: lzma_stream_decoder failed %s (%d)\n",
lzma_strerror(ret), ret);
return -1;
}
strm.next_in = NULL;
strm.avail_in = 0;
strm.next_out = buf_out;
strm.avail_out = sizeof(buf_out);
while (1) {
if (strm.avail_in == 0 && !feof(infile)) {
strm.next_in = buf_in;
strm.avail_in = fread(buf_in, 1, sizeof(buf_in), infile);
if (ferror(infile)) {
pr_err("lzma: read error: %s\n", strerror(errno));
return -1;
}
if (feof(infile))
action = LZMA_FINISH;
}
ret = lzma_code(&strm, action);
if (strm.avail_out == 0 || ret == LZMA_STREAM_END) {
ssize_t write_size = sizeof(buf_out) - strm.avail_out;
if (writen(output_fd, buf_out, write_size) != write_size) {
pr_err("lzma: write error: %s\n", strerror(errno));
return -1;
}
strm.next_out = buf_out;
strm.avail_out = sizeof(buf_out);
}
if (ret != LZMA_OK) {
if (ret == LZMA_STREAM_END)
return 0;
pr_err("lzma: failed %s\n", lzma_strerror(ret));
return -1;
}
}
fclose(infile);
return 0;
}
...@@ -460,30 +460,56 @@ int machine__process_lost_event(struct machine *machine __maybe_unused, ...@@ -460,30 +460,56 @@ int machine__process_lost_event(struct machine *machine __maybe_unused,
return 0; return 0;
} }
static struct dso*
machine__module_dso(struct machine *machine, struct kmod_path *m,
const char *filename)
{
struct dso *dso;
dso = dsos__find(&machine->kernel_dsos, m->name, true);
if (!dso) {
dso = dsos__addnew(&machine->kernel_dsos, m->name);
if (dso == NULL)
return NULL;
if (machine__is_host(machine))
dso->symtab_type = DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE;
else
dso->symtab_type = DSO_BINARY_TYPE__GUEST_KMODULE;
/* _KMODULE_COMP should be next to _KMODULE */
if (m->kmod && m->comp)
dso->symtab_type++;
dso__set_short_name(dso, strdup(m->name), true);
dso__set_long_name(dso, strdup(filename), true);
}
return dso;
}
struct map *machine__new_module(struct machine *machine, u64 start, struct map *machine__new_module(struct machine *machine, u64 start,
const char *filename) const char *filename)
{ {
struct map *map; struct map *map = NULL;
struct dso *dso = __dsos__findnew(&machine->kernel_dsos, filename); struct dso *dso;
bool compressed; struct kmod_path m;
if (dso == NULL) if (kmod_path__parse_name(&m, filename))
return NULL; return NULL;
dso = machine__module_dso(machine, &m, filename);
if (dso == NULL)
goto out;
map = map__new2(start, dso, MAP__FUNCTION); map = map__new2(start, dso, MAP__FUNCTION);
if (map == NULL) if (map == NULL)
return NULL; goto out;
if (machine__is_host(machine))
dso->symtab_type = DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE;
else
dso->symtab_type = DSO_BINARY_TYPE__GUEST_KMODULE;
/* _KMODULE_COMP should be next to _KMODULE */
if (is_kernel_module(filename, &compressed) && compressed)
dso->symtab_type++;
map_groups__insert(&machine->kmaps, map); map_groups__insert(&machine->kmaps, map);
out:
free(m.name);
return map; return map;
} }
...@@ -1044,40 +1070,11 @@ static int machine__process_kernel_mmap_event(struct machine *machine, ...@@ -1044,40 +1070,11 @@ static int machine__process_kernel_mmap_event(struct machine *machine,
strlen(kmmap_prefix) - 1) == 0; strlen(kmmap_prefix) - 1) == 0;
if (event->mmap.filename[0] == '/' || if (event->mmap.filename[0] == '/' ||
(!is_kernel_mmap && event->mmap.filename[0] == '[')) { (!is_kernel_mmap && event->mmap.filename[0] == '[')) {
char short_module_name[1024];
char *name, *dot;
if (event->mmap.filename[0] == '/') {
name = strrchr(event->mmap.filename, '/');
if (name == NULL)
goto out_problem;
++name; /* skip / */
dot = strrchr(name, '.');
if (dot == NULL)
goto out_problem;
/* On some system, modules are compressed like .ko.gz */
if (is_supported_compression(dot + 1))
dot -= 3;
if (!is_kmodule_extension(dot + 1))
goto out_problem;
snprintf(short_module_name, sizeof(short_module_name),
"[%.*s]", (int)(dot - name), name);
strxfrchar(short_module_name, '-', '_');
} else
strcpy(short_module_name, event->mmap.filename);
map = machine__new_module(machine, event->mmap.start, map = machine__new_module(machine, event->mmap.start,
event->mmap.filename); event->mmap.filename);
if (map == NULL) if (map == NULL)
goto out_problem; goto out_problem;
name = strdup(short_module_name);
if (name == NULL)
goto out_problem;
dso__set_short_name(map->dso, name, true);
map->end = map->start + event->mmap.len; map->end = map->start + event->mmap.len;
} else if (is_kernel_mmap) { } else if (is_kernel_mmap) {
const char *symbol_name = (event->mmap.filename + const char *symbol_name = (event->mmap.filename +
......
...@@ -2507,7 +2507,6 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, ...@@ -2507,7 +2507,6 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
int max_tevs, const char *target) int max_tevs, const char *target)
{ {
struct map *map = NULL; struct map *map = NULL;
struct kmap *kmap = NULL;
struct ref_reloc_sym *reloc_sym = NULL; struct ref_reloc_sym *reloc_sym = NULL;
struct symbol *sym; struct symbol *sym;
struct probe_trace_event *tev; struct probe_trace_event *tev;
...@@ -2540,8 +2539,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, ...@@ -2540,8 +2539,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
} }
if (!pev->uprobes && !pp->retprobe) { if (!pev->uprobes && !pp->retprobe) {
kmap = map__kmap(map); reloc_sym = kernel_get_ref_reloc_sym();
reloc_sym = kmap->ref_reloc_sym;
if (!reloc_sym) { if (!reloc_sym) {
pr_warning("Relocated base symbol is not found!\n"); pr_warning("Relocated base symbol is not found!\n");
ret = -EINVAL; ret = -EINVAL;
......
...@@ -329,4 +329,8 @@ bool find_process(const char *name); ...@@ -329,4 +329,8 @@ bool find_process(const char *name);
int gzip_decompress_to_file(const char *input, int output_fd); int gzip_decompress_to_file(const char *input, int output_fd);
#endif #endif
#ifdef HAVE_LZMA_SUPPORT
int lzma_decompress_to_file(const char *input, int output_fd);
#endif
#endif /* GIT_COMPAT_UTIL_H */ #endif /* GIT_COMPAT_UTIL_H */
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