diff mbox series

[v2,03/12] libtracefs: New kprobes APIs

Message ID 20211101090904.81454-4-tz.stoyanov@gmail.com (mailing list archive)
State Superseded
Headers show
Series libtracefs dynamic events support | expand

Commit Message

Tzvetomir Stoyanov (VMware) Nov. 1, 2021, 9:08 a.m. UTC
In order to be consistent with the other APIs, a new set of kprobe APIs
is introduced:
 tracefs_kprobe_alloc();
 tracefs_kretprobe_alloc();
 tracefs_kprobe_create();
 tracefs_kprobe_destroy();
 tracefs_kprobe_free();
These APIs work with kprobe context, represented by the tracefs_dynevent
structure.

Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
---
 include/tracefs.h     |   8 ++
 src/tracefs-kprobes.c | 168 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 176 insertions(+)

Comments

Yordan Karadzhov Nov. 1, 2021, 5:22 p.m. UTC | #1
On 1.11.21 г. 11:08, Tzvetomir Stoyanov (VMware) wrote:
> In order to be consistent with the other APIs, a new set of kprobe APIs
> is introduced:
>   tracefs_kprobe_alloc();
>   tracefs_kretprobe_alloc();
>   tracefs_kprobe_create();
>   tracefs_kprobe_destroy();
>   tracefs_kprobe_free();
> These APIs work with kprobe context, represented by the tracefs_dynevent
> structure.
> 
> Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
> ---
>   include/tracefs.h     |   8 ++
>   src/tracefs-kprobes.c | 168 ++++++++++++++++++++++++++++++++++++++++++
>   2 files changed, 176 insertions(+)
> 
> diff --git a/include/tracefs.h b/include/tracefs.h
> index 4e721eb..85185ce 100644
> --- a/include/tracefs.h
> +++ b/include/tracefs.h
> @@ -247,6 +247,14 @@ enum tracefs_kprobe_type {
>   	TRACEFS_KRETPROBE,
>   };
>   
> +struct tracefs_dynevent *
> +tracefs_kprobe_alloc(const char *system, const char *event, const char *addr, const char *format);
> +struct tracefs_dynevent *
> +tracefs_kretprobe_alloc(const char *system, const char *event,
> +			const char *addr, const char *format, int max);
> +int tracefs_kprobe_create(struct tracefs_dynevent *kprobe);
> +int tracefs_kprobe_destroy(struct tracefs_dynevent *kprobe, bool force);
> +void tracefs_kprobe_free(struct tracefs_dynevent *kprobe);
>   int tracefs_kprobe_raw(const char *system, const char *event,
>   		       const char *addr, const char *format);
>   int tracefs_kretprobe_raw(const char *system, const char *event,
> diff --git a/src/tracefs-kprobes.c b/src/tracefs-kprobes.c
> index d4c5f9e..906f914 100644
> --- a/src/tracefs-kprobes.c
> +++ b/src/tracefs-kprobes.c
> @@ -20,6 +20,134 @@
>   #define KPROBE_EVENTS "kprobe_events"
>   #define KPROBE_DEFAULT_GROUP "kprobes"
>   
> +static struct tracefs_dynevent *
> +kprobe_alloc(enum trace_dynevent_type type, const char *system, const char *event,
> +	     const char *addr, const char *format)
> +{
> +	struct tracefs_dynevent *kp;
> +	const char *sys = system;
> +	const char *ename = event;
> +	char *tmp;
> +
> +	if (!addr) {
> +		errno = EBADMSG;
> +		return NULL;
> +	}
> +	if (!sys)
> +		sys = KPROBE_DEFAULT_GROUP;
> +
> +	if (!event) {
> +		ename = strdup(addr);
> +		if (!ename)
> +			return NULL;
> +		tmp = strchr(ename, ':');
> +		if (tmp)
> +			*tmp = '\0';
> +	}
> +
> +	kp = dynevent_alloc(type, sys, ename, addr, format);
> +	if (!event)
> +		free((char *)ename);
> +
> +	return kp;
> +}
> +
> +
> +/**
> + * tracefs_kprobe_alloc - Allocate new kprobe context
> + * @system: The system name (NULL for the default kprobes)
> + * @event: The event to create (NULL to use @addr for the event)
> + * @addr: The function and offset (or address) to insert the probe
> + * @format: The format string to define the probe.
> + *
> + * Allocate a kprobe context that will be in the @system group (or kprobes if
> + * @system is NULL). Have the name of @event (or @addr if @event is
> + * NULL). Will be inserted to @addr (function name, with or without
> + * offset, or a address). And the @format will define the format
> + * of the kprobe. See the Linux documentation file under:
> + * Documentation/trace/kprobetrace.rst
> + * The kprobe is not created in the system.
> + *
> + * Return a pointer to a kprobe context on success, or NULL on error.
> + * The returned pointer must be freed with tracefs_kprobe_free()
> + *
> + * errno will be set to EBADMSG if addr is NULL.
> + */
> +struct tracefs_dynevent *
> +tracefs_kprobe_alloc(const char *system, const char *event, const char *addr, const char *format)
> +
> +{
> +	return kprobe_alloc(TRACE_DYNEVENT_KPROBE, system, event, addr, format);
> +}
> +
> +/**
> + * tracefs_kretprobe_alloc - Allocate new kretprobe context
> + * @system: The system name (NULL for the default kprobes)
> + * @event: The event to create (NULL to use @addr for the event)
> + * @addr: The function and offset (or address) to insert the retprobe
> + * @format: The format string to define the retprobe.
> + *

@max is not documented.

> + * Allocate a kretprobe that will be in the @system group (or kprobes if
> + * @system is NULL). Have the name of @event (or @addr if @event is
> + * NULL). Will be inserted to @addr (function name, with or without
> + * offset, or a address). And the @format will define the raw format
> + * of the kprobe. See the Linux documentation file under:
> + * Documentation/trace/kprobetrace.rst
> + * The kretprobe is not created in the system.
> + *
> + * Return a pointer to a kprobe context on success, or NULL on error.
> + * The returned pointer must be freed with tracefs_kprobe_free()
> + *
> + * errno will be set to EBADMSG if addr is NULL.
> + */
> +struct tracefs_dynevent *
> +tracefs_kretprobe_alloc(const char *system, const char *event,
> +			const char *addr, const char *format, int max)
> +{
> +	struct tracefs_dynevent *kp;
> +	int ret;
> +
> +	kp = kprobe_alloc(TRACE_DYNEVENT_KRETPROBE, system, event, addr, format);
> +	if (!kp)
> +		return NULL;
> +	if (max) {
> +		free(kp->prefix);
> +		kp->prefix = NULL;
> +		ret = asprintf(&kp->prefix, "r%d:", max);
> +		if (ret < 0)
> +			goto error;
> +	}
> +
> +	return kp;
> +error:
> +	dynevent_free(kp);
> +	return NULL;
> +}
> +
> +/**
> + * tracefs_kprobe_create - Create a kprobe or kretprobe in the system
> + * @kprobe: Pointer to a kprobe context, describing the probe
> + *
> + * Return 0 on success, or -1 on error.
> + */
> +int tracefs_kprobe_create(struct tracefs_dynevent *kprobe)
> +{
> +	return dynevent_create(kprobe);
> +}
> +

hmm, what will happen if if you have a code like this:


struct tracefs_dynevent *synth = tracefs_synth_alloc(...)

int ret = tracefs_kprobe_create(synth);


Are you just giving several additional fake names to the same function ('dynevent_create()')?


> +/**
> + * tracefs_kprobe_free - Free a kprobe context
> + * @kprobe: Pointer to a kprobe context
> + *
> + * The kprobe/kretprobe, described by this context, is not
> + * removed from the system by this API. It only frees the memory.
> + */
> +void tracefs_kprobe_free(struct tracefs_dynevent *kprobe)
> +{
> +	dynevent_free(kprobe);
> +}
> +

The same argument applys here.

Thanks!
Yordan

> +
>   static int insert_kprobe(const char *type, const char *system,
>   			 const char *event, const char *addr,
>   			 const char *format)
> @@ -474,3 +602,43 @@ int tracefs_kprobe_clear_probe(const char *system, const char *event, bool force
>   
>   	return ret < 0 ? -1 : 0;
>   }
> +
> +/**
> + * tracefs_kprobe_destroy - Remove a kprobe or kretprobe from the system
> + * @kprobe: A kprobe context, describing the kprobe that will be deleted.
> + *	    If NULL, all kprobes and kretprobes in the system will be deleted
> + * @force: Will attempt to disable all kprobe events and clear them
> + *
> + * The kprobe/kretprobe context is not freed by this API.
> + * It only removes the probe from the system.
> + *
> + * Return 0 on success, or -1 on error.
> + */
> +int tracefs_kprobe_destroy(struct tracefs_dynevent *kprobe, bool force)
> +{
> +	char **instance_list;
> +	int ret;
> +
> +	if (!kprobe) {
> +		if (tracefs_instance_file_clear(NULL, KPROBE_EVENTS) == 0)
> +			return 0;
> +		if (!force)
> +			return -1;
> +		/* Attempt to disable all kprobe events */
> +		return kprobe_clear_probes(NULL, force);
> +	}
> +
> +	/*
> +	 * Since we know we are disabling a specific event, try
> +	 * to disable it first before clearing it.
> +	 */
> +	if (force) {
> +		instance_list = tracefs_instances(NULL);
> +		disable_events(kprobe->system, kprobe->event, instance_list);
> +		tracefs_list_free(instance_list);
> +	}
> +
> +	ret = dynevent_destroy(kprobe);
> +
> +	return ret < 0 ? -1 : 0;
> +}
>
Tzvetomir Stoyanov (VMware) Nov. 2, 2021, 4:58 a.m. UTC | #2
On Mon, Nov 1, 2021 at 7:22 PM Yordan Karadzhov <y.karadz@gmail.com> wrote:
>
>
[ ... ]
> > +/**
> > + * tracefs_kprobe_create - Create a kprobe or kretprobe in the system
> > + * @kprobe: Pointer to a kprobe context, describing the probe
> > + *
> > + * Return 0 on success, or -1 on error.
> > + */
> > +int tracefs_kprobe_create(struct tracefs_dynevent *kprobe)
> > +{
> > +     return dynevent_create(kprobe);
> > +}
> > +
>
> hmm, what will happen if if you have a code like this:
>
>
> struct tracefs_dynevent *synth = tracefs_synth_alloc(...)
>
> int ret = tracefs_kprobe_create(synth);
>
>
> Are you just giving several additional fake names to the same function ('dynevent_create()')?
>
>

Yes, that will work. All dynamic events - kprobes, uprobes, eprobes
and synthetic events have almost the same configuration logic, which
is handled by these dynevent_... internal APIs. That makes the code
more consistent and reusable.
I see three possible approaches to handle that:
 1. Create structures like that:
      struct tracefs_kprobe {
         struct tracefs_dynevent dynevent;
      };
   and use only that type in krpobe specific APIs:
     int tracefs_kprobe_create(struct tracefs_kprobe *kprobe)
     {
         return dynevent_create(&kprobe->dynevent);
     }

2. Expose the dynevent_... APIs directly:
    struct tracefs_dynevent *synth =
tracefs_dynevent_alloc(TRACE_DYNEVENT_SYNTH, ...);
    struct tracefs_dynevent *kprobe =
tracefs_dynevent_alloc(TRACE_DYNEVENT_KPROBE, ...);

    ret = tracefs_dynevent_create(synth);
    ret = tracefs_dynevent_create(kprobe);

 The problem here is that there are some small differences between
dynamic events - some parameters are mandatory for one event and
optional for another. That can be handled in the
tracefs_dynevent_alloc() API, but could be confusing for the library
users.

3. Mixed between 1) and 2), the current approach.

The first one adds a lot of overhead, a different set of APIs for each
type of dynamic event - but maybe it is more clear for the library
users. The second is straightforward for implementation, less APIs -
but that could be confusing for the users.

> > +/**
> > + * tracefs_kprobe_free - Free a kprobe context
> > + * @kprobe: Pointer to a kprobe context
> > + *
> > + * The kprobe/kretprobe, described by this context, is not
> > + * removed from the system by this API. It only frees the memory.
> > + */
> > +void tracefs_kprobe_free(struct tracefs_dynevent *kprobe)
> > +{
> > +     dynevent_free(kprobe);
> > +}
> > +
>
> The same argument applys here.
>
> Thanks!
> Yordan
>

Thanks for the review!
Yordan Karadzhov Nov. 2, 2021, 7:43 a.m. UTC | #3
On 2.11.21 г. 6:58, Tzvetomir Stoyanov wrote:
> On Mon, Nov 1, 2021 at 7:22 PM Yordan Karadzhov <y.karadz@gmail.com> wrote:
>>
>>
> [ ... ]
>>> +/**
>>> + * tracefs_kprobe_create - Create a kprobe or kretprobe in the system
>>> + * @kprobe: Pointer to a kprobe context, describing the probe
>>> + *
>>> + * Return 0 on success, or -1 on error.
>>> + */
>>> +int tracefs_kprobe_create(struct tracefs_dynevent *kprobe)
>>> +{
>>> +     return dynevent_create(kprobe);
>>> +}
>>> +
>>
>> hmm, what will happen if if you have a code like this:
>>
>>
>> struct tracefs_dynevent *synth = tracefs_synth_alloc(...)
>>
>> int ret = tracefs_kprobe_create(synth);
>>
>>
>> Are you just giving several additional fake names to the same function ('dynevent_create()')?
>>
>>
> 
> Yes, that will work. All dynamic events - kprobes, uprobes, eprobes
> and synthetic events have almost the same configuration logic, which
> is handled by these dynevent_... internal APIs. That makes the code
> more consistent and reusable.
> I see three possible approaches to handle that:
>   1. Create structures like that:
>        struct tracefs_kprobe {
>           struct tracefs_dynevent dynevent;
>        };
>     and use only that type in krpobe specific APIs:
>       int tracefs_kprobe_create(struct tracefs_kprobe *kprobe)
>       {
>           return dynevent_create(&kprobe->dynevent);
>       }
> 
> 2. Expose the dynevent_... APIs directly:
>      struct tracefs_dynevent *synth =
> tracefs_dynevent_alloc(TRACE_DYNEVENT_SYNTH, ...);

I would prefer taking this approach. It is OK to also have several helper constructors like tracefs_kprobe_alloc(), 
tracefs_synth_alloc(), etc. that will call internally dynevent_alloc().

In principle having multiple constructors is OK.


>      struct tracefs_dynevent *kprobe =
> tracefs_dynevent_alloc(TRACE_DYNEVENT_KPROBE, ...);
> 
>      ret = tracefs_dynevent_create(synth);
>      ret = tracefs_dynevent_create(kprobe);
> 

Yes, this is OK as well.

We only have a single type (struct tracefs_dynevent) so there must be a single create. And what is even more important: 
there must be ONLY ONE destructor.

Think about the usecase of the APIs. You create an object using one of the constructors. Later at some point you want to 
use the objects and after this finally you want to destroy it. If we have multiple/specialized "create" and "destroy" 
versions for the same object type, the user must somehow keep its own record of which constructor had been used trough 
the entire live of the object in order to know which is the proper destructor to call at the end.

Thanks!
Yordan


>   The problem here is that there are some small differences between
> dynamic events - some parameters are mandatory for one event and
> optional for another. That can be handled in the
> tracefs_dynevent_alloc() API, but could be confusing for the library
> users.
> 
> 3. Mixed between 1) and 2), the current approach.
> 
> The first one adds a lot of overhead, a different set of APIs for each
> type of dynamic event - but maybe it is more clear for the library
> users. The second is straightforward for implementation, less APIs -
> but that could be confusing for the users.
> 
>>> +/**
>>> + * tracefs_kprobe_free - Free a kprobe context
>>> + * @kprobe: Pointer to a kprobe context
>>> + *
>>> + * The kprobe/kretprobe, described by this context, is not
>>> + * removed from the system by this API. It only frees the memory.
>>> + */
>>> +void tracefs_kprobe_free(struct tracefs_dynevent *kprobe)
>>> +{
>>> +     dynevent_free(kprobe);
>>> +}
>>> +
>>
>> The same argument applys here.
>>
>> Thanks!
>> Yordan
>>
> 
> Thanks for the review!
>
Steven Rostedt Nov. 2, 2021, 1:49 p.m. UTC | #4
On Tue, 2 Nov 2021 09:43:16 +0200
Yordan Karadzhov <y.karadz@gmail.com> wrote:

> > 2. Expose the dynevent_... APIs directly:
> >      struct tracefs_dynevent *synth =
> > tracefs_dynevent_alloc(TRACE_DYNEVENT_SYNTH, ...);  
> 
> I would prefer taking this approach. It is OK to also have several helper constructors like tracefs_kprobe_alloc(), 
> tracefs_synth_alloc(), etc. that will call internally dynevent_alloc().
> 
> In principle having multiple constructors is OK.

I agree with having multiple constructors, as that makes it easier to think
about what is being created and how to manipulate it. The dynamic event
structure has a type field and it should be used to make sure that
modifications that only should happen to kprobes only happens on dynamic
events created as a kprobe, and errors on everything else.

> 
> 
> >      struct tracefs_dynevent *kprobe =
> > tracefs_dynevent_alloc(TRACE_DYNEVENT_KPROBE, ...);
> > 
> >      ret = tracefs_dynevent_create(synth);
> >      ret = tracefs_dynevent_create(kprobe);
> >   
> 
> Yes, this is OK as well.

Agreed.

> 
> We only have a single type (struct tracefs_dynevent) so there must be a single create. And what is even more important: 
> there must be ONLY ONE destructor.

I wouldn't be so strong as to use "must" but I do agree that it will
simplify the usage of the API. It would be more analogous to free() where
you have several different constructors that state in the man page (must be
freed with free). Same here. Each constructor's man page must state: "must
be freed with tracefs_dynevent_free()"

Removing from the system any dynamic event should also just use
tracefs_dynevent_destroy(). Having the type of event in the structure means
that knowing how to destroy it can be multiplexed inside the library, and
not burden the user in such cases. This way, the user could create a bunch
of dynamic event types in an array or linked list, and destroy them at the
end without having to keep track of which one is which.

-- Steve

> 
> Think about the usecase of the APIs. You create an object using one of the constructors. Later at some point you want to 
> use the objects and after this finally you want to destroy it. If we have multiple/specialized "create" and "destroy" 
> versions for the same object type, the user must somehow keep its own record of which constructor had been used trough 
> the entire live of the object in order to know which is the proper destructor to call at the end.
diff mbox series

Patch

diff --git a/include/tracefs.h b/include/tracefs.h
index 4e721eb..85185ce 100644
--- a/include/tracefs.h
+++ b/include/tracefs.h
@@ -247,6 +247,14 @@  enum tracefs_kprobe_type {
 	TRACEFS_KRETPROBE,
 };
 
+struct tracefs_dynevent *
+tracefs_kprobe_alloc(const char *system, const char *event, const char *addr, const char *format);
+struct tracefs_dynevent *
+tracefs_kretprobe_alloc(const char *system, const char *event,
+			const char *addr, const char *format, int max);
+int tracefs_kprobe_create(struct tracefs_dynevent *kprobe);
+int tracefs_kprobe_destroy(struct tracefs_dynevent *kprobe, bool force);
+void tracefs_kprobe_free(struct tracefs_dynevent *kprobe);
 int tracefs_kprobe_raw(const char *system, const char *event,
 		       const char *addr, const char *format);
 int tracefs_kretprobe_raw(const char *system, const char *event,
diff --git a/src/tracefs-kprobes.c b/src/tracefs-kprobes.c
index d4c5f9e..906f914 100644
--- a/src/tracefs-kprobes.c
+++ b/src/tracefs-kprobes.c
@@ -20,6 +20,134 @@ 
 #define KPROBE_EVENTS "kprobe_events"
 #define KPROBE_DEFAULT_GROUP "kprobes"
 
+static struct tracefs_dynevent *
+kprobe_alloc(enum trace_dynevent_type type, const char *system, const char *event,
+	     const char *addr, const char *format)
+{
+	struct tracefs_dynevent *kp;
+	const char *sys = system;
+	const char *ename = event;
+	char *tmp;
+
+	if (!addr) {
+		errno = EBADMSG;
+		return NULL;
+	}
+	if (!sys)
+		sys = KPROBE_DEFAULT_GROUP;
+
+	if (!event) {
+		ename = strdup(addr);
+		if (!ename)
+			return NULL;
+		tmp = strchr(ename, ':');
+		if (tmp)
+			*tmp = '\0';
+	}
+
+	kp = dynevent_alloc(type, sys, ename, addr, format);
+	if (!event)
+		free((char *)ename);
+
+	return kp;
+}
+
+
+/**
+ * tracefs_kprobe_alloc - Allocate new kprobe context
+ * @system: The system name (NULL for the default kprobes)
+ * @event: The event to create (NULL to use @addr for the event)
+ * @addr: The function and offset (or address) to insert the probe
+ * @format: The format string to define the probe.
+ *
+ * Allocate a kprobe context that will be in the @system group (or kprobes if
+ * @system is NULL). Have the name of @event (or @addr if @event is
+ * NULL). Will be inserted to @addr (function name, with or without
+ * offset, or a address). And the @format will define the format
+ * of the kprobe. See the Linux documentation file under:
+ * Documentation/trace/kprobetrace.rst
+ * The kprobe is not created in the system.
+ *
+ * Return a pointer to a kprobe context on success, or NULL on error.
+ * The returned pointer must be freed with tracefs_kprobe_free()
+ *
+ * errno will be set to EBADMSG if addr is NULL.
+ */
+struct tracefs_dynevent *
+tracefs_kprobe_alloc(const char *system, const char *event, const char *addr, const char *format)
+
+{
+	return kprobe_alloc(TRACE_DYNEVENT_KPROBE, system, event, addr, format);
+}
+
+/**
+ * tracefs_kretprobe_alloc - Allocate new kretprobe context
+ * @system: The system name (NULL for the default kprobes)
+ * @event: The event to create (NULL to use @addr for the event)
+ * @addr: The function and offset (or address) to insert the retprobe
+ * @format: The format string to define the retprobe.
+ *
+ * Allocate a kretprobe that will be in the @system group (or kprobes if
+ * @system is NULL). Have the name of @event (or @addr if @event is
+ * NULL). Will be inserted to @addr (function name, with or without
+ * offset, or a address). And the @format will define the raw format
+ * of the kprobe. See the Linux documentation file under:
+ * Documentation/trace/kprobetrace.rst
+ * The kretprobe is not created in the system.
+ *
+ * Return a pointer to a kprobe context on success, or NULL on error.
+ * The returned pointer must be freed with tracefs_kprobe_free()
+ *
+ * errno will be set to EBADMSG if addr is NULL.
+ */
+struct tracefs_dynevent *
+tracefs_kretprobe_alloc(const char *system, const char *event,
+			const char *addr, const char *format, int max)
+{
+	struct tracefs_dynevent *kp;
+	int ret;
+
+	kp = kprobe_alloc(TRACE_DYNEVENT_KRETPROBE, system, event, addr, format);
+	if (!kp)
+		return NULL;
+	if (max) {
+		free(kp->prefix);
+		kp->prefix = NULL;
+		ret = asprintf(&kp->prefix, "r%d:", max);
+		if (ret < 0)
+			goto error;
+	}
+
+	return kp;
+error:
+	dynevent_free(kp);
+	return NULL;
+}
+
+/**
+ * tracefs_kprobe_create - Create a kprobe or kretprobe in the system
+ * @kprobe: Pointer to a kprobe context, describing the probe
+ *
+ * Return 0 on success, or -1 on error.
+ */
+int tracefs_kprobe_create(struct tracefs_dynevent *kprobe)
+{
+	return dynevent_create(kprobe);
+}
+
+/**
+ * tracefs_kprobe_free - Free a kprobe context
+ * @kprobe: Pointer to a kprobe context
+ *
+ * The kprobe/kretprobe, described by this context, is not
+ * removed from the system by this API. It only frees the memory.
+ */
+void tracefs_kprobe_free(struct tracefs_dynevent *kprobe)
+{
+	dynevent_free(kprobe);
+}
+
+
 static int insert_kprobe(const char *type, const char *system,
 			 const char *event, const char *addr,
 			 const char *format)
@@ -474,3 +602,43 @@  int tracefs_kprobe_clear_probe(const char *system, const char *event, bool force
 
 	return ret < 0 ? -1 : 0;
 }
+
+/**
+ * tracefs_kprobe_destroy - Remove a kprobe or kretprobe from the system
+ * @kprobe: A kprobe context, describing the kprobe that will be deleted.
+ *	    If NULL, all kprobes and kretprobes in the system will be deleted
+ * @force: Will attempt to disable all kprobe events and clear them
+ *
+ * The kprobe/kretprobe context is not freed by this API.
+ * It only removes the probe from the system.
+ *
+ * Return 0 on success, or -1 on error.
+ */
+int tracefs_kprobe_destroy(struct tracefs_dynevent *kprobe, bool force)
+{
+	char **instance_list;
+	int ret;
+
+	if (!kprobe) {
+		if (tracefs_instance_file_clear(NULL, KPROBE_EVENTS) == 0)
+			return 0;
+		if (!force)
+			return -1;
+		/* Attempt to disable all kprobe events */
+		return kprobe_clear_probes(NULL, force);
+	}
+
+	/*
+	 * Since we know we are disabling a specific event, try
+	 * to disable it first before clearing it.
+	 */
+	if (force) {
+		instance_list = tracefs_instances(NULL);
+		disable_events(kprobe->system, kprobe->event, instance_list);
+		tracefs_list_free(instance_list);
+	}
+
+	ret = dynevent_destroy(kprobe);
+
+	return ret < 0 ? -1 : 0;
+}