The reference counting was handled internal to trace-cmd. This can be a problem for plugins that may want to reference record, which is in particular required for python to work properly. As such, implement reference counting within libtraceevent. To remain backward compatible with older trace-cmd versions, this patchset redefines the priv pointer data be allocated by libtraceevent as part of the record itself. This allows backward compatibility as long as the user was not compiled with DEBUG_RECORD enabled. There might be alternatives to this, e.g.: * Set a high bit in the ref_count * Drop backward compatibility The alloc_addr is left in the structure, is set appropriately and may be overwritten by the API user for its own use. --- include/traceevent/event-parse.h | 37 +++++++++++++++++---- include/traceevent/event-utils.h | 3 -- src/event-parse.c | 55 ++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 10 deletions(-) diff --git a/include/traceevent/event-parse.h b/include/traceevent/event-parse.h index 0b911e1..c161754 100644 --- a/include/traceevent/event-parse.h +++ b/include/traceevent/event-parse.h @@ -22,6 +22,14 @@ #define DEBUG_RECORD 0 #endif +#ifndef __deprecated +#define __deprecated(msg) __attribute__((deprecated("msg"))) +#endif + +#ifndef __te_private +#define __te_private(msg) __attribute__((deprecated("msg"))) +#endif + struct tep_record { unsigned long long ts; unsigned long long offset; @@ -30,16 +38,32 @@ struct tep_record { int size; /* size of data */ void *data; int cpu; - int ref_count; - int locked; /* Do not free, even if ref_count is zero */ + + int ref_count __te_private(use ref/unref instead); + int locked __deprecated(do not use); void *priv; + #if DEBUG_RECORD - struct tep_record *prev; - struct tep_record *next; - long alloc_addr; + unsigned long alloc_addr; /* Location ref'ing the last time */ +#endif + +#ifdef TE_PRIVATE + /* These private fields are only valid if priv is pointing to + * _priv_alloc. If that is not the case, the API user has not yet + * been ported to the new API. + * NOTE: Adding new fields does not break ABI/API. + */ + void (*_finalize) (struct tep_record *record); + void *_priv_alloc[0]; #endif }; + +struct tep_record *tep_record_alloc(int priv_size, + void (*finalize) (struct tep_record *record)); +void tep_record_ref(struct tep_record *record); +void tep_record_unref(struct tep_record *record); + /* ----------------------- tep ----------------------- */ struct tep_handle; @@ -774,8 +798,7 @@ enum tep_loglevel { }; void tep_set_loglevel(enum tep_loglevel level); -/* DEPRECATED */ void tep_print_field(struct trace_seq *s, void *data, - struct tep_format_field *field); + struct tep_format_field *field) __deprecated(Use tep_print_field_content instead); #endif /* _PARSE_EVENTS_H */ diff --git a/include/traceevent/event-utils.h b/include/traceevent/event-utils.h index 44f7968..14edbf8 100644 --- a/include/traceevent/event-utils.h +++ b/include/traceevent/event-utils.h @@ -23,9 +23,6 @@ int tep_vprint(const char *name, enum tep_loglevel level, int __tep_vprint(const char *name, enum tep_loglevel level, bool print_err, const char *fmt, va_list ap); - -#define __deprecated(msg) __attribute__((deprecated("msg"))) - /* For backward compatibilty, do not use */ int tep_vwarning(const char *name, const char *fmt, va_list ap) __deprecated(Use tep_vprint instead); void pr_stat(const char *fmt, ...) __deprecated(Use tep_info instead); diff --git a/src/event-parse.c b/src/event-parse.c index 980e980..7e81a32 100644 --- a/src/event-parse.c +++ b/src/event-parse.c @@ -14,6 +14,7 @@ #include <stdlib.h> #include <string.h> #include <stdarg.h> +#include <stddef.h> #include <ctype.h> #include <errno.h> #include <stdint.h> @@ -22,6 +23,10 @@ #include <linux/time64.h> #include <netinet/in.h> + +#define __te_private(msg) +#define TE_PRIVATE 1 + #include "event-parse.h" #include "event-parse-local.h" @@ -8113,6 +8118,56 @@ int tep_get_ref(struct tep_handle *tep) return 0; } +struct tep_record *tep_record_alloc(int priv_size, + void (*finalize) (struct tep_record *record)) +{ + struct tep_record *res; + + res = malloc(sizeof(*res) + priv_size); + if (!res) + return NULL; + + memset(res, 0, sizeof(*res) + priv_size); + res->ref_count = 1; + res->priv = res->_priv_alloc; + res->_finalize = finalize; + + return res; +} + +void tep_record_ref(struct tep_record *record) +{ + record->ref_count++; + +#if DEBUG_RECORD + record->alloc_addr = (unsigned long)__builtin_return_address(0); +#endif +} + +void tep_record_unref(struct tep_record *record) +{ + if (record->ref_count <= 0) { + tep_warning("Invalid refcount on record %p", record); + return; + } + + record->ref_count--; + + if (record->ref_count) + return; + + if (record->priv != record->_priv_alloc) { + tep_warning("Leaking record %p, update user to new allocation API.", + record); + return; + } + + if (record->_finalize) + record->_finalize(record); + + free(record); +} + __hidden void free_tep_format_field(struct tep_format_field *field) { free(field->type); -- 2.37.3