diff mbox series

trace-cruncher: Add support for event probes

Message ID 20220124152020.459405-1-tz.stoyanov@gmail.com (mailing list archive)
State Superseded
Headers show
Series trace-cruncher: Add support for event probes | expand

Commit Message

Tzvetomir Stoyanov (VMware) Jan. 24, 2022, 3:20 p.m. UTC
Event probe (eprobe) is a new type of ftrace dynamic events, introduced
in the Linux kernel 5.15 version. It is useful to have support for it in
trace-cruncher, as it allows more flexibility when printing event's
data.

Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
---
 src/ftracepy-utils.c                          | 34 ++++++++++++++++
 src/ftracepy-utils.h                          |  3 ++
 src/ftracepy.c                                |  5 +++
 .../tests/1_unit/test_01_ftracepy_unit.py     | 39 +++++++++++++++++++
 4 files changed, 81 insertions(+)

Comments

Yordan Karadzhov Jan. 25, 2022, 1:28 p.m. UTC | #1
On 24.01.22 г. 17:20 ч., Tzvetomir Stoyanov (VMware) wrote:
> Event probe (eprobe) is a new type of ftrace dynamic events, introduced
> in the Linux kernel 5.15 version. It is useful to have support for it in
> trace-cruncher, as it allows more flexibility when printing event's
> data.
> 
> Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
> ---
>   src/ftracepy-utils.c                          | 34 ++++++++++++++++
>   src/ftracepy-utils.h                          |  3 ++
>   src/ftracepy.c                                |  5 +++
>   .../tests/1_unit/test_01_ftracepy_unit.py     | 39 +++++++++++++++++++
>   4 files changed, 81 insertions(+)
> 
> diff --git a/src/ftracepy-utils.c b/src/ftracepy-utils.c
> index cecb180..28ed7b9 100644
> --- a/src/ftracepy-utils.c
> +++ b/src/ftracepy-utils.c
> @@ -2064,6 +2064,40 @@ PyObject *PyFtrace_register_kretprobe(PyObject *self, PyObject *args,
>   	return PyDynevent_New(kprobe);
>   }
>   
> +PyObject *PyFtrace_register_eprobe(PyObject *self, PyObject *args,
> +						   PyObject *kwargs)
> +{

It seems that we have an inconsistency in the naming convention of the 'constructor' methods. The 'constructor' for 
kprobes is called 'register_kprobe', while for histograms and synthetic events we have just 'hist' and 'synth'. Lets use 
the short naming here (just 'eprobe') and you can remove the 'register' prefix from the constructor for kprobes in 
another patch.

> +	static char *kwlist[] = {"event", "target_system", "target_event", "fetchargs", NULL};
> +	const char *event, *target_system, *target_event, *fetchargs;
> +	struct tracefs_dynevent *eprobe;
> +
> +	if (!PyArg_ParseTupleAndKeywords(args,
> +					 kwargs,
> +					 "ssss",
> +					 kwlist,
> +					 &event,
> +					 &target_system,
> +					 &target_event,
> +					 &fetchargs)) {
> +		return NULL;
> +	}
> +
> +	eprobe = tracefs_eprobe_alloc(TC_SYS, event, target_system, target_event, fetchargs);
> +	if (!eprobe) {
> +		MEM_ERROR;
> +		return NULL;
> +	}
> +
> +	if (tracefs_dynevent_create(eprobe) < 0) {
> +		TfsError_fmt(NULL, "Failed to create eprobe '%s'", event);
> +		tracefs_dynevent_free(eprobe);
> +		return NULL;
> +	}
> +
> +	return PyDynevent_New(eprobe);
> +}
> +
> +
>   PyObject *PyDynevent_set_filter(PyDynevent *self, PyObject *args,
>   						  PyObject *kwargs)
>   {
> diff --git a/src/ftracepy-utils.h b/src/ftracepy-utils.h
> index fc5016c..1030a50 100644
> --- a/src/ftracepy-utils.h
> +++ b/src/ftracepy-utils.h
> @@ -207,6 +207,9 @@ PyObject *PyFtrace_register_kprobe(PyObject *self, PyObject *args,
>   PyObject *PyFtrace_register_kretprobe(PyObject *self, PyObject *args,
>   						      PyObject *kwargs);
>   
> +PyObject *PyFtrace_register_eprobe(PyObject *self, PyObject *args,
> +						   PyObject *kwargs);
> +
>   PyObject *PyFtrace_hist(PyObject *self, PyObject *args,
>   					PyObject *kwargs);
>   
> diff --git a/src/ftracepy.c b/src/ftracepy.c
> index f59bd4c..50ee4d3 100644
> --- a/src/ftracepy.c
> +++ b/src/ftracepy.c
> @@ -377,6 +377,11 @@ static PyMethodDef ftracepy_methods[] = {
>   	 METH_VARARGS | METH_KEYWORDS,
>   	 "Define a kretprobe."
>   	},
> +	{"register_eprobe",
> +	 (PyCFunction) PyFtrace_register_eprobe,
> +	 METH_VARARGS | METH_KEYWORDS,
> +	 "Define an eprobe."
> +	},
>   	{"hist",
>   	 (PyCFunction) PyFtrace_hist,
>   	 METH_VARARGS | METH_KEYWORDS,
> diff --git a/tracecruncher/tests/1_unit/test_01_ftracepy_unit.py b/tracecruncher/tests/1_unit/test_01_ftracepy_unit.py
> index 51970d3..12b58a0 100644
> --- a/tracecruncher/tests/1_unit/test_01_ftracepy_unit.py
> +++ b/tracecruncher/tests/1_unit/test_01_ftracepy_unit.py


Please move the tests in separate patch!

> @@ -457,6 +457,45 @@ class KprobeTestCase(unittest.TestCase):
>           ret = kp1.is_enabled(instance=inst)
>           self.assertEqual(ret, '0')
>   
> +class EprobeTestCase(unittest.TestCase):
> +    def test_register_eprobe(self):

This fails on my machine, because I am still on the 5.13 kernel. Maybe somewhere up in the file you can add a global 
variable defined like this:

kernel_version = tuple(map(int, (os.uname()[2].split('.')[:2])))

and here you can check the kernel version

	if kernel_version < (5, 15):
		return

> +        evt1 = 'sopen_in'
> +        evt1_tsys = 'syscalls'
> +        evt1_tevent = 'sys_enter_openat'
> +        evt1_args = 'file=+0($filename):ustring'
> +        evt2 = 'sopen_out'
> +        evt2_tsys = 'syscalls'
> +        evt2_tevent = 'sys_exit_openat'
> +        evt2_args = 'res=$ret:u64'
> +
> +        ep1 = ft.register_eprobe(event=evt1, target_system=evt1_tsys, target_event=evt1_tevent,
> +                                 fetchargs=evt1_args)
> +        self.assertEqual(evt1, ep1.event())
> +        self.assertEqual("{}.{}".format(evt1_tsys, evt1_tevent), ep1.address())
> +        self.assertEqual(evt1_args, ep1.probe())
> +
> +        ep2 = ft.register_eprobe(event=evt2, target_system=evt2_tsys, target_event=evt2_tevent,
> +                                 fetchargs=evt2_args)
> +        self.assertEqual(evt2, ep2.event())
> +        self.assertEqual("{}.{}".format(evt2_tsys, evt2_tevent), ep2.address())
> +        self.assertEqual(evt2_args, ep2.probe())
> +
> +    def test_enable_eprobe(self):

Another kernel version check

> +        evt1 = 'sopen_out'
> +        evt1_tsys = 'syscalls'
> +        evt1_tevent = 'sys_exit_openat'
> +        evt1_args = 'res=$ret:u64'
> +
> +        ep1 = ft.register_eprobe(event=evt1, target_system=evt1_tsys, target_event=evt1_tevent,
> +                                 fetchargs=evt1_args)
> +        inst = ft.create_instance(instance_name)
> +        ep1.enable(instance=inst)
> +        ret = ep1.is_enabled(instance=inst)
> +        self.assertEqual(ret, '1')
> +
> +        ep1.disable(instance=inst)
> +        ret = ep1.is_enabled(instance=inst)
> +        self.assertEqual(ret, '0')
>   
>   class TracingOnTestCase(unittest.TestCase):
>       def test_ON_OF(self):
> 


Ideally, there must be one more patch that will add an example.
Thanks!

Yordan
diff mbox series

Patch

diff --git a/src/ftracepy-utils.c b/src/ftracepy-utils.c
index cecb180..28ed7b9 100644
--- a/src/ftracepy-utils.c
+++ b/src/ftracepy-utils.c
@@ -2064,6 +2064,40 @@  PyObject *PyFtrace_register_kretprobe(PyObject *self, PyObject *args,
 	return PyDynevent_New(kprobe);
 }
 
+PyObject *PyFtrace_register_eprobe(PyObject *self, PyObject *args,
+						   PyObject *kwargs)
+{
+	static char *kwlist[] = {"event", "target_system", "target_event", "fetchargs", NULL};
+	const char *event, *target_system, *target_event, *fetchargs;
+	struct tracefs_dynevent *eprobe;
+
+	if (!PyArg_ParseTupleAndKeywords(args,
+					 kwargs,
+					 "ssss",
+					 kwlist,
+					 &event,
+					 &target_system,
+					 &target_event,
+					 &fetchargs)) {
+		return NULL;
+	}
+
+	eprobe = tracefs_eprobe_alloc(TC_SYS, event, target_system, target_event, fetchargs);
+	if (!eprobe) {
+		MEM_ERROR;
+		return NULL;
+	}
+
+	if (tracefs_dynevent_create(eprobe) < 0) {
+		TfsError_fmt(NULL, "Failed to create eprobe '%s'", event);
+		tracefs_dynevent_free(eprobe);
+		return NULL;
+	}
+
+	return PyDynevent_New(eprobe);
+}
+
+
 PyObject *PyDynevent_set_filter(PyDynevent *self, PyObject *args,
 						  PyObject *kwargs)
 {
diff --git a/src/ftracepy-utils.h b/src/ftracepy-utils.h
index fc5016c..1030a50 100644
--- a/src/ftracepy-utils.h
+++ b/src/ftracepy-utils.h
@@ -207,6 +207,9 @@  PyObject *PyFtrace_register_kprobe(PyObject *self, PyObject *args,
 PyObject *PyFtrace_register_kretprobe(PyObject *self, PyObject *args,
 						      PyObject *kwargs);
 
+PyObject *PyFtrace_register_eprobe(PyObject *self, PyObject *args,
+						   PyObject *kwargs);
+
 PyObject *PyFtrace_hist(PyObject *self, PyObject *args,
 					PyObject *kwargs);
 
diff --git a/src/ftracepy.c b/src/ftracepy.c
index f59bd4c..50ee4d3 100644
--- a/src/ftracepy.c
+++ b/src/ftracepy.c
@@ -377,6 +377,11 @@  static PyMethodDef ftracepy_methods[] = {
 	 METH_VARARGS | METH_KEYWORDS,
 	 "Define a kretprobe."
 	},
+	{"register_eprobe",
+	 (PyCFunction) PyFtrace_register_eprobe,
+	 METH_VARARGS | METH_KEYWORDS,
+	 "Define an eprobe."
+	},
 	{"hist",
 	 (PyCFunction) PyFtrace_hist,
 	 METH_VARARGS | METH_KEYWORDS,
diff --git a/tracecruncher/tests/1_unit/test_01_ftracepy_unit.py b/tracecruncher/tests/1_unit/test_01_ftracepy_unit.py
index 51970d3..12b58a0 100644
--- a/tracecruncher/tests/1_unit/test_01_ftracepy_unit.py
+++ b/tracecruncher/tests/1_unit/test_01_ftracepy_unit.py
@@ -457,6 +457,45 @@  class KprobeTestCase(unittest.TestCase):
         ret = kp1.is_enabled(instance=inst)
         self.assertEqual(ret, '0')
 
+class EprobeTestCase(unittest.TestCase):
+    def test_register_eprobe(self):
+        evt1 = 'sopen_in'
+        evt1_tsys = 'syscalls'
+        evt1_tevent = 'sys_enter_openat'
+        evt1_args = 'file=+0($filename):ustring'
+        evt2 = 'sopen_out'
+        evt2_tsys = 'syscalls'
+        evt2_tevent = 'sys_exit_openat'
+        evt2_args = 'res=$ret:u64'
+
+        ep1 = ft.register_eprobe(event=evt1, target_system=evt1_tsys, target_event=evt1_tevent,
+                                 fetchargs=evt1_args)
+        self.assertEqual(evt1, ep1.event())
+        self.assertEqual("{}.{}".format(evt1_tsys, evt1_tevent), ep1.address())
+        self.assertEqual(evt1_args, ep1.probe())
+
+        ep2 = ft.register_eprobe(event=evt2, target_system=evt2_tsys, target_event=evt2_tevent,
+                                 fetchargs=evt2_args)
+        self.assertEqual(evt2, ep2.event())
+        self.assertEqual("{}.{}".format(evt2_tsys, evt2_tevent), ep2.address())
+        self.assertEqual(evt2_args, ep2.probe())
+
+    def test_enable_eprobe(self):
+        evt1 = 'sopen_out'
+        evt1_tsys = 'syscalls'
+        evt1_tevent = 'sys_exit_openat'
+        evt1_args = 'res=$ret:u64'
+
+        ep1 = ft.register_eprobe(event=evt1, target_system=evt1_tsys, target_event=evt1_tevent,
+                                 fetchargs=evt1_args)
+        inst = ft.create_instance(instance_name)
+        ep1.enable(instance=inst)
+        ret = ep1.is_enabled(instance=inst)
+        self.assertEqual(ret, '1')
+
+        ep1.disable(instance=inst)
+        ret = ep1.is_enabled(instance=inst)
+        self.assertEqual(ret, '0')
 
 class TracingOnTestCase(unittest.TestCase):
     def test_ON_OF(self):