Linux-Trace-Devel Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH 0/2] [POC] Add support for uprobes
@ 2020-06-18  8:18 Tzvetomir Stoyanov (VMware)
  2020-06-18  8:18 ` [PATCH 1/2] trace-cmd: [POC] Add APIs for extracting DWARF information from a file Tzvetomir Stoyanov (VMware)
  2020-06-18  8:18 ` [PATCH 2/2] trace-cmd: [POC] Add support for uprobes Tzvetomir Stoyanov (VMware)
  0 siblings, 2 replies; 3+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2020-06-18  8:18 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel

Initial trace-cmd support for ftrace uprobes.

Tzvetomir Stoyanov (VMware) (2):
  trace-cmd: [POC] Add APIs for extracting DWARF information from a file
  trace-cmd: [POC] Add support for uprobes

 Makefile                                |  19 ++
 include/trace-cmd/trace-cmd.h           |  24 ++
 lib/trace-cmd/Makefile                  |  12 +
 lib/trace-cmd/include/trace-cmd-local.h |  13 +
 lib/trace-cmd/trace-obj-debug.c         | 340 +++++++++++++++++++++
 lib/trace-cmd/trace-uprobes.c           | 390 ++++++++++++++++++++++++
 tracecmd/include/trace-local.h          |   2 +
 tracecmd/trace-record.c                 |  79 ++++-
 tracecmd/trace-usage.c                  |   4 +
 9 files changed, 882 insertions(+), 1 deletion(-)
 create mode 100644 lib/trace-cmd/trace-obj-debug.c
 create mode 100644 lib/trace-cmd/trace-uprobes.c

-- 
2.26.2


^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH 1/2] trace-cmd: [POC] Add APIs for extracting DWARF information from a file
  2020-06-18  8:18 [PATCH 0/2] [POC] Add support for uprobes Tzvetomir Stoyanov (VMware)
@ 2020-06-18  8:18 ` Tzvetomir Stoyanov (VMware)
  2020-06-18  8:18 ` [PATCH 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:18 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         | 340 ++++++++++++++++++++++++
 4 files changed, 382 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..e98185a4
--- /dev/null
+++ b/lib/trace-cmd/trace-obj-debug.c
@@ -0,0 +1,340 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
+ * 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 void 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)
+		*val = uval;
+}
+
+static void 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;
+	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);
+	}
+
+	while (data) {
+		if (lowpc && !strcmp(name, data->name))
+			data->vma = lowpc;
+		data = data->next;
+	}
+
+	dwarf_dealloc(dbg, attrbuf, DW_DLA_LIST);
+}
+
+
+static void 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 = 0;
+	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;
+	}
+	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;
+	}
+	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;
+	}
+
+	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;
+		}
+		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);
+}
+
+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	[flat|nested] 3+ messages in thread

* [PATCH 2/2] trace-cmd: [POC] Add support for uprobes
  2020-06-18  8:18 [PATCH 0/2] [POC] Add support for uprobes Tzvetomir Stoyanov (VMware)
  2020-06-18  8:18 ` [PATCH 1/2] trace-cmd: [POC] Add APIs for extracting DWARF information from a file Tzvetomir Stoyanov (VMware)
@ 2020-06-18  8:18 ` Tzvetomir Stoyanov (VMware)
  1 sibling, 0 replies; 3+ messages in thread
From: Tzvetomir Stoyanov (VMware) @ 2020-06-18  8:18 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	[flat|nested] 3+ messages in thread

end of thread, back to index

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-06-18  8:18 [PATCH 0/2] [POC] Add support for uprobes Tzvetomir Stoyanov (VMware)
2020-06-18  8:18 ` [PATCH 1/2] trace-cmd: [POC] Add APIs for extracting DWARF information from a file Tzvetomir Stoyanov (VMware)
2020-06-18  8:18 ` [PATCH 2/2] trace-cmd: [POC] Add support for uprobes Tzvetomir Stoyanov (VMware)

Linux-Trace-Devel Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-trace-devel/0 linux-trace-devel/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-trace-devel linux-trace-devel/ https://lore.kernel.org/linux-trace-devel \
		linux-trace-devel@vger.kernel.org
	public-inbox-index linux-trace-devel

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-trace-devel


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git