diff mbox series

libtracefs: An API to set the filtering of functions

Message ID 1615393287-12344-1-git-send-email-sameeruddin.shaik8@gmail.com (mailing list archive)
State Superseded
Headers show
Series libtracefs: An API to set the filtering of functions | expand

Commit Message

sameeruddin shaik March 10, 2021, 4:21 p.m. UTC
This new API will write the function filters into the
set_ftrace_filter file.

tracefs_function_filter()

https://bugzilla.kernel.org/show_bug.cgi?id=210643

Signed-off-by: Sameeruddin shaik <sameeruddin.shaik8@gmail.com>

Comments

Tzvetomir Stoyanov (VMware) March 10, 2021, 5:28 a.m. UTC | #1
Hi Sameer,

On Tue, Mar 9, 2021 at 6:24 PM Sameeruddin shaik
<sameeruddin.shaik8@gmail.com> wrote:
>
> This new API will write the function filters into the
> set_ftrace_filter file.
>
> tracefs_function_filter()
>
> https://bugzilla.kernel.org/show_bug.cgi?id=210643
>
> Signed-off-by: Sameeruddin shaik <sameeruddin.shaik8@gmail.com>
>
> diff --git a/include/tracefs.h b/include/tracefs.h
> index f3eec62..a2249d0 100644
> --- a/include/tracefs.h
> +++ b/include/tracefs.h
> @@ -50,6 +50,7 @@ int tracefs_trace_on(struct tracefs_instance *instance);
>  int tracefs_trace_off(struct tracefs_instance *instance);
>  int tracefs_trace_on_fd(int fd);
>  int tracefs_trace_off_fd(int fd);
> +int tracefs_function_filter(struct tracefs_instance *instance, const char **filters, const char *module, bool reset, const char ***errs);

I would break that in two lines, try to keep the lines up to 100
characters, when possible. It is very subjective to find the
balance between that 70-100 char limit and readable code.
There could be exceptions, but in this case I think it is better
to split it in two lines:

int tracefs_function_filter(struct tracefs_instance *instance, const
char **filters,
                                        const char *module, bool
reset, const char ***errs);

Note the second line - you should use tabs and spaces to align the  arguments
on the second line with the arguments on the first line. I cannot put
tabs in this
reply because of the strange limitations of my mail client, but in the
code - use
tabs (set to 8 spaces each) + spaces, if needed, to align it.

>
>  /**
>   * tracefs_trace_on_get_fd - Get a file descriptor of "tracing_on" in given instance
> diff --git a/src/tracefs-tools.c b/src/tracefs-tools.c
> index e2dfc7b..4e168df 100644
> --- a/src/tracefs-tools.c
> +++ b/src/tracefs-tools.c
> @@ -18,6 +18,7 @@
>  #include "tracefs-local.h"
>
>  #define TRACE_CTRL     "tracing_on"
> +#define TRACE_FILTER      "set_ftrace_filter"

Use tabs instead of spaces to align the string with the previous define.

>
>  static const char * const options_map[] = {
>         "unknown",
> @@ -387,3 +388,96 @@ void tracefs_option_clear(struct tracefs_options_mask *options, enum tracefs_opt
>         if (options && id > TRACEFS_OPTION_INVALID)
>                 options->mask &= ~(1ULL << (id - 1));
>  }
> +
> +static int controlled_write(const char *filter_path, const char **filters, const char *module, bool reset, const char ***errs)

I would split that in two lines, aligning the arguments on both lines - see
the comment above.

> +{
> +       int flags = reset ? O_TRUNC : O_APPEND;
> +       int write_size;
> +       char *each_str;
> +       int ret = 0;
> +       int j = 0;
> +       int size;
> +       int slen;
> +       int fd;
> +       int i;
> +
> +       fd = open(filter_path, O_WRONLY | flags);
> +       if (fd < 0)
> +               return 1;
> +
> +       for (i = 0; filters[i]; i++) {
> +               slen = strlen(filters[i]);
> +               if (!slen)
> +                       continue;
> +
> +               if (module)
> +                       /* Adding 5 extra bytes for ":mod:"*/
> +                       slen += strlen(module) + 5;
> +
> +               /* Adding 2 extra byte for the space and '\0' at the end*/
> +               slen += 2;
> +               each_str = calloc(1, slen);
> +               if (!each_str)
> +                       return 1;

This return will leak the opened fd. It is better to have a "goto error"
label and to clean all allocated resources in case of an error return.
I would suggest to initialize all resources that are allocated internally
with some default values (i.e. fd with -1, each_str with NULL) and
in this "error clean up logic" to free them, if they are allocated.

> +               if (module)
> +                       write_size = snprintf(each_str, slen, "%s:mod:%s ", filters[i], module);
> +               else
> +                       write_size = snprintf(each_str, slen, "%s ", filters[i]);

You could use asprintf() instead of snprintf(), it will take care of the string
allocation and size instead of you, and will simplify the code. But note
that asprintf() has different behaviour in case of an error, it will not set
each_str to NULL. Take this into account in your logic.

> +
> +               size = write(fd, each_str, write_size);
> +               /* compare written bytes*/
> +               if (size < write_size) {
> +                       if (errs) {
> +                               errs[j++] = &filters[i];

The errs must be reallocated here, I'll comment that on
your second email. Without realloc, it will cause a seg fault.

> +                               ret -= 1;
> +                       }
> +               }
> +               free(each_str);
> +       }
> +       errs[j] = NULL;

errs is an optional argument, it could be NULL - check it
before writing to it. Also, errs is a triple pointer:

if (errs)
    (*errs)[j] = NULL;

> +       close(fd);
> +       return ret;
> +}
> +
> +/**
> + * tracefs_function_filter - write to set_ftrace_filter file to trace particular functions
> + * @instance: ftrace instance, can be NULL for top tracing instance
> + * @filters: An array of function names ending with a NULL pointer
> + * @module: Module to be traced
> + * @reset: set to true to reset the file before applying the filter
> + * @errs: A pointer to array of constant strings that will be allocated
> + * on negative return of this function, pointing to the filters that failed.
> + * May be NULL, in which case this field will be ignored.
> + *
> + * The @filters is an array of strings, where each string will be used to set
> + * a function or functions to be traced.
> + *
> + * If @reset is true, then all functions in the filter are cleared before
> + * adding functions from @filters. Otherwise, the functions set by @filters
> + * will be appended to the filter file
> + *
> + * returns -x on filter errors (where x is number of failed filter srtings)
> + * and if @errs is not NULL will be an allocated string array pointing to the
> + * strings in @filters that failed and must be freed with free().
> + *
> + * returns 1 on general errors not realted to setting the filter.
> + * @errs is not set even if supplied.
> + *
> + * return 0 on success and @errs is not set.
> + */
> +int tracefs_function_filter(struct tracefs_instance *instance, const char **filters, const char *module, bool reset, const char ***errs)

This also should be splitted in two lines.

> +{
> +       char *ftrace_filter_path;
> +       int ret = 0;
> +
> +       if (!filters)
> +               return 1;
> +
> +       ftrace_filter_path = tracefs_instance_get_file(instance, TRACE_FILTER);
> +       if (!ftrace_filter_path)
> +               return 1;
> +
> +       ret = controlled_write(ftrace_filter_path, filters, module, reset, errs);
> +       tracefs_put_tracing_file(ftrace_filter_path);
> +       return ret;
> +}
> --
> 2.7.4
>

Thanks for sending the next version, Sameer!

One hint when sending the next versions of the same patch:
use the "-v" option of the "git format-patch" command, that helps
to track the patch history. Also, it is useful to write the changes of
each version of the patch. In case of a patch set - the version changes
should be in the cover letter. In your case, as it is a single patch and
there is no need of a cover letter - add the version changes before
sending the patch just after the "---" line, manually. That way this
will not be part of the patch description, but still will be part of the
patch.
Tzvetomir Stoyanov (VMware) March 10, 2021, 5:28 a.m. UTC | #2
On Tue, Mar 9, 2021 at 6:51 PM Sameeruddin Shaik
<sameeruddin.shaik8@gmail.com> wrote:
>
> This is my test code, I tried handling the triple pointers in the library,
> but when it comes to user program, at first "errs" has NULL at the
> end(checked it using the gdb), but when we try to dereference and
> print, gradually position holding NULL in "errs" is getting assinged
> to some other location, and i am getting the segmentation fault.
>
> >Since a triple pointer is difficult to manage in the code, you could have:
> >
> >        const char **e = NULL;
> >
> >
> >               if (errs) {
> >                        e = realloc(sizeof(*e), j + 1);
> >                        e[j++] = filters[i];
> >                }
> >
> >Then at the end:
> >
> >        if (errs)
> >                *errs = e;
>
> i didn't got the clear picture of what, the above code snippet is doing.
> particularly the below line
>
> >e = realloc(sizeof(*e), j + 1);
 It should be just
   e = realloc(e, (j + 1) * sizeof(char *));
we need to store one more pointer to char,
 On the first call, e is NULL and j is 0 and this behaves
 as a regular malloc:
   e = realloc(NULL, 1 * sizeof(char *));
on each subsequent call it allocates memory for
one additional pointer to char, at the end of the existing
array e.
Note that the allocated memory is not set to 0, it contains
random data.

>
> From man page i got
>
> void *realloc(void *ptr, size_t size);
>
> could you please brief me with an example?
>
> Subject: [PATCH] test: Test code
> diff --git a/test.c b/test.c
> index d38fc92..3e90711 100644
> --- a/test.c
> +++ b/test.c
> @@ -1,7 +1,39 @@
>  #include <tracefs.h>
> +#include <stdio.h>
> +#include <fcntl.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <stdlib.h>
> +
> +
> +
> +#define INST "dummy"
> +
> +const char *filters[] = {"kvm_pmu_reset", "kvm_pmu_init",
> "dir_item_err", "tzvetomir", "sameer", "steve", NULL};
>
>  int main()
>  {
> -    tracefs_tracing_dir();
> +    struct tracefs_instance *instance = NULL;
> +    const char ***errs;

For the user of the API, errs is just an array of strings,
regular double pointer. It is not allocated by the caller,
so it is important to be initialised with NULL;

 const char **errs = NULL;

> +    int ret;
> +    int fd;
> +    int n;
> +    int i = 0;
> +
> +    errs = malloc(sizeof(char *));

No need to allocate it here, as the size is not known -
it depends on the number of failed filters. The API
will do the job.

> +    printf("%s\n",tracefs_tracing_dir());
> +    instance = tracefs_instance_create(INST);
> +    ret = tracefs_function_filter(instance, filters, "kvm", 1, errs);

As the errs will be allocated by the API, you should pass the address
of it:
   ret = tracefs_function_filter(instance, filters, "kvm", 1, &errs);
That makes the pointer triple, and it is important to be initialised
with NULL.

If the caller is not interested of the errors, it could pass NULL:
   ret = tracefs_function_filter(instance, filters, "kvm", 1, NULL);

> +
> +    if(ret < -1)
> +        if (errs)
> +            while (errs[i])
> +                printf("%s\n", *errs[i++]);
Yes, that is exactly how it should be used, except
that additional "*":
if (ret < 0 && errs) {
    while (errs[i])
          printf("%s\n", errs[i++]);
}

> +
> +    getchar();
> +    tracefs_instance_destroy(instance);
> +    tracefs_instance_free(instance);
> +    free(errs);
>      return 0;
>  }
>
> On Tue, Mar 9, 2021 at 9:51 PM Sameeruddin shaik
> <sameeruddin.shaik8@gmail.com> wrote:
> >
> > This new API will write the function filters into the
> > set_ftrace_filter file.
> >
> > tracefs_function_filter()
> >
> > https://bugzilla.kernel.org/show_bug.cgi?id=210643
> >
> > Signed-off-by: Sameeruddin shaik <sameeruddin.shaik8@gmail.com>
> >
> > diff --git a/include/tracefs.h b/include/tracefs.h
> > index f3eec62..a2249d0 100644
> > --- a/include/tracefs.h
> > +++ b/include/tracefs.h
> > @@ -50,6 +50,7 @@ int tracefs_trace_on(struct tracefs_instance *instance);
> >  int tracefs_trace_off(struct tracefs_instance *instance);
> >  int tracefs_trace_on_fd(int fd);
> >  int tracefs_trace_off_fd(int fd);
> > +int tracefs_function_filter(struct tracefs_instance *instance, const char **filters, const char *module, bool reset, const char ***errs);
> >
> >  /**
> >   * tracefs_trace_on_get_fd - Get a file descriptor of "tracing_on" in given instance
> > diff --git a/src/tracefs-tools.c b/src/tracefs-tools.c
> > index e2dfc7b..4e168df 100644
> > --- a/src/tracefs-tools.c
> > +++ b/src/tracefs-tools.c
> > @@ -18,6 +18,7 @@
> >  #include "tracefs-local.h"
> >
> >  #define TRACE_CTRL     "tracing_on"
> > +#define TRACE_FILTER      "set_ftrace_filter"
> >
> >  static const char * const options_map[] = {
> >         "unknown",
> > @@ -387,3 +388,96 @@ void tracefs_option_clear(struct tracefs_options_mask *options, enum tracefs_opt
> >         if (options && id > TRACEFS_OPTION_INVALID)
> >                 options->mask &= ~(1ULL << (id - 1));
> >  }
> > +
> > +static int controlled_write(const char *filter_path, const char **filters, const char *module, bool reset, const char ***errs)
> > +{
> > +       int flags = reset ? O_TRUNC : O_APPEND;
> > +       int write_size;
> > +       char *each_str;
> > +       int ret = 0;
> > +       int j = 0;
> > +       int size;
> > +       int slen;
> > +       int fd;
> > +       int i;
> > +
> > +       fd = open(filter_path, O_WRONLY | flags);
> > +       if (fd < 0)
> > +               return 1;
> > +
> > +       for (i = 0; filters[i]; i++) {
> > +               slen = strlen(filters[i]);
> > +               if (!slen)
> > +                       continue;
> > +
> > +               if (module)
> > +                       /* Adding 5 extra bytes for ":mod:"*/
> > +                       slen += strlen(module) + 5;
> > +
> > +               /* Adding 2 extra byte for the space and '\0' at the end*/
> > +               slen += 2;
> > +               each_str = calloc(1, slen);
> > +               if (!each_str)
> > +                       return 1;
> > +               if (module)
> > +                       write_size = snprintf(each_str, slen, "%s:mod:%s ", filters[i], module);
> > +               else
> > +                       write_size = snprintf(each_str, slen, "%s ", filters[i]);
> > +
> > +               size = write(fd, each_str, write_size);
> > +               /* compare written bytes*/
> > +               if (size < write_size) {
> > +                       if (errs) {
> > +                               errs[j++] = &filters[i];
> > +                               ret -= 1;
> > +                       }
> > +               }
> > +               free(each_str);
> > +       }
> > +       errs[j] = NULL;
> > +       close(fd);
> > +       return ret;
> > +}
> > +
> > +/**
> > + * tracefs_function_filter - write to set_ftrace_filter file to trace particular functions
> > + * @instance: ftrace instance, can be NULL for top tracing instance
> > + * @filters: An array of function names ending with a NULL pointer
> > + * @module: Module to be traced
> > + * @reset: set to true to reset the file before applying the filter
> > + * @errs: A pointer to array of constant strings that will be allocated
> > + * on negative return of this function, pointing to the filters that failed.
> > + * May be NULL, in which case this field will be ignored.
> > + *
> > + * The @filters is an array of strings, where each string will be used to set
> > + * a function or functions to be traced.
> > + *
> > + * If @reset is true, then all functions in the filter are cleared before
> > + * adding functions from @filters. Otherwise, the functions set by @filters
> > + * will be appended to the filter file
> > + *
> > + * returns -x on filter errors (where x is number of failed filter srtings)
> > + * and if @errs is not NULL will be an allocated string array pointing to the
> > + * strings in @filters that failed and must be freed with free().
> > + *
> > + * returns 1 on general errors not realted to setting the filter.
> > + * @errs is not set even if supplied.
> > + *
> > + * return 0 on success and @errs is not set.
> > + */
> > +int tracefs_function_filter(struct tracefs_instance *instance, const char **filters, const char *module, bool reset, const char ***errs)
> > +{
> > +       char *ftrace_filter_path;
> > +       int ret = 0;
> > +
> > +       if (!filters)
> > +               return 1;
> > +
> > +       ftrace_filter_path = tracefs_instance_get_file(instance, TRACE_FILTER);
> > +       if (!ftrace_filter_path)
> > +               return 1;
> > +
> > +       ret = controlled_write(ftrace_filter_path, filters, module, reset, errs);
> > +       tracefs_put_tracing_file(ftrace_filter_path);
> > +       return ret;
> > +}
> > --
> > 2.7.4
> >
sameeruddin shaik March 10, 2021, 4:51 p.m. UTC | #3
This is my test code, I tried handling the triple pointers in the library,
but when it comes to user program, at first "errs" has NULL at the
end(checked it using the gdb), but when we try to dereference and
print, gradually position holding NULL in "errs" is getting assinged
to some other location, and i am getting the segmentation fault.

>Since a triple pointer is difficult to manage in the code, you could have:
>
>        const char **e = NULL;
>
>
>               if (errs) {
>                        e = realloc(sizeof(*e), j + 1);
>                        e[j++] = filters[i];
>                }
>
>Then at the end:
>
>        if (errs)
>                *errs = e;

i didn't got the clear picture of what, the above code snippet is doing.
particularly the below line

>e = realloc(sizeof(*e), j + 1);

From man page i got

void *realloc(void *ptr, size_t size);

could you please brief me with an example?

Subject: [PATCH] test: Test code
diff --git a/test.c b/test.c
index d38fc92..3e90711 100644
--- a/test.c
+++ b/test.c
@@ -1,7 +1,39 @@
 #include <tracefs.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+
+
+#define INST "dummy"
+
+const char *filters[] = {"kvm_pmu_reset", "kvm_pmu_init",
"dir_item_err", "tzvetomir", "sameer", "steve", NULL};

 int main()
 {
-    tracefs_tracing_dir();
+    struct tracefs_instance *instance = NULL;
+    const char ***errs;
+    int ret;
+    int fd;
+    int n;
+    int i = 0;
+
+    errs = malloc(sizeof(char *));
+    printf("%s\n",tracefs_tracing_dir());
+    instance = tracefs_instance_create(INST);
+    ret = tracefs_function_filter(instance, filters, "kvm", 1, errs);
+
+    if(ret < -1)
+        if (errs)
+            while (errs[i])
+                printf("%s\n", *errs[i++]);
+
+    getchar();
+    tracefs_instance_destroy(instance);
+    tracefs_instance_free(instance);
+    free(errs);
     return 0;
 }

On Tue, Mar 9, 2021 at 9:51 PM Sameeruddin shaik
<sameeruddin.shaik8@gmail.com> wrote:
>
> This new API will write the function filters into the
> set_ftrace_filter file.
>
> tracefs_function_filter()
>
> https://bugzilla.kernel.org/show_bug.cgi?id=210643
>
> Signed-off-by: Sameeruddin shaik <sameeruddin.shaik8@gmail.com>
>
> diff --git a/include/tracefs.h b/include/tracefs.h
> index f3eec62..a2249d0 100644
> --- a/include/tracefs.h
> +++ b/include/tracefs.h
> @@ -50,6 +50,7 @@ int tracefs_trace_on(struct tracefs_instance *instance);
>  int tracefs_trace_off(struct tracefs_instance *instance);
>  int tracefs_trace_on_fd(int fd);
>  int tracefs_trace_off_fd(int fd);
> +int tracefs_function_filter(struct tracefs_instance *instance, const char **filters, const char *module, bool reset, const char ***errs);
>
>  /**
>   * tracefs_trace_on_get_fd - Get a file descriptor of "tracing_on" in given instance
> diff --git a/src/tracefs-tools.c b/src/tracefs-tools.c
> index e2dfc7b..4e168df 100644
> --- a/src/tracefs-tools.c
> +++ b/src/tracefs-tools.c
> @@ -18,6 +18,7 @@
>  #include "tracefs-local.h"
>
>  #define TRACE_CTRL     "tracing_on"
> +#define TRACE_FILTER      "set_ftrace_filter"
>
>  static const char * const options_map[] = {
>         "unknown",
> @@ -387,3 +388,96 @@ void tracefs_option_clear(struct tracefs_options_mask *options, enum tracefs_opt
>         if (options && id > TRACEFS_OPTION_INVALID)
>                 options->mask &= ~(1ULL << (id - 1));
>  }
> +
> +static int controlled_write(const char *filter_path, const char **filters, const char *module, bool reset, const char ***errs)
> +{
> +       int flags = reset ? O_TRUNC : O_APPEND;
> +       int write_size;
> +       char *each_str;
> +       int ret = 0;
> +       int j = 0;
> +       int size;
> +       int slen;
> +       int fd;
> +       int i;
> +
> +       fd = open(filter_path, O_WRONLY | flags);
> +       if (fd < 0)
> +               return 1;
> +
> +       for (i = 0; filters[i]; i++) {
> +               slen = strlen(filters[i]);
> +               if (!slen)
> +                       continue;
> +
> +               if (module)
> +                       /* Adding 5 extra bytes for ":mod:"*/
> +                       slen += strlen(module) + 5;
> +
> +               /* Adding 2 extra byte for the space and '\0' at the end*/
> +               slen += 2;
> +               each_str = calloc(1, slen);
> +               if (!each_str)
> +                       return 1;
> +               if (module)
> +                       write_size = snprintf(each_str, slen, "%s:mod:%s ", filters[i], module);
> +               else
> +                       write_size = snprintf(each_str, slen, "%s ", filters[i]);
> +
> +               size = write(fd, each_str, write_size);
> +               /* compare written bytes*/
> +               if (size < write_size) {
> +                       if (errs) {
> +                               errs[j++] = &filters[i];
> +                               ret -= 1;
> +                       }
> +               }
> +               free(each_str);
> +       }
> +       errs[j] = NULL;
> +       close(fd);
> +       return ret;
> +}
> +
> +/**
> + * tracefs_function_filter - write to set_ftrace_filter file to trace particular functions
> + * @instance: ftrace instance, can be NULL for top tracing instance
> + * @filters: An array of function names ending with a NULL pointer
> + * @module: Module to be traced
> + * @reset: set to true to reset the file before applying the filter
> + * @errs: A pointer to array of constant strings that will be allocated
> + * on negative return of this function, pointing to the filters that failed.
> + * May be NULL, in which case this field will be ignored.
> + *
> + * The @filters is an array of strings, where each string will be used to set
> + * a function or functions to be traced.
> + *
> + * If @reset is true, then all functions in the filter are cleared before
> + * adding functions from @filters. Otherwise, the functions set by @filters
> + * will be appended to the filter file
> + *
> + * returns -x on filter errors (where x is number of failed filter srtings)
> + * and if @errs is not NULL will be an allocated string array pointing to the
> + * strings in @filters that failed and must be freed with free().
> + *
> + * returns 1 on general errors not realted to setting the filter.
> + * @errs is not set even if supplied.
> + *
> + * return 0 on success and @errs is not set.
> + */
> +int tracefs_function_filter(struct tracefs_instance *instance, const char **filters, const char *module, bool reset, const char ***errs)
> +{
> +       char *ftrace_filter_path;
> +       int ret = 0;
> +
> +       if (!filters)
> +               return 1;
> +
> +       ftrace_filter_path = tracefs_instance_get_file(instance, TRACE_FILTER);
> +       if (!ftrace_filter_path)
> +               return 1;
> +
> +       ret = controlled_write(ftrace_filter_path, filters, module, reset, errs);
> +       tracefs_put_tracing_file(ftrace_filter_path);
> +       return ret;
> +}
> --
> 2.7.4
>
diff mbox series

Patch

diff --git a/include/tracefs.h b/include/tracefs.h
index f3eec62..a2249d0 100644
--- a/include/tracefs.h
+++ b/include/tracefs.h
@@ -50,6 +50,7 @@  int tracefs_trace_on(struct tracefs_instance *instance);
 int tracefs_trace_off(struct tracefs_instance *instance);
 int tracefs_trace_on_fd(int fd);
 int tracefs_trace_off_fd(int fd);
+int tracefs_function_filter(struct tracefs_instance *instance, const char **filters, const char *module, bool reset, const char ***errs);
 
 /**
  * tracefs_trace_on_get_fd - Get a file descriptor of "tracing_on" in given instance
diff --git a/src/tracefs-tools.c b/src/tracefs-tools.c
index e2dfc7b..4e168df 100644
--- a/src/tracefs-tools.c
+++ b/src/tracefs-tools.c
@@ -18,6 +18,7 @@ 
 #include "tracefs-local.h"
 
 #define TRACE_CTRL	"tracing_on"
+#define TRACE_FILTER      "set_ftrace_filter"
 
 static const char * const options_map[] = {
 	"unknown",
@@ -387,3 +388,96 @@  void tracefs_option_clear(struct tracefs_options_mask *options, enum tracefs_opt
 	if (options && id > TRACEFS_OPTION_INVALID)
 		options->mask &= ~(1ULL << (id - 1));
 }
+
+static int controlled_write(const char *filter_path, const char **filters, const char *module, bool reset, const char ***errs)
+{
+	int flags = reset ? O_TRUNC : O_APPEND;
+	int write_size;
+	char *each_str;
+	int ret = 0;
+	int j = 0;
+	int size;
+	int slen;
+	int fd;
+	int i;
+
+	fd = open(filter_path, O_WRONLY | flags);
+	if (fd < 0)
+		return 1;
+
+	for (i = 0; filters[i]; i++) {
+		slen = strlen(filters[i]);
+		if (!slen)
+			continue;
+
+		if (module)
+			/* Adding 5 extra bytes for ":mod:"*/
+			slen += strlen(module) + 5;
+
+		/* Adding 2 extra byte for the space and '\0' at the end*/
+		slen += 2;
+		each_str = calloc(1, slen);
+		if (!each_str)
+			return 1;
+		if (module)
+			write_size = snprintf(each_str, slen, "%s:mod:%s ", filters[i], module);
+		else
+			write_size = snprintf(each_str, slen, "%s ", filters[i]);
+
+		size = write(fd, each_str, write_size);
+		/* compare written bytes*/
+		if (size < write_size) {
+			if (errs) {
+				errs[j++] = &filters[i];
+				ret -= 1;
+			}
+		}
+		free(each_str);
+	}
+	errs[j] = NULL;
+	close(fd);
+	return ret;
+}
+
+/**
+ * tracefs_function_filter - write to set_ftrace_filter file to trace particular functions
+ * @instance: ftrace instance, can be NULL for top tracing instance
+ * @filters: An array of function names ending with a NULL pointer
+ * @module: Module to be traced
+ * @reset: set to true to reset the file before applying the filter
+ * @errs: A pointer to array of constant strings that will be allocated
+ * on negative return of this function, pointing to the filters that failed.
+ * May be NULL, in which case this field will be ignored.
+ *
+ * The @filters is an array of strings, where each string will be used to set
+ * a function or functions to be traced.
+ *
+ * If @reset is true, then all functions in the filter are cleared before
+ * adding functions from @filters. Otherwise, the functions set by @filters
+ * will be appended to the filter file
+ *
+ * returns -x on filter errors (where x is number of failed filter srtings)
+ * and if @errs is not NULL will be an allocated string array pointing to the
+ * strings in @filters that failed and must be freed with free().
+ *
+ * returns 1 on general errors not realted to setting the filter.
+ * @errs is not set even if supplied.
+ *
+ * return 0 on success and @errs is not set.
+ */
+int tracefs_function_filter(struct tracefs_instance *instance, const char **filters, const char *module, bool reset, const char ***errs)
+{
+	char *ftrace_filter_path;
+	int ret = 0;
+
+	if (!filters)
+		return 1;
+
+	ftrace_filter_path = tracefs_instance_get_file(instance, TRACE_FILTER);
+	if (!ftrace_filter_path)
+		return 1;
+
+	ret = controlled_write(ftrace_filter_path, filters, module, reset, errs);
+	tracefs_put_tracing_file(ftrace_filter_path);
+	return ret;
+}