Message ID | 20231005180228.32449777@gandalf.local.home (mailing list archive) |
---|---|
State | Accepted |
Headers | show |
Series | [v2] libtraceeval: Use trick to force static array usage where needed | expand |
On Thu, Oct 05, 2023 at 06:02:28PM -0400, Steven Rostedt wrote: > From: "Steven Rostedt (Google)" <rostedt@goodmis.org> > > Some functions use the TRACEEVAL_ARRAY_SIZE() macro to determine the size > of the array passed to it. But this will not work if the developer uses a > pointer to the array. gcc may give a warning, but it will still happily > compile it leaving the developer wondering why their code does not work. > > Use a little trick that checks that tests if the array is static, and will > fail the build if it is not. > > #define TRACEEVAL_ARRAY_SIZE(data) \ > ((sizeof(data) / sizeof((data)[0])) + \ > (int)(sizeof(struct { \ > int:(-!!(__builtin_types_compatible_p(typeof(data), \ > typeof(&((data)[0]))))); \ > }))) > > Going backwards to explain the above, we have: > > __builtin_types_compatible_p(typeof(data), typeof(&((data)[0])) > > Which is a gcc/clang compiler directive that returns true if the two > pointers are compatible. For a static array, we would have > > struct traceeval_data data[5] > > Where typeof(data) is "struct traceeval []" and the type of > &data[0] is a pointer to "struct traceeval_data", and the above > would return false (zero). > > For pointers: > > struct traceeval_data *data; > > Then type of data is the same as &data[0] and it would return true (1). > > Now we have a structure defined: > > struct { > int: (-!!(<result>)); > } > > Which if <result> of the __builtin_types_compatible_p() returned false > (zero), then it would be: > > struct { > int: 0; > } > > Which is perfectly valid. But if <result> returned true (as it would if it > was a pointer and not a static array), then it would be: > > struct { > int: -1; > } > > Which is not valid to compile, and the build will fail. > > But in order to make sure this is in the code that uses > TRACEEVAL_ARRAY_SIZE(), it needs to be part of the computation. To do > that, as "struct { int:0; }" is of size zero, we can simply add a sizeof() > around it, and attach the above with an addition "+". > > ... + sizeof((int)(sizeof(struct { int:0;}))) is the same as: > > ... + 0 > > Now with this logic, if a pointer is passed to something like > traceeval_init(), it will fail to build, and not cause hours of scratching > head debugging for the developer at runtime. > > Of course, the developer will likely to now scratch their head on why it > doesn't build, but that's because they didn't RTFM! > > Reviewed-by: Ross Zwisler <zwisler@google.com> > Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org> > --- > Changes since v1: https://lore.kernel.org/linux-trace-devel/20231004185225.366c1739@gandalf.local.home/ > > - Added comment to why the compile may fail due to the above trick. > > include/traceeval.h | 19 ++++++++++++++++++- > 1 file changed, 18 insertions(+), 1 deletion(-) > > diff --git a/include/traceeval.h b/include/traceeval.h > index 4cc5eb6ef3de..a70a5b09057b 100644 > --- a/include/traceeval.h > +++ b/include/traceeval.h > @@ -17,7 +17,24 @@ > /* Field name/descriptor for number of hits */ > #define TRACEEVAL_VAL_HITS ((const char *)(-1UL)) > > -#define TRACEEVAL_ARRAY_SIZE(data) (sizeof(data) / sizeof((data)[0])) > +/* > + * If your compile failed due to the below macro, it is likely you used > + * a pointer to struct traceeval_data, and not a static array. > + * > + * That is: > + * > + * struct traceeval_data *keys; > + * > + * and not: > + * > + * struct traceeval_data keys[] = { ... }; > + */ Perfect, thanks! > +#define TRACEEVAL_ARRAY_SIZE(data) \ > + ((sizeof(data) / sizeof((data)[0])) + \ > + (int)(sizeof(struct { \ > + int:(-!!(__builtin_types_compatible_p(typeof(data), \ > + typeof(&((data)[0]))))); \ > + }))) > > /* Data type distinguishers */ > enum traceeval_data_type { > -- > 2.40.1 >
diff --git a/include/traceeval.h b/include/traceeval.h index 4cc5eb6ef3de..a70a5b09057b 100644 --- a/include/traceeval.h +++ b/include/traceeval.h @@ -17,7 +17,24 @@ /* Field name/descriptor for number of hits */ #define TRACEEVAL_VAL_HITS ((const char *)(-1UL)) -#define TRACEEVAL_ARRAY_SIZE(data) (sizeof(data) / sizeof((data)[0])) +/* + * If your compile failed due to the below macro, it is likely you used + * a pointer to struct traceeval_data, and not a static array. + * + * That is: + * + * struct traceeval_data *keys; + * + * and not: + * + * struct traceeval_data keys[] = { ... }; + */ +#define TRACEEVAL_ARRAY_SIZE(data) \ + ((sizeof(data) / sizeof((data)[0])) + \ + (int)(sizeof(struct { \ + int:(-!!(__builtin_types_compatible_p(typeof(data), \ + typeof(&((data)[0]))))); \ + }))) /* Data type distinguishers */ enum traceeval_data_type {