* [PATCH v2 0/2] libtraceevent: Handling cpumask event fields
@ 2022-11-16 14:41 Valentin Schneider
2022-11-16 14:41 ` [PATCH v2 1/2] libtraceevent: Add boiler-plate code for cpumask types Valentin Schneider
2022-11-16 14:46 ` [PATCH v2 2/2] libtraceevent: Pretty-print cpumask fields as a cpulist Valentin Schneider
0 siblings, 2 replies; 7+ messages in thread
From: Valentin Schneider @ 2022-11-16 14:41 UTC (permalink / raw)
To: linux-trace-devel
Cc: Steven Rostedt, Daniel Bristot de Oliveira, Clark Williams,
Douglas RAILLARD
Hi folks,
While discussing around [1], Steve had the idea [2] of creating a "proper" type
for cpumask event fields so that userspace tooling doesn't have to guess whether
an unsigned long [] is meant to be a cpumask or not.
It takes a small amount of boiler plate to get cpumask fields on the same level
as bitmask fields, which is patch 1.
Patch 2 adds on top a nicer pretty-print output for cpumasks. I'm not massively
fond of the homegrown cpumask iterating function, but I've done my best to pit
it against corner cases.
Cheers,
Valentin
Revisions
=========
v1 -> v2
++++++++
o Rebased on top of latest libtraceevent
o Fixed s/__get_cpumask/__get_rel_cpumask/ typo
[1]: http://lore.kernel.org/r/xhsmhilkqfi7z.mognet@vschneid.remote.csb
[2]: http://lore.kernel.org/r/20221014080456.1d32b989@rorschach.local.home
Valentin Schneider (2):
libtraceevent: Add boiler-plate code for cpumask types
libtraceevent: Pretty-print cpumask fields as a cpulist
include/traceevent/event-parse.h | 1 +
src/event-parse.c | 190 +++++++++++++++++++++++++++++++
2 files changed, 191 insertions(+)
--
2.31.1
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 1/2] libtraceevent: Add boiler-plate code for cpumask types
2022-11-16 14:41 [PATCH v2 0/2] libtraceevent: Handling cpumask event fields Valentin Schneider
@ 2022-11-16 14:41 ` Valentin Schneider
2022-12-06 20:08 ` Steven Rostedt
2022-11-16 14:46 ` [PATCH v2 2/2] libtraceevent: Pretty-print cpumask fields as a cpulist Valentin Schneider
1 sibling, 1 reply; 7+ messages in thread
From: Valentin Schneider @ 2022-11-16 14:41 UTC (permalink / raw)
To: linux-trace-devel
Cc: Steven Rostedt, Daniel Bristot de Oliveira, Clark Williams,
Douglas RAILLARD
A cpumask event field type was recently added to Linux, which helps
distinguish any odd bitmask from a cpumask. Right now this field type is
not recognized by libtraceevent:
[ipi:ipi_send_cpumask] function __get_cpumask not defined
CPU 0 is empty
CPU 1 is empty
CPU 3 is empty
cpus=4
echo-173 [002] 11.859745: ipi_send_cpumask: [FAILED TO PARSE] cpumask=ARRAY[02,
00, 00, 00, 00, 00, 00, 00] callsite=0xffffffff81121013
Since a cpumask is still a bitmask, define the boiler plate code for this
new field type and wire it all to bitmask handling.
Signed-off-by: Valentin Schneider <vschneid@redhat.com>
---
include/traceevent/event-parse.h | 1 +
src/event-parse.c | 24 ++++++++++++++++++++++++
2 files changed, 25 insertions(+)
diff --git a/include/traceevent/event-parse.h b/include/traceevent/event-parse.h
index f749cc2..2bb0f3e 100644
--- a/include/traceevent/event-parse.h
+++ b/include/traceevent/event-parse.h
@@ -242,6 +242,7 @@ enum tep_print_arg_type {
TEP_PRINT_OP,
TEP_PRINT_FUNC,
TEP_PRINT_BITMASK,
+ TEP_PRINT_CPUMASK,
TEP_PRINT_DYNAMIC_ARRAY_LEN,
TEP_PRINT_HEX_STR,
};
diff --git a/src/event-parse.c b/src/event-parse.c
index d842f9d..f447708 100644
--- a/src/event-parse.c
+++ b/src/event-parse.c
@@ -1083,6 +1083,7 @@ static void free_arg(struct tep_print_arg *arg)
free(arg->string.string);
break;
case TEP_PRINT_BITMASK:
+ case TEP_PRINT_CPUMASK:
free(arg->bitmask.bitmask);
break;
case TEP_PRINT_DYNAMIC_ARRAY:
@@ -2816,6 +2817,7 @@ static int arg_num_eval(struct tep_print_arg *arg, long long *val)
case TEP_PRINT_STRING:
case TEP_PRINT_BSTRING:
case TEP_PRINT_BITMASK:
+ case TEP_PRINT_CPUMASK:
default:
do_warning("invalid eval type %d", arg->type);
ret = 0;
@@ -2845,6 +2847,7 @@ static char *arg_eval (struct tep_print_arg *arg)
case TEP_PRINT_STRING:
case TEP_PRINT_BSTRING:
case TEP_PRINT_BITMASK:
+ case TEP_PRINT_CPUMASK:
default:
do_warning("invalid eval type %d", arg->type);
break;
@@ -3325,6 +3328,17 @@ process_bitmask(struct tep_event *event __maybe_unused, struct tep_print_arg *ar
return TEP_EVENT_ERROR;
}
+static enum tep_event_type
+process_cpumask(struct tep_event *event __maybe_unused, struct tep_print_arg *arg,
+ char **tok)
+{
+ enum tep_event_type type = process_bitmask(event, arg, tok);
+ if (type != TEP_EVENT_ERROR)
+ arg->type = TEP_PRINT_CPUMASK;
+
+ return type;
+}
+
static struct tep_function_handler *
find_func_handler(struct tep_handle *tep, char *func_name)
{
@@ -3484,6 +3498,11 @@ process_function(struct tep_event *event, struct tep_print_arg *arg,
free_token(token);
return process_bitmask(event, arg, tok);
}
+ if (strcmp(token, "__get_cpumask") == 0 ||
+ strcmp(token, "__get_rel_cpumask") == 0) {
+ free_token(token);
+ return process_cpumask(event, arg, tok);
+ }
if (strcmp(token, "__get_dynamic_array") == 0 ||
strcmp(token, "__get_rel_dynamic_array") == 0 ||
strcmp(token, "__get_sockaddr") == 0 ||
@@ -4144,6 +4163,7 @@ eval_num_arg(void *data, int size, struct tep_event *event, struct tep_print_arg
case TEP_PRINT_STRING:
case TEP_PRINT_BSTRING:
case TEP_PRINT_BITMASK:
+ case TEP_PRINT_CPUMASK:
return 0;
case TEP_PRINT_FUNC: {
struct trace_seq s;
@@ -4637,6 +4657,7 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
case TEP_PRINT_BSTRING:
print_str_to_seq(s, format, len_arg, arg->string.string);
break;
+ case TEP_PRINT_CPUMASK:
case TEP_PRINT_BITMASK: {
if (!arg->bitmask.field) {
arg->bitmask.field = tep_find_any_field(event, arg->bitmask.bitmask);
@@ -7158,6 +7179,9 @@ static void print_args(struct tep_print_arg *args)
case TEP_PRINT_BITMASK:
printf("__get_bitmask(%s)", args->bitmask.bitmask);
break;
+ case TEP_PRINT_CPUMASK:
+ printf("__get_cpumask(%s)", args->bitmask.bitmask);
+ break;
case TEP_PRINT_TYPE:
printf("(%s)", args->typecast.type);
print_args(args->typecast.item);
--
2.31.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v2 2/2] libtraceevent: Pretty-print cpumask fields as a cpulist
2022-11-16 14:41 [PATCH v2 0/2] libtraceevent: Handling cpumask event fields Valentin Schneider
2022-11-16 14:41 ` [PATCH v2 1/2] libtraceevent: Add boiler-plate code for cpumask types Valentin Schneider
@ 2022-11-16 14:46 ` Valentin Schneider
2022-12-06 20:32 ` Steven Rostedt
1 sibling, 1 reply; 7+ messages in thread
From: Valentin Schneider @ 2022-11-16 14:46 UTC (permalink / raw)
To: linux-trace-devel
Cc: Steven Rostedt, Daniel Bristot de Oliveira, Clark Williams,
Douglas RAILLARD
Now that we can denote which bitmasks are cpumasks, it makes sense to
pretty-print them to a more user-friendly format: a cpulist.
There's two hurdles to that:
1) Estimating the required string buffer size.
I've tried to condense it down to an estimator function that is
computationally simple enough, though it overestimates by ~1/3.
For reference, this estimates:
180 bytes for NR_CPUS=64 (x86 defconfig)
911 bytes for NR_CPUS=256 (arm64 defconfig)
2) Iterating through the bits and bytes.
The kernel has a collection of carefully crafted bitmask iterators which
make this relatively simple (cf. bitmap_list_string()), but I didn't
feel justified in importing half a dozen helpers just for one function.
I've implemented a "homegrown" byte-parsing logic which isn't the
fastest, but is at least condensed to a single function.
Signed-off-by: Valentin Schneider <vschneid@redhat.com>
---
src/event-parse.c | 168 +++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 167 insertions(+), 1 deletion(-)
diff --git a/src/event-parse.c b/src/event-parse.c
index f447708..5e597f6 100644
--- a/src/event-parse.c
+++ b/src/event-parse.c
@@ -4454,6 +4454,161 @@ static void print_bitmask_to_seq(struct tep_handle *tep,
free(str);
}
+#define log10(n) \
+( \
+ n < 10UL ? 0 : \
+ n < 100UL ? 1 : \
+ n < 1000UL ? 2 : \
+ n < 10000UL ? 3 : \
+ n < 100000UL ? 4 : \
+ n < 1000000UL ? 5 : \
+ n < 10000000UL ? 6 : \
+ n < 100000000UL ? 7 : \
+ n < 1000000000UL ? 8 : \
+ 9 \
+)
+
+/* ilog10(0) should be 1 but the 0 simplifies below math */
+#define ilog10(n) \
+( \
+ n == 0 ? 0UL : \
+ n == 1 ? 10UL : \
+ n == 2 ? 100UL : \
+ n == 3 ? 1000UL : \
+ n == 4 ? 10000UL : \
+ n == 5 ? 100000UL : \
+ n == 6 ? 1000000UL : \
+ n == 7 ? 10000000UL : \
+ n == 8 ? 100000000UL : \
+ 1000000000UL \
+)
+
+static unsigned int cpumask_worst_size(unsigned int nr_bits)
+{
+ /*
+ * Printing all the CPUs separated by a comma is a decent bound for the
+ * maximum memory required to print a cpumask (a slightly better bound
+ * is chunks of 2 bits set, i.e. 0-1,3-4,6-7...).
+ *
+ * e.g. for nr_bits=132:
+ * - 131 commas
+ * - 10 * 1 chars for CPUS [0, 9]
+ * - 90 * 2 chars for CPUS [10-99]
+ * - 32 * 3 chars for CPUS [100-131]
+ */
+ unsigned int last_cpu = nr_bits - 1;
+ unsigned int nr_chars = nr_bits - 1;
+ int last_lvl = log10(last_cpu);
+
+ /* All log10 levels before the last one have all values used */
+ for (int lvl = 0; lvl < last_lvl; lvl++) {
+ int nr_values = ilog10(lvl + 1) - ilog10(lvl);
+
+ nr_chars += nr_values * (lvl + 1);
+ }
+ /* Last level is incomplete */
+ nr_chars += (nr_bits - ilog10(last_lvl)) * (last_lvl + 1);
+
+ return nr_chars;
+}
+
+static void print_cpumask_to_seq(struct tep_handle *tep,
+ struct trace_seq *s, const char *format,
+ int len_arg, const void *data, int size)
+{
+ int firstone = -1, firstzero = -1;
+ int nr_bits = size * 8;
+ bool first = true;
+ int str_size = 0;
+ char buf[12]; /* '-' + log10(2^32) + 1 digits + '\0' */
+ char *str;
+ int index;
+ int i;
+
+ str = malloc(cpumask_worst_size(nr_bits) + 1);
+ if (!str) {
+ do_warning("%s: not enough memory!", __func__);
+ return;
+ }
+
+ for (i = 0; i < size; i++) {
+ unsigned char byte;
+ int fmtsize;
+
+ if (tep->file_bigendian)
+ index = size - (i + 1);
+ else
+ index = i;
+
+ /* Byte by byte scan, not the best... */
+ byte = *(((unsigned char *)data) + index);
+more:
+ /* First find a bit set to one...*/
+ if (firstone < 0 && byte) {
+ /*
+ * Set all lower bits, so a later ffz on this same byte
+ * is guaranteed to find a later bit.
+ */
+ firstone = ffs(byte) - 1;
+ byte |= (1 << firstone) - 1;
+ firstone += i * 8;
+ }
+
+ if (firstone < 0)
+ continue;
+
+ /* ...Then find a bit set to zero */
+ if ((~byte) & 0xFF) {
+ /*
+ * Clear all lower bits, so a later ffs on this same
+ * byte is guaranteed to find a later bit.
+ */
+ firstzero = ffs(~byte) - 1;
+ byte &= ~((1 << (firstzero)) - 1);
+ firstzero += i * 8;
+ } else if (i == size - 1) { /* ...Or reach the end of the mask */
+ firstzero = nr_bits;
+ byte = 0;
+ } else {
+ continue;
+ }
+
+ /* We've found a bit set to one, and a later bit set to zero. */
+ if (!first) {
+ str[str_size] = ',';
+ str_size++;
+ }
+ first = false;
+
+ /* It takes {log10(number) + 1} chars to format a number */
+ fmtsize = log10(firstone) + 1;
+ snprintf(buf, fmtsize + 1, "%d", firstone);
+ memcpy(str + str_size, buf, fmtsize);
+ str_size += fmtsize;
+
+ if (firstzero > firstone + 1) {
+ fmtsize = log10(firstzero - 1) + 2;
+ snprintf(buf, fmtsize + 1, "-%d", firstzero - 1);
+ memcpy(str + str_size, buf, fmtsize);
+ str_size += fmtsize;
+ }
+
+ firstzero = firstone = -1;
+ if (byte)
+ goto more;
+ }
+
+ str[str_size] = 0;
+ str_size++;
+
+ if (len_arg >= 0)
+ trace_seq_printf(s, format, len_arg, str);
+ else
+ trace_seq_printf(s, format, str);
+
+ free(str);
+}
+
static void print_str_arg(struct trace_seq *s, void *data, int size,
struct tep_event *event, const char *format,
int len_arg, struct tep_print_arg *arg)
@@ -4657,7 +4812,6 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
case TEP_PRINT_BSTRING:
print_str_to_seq(s, format, len_arg, arg->string.string);
break;
- case TEP_PRINT_CPUMASK:
case TEP_PRINT_BITMASK: {
if (!arg->bitmask.field) {
arg->bitmask.field = tep_find_any_field(event, arg->bitmask.bitmask);
@@ -4670,6 +4824,18 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
data + offset, len);
break;
}
+ case TEP_PRINT_CPUMASK: {
+ if (!arg->bitmask.field) {
+ arg->bitmask.field = tep_find_any_field(event, arg->bitmask.bitmask);
+ arg->bitmask.offset = arg->bitmask.field->offset;
+ }
+ if (!arg->bitmask.field)
+ break;
+ dynamic_offset_field(tep, arg->bitmask.field, data, size, &offset, &len);
+ print_cpumask_to_seq(tep, s, format, len_arg,
+ data + offset, len);
+ break;
+ }
case TEP_PRINT_OP:
/*
* The only op for string should be ? :
--
2.31.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v2 1/2] libtraceevent: Add boiler-plate code for cpumask types
2022-11-16 14:41 ` [PATCH v2 1/2] libtraceevent: Add boiler-plate code for cpumask types Valentin Schneider
@ 2022-12-06 20:08 ` Steven Rostedt
2022-12-07 9:55 ` Valentin Schneider
0 siblings, 1 reply; 7+ messages in thread
From: Steven Rostedt @ 2022-12-06 20:08 UTC (permalink / raw)
To: Valentin Schneider
Cc: linux-trace-devel, Daniel Bristot de Oliveira, Clark Williams,
Douglas RAILLARD
On Wed, 16 Nov 2022 14:41:53 +0000
Valentin Schneider <vschneid@redhat.com> wrote:
> diff --git a/include/traceevent/event-parse.h b/include/traceevent/event-parse.h
> index f749cc2..2bb0f3e 100644
> --- a/include/traceevent/event-parse.h
> +++ b/include/traceevent/event-parse.h
> @@ -242,6 +242,7 @@ enum tep_print_arg_type {
> TEP_PRINT_OP,
> TEP_PRINT_FUNC,
> TEP_PRINT_BITMASK,
> + TEP_PRINT_CPUMASK,
> TEP_PRINT_DYNAMIC_ARRAY_LEN,
> TEP_PRINT_HEX_STR,
> };
I was about to apply this when I realized that this enum is exposed to
applications. Any new enum must go at the end.
-- Steve
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 2/2] libtraceevent: Pretty-print cpumask fields as a cpulist
2022-11-16 14:46 ` [PATCH v2 2/2] libtraceevent: Pretty-print cpumask fields as a cpulist Valentin Schneider
@ 2022-12-06 20:32 ` Steven Rostedt
2022-12-07 9:55 ` Valentin Schneider
0 siblings, 1 reply; 7+ messages in thread
From: Steven Rostedt @ 2022-12-06 20:32 UTC (permalink / raw)
To: Valentin Schneider
Cc: linux-trace-devel, Daniel Bristot de Oliveira, Clark Williams,
Douglas RAILLARD
On Wed, 16 Nov 2022 14:46:46 +0000
Valentin Schneider <vschneid@redhat.com> wrote:
> +static void print_cpumask_to_seq(struct tep_handle *tep,
> + struct trace_seq *s, const char *format,
> + int len_arg, const void *data, int size)
> +{
> + int firstone = -1, firstzero = -1;
> + int nr_bits = size * 8;
> + bool first = true;
> + int str_size = 0;
> + char buf[12]; /* '-' + log10(2^32) + 1 digits + '\0' */
> + char *str;
> + int index;
> + int i;
> +
> + str = malloc(cpumask_worst_size(nr_bits) + 1);
> + if (!str) {
> + do_warning("%s: not enough memory!", __func__);
> + return;
> + }
> +
> + for (i = 0; i < size; i++) {
> + unsigned char byte;
> + int fmtsize;
> +
> + if (tep->file_bigendian)
> + index = size - (i + 1);
> + else
> + index = i;
> +
> + /* Byte by byte scan, not the best... */
> + byte = *(((unsigned char *)data) + index);
> +more:
> + /* First find a bit set to one...*/
> + if (firstone < 0 && byte) {
> + /*
> + * Set all lower bits, so a later ffz on this same byte
> + * is guaranteed to find a later bit.
> + */
> + firstone = ffs(byte) - 1;
> + byte |= (1 << firstone) - 1;
> + firstone += i * 8;
> + }
> +
> + if (firstone < 0)
> + continue;
> +
> + /* ...Then find a bit set to zero */
> + if ((~byte) & 0xFF) {
> + /*
> + * Clear all lower bits, so a later ffs on this same
> + * byte is guaranteed to find a later bit.
> + */
> + firstzero = ffs(~byte) - 1;
> + byte &= ~((1 << (firstzero)) - 1);
> + firstzero += i * 8;
> + } else if (i == size - 1) { /* ...Or reach the end of the mask */
> + firstzero = nr_bits;
> + byte = 0;
> + } else {
> + continue;
> + }
> +
> + /* We've found a bit set to one, and a later bit set to zero. */
> + if (!first) {
> + str[str_size] = ',';
> + str_size++;
> + }
> + first = false;
> +
> + /* It takes {log10(number) + 1} chars to format a number */
> + fmtsize = log10(firstone) + 1;
> + snprintf(buf, fmtsize + 1, "%d", firstone);
> + memcpy(str + str_size, buf, fmtsize);
> + str_size += fmtsize;
> +
> + if (firstzero > firstone + 1) {
> + fmtsize = log10(firstzero - 1) + 2;
> + snprintf(buf, fmtsize + 1, "-%d", firstzero - 1);
> + memcpy(str + str_size, buf, fmtsize);
> + str_size += fmtsize;
> + }
> +
> + firstzero = firstone = -1;
> + if (byte)
> + goto more;
> + }
> +
> + str[str_size] = 0;
> + str_size++;
> +
> + if (len_arg >= 0)
> + trace_seq_printf(s, format, len_arg, str);
> + else
> + trace_seq_printf(s, format, str);
> +
> + free(str);
> +}
> +
This is a rather complex algorithm (I'm too tired to try to grasp it). It
really needs a unit test to make sure it's working as expected.
-- Steve
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 1/2] libtraceevent: Add boiler-plate code for cpumask types
2022-12-06 20:08 ` Steven Rostedt
@ 2022-12-07 9:55 ` Valentin Schneider
0 siblings, 0 replies; 7+ messages in thread
From: Valentin Schneider @ 2022-12-07 9:55 UTC (permalink / raw)
To: Steven Rostedt
Cc: linux-trace-devel, Daniel Bristot de Oliveira, Clark Williams,
Douglas RAILLARD
On 06/12/22 15:08, Steven Rostedt wrote:
> On Wed, 16 Nov 2022 14:41:53 +0000
> Valentin Schneider <vschneid@redhat.com> wrote:
>
>> diff --git a/include/traceevent/event-parse.h b/include/traceevent/event-parse.h
>> index f749cc2..2bb0f3e 100644
>> --- a/include/traceevent/event-parse.h
>> +++ b/include/traceevent/event-parse.h
>> @@ -242,6 +242,7 @@ enum tep_print_arg_type {
>> TEP_PRINT_OP,
>> TEP_PRINT_FUNC,
>> TEP_PRINT_BITMASK,
>> + TEP_PRINT_CPUMASK,
>> TEP_PRINT_DYNAMIC_ARRAY_LEN,
>> TEP_PRINT_HEX_STR,
>> };
>
> I was about to apply this when I realized that this enum is exposed to
> applications. Any new enum must go at the end.
>
Woops, will do.
> -- Steve
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 2/2] libtraceevent: Pretty-print cpumask fields as a cpulist
2022-12-06 20:32 ` Steven Rostedt
@ 2022-12-07 9:55 ` Valentin Schneider
0 siblings, 0 replies; 7+ messages in thread
From: Valentin Schneider @ 2022-12-07 9:55 UTC (permalink / raw)
To: Steven Rostedt
Cc: linux-trace-devel, Daniel Bristot de Oliveira, Clark Williams,
Douglas RAILLARD
On 06/12/22 15:32, Steven Rostedt wrote:
> On Wed, 16 Nov 2022 14:46:46 +0000
> Valentin Schneider <vschneid@redhat.com> wrote:
>
>
> This is a rather complex algorithm (I'm too tired to try to grasp it). It
> really needs a unit test to make sure it's working as expected.
>
I had missed there was already a testing infra for libtraceevent, that's my
bad. I'll convert my local tests to that and ship it in v3. Thanks!
> -- Steve
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2022-12-07 9:56 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-11-16 14:41 [PATCH v2 0/2] libtraceevent: Handling cpumask event fields Valentin Schneider
2022-11-16 14:41 ` [PATCH v2 1/2] libtraceevent: Add boiler-plate code for cpumask types Valentin Schneider
2022-12-06 20:08 ` Steven Rostedt
2022-12-07 9:55 ` Valentin Schneider
2022-11-16 14:46 ` [PATCH v2 2/2] libtraceevent: Pretty-print cpumask fields as a cpulist Valentin Schneider
2022-12-06 20:32 ` Steven Rostedt
2022-12-07 9:55 ` Valentin Schneider
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).