* [PATCH v2 1/2] trace-cmd: [POC] Add APIs for extracting DWARF information from a file
2020-06-18 8:53 [PATCH v2 0/2] POC] Add support for uprobes Tzvetomir Stoyanov (VMware)
@ 2020-06-18 8:53 ` Tzvetomir Stoyanov (VMware)
2020-06-18 8:53 ` [PATCH v2 2/2] trace-cmd: [POC] Add support for uprobes Tzvetomir Stoyanov (VMware)
1 sibling, 0 replies; 3+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2020-06-18 8:53 UTC (permalink / raw)
To: rostedt; +Cc: linux-trace-devel
Added initial infrastructure and trace-cmd library APIs for working with
DWARF debug information:
trace_obj_debug_create()
trace_obj_debug_destroy()
trace_obj_debug_get_fileoffset()
These internal APIs utilize libdwarf and libbfd for parsing ELF and
DWARF headers of the files. The trace_obj_debug_get_fileoffset()
API retrieves the offset in the file of the given functions. These
offsets can be used to set a uprobes to the functions.
Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
---
Makefile | 12 +
lib/trace-cmd/Makefile | 17 ++
lib/trace-cmd/include/trace-cmd-local.h | 13 +
lib/trace-cmd/trace-obj-debug.c | 348 ++++++++++++++++++++++++
4 files changed, 390 insertions(+)
create mode 100644 lib/trace-cmd/trace-obj-debug.c
diff --git a/Makefile b/Makefile
index 2f9620e4..d737b588 100644
--- a/Makefile
+++ b/Makefile
@@ -243,6 +243,18 @@ endif
CUNIT_INSTALLED := $(shell if (echo -e "\#include <CUnit/Basic.h>\n void main(){CU_initialize_registry();}" | $(CC) -x c -lcunit - >/dev/null 2>&1) ; then echo 1; else echo 0 ; fi)
export CUNIT_INSTALLED
+DWARF_INSTALLED := $(shell if (echo -e "\#include <libdwarf/libdwarf.h>\n void main(){dwarf_init(-1, 0, 0, 0, 0, 0);}" | $(CC) -xc - -ldwarf >/dev/null 2>&1) ; then echo 1; else echo 0 ; fi)
+export DWARF_INSTALLED
+BFD_INSTALLED := $(shell if (echo -e "\#include <bfd.h>\n void main(){bfd_init();}" | $(CC) -xc - -lbfd >/dev/null 2>&1) ; then echo 1; else echo 0 ; fi)
+export BFD_INSTALLED
+
+ifeq ($(BFD_INSTALLED), 1)
+LIBS += -lbfd
+endif
+ifeq ($(DWARF_INSTALLED), 1)
+LIBS += -ldwarf
+endif
+
export CFLAGS
export INCLUDES
diff --git a/lib/trace-cmd/Makefile b/lib/trace-cmd/Makefile
index 666a1ebf..f8fb8390 100644
--- a/lib/trace-cmd/Makefile
+++ b/lib/trace-cmd/Makefile
@@ -24,6 +24,16 @@ endif
OBJS += trace-blk-hack.o
OBJS += trace-ftrace.o
+ifeq ($(BFD_INSTALLED), 1)
+ifeq ($(DWARF_INSTALLED), 1)
+OBJS += trace-obj-debug.o
+else
+$(warning libdwarf is not installed)
+endif
+else
+$(warning libbfd is not installed)
+endif
+
OBJS := $(OBJS:%.o=$(bdir)/%.o)
DEPS := $(OBJS:$(bdir)/%.o=$(bdir)/.%.d)
@@ -40,6 +50,13 @@ $(bdir)/libtracecmd.a: $(OBJS)
LIBS = -L$(obj)/lib/traceevent -ltraceevent
+ifeq ($(BFD_INSTALLED), 1)
+LIBS += -lbfd
+endif
+ifeq ($(DWARF_INSTALLED), 1)
+LIBS += -ldwarf
+endif
+
$(bdir)/libtracecmd.so: $(OBJS)
$(Q)$(call do_compile_shared_library)
diff --git a/lib/trace-cmd/include/trace-cmd-local.h b/lib/trace-cmd/include/trace-cmd-local.h
index 95dce66c..d4b31393 100644
--- a/lib/trace-cmd/include/trace-cmd-local.h
+++ b/lib/trace-cmd/include/trace-cmd-local.h
@@ -26,5 +26,18 @@ void warning(const char *fmt, ...);
#endif
#endif
+struct trace_obj_debug;
+struct trace_obj_symbols {
+ struct trace_obj_symbols *next;
+ char *name; /* symbol name */
+ unsigned long long vma; /* symbol virtual memory address */
+ unsigned long long foffset; /* symbol file offset */
+};
+
+struct trace_obj_debug *trace_obj_debug_create(char *file);
+void trace_obj_debug_destroy(struct trace_obj_debug *obj);
+int trace_obj_debug_get_fileoffset(struct trace_obj_debug *obj,
+ struct trace_obj_symbols *symbols);
+
#endif /* _TRACE_CMD_LOCAL_H */
diff --git a/lib/trace-cmd/trace-obj-debug.c b/lib/trace-cmd/trace-obj-debug.c
new file mode 100644
index 00000000..80d626c8
--- /dev/null
+++ b/lib/trace-cmd/trace-obj-debug.c
@@ -0,0 +1,348 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2020, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+ *
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <dwarf.h>
+#include <libdwarf/libdwarf.h>
+#include <bfd.h>
+#include "trace-cmd-local.h"
+#include "trace-cmd.h"
+
+struct trace_obj_debug {
+ Dwarf_Debug dwarf;
+ bfd *bfd;
+};
+
+static void dwarf_error_handler(Dwarf_Error error, Dwarf_Ptr errarg)
+{
+ warning("\nlibdwarf error detected: 0x%" DW_PR_DUx " %s\n",
+ dwarf_errno(error), dwarf_errmsg(error));
+}
+
+static int get_addr(Dwarf_Attribute attr, Dwarf_Addr *val)
+{
+ Dwarf_Error error = 0;
+ Dwarf_Addr uval = 0;
+ int res;
+
+ res = dwarf_formaddr(attr, &uval, &error);
+ if (res != DW_DLV_OK)
+ return -1;
+
+ *val = uval;
+ return 0;
+}
+
+static int dwarf_get_subprog(Dwarf_Debug dbg, Dwarf_Die die,
+ struct trace_obj_symbols *data, const char *name)
+{
+ Dwarf_Attribute *attrbuf = 0;
+ Dwarf_Signed attrcount = 0;
+ Dwarf_Error error = 0;
+ Dwarf_Addr lowpc = 0;
+ Dwarf_Half aform;
+ Dwarf_Signed i;
+ int res;
+
+ res = dwarf_attrlist(die, &attrbuf, &attrcount, &error);
+ if (res != DW_DLV_OK)
+ return -1;
+ for (i = 0; i < attrcount ; ++i) {
+ res = dwarf_whatattr(attrbuf[i], &aform, &error);
+ if (res == DW_DLV_OK) {
+ if (aform == DW_AT_low_pc)
+ get_addr(attrbuf[i], &lowpc);
+ }
+ dwarf_dealloc(dbg, attrbuf[i], DW_DLA_ATTR);
+ if (lowpc)
+ break;
+ }
+
+ while (data) {
+ if (lowpc && !strcmp(name, data->name) && !data->vma)
+ data->vma = lowpc;
+ data = data->next;
+ }
+
+ dwarf_dealloc(dbg, attrbuf, DW_DLA_LIST);
+
+ return 0;
+}
+
+
+static int dwarf_get_die_data(Dwarf_Debug dbg, Dwarf_Die die,
+ struct trace_obj_symbols *data)
+{
+ const char *formname = 0;
+ Dwarf_Attribute attr = 0;
+ const char *tagname = 0;
+ Dwarf_Half formnum = 0;
+ Dwarf_Error error = 0;
+ Dwarf_Half tag = 0;
+ char *name = NULL;
+ int res = 0;
+
+ res = dwarf_diename(die, &name, &error);
+ if (res == DW_DLV_ERROR) {
+ warning("Error in dwarf_diename: %s\n", dwarf_errmsg(error));
+ return -1;
+ }
+ if (res == DW_DLV_NO_ENTRY)
+ name = "<no DW_AT_name attr>";
+
+ res = dwarf_tag(die, &tag, &error);
+ if (res != DW_DLV_OK) {
+ warning("Error in dwarf_tag: %s n", dwarf_errmsg(error));
+ return -1;
+ }
+ res = dwarf_get_TAG_name(tag, &tagname);
+ if (res != DW_DLV_OK) {
+ warning("Error in dwarf_get_TAG_name: %s\n", dwarf_errmsg(error));
+ return -1;
+ }
+
+ res = dwarf_attr(die, DW_AT_name, &attr, &error);
+ if (res == DW_DLV_OK) {
+ res = dwarf_whatform(attr, &formnum, &error);
+ if (res != DW_DLV_OK) {
+ warning("Error in dwarf_whatform: %s\n",
+ dwarf_errmsg(error));
+ return -1;
+ }
+ formname = "form-name-unavailable";
+ res = dwarf_get_FORM_name(formnum, &formname);
+ if (res != DW_DLV_OK)
+ formname = "UNKNoWn FORM!";
+ dwarf_dealloc(dbg, attr, DW_DLA_ATTR);
+ }
+
+ if (tag == DW_TAG_subprogram)
+ dwarf_get_subprog(dbg, die, data, name);
+
+ return 0;
+}
+
+static void dwarf_get_die_and_siblings(Dwarf_Debug dbg, Dwarf_Die in_die,
+ struct trace_obj_symbols *data)
+{
+ Dwarf_Die cur_die = in_die;
+ int res = DW_DLV_ERROR;
+ Dwarf_Error error = 0;
+ Dwarf_Die child = 0;
+ Dwarf_Die sib_die;
+
+ dwarf_get_die_data(dbg, in_die, data);
+ for (;;) {
+ sib_die = 0;
+ res = dwarf_child(cur_die, &child, &error);
+ if (res == DW_DLV_ERROR) {
+ warning("Error in dwarf_child: %s\n", dwarf_errmsg(error));
+ return;
+ }
+ if (res == DW_DLV_OK) {
+ dwarf_get_die_and_siblings(dbg, child, data);
+ /* No longer need 'child' die. */
+ dwarf_dealloc(dbg, child, DW_DLA_DIE);
+ child = 0;
+ }
+ /* res == DW_DLV_NO_ENTRY or DW_DLV_OK */
+ res = dwarf_siblingof_b(dbg, cur_die, 1, &sib_die, &error);
+ if (res == DW_DLV_ERROR) {
+ warning("Error in dwarf_siblingof_b :%s\n",
+ dwarf_errmsg(error));
+ return;
+ }
+ if (res == DW_DLV_NO_ENTRY)
+ break; /* Done at this level. */
+
+ if (cur_die != in_die) {
+ dwarf_dealloc(dbg, cur_die, DW_DLA_DIE);
+ cur_die = 0;
+ }
+ cur_die = sib_die;
+ dwarf_get_die_data(dbg, cur_die, data);
+ }
+}
+
+static int dwarf_read_all(Dwarf_Debug dwarf, struct trace_obj_symbols *data)
+{
+ Dwarf_Half header_cu_type = DW_UT_compile;
+ Dwarf_Unsigned cu_header_length = 0;
+ Dwarf_Unsigned next_cu_header = 0;
+ Dwarf_Unsigned abbrev_offset = 0;
+ Dwarf_Unsigned typeoffset = 0;
+ Dwarf_Half extension_size = 0;
+ Dwarf_Half version_stamp = 0;
+ Dwarf_Half address_size = 0;
+ Dwarf_Half offset_size = 0;
+ Dwarf_Sig8 signature;
+ Dwarf_Die no_die = 0;
+ Dwarf_Die cu_die = 0;
+ Dwarf_Error error;
+ Dwarf_Sig8 sig;
+ int number = 0;
+ int res;
+
+ for (;; ++number) {
+ no_die = 0;
+ cu_die = 0;
+ memset(&sig, 0, sizeof(sig));
+
+ res = dwarf_next_cu_header_d(dwarf, 1, &cu_header_length,
+ &version_stamp, &abbrev_offset,
+ &address_size, &offset_size,
+ &extension_size, &signature,
+ &typeoffset, &next_cu_header,
+ &header_cu_type, &error);
+ if (res == DW_DLV_ERROR) {
+ warning("Error in dwarf_next_cu_header: %s\n",
+ dwarf_errmsg(error));
+ goto out_error;
+ }
+ if (res == DW_DLV_NO_ENTRY)
+ break;
+
+ res = dwarf_siblingof_b(dwarf, no_die, 1, &cu_die, &error);
+ if (res == DW_DLV_ERROR) {
+ warning("Error in dwarf_siblingof_b on CU die: %s\n",
+ dwarf_errmsg(error));
+ goto out_error;
+ }
+ if (res == DW_DLV_NO_ENTRY) {
+ warning("no entry! in dwarf_siblingof on CU die\n");
+ goto out_error;
+ }
+ dwarf_get_die_and_siblings(dwarf, cu_die, data);
+ dwarf_dealloc(dwarf, cu_die, DW_DLA_DIE);
+ }
+ return 0;
+
+out_error:
+ return -1;
+}
+
+static void bfd_dwarf_section(bfd *abfd, asection *section, void *param)
+{
+ struct trace_obj_symbols *data = (struct trace_obj_symbols *)param;
+
+ while (data) {
+ if ((section->flags & SEC_CODE) && section->vma <= data->vma &&
+ (section->vma + section->size) > data->vma)
+ data->foffset = section->filepos + (data->vma - section->vma);
+
+ data = data->next;
+ }
+}
+
+static int bfd_process_object(bfd *abfd, struct trace_obj_symbols *data)
+{
+ int ret = 0;
+
+ if (bfd_check_format_matches(abfd, bfd_object, NULL) ||
+ bfd_check_format_matches(abfd, bfd_core, NULL))
+ bfd_map_over_sections(abfd, bfd_dwarf_section, data);
+ else
+ ret = -1;
+
+ return ret;
+}
+
+static int bfd_read_all(bfd *handle, struct trace_obj_symbols *data)
+{
+ bfd *last_arfile = NULL;
+ bfd *arfile = NULL;
+ int ret = 0;
+
+ if (bfd_check_format(handle, bfd_archive)) {
+ for (;;) {
+ bfd_set_error(bfd_error_no_error);
+ arfile = bfd_openr_next_archived_file(handle, arfile);
+ if (arfile == NULL) {
+ if (bfd_get_error() != bfd_error_no_more_archived_files)
+ break;
+ }
+ ret = bfd_read_all(arfile, data);
+ if (last_arfile != NULL)
+ bfd_close(last_arfile);
+ last_arfile = arfile;
+ }
+ if (last_arfile != NULL)
+ bfd_close(last_arfile);
+ } else
+ ret = bfd_process_object(handle, data);
+
+ return ret;
+}
+
+/**
+ * trace_obj_debug_get_fileoffset - Get symbols VMA and file offset
+ * @obj - pointer to object, returned by trace_obj_debug_create()
+ * @symbols - link list with desired symbols
+ *
+ * Get VMA and file offset of the given symbols, using file debug information
+ * Return 0 on success, -1 on error
+ */
+int trace_obj_debug_get_fileoffset(struct trace_obj_debug *obj,
+ struct trace_obj_symbols *symbols)
+{
+ int ret;
+
+ ret = dwarf_read_all(obj->dwarf, symbols);
+ if (!ret)
+ ret = bfd_read_all(obj->bfd, symbols);
+
+ return ret;
+}
+
+/**
+ * trace_symbols_destroy - Close file opened with trace_obj_debug_create()
+ * @obj - pointer to object, returned by trace_obj_debug_create()
+ *
+ * Close the file and free any allocated resources, related to file's debug
+ * information
+ */
+void trace_obj_debug_destroy(struct trace_obj_debug *obj)
+{
+ if (obj && obj->dwarf)
+ dwarf_finish(obj->dwarf, NULL);
+ if (obj && obj->bfd)
+ bfd_close(obj->bfd);
+ free(obj);
+}
+
+/**
+ * trace_obj_debug_create - Open binary file for parsing ELF and DWARF information
+ * @name: Name of the binary ELF file.
+ *
+ * Return pointer to trace_obj_debug structure, that can be passed to other APIs
+ * for extracting debug information from the file. NULL in case of an error.
+ */
+struct trace_obj_debug *trace_obj_debug_create(char *file)
+{
+ struct trace_obj_debug *obj = NULL;
+ Dwarf_Error dw_error;
+ int res;
+
+ obj = calloc(1, sizeof(*obj));
+ if (!obj)
+ return NULL;
+
+ res = dwarf_init_path(file, NULL, 0, DW_DLC_READ, DW_GROUPNUMBER_ANY,
+ dwarf_error_handler, NULL, &obj->dwarf,
+ 0, 0, 0, &dw_error);
+ if (res != DW_DLV_OK)
+ goto error;
+ bfd_init();
+ obj->bfd = bfd_openr(file, NULL);
+ if (!obj->bfd)
+ goto error;
+ return obj;
+
+error:
+ trace_obj_debug_destroy(obj);
+ return NULL;
+}
--
2.26.2
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH v2 2/2] trace-cmd: [POC] Add support for uprobes
2020-06-18 8:53 [PATCH v2 0/2] POC] Add support for uprobes Tzvetomir Stoyanov (VMware)
2020-06-18 8:53 ` [PATCH v2 1/2] trace-cmd: [POC] Add APIs for extracting DWARF information from a file Tzvetomir Stoyanov (VMware)
@ 2020-06-18 8:53 ` Tzvetomir Stoyanov (VMware)
1 sibling, 0 replies; 3+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2020-06-18 8:53 UTC (permalink / raw)
To: rostedt; +Cc: linux-trace-devel
Initial implementaton of trace-cmd support for ftrace uprobes.
Two new trace-cmd record / set argumemnts are introduced:
--uprobe file:function
--uprobe-ret file:function
The ftrace (return) probe is set on given function from the file.
Note: the file must contain debug DWARF information.
Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
---
Makefile | 15 +-
include/trace-cmd/trace-cmd.h | 24 ++
lib/trace-cmd/Makefile | 9 +-
lib/trace-cmd/trace-uprobes.c | 390 +++++++++++++++++++++++++++++++++
tracecmd/include/trace-local.h | 2 +
tracecmd/trace-record.c | 79 ++++++-
tracecmd/trace-usage.c | 4 +
7 files changed, 511 insertions(+), 12 deletions(-)
create mode 100644 lib/trace-cmd/trace-uprobes.c
diff --git a/Makefile b/Makefile
index d737b588..10effd57 100644
--- a/Makefile
+++ b/Makefile
@@ -244,16 +244,23 @@ CUNIT_INSTALLED := $(shell if (echo -e "\#include <CUnit/Basic.h>\n void main(){
export CUNIT_INSTALLED
DWARF_INSTALLED := $(shell if (echo -e "\#include <libdwarf/libdwarf.h>\n void main(){dwarf_init(-1, 0, 0, 0, 0, 0);}" | $(CC) -xc - -ldwarf >/dev/null 2>&1) ; then echo 1; else echo 0 ; fi)
-export DWARF_INSTALLED
BFD_INSTALLED := $(shell if (echo -e "\#include <bfd.h>\n void main(){bfd_init();}" | $(CC) -xc - -lbfd >/dev/null 2>&1) ; then echo 1; else echo 0 ; fi)
-export BFD_INSTALLED
+OBJECT_DEBUG=0
ifeq ($(BFD_INSTALLED), 1)
-LIBS += -lbfd
-endif
ifeq ($(DWARF_INSTALLED), 1)
+OBJECT_DEBUG=1
+CFLAGS += -DOBJECT_DEBUG
+LIBS += -lbfd
LIBS += -ldwarf
+else
+$(warning libdwarf is not installed, no uprobes support)
endif
+else
+$(warning libbfd is not installed, no uprobes support)
+endif
+
+export OBJECT_DEBUG
export CFLAGS
export INCLUDES
diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h
index f3c95f30..6ac00006 100644
--- a/include/trace-cmd/trace-cmd.h
+++ b/include/trace-cmd/trace-cmd.h
@@ -7,6 +7,7 @@
#define _TRACE_CMD_H
#include "traceevent/event-parse.h"
+#include "tracefs.h"
#define TRACECMD_MAGIC { 23, 8, 68 }
@@ -483,6 +484,29 @@ void tracecmd_plog(const char *fmt, ...);
void tracecmd_plog_error(const char *fmt, ...);
int tracecmd_set_logfile(char *logfile);
+/* --- Uprobes --- */
+struct tracecmd_uprobe;
+int tracecmd_uprobe_new(struct tracecmd_uprobe **list,
+ char *file, char *func, bool pret);
+int tracecmd_uprobe_create(struct tracecmd_uprobe *probes);
+int tracecmd_uprobe_remove(struct tracecmd_uprobe *probes);
+void tracecmd_uprobe_free(struct tracecmd_uprobe *probes);
+int tracecmd_uprobe_enable(struct tracefs_instance *instance,
+ struct tracecmd_uprobe *probes);
+int tracecmd_uprobe_disable(struct tracefs_instance *instance,
+ struct tracecmd_uprobe *probes);
+
+struct tracecmd_uprobe_desc {
+ struct tracecmd_uprobe_desc *next;
+ int active; /* is the probe configured */
+ char *file; /* executable file that will be traced */
+ char *symbol; /* symbol from the file, set as uprobe */
+ char *event; /* name of the created event for this uprobe */
+};
+struct tracecmd_uprobe_desc *
+tracecmd_uprobes_get_list(struct tracecmd_uprobe *probes);
+void tracecmd_uprobes_free_list(struct tracecmd_uprobe_desc *list);
+
/* --- System --- */
unsigned long long tracecmd_generate_traceid(void);
int tracecmd_count_cpus(void);
diff --git a/lib/trace-cmd/Makefile b/lib/trace-cmd/Makefile
index f8fb8390..5375a76d 100644
--- a/lib/trace-cmd/Makefile
+++ b/lib/trace-cmd/Makefile
@@ -16,6 +16,7 @@ OBJS += trace-util.o
OBJS += trace-filter-hash.o
OBJS += trace-msg.o
OBJS += trace-plugin.o
+OBJS += trace-uprobes.o
ifeq ($(VSOCK_DEFINED), 1)
OBJS += trace-timesync.o
endif
@@ -24,14 +25,8 @@ endif
OBJS += trace-blk-hack.o
OBJS += trace-ftrace.o
-ifeq ($(BFD_INSTALLED), 1)
-ifeq ($(DWARF_INSTALLED), 1)
+ifeq ($(OBJECT_DEBUG), 1)
OBJS += trace-obj-debug.o
-else
-$(warning libdwarf is not installed)
-endif
-else
-$(warning libbfd is not installed)
endif
OBJS := $(OBJS:%.o=$(bdir)/%.o)
diff --git a/lib/trace-cmd/trace-uprobes.c b/lib/trace-cmd/trace-uprobes.c
new file mode 100644
index 00000000..48d9ac6e
--- /dev/null
+++ b/lib/trace-cmd/trace-uprobes.c
@@ -0,0 +1,390 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+ *
+ */
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <linux/limits.h>
+#include <ctype.h>
+
+#include "tracefs.h"
+#include "trace-cmd-local.h"
+#include "trace-cmd.h"
+
+#ifdef OBJECT_DEBUG
+
+#define UPROBE_FILE "uprobe_events"
+
+struct trace_uprobe_symbols {
+ struct trace_uprobe_symbols *next;
+ char *event;
+ bool ret_probe;
+ struct trace_obj_symbols debug;
+};
+
+struct tracecmd_uprobe {
+ struct tracecmd_uprobe *next;
+
+ char *file;
+ struct trace_obj_debug *debug;
+ struct trace_uprobe_symbols *symbols;
+};
+
+static char *uprobe_event_name(char *file, char *func, bool pret)
+{
+ char *event = NULL;
+ char *fname;
+ char name[10];
+ int len;
+
+ fname = strrchr(file, '/');
+ if (fname)
+ fname++;
+ if (!fname || *fname == '\0')
+ fname = file;
+ strncpy(name, fname, 10);
+ for (len = 0; len < 10 && name[len]; len++) {
+ if (!isalpha(name[len]))
+ name[len] = '_';
+ }
+
+ asprintf(&event, "%c_%.*s_%.10s", pret ? 'r':'p', len, name, func);
+ return event;
+}
+
+/**
+ * tracecmd_uprobe_new - Add new uprobe in the uprobe list
+ * @list - list with uprobes, the new one will be added in this list
+ * @file - executable file that will be traced
+ * @func - function from @file
+ * @pret - indicate if this is return probe (true for return uprobe)
+ *
+ * Returns 0 on success or -1 on failure
+ */
+int tracecmd_uprobe_new(struct tracecmd_uprobe **list,
+ char *file, char *func, bool pret)
+{
+ struct trace_uprobe_symbols *pfunc = NULL;
+ struct tracecmd_uprobe *probe = *list;
+ bool new_file = false;
+
+ while (probe) {
+ if (!strcmp(probe->file, file))
+ break;
+ probe = probe->next;
+ }
+
+ if (!probe) {
+ probe = calloc(1, sizeof(*probe));
+ if (!probe)
+ return -1;
+
+ probe->file = strdup(file);
+ probe->next = *list;
+ new_file = true;
+ }
+
+ pfunc = probe->symbols;
+ while (pfunc) {
+ if (!strcmp(func, pfunc->debug.name) && pret == pfunc->ret_probe)
+ break;
+ pfunc = pfunc->next;
+ }
+
+ if (!pfunc) {
+ pfunc = calloc(1, sizeof(*pfunc));
+ if (!pfunc)
+ goto error;
+ pfunc->debug.name = strdup(func);
+ pfunc->ret_probe = pret;
+ pfunc->event = uprobe_event_name(file, func, pret);
+ pfunc->next = probe->symbols;
+ probe->symbols = pfunc;
+ }
+
+ if (new_file)
+ *list = probe;
+
+ return 0;
+
+error:
+ if (new_file)
+ free(probe);
+
+ return -1;
+}
+
+static void uprobe_symbols_free(struct trace_uprobe_symbols *symbols)
+{
+ struct trace_uprobe_symbols *del;
+
+ while (symbols) {
+ del = symbols;
+ symbols = symbols->next;
+ free(del->debug.name);
+ free(del->event);
+ free(del);
+ }
+}
+
+/**
+ * tracecmd_uprobe_free - Free uprobe list
+ * @list - list with uprobes, that wil be freed
+ *
+ */
+void tracecmd_uprobe_free(struct tracecmd_uprobe *probes)
+{
+ struct tracecmd_uprobe *del;
+
+ while (probes) {
+ del = probes;
+ probes = probes->next;
+ trace_obj_debug_destroy(del->debug);
+ uprobe_symbols_free(del->symbols);
+ free(del->file);
+ free(del);
+ }
+}
+
+static void uprobe_resolve(struct tracecmd_uprobe *probes)
+{
+ while (probes) {
+ if (!probes->debug)
+ probes->debug = trace_obj_debug_create(probes->file);
+ if (probes->debug)
+ trace_obj_debug_get_fileoffset(probes->debug,
+ &probes->symbols->debug);
+ probes = probes->next;
+ }
+}
+
+static int uprobe_symbols(int fd, char *file, bool add,
+ struct trace_uprobe_symbols *symbols)
+{
+ char probe_str[BUFSIZ];
+
+ for (; symbols; symbols = symbols->next) {
+ if (add) {
+ if (!symbols->debug.foffset || !symbols->event)
+ continue;
+ snprintf(probe_str, BUFSIZ,
+ "%c:%s %s:0x%llx", symbols->ret_probe?'r':'p',
+ symbols->event, file, symbols->debug.foffset);
+ } else {
+ if (!symbols->event)
+ continue;
+ snprintf(probe_str, BUFSIZ,
+ "-:%s", symbols->event);
+ }
+ write(fd, probe_str, strlen(probe_str));
+ }
+
+ return 0;
+}
+
+static int uprobe_modify(struct tracecmd_uprobe *probes, bool add)
+{
+ char *ufile = tracefs_instance_get_file(NULL, UPROBE_FILE);
+ int fd = -1;
+
+ if (!ufile)
+ return -1;
+ fd = open(ufile, O_WRONLY | O_APPEND);
+ tracefs_put_tracing_file(ufile);
+ if (fd < 0)
+ return -1;
+
+ for (; probes; probes = probes->next) {
+ if (!probes->debug)
+ continue;
+ uprobe_symbols(fd, probes->file, add, probes->symbols);
+ }
+
+ close(fd);
+ return 0;
+}
+
+/**
+ * tracecmd_uprobe_create - Create uprobes in ftrace
+ * @list - list with uprobes, that will be created
+ *
+ * Returns 0 on success or -1 on failure
+ */
+int tracecmd_uprobe_create(struct tracecmd_uprobe *probes)
+{
+ uprobe_resolve(probes);
+ return uprobe_modify(probes, true);
+}
+
+/**
+ * tracecmd_uprobe_remove - Remove uprobes from ftrace
+ * @list - list with uprobes, that will be removed
+ *
+ * Returns 0 on success or -1 on failure
+ */
+int tracecmd_uprobe_remove(struct tracecmd_uprobe *probes)
+{
+ return uprobe_modify(probes, false);
+}
+
+static int uprobe_config(struct tracefs_instance *instance,
+ struct tracecmd_uprobe *probes, bool enable)
+{
+ struct trace_uprobe_symbols *symb;
+ char event[PATH_MAX];
+
+ for (symb = probes->symbols; symb; symb = symb->next) {
+ if (!symb->event)
+ continue;
+ snprintf(event, PATH_MAX, "events/uprobes/%s/enable", symb->event);
+ tracefs_instance_file_write(instance, event, enable?"1":"0");
+ }
+
+ return 0;
+}
+
+/**
+ * tracecmd_uprobe_enable - Enable uprobes for tracing
+ * @instance - Ftrace instance in which scope the uprobes will be enabled
+ * @list - list with uprobes, that will be enabled
+ *
+ * Returns 0 on success or -1 on failure
+ */
+int tracecmd_uprobe_enable(struct tracefs_instance *instance,
+ struct tracecmd_uprobe *probes)
+{
+ return uprobe_config(instance, probes, true);
+}
+
+/**
+ * tracecmd_uprobe_disable - Disable uprobes for tracing
+ * @instance - Ftrace instance in which scope the uprobes are enabled
+ * @list - list with uprobes, that will be disabled
+ *
+ * Returns 0 on success or -1 on failure
+ */
+int tracecmd_uprobe_disable(struct tracefs_instance *instance,
+ struct tracecmd_uprobe *probes)
+{
+ return uprobe_config(instance, probes, false);
+}
+
+/**
+ * tracecmd_uprobes_free_list - Free list with uprobes description
+ * @list - list with uprobes descriptions, that will be freed
+ *
+ * Frees @list returned by tracecmd_uprobes_get_list()
+ * Returns 0 on success or -1 on failure
+ */
+void tracecmd_uprobes_free_list(struct tracecmd_uprobe_desc *list)
+{
+ struct tracecmd_uprobe_desc *del;
+
+ while (list) {
+ del = list;
+ list = list->next;
+ free(del->event);
+ free(del->symbol);
+ free(del->file);
+ free(del);
+ }
+}
+
+/**
+ * tracecmd_uprobes_get_list - Get list with uprobes description
+ * @list - list with configured uprobes
+ *
+ * The returned list should be freed by tracecmd_uprobes_free_list()
+ * Returns pointer to newly allocated list with uprobes description
+ * on success or NULL on failure.
+ */
+struct tracecmd_uprobe_desc *
+tracecmd_uprobes_get_list(struct tracecmd_uprobe *probes)
+{
+ struct trace_uprobe_symbols *s;
+ struct tracecmd_uprobe_desc *desc, *list = NULL;
+
+ for (; probes; probes = probes->next) {
+ for (s = probes->symbols; s; s = s->next) {
+ if (!s->event)
+ continue;
+ desc = calloc(1, sizeof(*desc));
+ if (!desc)
+ goto error;
+ desc->next = list;
+ list = desc;
+ desc->event = strdup(s->event);
+ if (!desc->event)
+ goto error;
+ desc->file = strdup(probes->file);
+ if (!desc->file)
+ goto error;
+ desc->symbol = strdup(s->debug.name);
+ if (!desc->symbol)
+ goto error;
+ if (s->debug.foffset)
+ desc->active = 1;
+ }
+ }
+
+ return list;
+error:
+ tracecmd_uprobes_free_list(list);
+ return NULL;
+}
+
+#else /* !OBJECT_DEBUG */
+
+int tracecmd_uprobe_new(struct tracecmd_uprobe **list,
+ char *file, char *func, bool pret)
+{
+ return -1;
+}
+
+void tracecmd_uprobe_resolve(struct tracecmd_uprobe *probes)
+{
+
+}
+
+int tracecmd_uprobe_create(struct tracecmd_uprobe *probes)
+{
+ return -1;
+}
+
+int tracecmd_uprobe_remove(struct tracecmd_uprobe *probes)
+{
+ return -1;
+}
+
+void tracecmd_uprobe_free(struct tracecmd_uprobe *probes)
+{
+
+}
+
+int tracecmd_uprobe_enable(struct tracefs_instance *instance,
+ struct tracecmd_uprobe *probes)
+{
+ return -1;
+}
+
+int tracecmd_uprobe_disable(struct tracefs_instance *instance,
+ struct tracecmd_uprobe *probes)
+{
+ return -1;
+}
+
+struct tracecmd_uprobe_desc *
+tracecmd_uprobes_get_list(struct tracecmd_uprobe *probes)
+{
+ return NULL;
+}
+
+void tracecmd_uprobes_free_list(struct tracecmd_uprobe_desc *list)
+{
+
+}
+
+#endif /* OBJECT_DEBUG */
diff --git a/tracecmd/include/trace-local.h b/tracecmd/include/trace-local.h
index d148aa16..2a476d8a 100644
--- a/tracecmd/include/trace-local.h
+++ b/tracecmd/include/trace-local.h
@@ -216,6 +216,8 @@ struct buffer_instance {
struct func_list *filter_funcs;
struct func_list *notrace_funcs;
+ struct tracecmd_uprobe *uprobes;
+
struct opt_list *options;
struct filter_pids *filter_pids;
struct filter_pids *process_pids;
diff --git a/tracecmd/trace-record.c b/tracecmd/trace-record.c
index bd004574..828d7d6f 100644
--- a/tracecmd/trace-record.c
+++ b/tracecmd/trace-record.c
@@ -5126,7 +5126,8 @@ static void check_function_plugin(void)
static int __check_doing_something(struct buffer_instance *instance)
{
return is_guest(instance) || (instance->flags & BUFFER_FL_PROFILE) ||
- instance->plugin || instance->events || instance->get_procmap;
+ instance->plugin || instance->events || instance->get_procmap ||
+ instance->uprobes;
}
static void check_doing_something(void)
@@ -5541,6 +5542,8 @@ void init_top_instance(void)
}
enum {
+ OPT_retuprobe = 239,
+ OPT_uprobe = 240,
OPT_fork = 241,
OPT_tsyncinterval = 242,
OPT_user = 243,
@@ -5730,6 +5733,22 @@ void trace_reset(int argc, char **argv)
exit(0);
}
+static int
+uprobe_param(struct buffer_instance *instance, char *param, bool pret)
+{
+ char *str, *file, *func;
+
+ if (!param)
+ return -1;
+
+ file = strtok_r(param, ":", &str);
+ func = strtok_r(NULL, ":", &str);
+
+ if (!file || !func)
+ return -1;
+ return tracecmd_uprobe_new(&instance->uprobes, file, func, pret);
+}
+
static void init_common_record_context(struct common_record_context *ctx,
enum trace_cmd curr_cmd)
{
@@ -5884,6 +5903,8 @@ static void parse_record_options(int argc,
{"module", required_argument, NULL, OPT_module},
{"tsync-interval", required_argument, NULL, OPT_tsyncinterval},
{"fork", no_argument, NULL, OPT_fork},
+ {"uprobe", required_argument, NULL, OPT_uprobe},
+ {"uprobe-ret", required_argument, NULL, OPT_retuprobe},
{NULL, 0, NULL, 0}
};
@@ -6283,6 +6304,14 @@ static void parse_record_options(int argc,
die("--fork option used for 'start' command only");
fork_process = true;
break;
+ case OPT_uprobe:
+ check_instance_die(ctx->instance, "--uprobe");
+ uprobe_param(ctx->instance, optarg, false);
+ break;
+ case OPT_retuprobe:
+ check_instance_die(ctx->instance, "--uprobe-ret");
+ uprobe_param(ctx->instance, optarg, true);
+ break;
case OPT_quiet:
case 'q':
quiet = true;
@@ -6394,6 +6423,13 @@ static void finalize_record_trace(struct common_record_context *ctx)
set_plugin("nop");
+ for_all_instances(instance) {
+ if (instance->uprobes) {
+ tracecmd_uprobe_remove(instance->uprobes);
+ tracecmd_uprobe_free(instance->uprobes);
+ }
+ }
+
tracecmd_remove_instances();
/* If tracing_on was enabled before we started, set it on now */
@@ -6425,6 +6461,42 @@ static bool has_local_instances(void)
return false;
}
+static int uprobes_set(struct buffer_instance *instance)
+{
+ struct tracecmd_uprobe_desc *probes, *list;
+ struct event_list *event;
+ int ret;
+
+ ret = tracecmd_uprobe_create(instance->uprobes);
+ if (ret < 0)
+ return ret;
+ probes = tracecmd_uprobes_get_list(instance->uprobes);
+ if (!probes)
+ return -1;
+ ret = 0;
+ for (list = probes; list; list = list->next) {
+ if (!list->active) {
+ warning("Failed to set %s:%s uprobe",
+ list->file, list->symbol);
+ continue;
+ }
+ event = calloc(1, sizeof(*event));
+ if (!event) {
+ ret = -1;
+ goto out;
+ }
+ event->event = strdup(list->event);
+ add_event(instance, event);
+
+ if (!recording_all_events())
+ list_event(event->event);
+ }
+
+out:
+ tracecmd_uprobes_free_list(probes);
+ return ret;
+}
+
/*
* This function contains common code for the following commands:
* record, start, stream, profile.
@@ -6468,6 +6540,11 @@ static void record_trace(int argc, char **argv,
/* Some instances may not be created yet */
if (instance->tracing_on_init_val < 0)
instance->tracing_on_init_val = 1;
+ if (instance->uprobes) {
+ ctx->events = 1;
+ if (uprobes_set(instance) < 0)
+ die("Failed to set uprobes");
+ }
}
if (ctx->events)
diff --git a/tracecmd/trace-usage.c b/tracecmd/trace-usage.c
index ada44c68..4a13af19 100644
--- a/tracecmd/trace-usage.c
+++ b/tracecmd/trace-usage.c
@@ -65,6 +65,8 @@ static struct usage_help usage_help[] = {
" If a negative number is specified, timestamps synchronization is disabled"
" If 0 is specified, no loop is performed - timestamps offset is calculated only twice,"
" at the beginnig and at the end of the trace\n"
+ " --uprobe set the specified [file:function] as uprobe\n"
+ " --uprobe-ret set the specified [file:function] as return uprobe\n"
},
{
"set",
@@ -101,6 +103,8 @@ static struct usage_help usage_help[] = {
" --cmdlines-size change kernel saved_cmdlines_size\n"
" --user execute the specified [command ...] as given user\n"
" --fork return immediately if a command is specified\n"
+ " --uprobe set the specified [file:function] as uprobe\n"
+ " --uprobe-ret set the specified [file:function] as return uprobe\n"
},
{
"start",
--
2.26.2
^ permalink raw reply related [flat|nested] 3+ messages in thread