Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package libtracefs for openSUSE:Factory checked in at 2022-06-17 21:21:06 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/libtracefs (Old) and /work/SRC/openSUSE:Factory/.libtracefs.new.1548 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "libtracefs" Fri Jun 17 21:21:06 2022 rev:5 rq:983248 version:1.4.1 Changes: -------- --- /work/SRC/openSUSE:Factory/libtracefs/libtracefs.changes 2022-04-10 00:42:23.504788606 +0200 +++ /work/SRC/openSUSE:Factory/.libtracefs.new.1548/libtracefs.changes 2022-06-17 21:23:26.522803603 +0200 @@ -1,0 +2,11 @@ +Tue Jun 7 08:34:18 UTC 2022 - Daniel Wagner <daniel.wag...@suse.com> + +- Update to latest upstream version (1.4.1) + * Raw uprobe API: tracefs_uprobe_alloc(), tracefs_uretprobe_alloc + * Return the debugfs mount point from tracefs_debug_dir() + * sqlhst: Fix labels being ignored for synthetic event field + * sqlhst: Allow same event to be both the start and end event + * sqlhst: Use unique names for labels + * sqlhst: Report errors when executing commands + +------------------------------------------------------------------- Old: ---- libtracefs-1.3.1.tar.gz New: ---- libtracefs-1.4.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ libtracefs.spec ++++++ --- /var/tmp/diff_new_pack.otNxc0/_old 2022-06-17 21:23:26.870803791 +0200 +++ /var/tmp/diff_new_pack.otNxc0/_new 2022-06-17 21:23:26.874803794 +0200 @@ -18,7 +18,7 @@ Name: libtracefs %define lname libtracefs1 -Version: 1.3.1 +Version: 1.4.1 Release: 0 Summary: Linux kernel trace file system library License: LGPL-2.1-only ++++++ libtracefs-1.3.1.tar.gz -> libtracefs-1.4.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtracefs-1.3.1/Documentation/libtracefs-files.txt new/libtracefs-1.4.1/Documentation/libtracefs-files.txt --- old/libtracefs-1.3.1/Documentation/libtracefs-files.txt 2022-03-10 16:24:35.000000000 +0100 +++ new/libtracefs-1.4.1/Documentation/libtracefs-files.txt 2022-06-16 18:57:15.000000000 +0200 @@ -3,7 +3,7 @@ NAME ---- -tracefs_get_tracing_file, tracefs_put_tracing_file, tracefs_tracing_dir - +tracefs_get_tracing_file, tracefs_put_tracing_file, tracefs_tracing_dir, tracefs_debug_dir - Find locations of trace directory and files. SYNOPSIS @@ -15,6 +15,7 @@ char pass:[*]*tracefs_get_tracing_file*(const char pass:[*]_name_); void *tracefs_put_tracing_file*(char pass:[*]_name_); const char pass:[*]*tracefs_tracing_dir*(void); +const char pass:[*]*tracefs_debug_dir*(void); -- DESCRIPTION @@ -38,6 +39,11 @@ if it is not mounted. On any subsequent call the cached path is returned. The return string must _not_ be freed. +The *tracefs_debug_dir()* is similar to *tracefs_tracing_dir()* except +that it will return where the debugfs file system is mounted. If it +is not mounted it will try to mount it. The return string must _not_ +be freed. + RETURN VALUE ------------ The *tracefs_get_tracing_file()* function returns a string or NULL in case @@ -46,6 +52,9 @@ The *tracefs_tracing_dir()* function returns a constant string or NULL in case of an error. The returned string must _not_ be freed. +The *tracefs_debug_dir()* function returns a constant string or NULL +in case of an error. The returned string must _not_ be freed. + EXAMPLE ------- [source,c] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtracefs-1.3.1/Documentation/libtracefs-sql.txt new/libtracefs-1.4.1/Documentation/libtracefs-sql.txt --- old/libtracefs-1.3.1/Documentation/libtracefs-sql.txt 2022-03-10 16:24:35.000000000 +0100 +++ new/libtracefs-1.4.1/Documentation/libtracefs-sql.txt 2022-06-16 18:57:15.000000000 +0200 @@ -195,7 +195,7 @@ [source,c] -- - select CAST(common_pid AS comm, CAST(id AS syscall) FROM sys_enter + select CAST(common_pid AS comm), CAST(id AS syscall) FROM sys_enter -- Which produces: @@ -407,8 +407,13 @@ } } tracefs_synth_echo_cmd(&seq, synth); - if (execute) - tracefs_synth_create(synth); + if (execute) { + ret = tracefs_synth_create(synth); + if (ret < 0) { + fprintf(stderr, "%s\n", tracefs_error_last(NULL)); + exit(-1); + } + } } else { struct tracefs_instance *instance = NULL; struct tracefs_hist *hist; @@ -430,8 +435,13 @@ } } tracefs_hist_echo_cmd(&seq, instance, hist, 0); - if (execute) - tracefs_hist_start(instance, hist); + if (execute) { + ret = tracefs_hist_start(instance, hist); + if (ret < 0) { + fprintf(stderr, "%s\n", tracefs_error_last(instance)); + exit(-1); + } + } } tracefs_synth_free(synth); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtracefs-1.3.1/Documentation/libtracefs-traceon.txt new/libtracefs-1.4.1/Documentation/libtracefs-traceon.txt --- old/libtracefs-1.3.1/Documentation/libtracefs-traceon.txt 2022-03-10 16:24:35.000000000 +0100 +++ new/libtracefs-1.4.1/Documentation/libtracefs-traceon.txt 2022-06-16 18:57:15.000000000 +0200 @@ -62,7 +62,7 @@ ret = tracefs_trace_is_on(NULL); if (ret == 0) { /* Tracing is disabled in the top instance */ - } else if (ret == 1) {" + } else if (ret == 1) { /* Tracing is enabled in the top instance */ } else { /* Error getting tracing state of the top instance */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtracefs-1.3.1/Documentation/libtracefs-uprobes.txt new/libtracefs-1.4.1/Documentation/libtracefs-uprobes.txt --- old/libtracefs-1.3.1/Documentation/libtracefs-uprobes.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/libtracefs-1.4.1/Documentation/libtracefs-uprobes.txt 2022-06-16 18:57:15.000000000 +0200 @@ -0,0 +1,189 @@ +libtracefs(3) +============= + +NAME +---- +tracefs_uprobe_alloc,tracefs_uretprobe_alloc - Allocate new user (return) probe + +SYNOPSIS +-------- +[verse] +-- +*#include <tracefs.h>* + +struct tracefs_dynevent pass:[*] +*tracefs_uprobe_alloc*(const char pass:[*]_system_, const char pass:[*]_event_, + const char pass:[*]_file_, unsigned long long _offset_, const char pass:[*]_fetchargs_) +struct tracefs_dynevent pass:[*] +*tracefs_uretprobe_alloc*(const char pass:[*]_system_, const char pass:[*]_event_, + const char pass:[*]_file_, unsigned long long _offset_, const char pass:[*]_fetchargs_) +-- + +DESCRIPTION +----------- +*tracefs_uprobe_alloc*() allocates a new uprobe context. It will be in the _system_ group +(or uprobes if _system_ is NULL) and with _event_ name. The uprobe will be attached to _offset_ +within the _file_. The list of arguments, described in _fetchargs_, will be fetched with the uprobe. +The returned pointer to the user probe context must be freed with *tracefs_dynevent_free*(). +The ubrobe is not configured in the system, tracefs_dynevent_* set of APIs can be used to configure +it. + +The *tracefs_uretprobe_alloc*() behaves the same as *tracefs_uprobe_alloc*(), the only difference is +that it allocates context to user return probe (uretprobe). + +RETURN VALUE +------------ +The *tracefs_uprobe_alloc*() and *tracefs_uretprobe_alloc*() APIs return a pointer to an allocated +tracefs_dynevent structure, describing the user probe. This pointer must be freed with +*tracefs_dynevent_free*(3). Note, this only allocates a descriptor representing the uprobe. It does +not modify the running system. On error NULL is returned. + +EXAMPLE +------- +[source,c] +-- + +#include <stdlib.h> +#include <unistd.h> +#include <sys/wait.h> + +#include <tracefs.h> + +static int callback(struct tep_event *event, struct tep_record *record, + int cpu, void *data) +{ + struct trace_seq seq; + + trace_seq_init(&seq); + tep_print_event(event->tep, &seq, record, "%d-%s: %s", + TEP_PRINT_PID, TEP_PRINT_COMM, TEP_PRINT_NAME); + trace_seq_puts(&seq, "'\n"); + + trace_seq_terminate(&seq); + trace_seq_do_printf(&seq); + trace_seq_destroy(&seq); + + return 0; +} + +static pid_t run_exec(char **argv, char **env) +{ + pid_t pid; + + pid = fork(); + if (pid) + return pid; + + execve(argv[0], argv, env); + perror("exec"); + exit(-1); +} + +const char *myprobe = "my_urobes"; + +int main (int argc, char **argv, char **env) +{ + struct tracefs_dynevent *uprobe, *uretprobe; + struct tep_handle *tep; + struct tracefs_instance *instance; + const char *sysnames[] = { myprobe, NULL }; + long addr; + pid_t pid; + + if (argc < 3) { + printf("usage: %s file_offset command\n", argv[0]); + exit(-1); + } + addr = strtol(argv[1], NULL, 0); + + instance = tracefs_instance_create("exec_open"); + if (!instance) { + perror("creating instance"); + exit(-1); + } + + tracefs_dynevent_destroy_all(TRACEFS_DYNEVENT_UPROBE|TRACEFS_DYNEVENT_URETPROBE, true); + + uprobe = tracefs_uprobe_alloc(myprobe, "user_probe", argv[2], addr, NULL); + uretprobe = tracefs_uretprobe_alloc(myprobe, "user_retprobe", argv[2], addr, NULL); + if (!uprobe || !uretprobe) { + perror("allocating user probes"); + exit(-1); + } + + if (tracefs_dynevent_create(uprobe) || + tracefs_dynevent_create(uretprobe)) { + perror("creating user probes"); + exit(-1); + } + + tep = tracefs_local_events_system(NULL, sysnames); + if (!tep) { + perror("reading events"); + exit(-1); + } + + tracefs_event_enable(instance, myprobe, "user_probe"); + tracefs_event_enable(instance, myprobe, "user_retprobe"); + + pid = run_exec(&argv[2], env); + + /* Let the child start to run */ + sched_yield(); + + do { + tracefs_load_cmdlines(NULL, tep); + tracefs_iterate_raw_events(tep, instance, NULL, 0, callback, NULL); + } while (waitpid(pid, NULL, WNOHANG) != pid); + + /* disable and destroy the events */ + tracefs_dynevent_destroy(uprobe, true); + tracefs_dynevent_destroy(uretprobe, true); + tracefs_dynevent_free(uprobe); + tracefs_dynevent_free(uretprobe); + tracefs_instance_destroy(instance); + + return 0; +} +-- + +FILES +----- +[verse] +-- +*tracefs.h* + Header file to include in order to have access to the library APIs. +*-ltracefs* + Linker switch to add when building a program that uses the library. +-- + +SEE ALSO +-------- +*libtracefs*(3), +*libtraceevent*(3), +*trace-cmd*(1) + +AUTHOR +------ +[verse] +-- +*Steven Rostedt* <rost...@goodmis.org> +*Tzvetomir Stoyanov* <tz.stoya...@gmail.com> +-- + +REPORTING BUGS +-------------- +Report bugs to <linux-trace-de...@vger.kernel.org> + +LICENSE +------- +libtracefs is Free Software licensed under the GNU LGPL 2.1 + +RESOURCES +--------- +https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/ + +COPYING +------- +Copyright \(C) 2022 VMware, Inc. Free use of this software is granted under +the terms of the GNU Public License (GPL). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtracefs-1.3.1/Makefile new/libtracefs-1.4.1/Makefile --- old/libtracefs-1.3.1/Makefile 2022-03-10 16:24:35.000000000 +0100 +++ new/libtracefs-1.4.1/Makefile 2022-06-16 18:57:15.000000000 +0200 @@ -1,7 +1,7 @@ # SPDX-License-Identifier: LGPL-2.1 # libtracefs version TFS_VERSION = 1 -TFS_PATCHLEVEL = 3 +TFS_PATCHLEVEL = 4 TFS_EXTRAVERSION = 1 TRACEFS_VERSION = $(TFS_VERSION).$(TFS_PATCHLEVEL).$(TFS_EXTRAVERSION) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtracefs-1.3.1/include/tracefs-local.h new/libtracefs-1.4.1/include/tracefs-local.h --- old/libtracefs-1.3.1/include/tracefs-local.h 2022-03-10 16:24:35.000000000 +0100 +++ new/libtracefs-1.4.1/include/tracefs-local.h 2022-06-16 18:57:15.000000000 +0200 @@ -7,6 +7,7 @@ #define _TRACE_FS_LOCAL_H #define __hidden __attribute__((visibility ("hidden"))) +#define __internal __attribute__((visibility ("internal"))) #define __weak __attribute__((weak)) #define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) @@ -15,6 +16,8 @@ #define BUILD_BUG_ON(cond) \ do { if (!(1/!(cond))) { } } while (0) +#define HASH_BITS 10 + struct tracefs_options_mask { unsigned long long mask; }; @@ -50,7 +53,7 @@ int str_read_file(const char *file, char **buffer, bool warn); char *trace_append_file(const char *dir, const char *name); -char *trace_find_tracing_dir(void); +char *trace_find_tracing_dir(bool debugfs); #ifndef ACCESSPERMS #define ACCESSPERMS (S_IRWXU|S_IRWXG|S_IRWXO) /* 0777 */ @@ -120,4 +123,6 @@ struct tep_event *get_tep_event(struct tep_handle *tep, const char *system, const char *name); +unsigned int quick_hash(const char *str); + #endif /* _TRACE_FS_LOCAL_H */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtracefs-1.3.1/include/tracefs.h new/libtracefs-1.4.1/include/tracefs.h --- old/libtracefs-1.3.1/include/tracefs.h 2022-03-10 16:24:35.000000000 +0100 +++ new/libtracefs-1.4.1/include/tracefs.h 2022-06-16 18:57:15.000000000 +0200 @@ -15,6 +15,7 @@ /* the returned string must *not* be freed */ const char *tracefs_tracing_dir(void); +const char *tracefs_debug_dir(void); /* ftrace instances */ struct tracefs_instance; @@ -280,6 +281,12 @@ struct tracefs_dynevent * tracefs_eprobe_alloc(const char *system, const char *event, const char *target_system, const char *target_event, const char *fetchargs); +struct tracefs_dynevent * +tracefs_uprobe_alloc(const char *system, const char *event, + const char *file, unsigned long long offset, const char *fetchargs); +struct tracefs_dynevent * +tracefs_uretprobe_alloc(const char *system, const char *event, + const char *file, unsigned long long offset, const char *fetchargs); struct tracefs_dynevent * tracefs_kprobe_alloc(const char *system, const char *event, const char *addr, const char *format); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtracefs-1.3.1/samples/Makefile new/libtracefs-1.4.1/samples/Makefile --- old/libtracefs-1.3.1/samples/Makefile 2022-03-10 16:24:35.000000000 +0100 +++ new/libtracefs-1.4.1/samples/Makefile 2022-06-16 18:57:15.000000000 +0200 @@ -11,6 +11,7 @@ EXAMPLES += dynevents EXAMPLES += kprobes EXAMPLES += eprobes +EXAMPLES += uprobes EXAMPLES += synth EXAMPLES += error EXAMPLES += filter @@ -45,7 +46,7 @@ $(TARGETS): $(sdir) # sqlhist is unique and stands on its own -$(sdir)/sqlhist: $(bdir)/sqlhist.c +$(sdir)/sqlhist: $(bdir)/sqlhist.c $(LIBTRACEFS_STATIC) $(call do_sample_build,$@,$<) $(sdir)/%: $(bdir)/%.o @@ -66,3 +67,5 @@ clean: $(Q)$(call do_clean,$(sdir)/* $(bdir)/sqlhist.c $(bdir)/sqlhist.o) + +.PHONY: sqlhist diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtracefs-1.3.1/src/Makefile new/libtracefs-1.4.1/src/Makefile --- old/libtracefs-1.3.1/src/Makefile 2022-03-10 16:24:35.000000000 +0100 +++ new/libtracefs-1.4.1/src/Makefile 2022-06-16 18:57:15.000000000 +0200 @@ -13,6 +13,7 @@ OBJS += tracefs-filter.o OBJS += tracefs-dynevents.o OBJS += tracefs-eprobes.o +OBJS += tracefs-uprobes.o # Order matters for the the three below OBJS += sqlhist-lex.o diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtracefs-1.3.1/src/sqlhist-parse.h new/libtracefs-1.4.1/src/sqlhist-parse.h --- old/libtracefs-1.3.1/src/sqlhist-parse.h 2022-03-10 16:24:35.000000000 +0100 +++ new/libtracefs-1.4.1/src/sqlhist-parse.h 2022-06-16 18:57:15.000000000 +0200 @@ -4,8 +4,9 @@ #include <stdarg.h> #include <tracefs.h> +#include <tracefs-local.h> + struct str_hash; -#define HASH_BITS 10 struct sql_table; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtracefs-1.3.1/src/tracefs-hist.c new/libtracefs-1.4.1/src/tracefs-hist.c --- old/libtracefs-1.3.1/src/tracefs-hist.c 2022-03-10 16:24:35.000000000 +0100 +++ new/libtracefs-1.4.1/src/tracefs-hist.c 2022-06-16 18:57:15.000000000 +0200 @@ -654,6 +654,12 @@ char *save; }; +struct name_hash { + struct name_hash *next; + char *name; + char *hash; +}; + /* * @name: name of the synthetic event * @start_system: system of the starting event @@ -684,6 +690,7 @@ struct action *actions; struct action **next_action; struct tracefs_dynevent *dyn_event; + struct name_hash *name_hash[1 << HASH_BITS]; char *start_hist; char *end_hist; char *name; @@ -724,6 +731,21 @@ free(action); } +static void free_name_hash(struct name_hash **hash) +{ + struct name_hash *item; + int i; + + for (i = 0; i < 1 << HASH_BITS; i++) { + while ((item = hash[i])) { + hash[i] = item->next; + free(item->name); + free(item->hash); + free(item); + } + } +} + /** * tracefs_synth_free - free the resources alloced to a synth * @synth: The tracefs_synth descriptor @@ -750,6 +772,7 @@ tracefs_list_free(synth->end_keys); tracefs_list_free(synth->start_vars); tracefs_list_free(synth->end_vars); + free_name_hash(synth->name_hash); free(synth->start_filter); free(synth->end_filter); free(synth->start_type); @@ -1096,7 +1119,7 @@ return -1; synth->synthetic_fields = list; - ret = asprintf(&str, "$%s", name); + ret = asprintf(&str, "$%s", var ? : name); if (ret < 0) { trace_list_pop(synth->synthetic_fields); return -1; @@ -1196,7 +1219,7 @@ return((unsigned)(seed/65536) % 32768); } -static char *new_arg(struct tracefs_synth *synth) +static char *new_name(struct tracefs_synth *synth, const char *name) { int cnt = synth->arg_cnt + 1; char *arg; @@ -1205,9 +1228,9 @@ /* Create a unique argument name */ if (!synth->arg_name[0]) { /* make_rand() returns at most 32768 (total 13 bytes in use) */ - sprintf(synth->arg_name, "__arg_%u_", make_rand()); + sprintf(synth->arg_name, "%u", make_rand()); } - ret = asprintf(&arg, "%s%d", synth->arg_name, cnt); + ret = asprintf(&arg, "__%s_%s_%d", name, synth->arg_name, cnt); if (ret < 0) return NULL; @@ -1215,6 +1238,55 @@ return arg; } +static struct name_hash *find_name(struct tracefs_synth *synth, const char *name) +{ + unsigned int key = quick_hash(name); + struct name_hash *hash = synth->name_hash[key]; + + for (; hash; hash = hash->next) { + if (!strcmp(hash->name, name)) + return hash; + } + return NULL; +} + +static const char *hash_name(struct tracefs_synth *synth, const char *name) +{ + struct name_hash *hash; + int key; + + hash = find_name(synth, name); + if (hash) + return hash->hash; + + hash = malloc(sizeof(*hash)); + if (!hash) + return name; + + hash->hash = new_name(synth, name); + if (!hash->hash) { + free(hash); + return name; + } + + key = quick_hash(name); + hash->next = synth->name_hash[key]; + synth->name_hash[key] = hash; + + hash->name = strdup(name); + if (!hash->name) { + free(hash->hash); + free(hash); + return name; + } + return hash->hash; +} + +static char *new_arg(struct tracefs_synth *synth) +{ + return new_name(synth, "arg"); +} + /** * tracefs_synth_add_compare_field - add a comparison between start and end * @synth: The tracefs_synth descriptor @@ -1245,6 +1317,7 @@ const char *name) { const struct tep_format_field *start_field; + const char *hname; char *start_arg; char *compare; int ret; @@ -1296,11 +1369,12 @@ if (ret < 0) return -1; - ret = add_var(&synth->end_vars, name, compare, false); + hname = hash_name(synth, name); + ret = add_var(&synth->end_vars, hname, compare, false); if (ret < 0) goto out_free; - ret = add_synth_fields(synth, start_field, name, NULL); + ret = add_synth_fields(synth, start_field, name, hname); if (ret < 0) goto out_free; @@ -1316,6 +1390,7 @@ enum tracefs_hist_key_type type) { const struct tep_format_field *field; + const char *var; char *start_arg; char **tmp; int *types; @@ -1330,6 +1405,8 @@ if (!name) name = start_field; + var = hash_name(synth, name); + if (!trace_verify_event_field(synth->start_event, start_field, &field)) return -1; @@ -1341,11 +1418,11 @@ if (ret) goto out_free; - ret = add_var(&synth->end_vars, name, start_arg, true); + ret = add_var(&synth->end_vars, var, start_arg, true); if (ret) goto out_free; - ret = add_synth_fields(synth, field, name, NULL); + ret = add_synth_fields(synth, field, name, var); if (ret) goto out_free; @@ -1417,6 +1494,7 @@ const char *name) { const struct tep_format_field *field; + const char *hname = NULL; char *tmp_var = NULL; int ret; @@ -1425,17 +1503,24 @@ return -1; } + if (name) { + if (strncmp(name, "__arg", 5) != 0) + hname = hash_name(synth, name); + else + hname = name; + } + if (!name) tmp_var = new_arg(synth); if (!trace_verify_event_field(synth->end_event, end_field, &field)) return -1; - ret = add_var(&synth->end_vars, name ? : tmp_var, end_field, false); + ret = add_var(&synth->end_vars, name ? hname : tmp_var, end_field, false); if (ret) goto out; - ret = add_synth_fields(synth, field, name, tmp_var); + ret = add_synth_fields(synth, field, name, hname ? : tmp_var); free(tmp_var); out: return ret; @@ -2194,7 +2279,7 @@ new_event = true; } - path = trace_find_tracing_dir(); + path = trace_find_tracing_dir(false); if (!path) goto out_free; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtracefs-1.3.1/src/tracefs-instance.c new/libtracefs-1.4.1/src/tracefs-instance.c --- old/libtracefs-1.3.1/src/tracefs-instance.c 2022-03-10 16:24:35.000000000 +0100 +++ new/libtracefs-1.4.1/src/tracefs-instance.c 2022-06-16 18:57:15.000000000 +0200 @@ -334,7 +334,7 @@ int ret; if (!instance) /* Top instance of default system trace directory */ - return trace_find_tracing_dir(); + return trace_find_tracing_dir(false); if (!instance->name) return strdup(instance->trace_dir); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtracefs-1.3.1/src/tracefs-sqlhist.c new/libtracefs-1.4.1/src/tracefs-sqlhist.c --- old/libtracefs-1.3.1/src/tracefs-sqlhist.c 2022-03-10 16:24:35.000000000 +0100 +++ new/libtracefs-1.4.1/src/tracefs-sqlhist.c 2022-06-16 18:57:15.000000000 +0200 @@ -30,6 +30,12 @@ ALIAS_FIELD, }; +enum field_type { + FIELD_NONE, + FIELD_FROM, + FIELD_TO, +}; + #define for_each_field(expr, field, table) \ for (expr = (table)->fields; expr; expr = (field)->next) @@ -42,6 +48,7 @@ const char *label; const char *field; const char *type; + enum field_type ftype; }; struct filter { @@ -102,7 +109,7 @@ struct expr **next_selection; }; -__hidden int my_yyinput(void *extra, char *buf, int max) +__internal int my_yyinput(void *extra, char *buf, int max) { struct sqlhist_bison *sb = extra; @@ -120,8 +127,8 @@ return max; } -__hidden void sql_parse_error(struct sqlhist_bison *sb, const char *text, - const char *fmt, va_list ap) +__internal void sql_parse_error(struct sqlhist_bison *sb, const char *text, + const char *fmt, va_list ap) { const char *buffer = sb->buffer; struct trace_seq s; @@ -167,7 +174,7 @@ va_end(ap); } -static inline unsigned int quick_hash(const char *str) +__hidden unsigned int quick_hash(const char *str) { unsigned int val = 0; int len = strlen(str); @@ -222,7 +229,7 @@ return &hash->str; } -__hidden char *store_str(struct sqlhist_bison *sb, const char *str) +__internal char *store_str(struct sqlhist_bison *sb, const char *str) { char **pstr = add_hash(sb, str); @@ -235,8 +242,8 @@ return *pstr; } -__hidden void *add_cast(struct sqlhist_bison *sb, - void *data, const char *type) +__internal void *add_cast(struct sqlhist_bison *sb, + void *data, const char *type) { struct expr *expr = data; struct field *field = &expr->field; @@ -245,14 +252,15 @@ return expr; } -__hidden int add_selection(struct sqlhist_bison *sb, void *select, - const char *name) +__internal int add_selection(struct sqlhist_bison *sb, void *select, + const char *name) { struct sql_table *table = sb->table; struct expr *expr = select; switch (expr->type) { case EXPR_FIELD: + expr->field.label = name; break; case EXPR_COMPARE: expr->compare.name = name; @@ -285,6 +293,8 @@ if (!strcmp(field->raw, raw)) { if (label && !field->label) field->label = label; + if (label && strcmp(label, field->label) != 0) + continue; return expr; } @@ -358,8 +368,8 @@ #define create_number(var, expr) \ __create_expr(var, long, NUMBER, expr) -__hidden void *add_field(struct sqlhist_bison *sb, - const char *field_name, const char *label) +__internal void *add_field(struct sqlhist_bison *sb, + const char *field_name, const char *label) { struct sql_table *table = sb->table; struct expr *expr; @@ -380,8 +390,8 @@ return expr; } -__hidden void *add_filter(struct sqlhist_bison *sb, - void *A, void *B, enum filter_type op) +__internal void *add_filter(struct sqlhist_bison *sb, + void *A, void *B, enum filter_type op) { struct filter *filter; struct expr *expr; @@ -396,7 +406,7 @@ return expr; } -__hidden int add_match(struct sqlhist_bison *sb, void *A, void *B) +__internal int add_match(struct sqlhist_bison *sb, void *A, void *B) { struct sql_table *table = sb->table; struct match *match; @@ -413,8 +423,9 @@ return 0; } -__hidden void *add_compare(struct sqlhist_bison *sb, - void *A, void *B, enum compare_type type) + +__internal void *add_compare(struct sqlhist_bison *sb, + void *A, void *B, enum compare_type type) { struct compare *compare; struct expr *expr; @@ -429,7 +440,7 @@ return expr; } -__hidden int add_where(struct sqlhist_bison *sb, void *item) +__internal int add_where(struct sqlhist_bison *sb, void *item) { struct expr *expr = item; struct sql_table *table = sb->table; @@ -446,7 +457,7 @@ return 0; } -__hidden int add_from(struct sqlhist_bison *sb, void *item) +__internal int add_from(struct sqlhist_bison *sb, void *item) { struct expr *expr = item; @@ -458,7 +469,7 @@ return 0; } -__hidden int add_to(struct sqlhist_bison *sb, void *item) +__internal int add_to(struct sqlhist_bison *sb, void *item) { struct expr *expr = item; @@ -470,7 +481,7 @@ return 0; } -__hidden void *add_string(struct sqlhist_bison *sb, const char *str) +__internal void *add_string(struct sqlhist_bison *sb, const char *str) { struct expr *expr; const char **str_p; @@ -480,7 +491,7 @@ return expr; } -__hidden void *add_number(struct sqlhist_bison *sb, long val) +__internal void *add_number(struct sqlhist_bison *sb, long val) { struct expr *expr; long *num; @@ -490,7 +501,7 @@ return expr; } -__hidden int table_start(struct sqlhist_bison *sb) +__internal int table_start(struct sqlhist_bison *sb) { struct sql_table *table; @@ -580,6 +591,7 @@ { struct sqlhist_bison *sb = table->sb; struct field *event_field = &expr->field; + enum field_type ftype = FIELD_NONE; struct tep_event *event; struct field *field; const char *label; @@ -589,6 +601,11 @@ const char *p; int label_len = 0, event_len, system_len; + if (expr == table->to) + ftype = FIELD_TO; + else if (expr == table->from) + ftype = FIELD_FROM; + p = strchr(raw, '.'); if (p) { char *str; @@ -670,6 +687,7 @@ field->event_name = event_name; field->event = event; field->field = raw + len + 1; + field->ftype = ftype; if (!strcmp(field->field, "TIMESTAMP")) field->field = store_str(sb, TRACEFS_TIMESTAMP); @@ -887,20 +905,21 @@ } static int do_verify_filter(struct sqlhist_bison *sb, struct filter *filter, - const char **system, const char **event) + const char **system, const char **event, + enum field_type *ftype) { int ret; if (filter->type == FILTER_OR || filter->type == FILTER_AND) { - ret = do_verify_filter(sb, &filter->lval->filter, system, event); + ret = do_verify_filter(sb, &filter->lval->filter, system, event, ftype); if (ret) return ret; - return do_verify_filter(sb, &filter->rval->filter, system, event); + return do_verify_filter(sb, &filter->rval->filter, system, event, ftype); } if (filter->type == FILTER_GROUP || filter->type == FILTER_NOT_GROUP) { - return do_verify_filter(sb, &filter->lval->filter, system, event); + return do_verify_filter(sb, &filter->lval->filter, system, event, ftype); } /* @@ -910,6 +929,7 @@ if (!*system && !*event) { *system = filter->lval->field.system; *event = filter->lval->field.event_name; + *ftype = filter->lval->field.ftype; return 0; } @@ -921,7 +941,8 @@ } static int verify_filter(struct sqlhist_bison *sb, struct filter *filter, - const char **system, const char **event) + const char **system, const char **event, + enum field_type *ftype) { int ret; @@ -932,17 +953,17 @@ case FILTER_NOT_GROUP: break; default: - return do_verify_filter(sb, filter, system, event); + return do_verify_filter(sb, filter, system, event, ftype); } - ret = do_verify_filter(sb, &filter->lval->filter, system, event); + ret = do_verify_filter(sb, &filter->lval->filter, system, event, ftype); if (ret) return ret; switch (filter->type) { case FILTER_OR: case FILTER_AND: - return do_verify_filter(sb, &filter->rval->filter, system, event); + return do_verify_filter(sb, &filter->rval->filter, system, event, ftype); default: return 0; } @@ -1449,7 +1470,8 @@ if (expr->type == EXPR_FIELD) { ret = -1; field = &expr->field; - if (field->system == start_system && + if (field->ftype != FIELD_TO && + field->system == start_system && field->event_name == start_event) { int type; type = verify_field_type(tep, table->sb, expr); @@ -1498,16 +1520,18 @@ for (expr = table->where; expr; expr = expr->next) { const char *filter_system = NULL; const char *filter_event = NULL; + enum field_type ftype = FIELD_NONE; bool *started; bool start; ret = verify_filter(table->sb, &expr->filter, &filter_system, - &filter_event); + &filter_event, &ftype); if (ret < 0) goto free; start = filter_system == start_system && - filter_event == start_event; + filter_event == start_event && + ftype != FIELD_TO; if (start) started = &started_start; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtracefs-1.3.1/src/tracefs-uprobes.c new/libtracefs-1.4.1/src/tracefs-uprobes.c --- old/libtracefs-1.3.1/src/tracefs-uprobes.c 1970-01-01 01:00:00.000000000 +0100 +++ new/libtracefs-1.4.1/src/tracefs-uprobes.c 2022-06-16 18:57:15.000000000 +0200 @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * Copyright (C) 2022, VMware, Tzvetomir Stoyanov <tz.stoya...@gmail.com> + * + */ +#include <stdlib.h> +#include <errno.h> + +#include "tracefs.h" +#include "tracefs-local.h" + +#define UPROBE_DEFAULT_GROUP "uprobes" + +static struct tracefs_dynevent * +uprobe_alloc(enum tracefs_dynevent_type type, const char *system, const char *event, + const char *file, unsigned long long offset, const char *fetchargs) +{ + struct tracefs_dynevent *kp; + char *target; + + if (!event || !file) { + errno = EINVAL; + return NULL; + } + + if (!system) + system = UPROBE_DEFAULT_GROUP; + + if (asprintf(&target, "%s:0x%0*llx", file, (int)(sizeof(void *) * 2), offset) < 0) + return NULL; + + kp = dynevent_alloc(type, system, event, target, fetchargs); + free(target); + + return kp; +} + +/** + * tracefs_uprobe_alloc - Allocate new user probe (uprobe) + * @system: The system name (NULL for the default uprobes) + * @event: The name of the event to create + * @file: The full path to the binary file, where the uprobe will be set + * @offset: Offset within the @file + * @fetchargs: String with arguments, that will be fetched with the uprobe + * + * Allocate new uprobe context that will be in the @system group + * (or uprobes if @system is NULL) and with @event name. The new uprobe will be + * attached to @offset within the @file. The arguments described in @fetchargs + * will fetched with the uprobe. See linux/Documentation/trace/uprobetracer.rst + * for more details. + * + * The uprobe is not created in the system. + * + * Return a pointer to a uprobe context on success, or NULL on error. + * The returned pointer must be freed with tracefs_dynevent_free() + * + */ +struct tracefs_dynevent * +tracefs_uprobe_alloc(const char *system, const char *event, + const char *file, unsigned long long offset, const char *fetchargs) +{ + return uprobe_alloc(TRACEFS_DYNEVENT_UPROBE, system, event, file, offset, fetchargs); +} + +/** + * tracefs_uretprobe_alloc - Allocate new user return probe (uretprobe) + * @system: The system name (NULL for the default uprobes) + * @event: The name of the event to create + * @file: The full path to the binary file, where the uretprobe will be set + * @offset: Offset within the @file + * @fetchargs: String with arguments, that will be fetched with the uretprobe + * + * Allocate mew uretprobe context that will be in the @system group + * (or uprobes if @system is NULL) and with @event name. The new uretprobe will + * be attached to @offset within the @file. The arguments described in @fetchargs + * will fetched with the uprobe. See linux/Documentation/trace/uprobetracer.rst + * for more details. + * + * The uretprobe is not created in the system. + * + * Return a pointer to a uretprobe context on success, or NULL on error. + * The returned pointer must be freed with tracefs_dynevent_free() + * + */ +struct tracefs_dynevent * +tracefs_uretprobe_alloc(const char *system, const char *event, + const char *file, unsigned long long offset, const char *fetchargs) +{ + return uprobe_alloc(TRACEFS_DYNEVENT_URETPROBE, system, event, file, offset, fetchargs); +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtracefs-1.3.1/src/tracefs-utils.c new/libtracefs-1.4.1/src/tracefs-utils.c --- old/libtracefs-1.3.1/src/tracefs-utils.c 2022-03-10 16:24:35.000000000 +0100 +++ new/libtracefs-1.4.1/src/tracefs-utils.c 2022-06-16 18:57:15.000000000 +0200 @@ -86,11 +86,12 @@ /** * trace_find_tracing_dir - Find tracing directory + * @debugfs: Boolean to just return the debugfs directory * * Returns string containing the full path to the system's tracing directory. * The string must be freed by free() */ -__hidden char *trace_find_tracing_dir(void) +__hidden char *trace_find_tracing_dir(bool debugfs) { char *debug_str = NULL; char fspath[PATH_MAX+1]; @@ -109,9 +110,11 @@ STR(PATH_MAX) "s %99s %*s %*d %*d\n", fspath, type) == 2) { - if (strcmp(type, "tracefs") == 0) + if (!debugfs && strcmp(type, "tracefs") == 0) break; if (!debug_str && strcmp(type, "debugfs") == 0) { + if (debugfs) + break; debug_str = strdup(fspath); if (!debug_str) { fclose(fp); @@ -121,7 +124,13 @@ } fclose(fp); - if (strcmp(type, "tracefs") != 0) { + if (debugfs) { + if (strcmp(type, "debugfs") != 0) { + if (mount_debugfs() < 0) + return NULL; + strcpy(fspath, DEBUGFS_PATH); + } + } else if (strcmp(type, "tracefs") != 0) { if (mount_tracefs() < 0) { if (debug_str) { strncpy(fspath, debug_str, PATH_MAX); @@ -168,11 +177,29 @@ if (tracing_dir) return tracing_dir; - tracing_dir = trace_find_tracing_dir(); + tracing_dir = trace_find_tracing_dir(false); return tracing_dir; } /** + * tracefs_debug_dir - Get debugfs directory path + * + * Returns string containing the full path to the system's debugfs directory. + * + * The returned string must *not* be freed. + */ +const char *tracefs_debug_dir(void) +{ + static const char *debug_dir; + + if (debug_dir) + return debug_dir; + + debug_dir = trace_find_tracing_dir(true); + return debug_dir; +} + +/** * tracefs_get_tracing_file - Get tracing file * @name: tracing file name * diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtracefs-1.3.1/utest/tracefs-utest.c new/libtracefs-1.4.1/utest/tracefs-utest.c --- old/libtracefs-1.3.1/utest/tracefs-utest.c 2022-03-10 16:24:35.000000000 +0100 +++ new/libtracefs-1.4.1/utest/tracefs-utest.c 2022-06-16 18:57:15.000000000 +0200 @@ -873,6 +873,79 @@ test_eprobes_instance(test_instance); } +#define FOFFSET 1000ll +static void test_uprobes_instance(struct tracefs_instance *instance) +{ + struct probe_test utests[] = { + { TRACEFS_DYNEVENT_UPROBE, "p", "utest", "utest_u", NULL, "arg1=$stack2" }, + { TRACEFS_DYNEVENT_URETPROBE, "r", "utest", "utest_r", NULL, "arg1=$retval" }, + }; + int count = sizeof(utests) / sizeof((utests)[0]); + struct tracefs_dynevent **duprobes; + struct tracefs_dynevent **duvents; + char self[PATH_MAX] = { 0 }; + struct tep_handle *tep; + char *target = NULL; + int i; + + tep = tep_alloc(); + CU_TEST(tep != NULL); + + duprobes = calloc(count + 1, sizeof(*duvents)); + CU_TEST(duprobes != NULL); + CU_TEST(readlink("/proc/self/exe", self, sizeof(self)) > 0); + CU_TEST(asprintf(&target, "%s:0x%0*llx", self, (int)(sizeof(void *) * 2), FOFFSET) > 0); + + for (i = 0; i < count; i++) + utests[i].address = target; + + /* Invalid parameters */ + CU_TEST(tracefs_uprobe_alloc(NULL, NULL, self, 0, NULL) == NULL); + CU_TEST(tracefs_uprobe_alloc(NULL, "test", NULL, 0, NULL) == NULL); + CU_TEST(tracefs_uretprobe_alloc(NULL, NULL, self, 0, NULL) == NULL); + CU_TEST(tracefs_uretprobe_alloc(NULL, "test", NULL, 0, NULL) == NULL); + + for (i = 0; i < count; i++) { + if (utests[i].type == TRACEFS_DYNEVENT_UPROBE) + duprobes[i] = tracefs_uprobe_alloc(utests[i].system, utests[i].event, + self, FOFFSET, utests[i].format); + else + duprobes[i] = tracefs_uretprobe_alloc(utests[i].system, utests[i].event, + self, FOFFSET, utests[i].format); + CU_TEST(duprobes[i] != NULL); + } + duprobes[i] = NULL; + + get_dynevents_check(TRACEFS_DYNEVENT_UPROBE | TRACEFS_DYNEVENT_URETPROBE, 0); + CU_TEST(check_probes(utests, count, duprobes, false, instance, tep)); + + for (i = 0; i < count; i++) { + CU_TEST(tracefs_dynevent_create(duprobes[i]) == 0); + } + + duvents = get_dynevents_check(TRACEFS_DYNEVENT_UPROBE | TRACEFS_DYNEVENT_URETPROBE, count); + CU_TEST(check_probes(utests, count, duvents, true, instance, tep)); + tracefs_dynevent_list_free(duvents); + + for (i = 0; i < count; i++) { + CU_TEST(tracefs_dynevent_destroy(duprobes[i], false) == 0); + } + get_dynevents_check(TRACEFS_DYNEVENT_UPROBE | TRACEFS_DYNEVENT_URETPROBE, 0); + CU_TEST(check_probes(utests, count, duprobes, false, instance, tep)); + + for (i = 0; i < count; i++) + tracefs_dynevent_free(duprobes[i]); + + free(duprobes); + free(target); + tep_free(tep); +} + +static void test_uprobes(void) +{ + test_uprobes_instance(test_instance); +} + static void test_instance_file(void) { struct tracefs_instance *instance = NULL; @@ -1708,4 +1781,5 @@ CU_add_test(suite, "kprobes", test_kprobes); CU_add_test(suite, "synthetic events", test_synthetic); CU_add_test(suite, "eprobes", test_eprobes); + CU_add_test(suite, "uprobes", test_uprobes); }