diff options
| -rw-r--r-- | bgpd/bgp_table.c | 2 | ||||
| -rw-r--r-- | doc/developer/logging.rst | 144 | ||||
| -rw-r--r-- | lib/monotime.h | 75 | ||||
| -rw-r--r-- | lib/nexthop.c | 2 | ||||
| -rw-r--r-- | lib/prefix.c | 58 | ||||
| -rw-r--r-- | lib/printf/glue.c | 4 | ||||
| -rw-r--r-- | lib/printfrr.h | 14 | ||||
| -rw-r--r-- | lib/sockunion.c | 6 | ||||
| -rw-r--r-- | lib/srcdest_table.c | 2 | ||||
| -rw-r--r-- | lib/strformat.c | 333 | ||||
| -rw-r--r-- | lib/thread.c | 67 | ||||
| -rw-r--r-- | lib/thread.h | 4 | ||||
| -rw-r--r-- | pimd/pim_addr.c | 4 | ||||
| -rw-r--r-- | tests/lib/test_printfrr.c | 114 | ||||
| -rwxr-xr-x | tools/checkpatch.pl | 2 |
15 files changed, 806 insertions, 25 deletions
diff --git a/bgpd/bgp_table.c b/bgpd/bgp_table.c index 4ed8c7c59b..0ca78d0bb6 100644 --- a/bgpd/bgp_table.c +++ b/bgpd/bgp_table.c @@ -237,7 +237,7 @@ struct bgp_node *bgp_table_subtree_lookup(const struct bgp_table *table, return matched; } -printfrr_ext_autoreg_p("BD", printfrr_bd) +printfrr_ext_autoreg_p("BD", printfrr_bd); static ssize_t printfrr_bd(struct fbuf *buf, struct printfrr_eargs *ea, const void *ptr) { diff --git a/doc/developer/logging.rst b/doc/developer/logging.rst index e608046820..4e6fc04206 100644 --- a/doc/developer/logging.rst +++ b/doc/developer/logging.rst @@ -123,10 +123,14 @@ Networking data types :frrfmtout:`1.2.3.4` + ``%pI4s``: :frrfmtout:`*` — print star instead of ``0.0.0.0`` (for multicast) + .. frrfmt:: %pI6 (struct in6_addr *) :frrfmtout:`fe80::1234` + ``%pI6s``: :frrfmtout:`*` — print star instead of ``::`` (for multicast) + .. frrfmt:: %pEA (struct ethaddr *) :frrfmtout:`01:23:45:67:89:ab` @@ -135,6 +139,8 @@ Networking data types :frrfmtout:`1.2.3.4` / :frrfmtout:`fe80::1234` + ``%pIAs``: — print star instead of zero address (for multicast) + .. frrfmt:: %pFX (struct prefix *) :frrfmtout:`1.2.3.0/24` / :frrfmtout:`fe80::1234/64` @@ -213,6 +219,144 @@ Networking data types :frrfmtout:`SOCK_STREAM` +Time/interval formats +^^^^^^^^^^^^^^^^^^^^^ + +.. frrfmt:: %pTS (struct timespec *) + +.. frrfmt:: %pTV (struct timeval *) + +.. frrfmt:: %pTT (time_t *) + + Above 3 options internally result in the same code being called, support + the same flags and produce equal output with one exception: ``%pTT`` + has no sub-second precision and the formatter will never print a + (nonsensical) ``.000``. + + Exactly one of ``I``, ``M`` or ``R`` must immediately follow after + ``TS``/``TV``/``TT`` to specify whether the input is an interval, monotonic + timestamp or realtime timestamp: + + ``%pTVI``: input is an interval, not a timestamp. Print interval. + + ``%pTVIs``: input is an interval, convert to wallclock by subtracting it + from current time (i.e. interval has passed **s**\ ince.) + + ``%pTVIu``: input is an interval, convert to wallclock by adding it to + current time (i.e. **u**\ ntil interval has passed.) + + ``%pTVM`` - input is a timestamp on CLOCK_MONOTONIC, convert to wallclock + time (by grabbing current CLOCK_MONOTONIC and CLOCK_REALTIME and doing the + math) and print calendaric date. + + ``%pTVMs`` - input is a timestamp on CLOCK_MONOTONIC, print interval + **s**\ ince that timestamp (elapsed.) + + ``%pTVMu`` - input is a timestamp on CLOCK_MONOTONIC, print interval + **u**\ ntil that timestamp (deadline.) + + ``%pTVR`` - input is a timestamp on CLOCK_REALTIME, print calendaric date. + + ``%pTVRs`` - input is a timestamp on CLOCK_REALTIME, print interval + **s**\ ince that timestamp. + + ``%pTVRu`` - input is a timestamp on CLOCK_REALTIME, print interval + **u**\ ntil that timestamp. + + ``%pTVA`` - reserved for CLOCK_TAI in case a PTP implementation is + interfaced to FRR. Not currently implemented. + + .. note:: + + If ``%pTVRs`` or ``%pTVRu`` are used, this is generally an indication + that a CLOCK_MONOTONIC timestamp should be used instead (or added in + parallel.) CLOCK_REALTIME might be adjusted by NTP, PTP or similar + procedures, causing bogus intervals to be printed. + + ``%pTVM`` on first look might be assumed to have the same problem, but + on closer thought the assumption is always that current system time is + correct. And since a CLOCK_MONOTONIC interval is also quite safe to + assume to be correct, the (past) absolute timestamp to be printed from + this can likely be correct even if it doesn't match what CLOCK_REALTIME + would have indicated at that point in the past. This logic does, + however, not quite work for *future* times. + + Generally speaking, almost all use cases in FRR should (and do) use + CLOCK_MONOTONIC (through :c:func:`monotime()`.) + + Flags common to printing calendar times and intervals: + + ``p``: include spaces in appropriate places (depends on selected format.) + + ``%p.3TV...``: specify sub-second resolution (use with ``FMT_NSTD`` to + suppress gcc warning.) As noted above, ``%pTT`` will never print sub-second + digits since there are none. Only some formats support printing sub-second + digits and the default may vary. + + The following flags are available for printing calendar times/dates: + + (no flag): :frrfmtout:`Sat Jan 1 00:00:00 2022` - print output from + ``ctime()``, in local time zone. Since FRR does not currently use/enable + locale support, this is always the C locale. (Locale support getting added + is unlikely for the time being and would likely break other things worse + than this.) + + ``i``: :frrfmtout:`2022-01-01T00:00:00.123` - ISO8601 timestamp in local + time zone (note there is no ``Z`` or ``+00:00`` suffix.) Defaults to + millisecond precision. + + ``ip``: :frrfmtout:`2022-01-01 00:00:00.123` - use readable form of ISO8601 + with space instead of ``T`` separator. + + The following flags are available for printing intervals: + + (no flag): :frrfmtout:`9w9d09:09:09.123` - does not match any + preexisting format; added because it does not lose precision (like ``t``) + for longer intervals without printing huge numbers (like ``h``/``m``). + Defaults to millisecond precision. The week/day fields are left off if + they're zero, ``p`` adds a space after the respective letter. + + ``t``: :frrfmtout:`9w9d09h`, :frrfmtout:`9d09h09m`, :frrfmtout:`09:09:09` - + this replaces :c:func:`frrtime_to_interval()`. ``p`` adds spaces after + week/day/hour letters. + + ``d``: print decimal number of seconds. Defaults to millisecond precision. + + ``x`` / ``tx`` / ``dx``: Like no flag / ``t`` / ``d``, but print + :frrfmtout:`-` for zero or negative intervals (for use with unset timers.) + + ``h``: :frrfmtout:`09:09:09` + + ``hx``: :frrfmtout:`09:09:09`, :frrfmtout:`--:--:--` - this replaces + :c:func:`pim_time_timer_to_hhmmss()`. + + ``m``: :frrfmtout:`09:09` + + ``mx``: :frrfmtout:`09:09`, :frrfmtout:`--:--` - this replaces + :c:func:`pim_time_timer_to_mmss()`. + +FRR library helper formats +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. frrfmt:: %pTH (struct thread *) + + Print remaining time on timer thread. Interval-printing flag characters + listed above for ``%pTV`` can be added, e.g. ``%pTHtx``. + + ``NULL`` pointers are printed as ``-``. + +.. frrfmt:: %pTHD (struct thread *) + + Print debugging information for given thread. Sample output: + + .. code-block:: none + + {(thread *)NULL} + {(thread *)0x55a3b5818910 arg=0x55a3b5827c50 timer r=7.824 mld_t_query() &mld_ifp->t_query from pimd/pim6_mld.c:1369} + {(thread *)0x55a3b5827230 arg=0x55a3b5827c50 read fd=16 mld_t_recv() &mld_ifp->t_recv from pimd/pim6_mld.c:1186} + + (The output is aligned to some degree.) + General utility formats ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/lib/monotime.h b/lib/monotime.h index dda763784f..15b6933955 100644 --- a/lib/monotime.h +++ b/lib/monotime.h @@ -25,6 +25,9 @@ extern "C" { #endif +struct fbuf; +struct printfrr_eargs; + #ifndef TIMESPEC_TO_TIMEVAL /* should be in sys/time.h on BSD & Linux libcs */ #define TIMESPEC_TO_TIMEVAL(tv, ts) \ @@ -42,6 +45,31 @@ extern "C" { } while (0) #endif +/* Linux/glibc is sadly missing these timespec helpers */ +#ifndef timespecadd +#define timespecadd(tsp, usp, vsp) \ + do { \ + (vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \ + (vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \ + if ((vsp)->tv_nsec >= 1000000000L) { \ + (vsp)->tv_sec++; \ + (vsp)->tv_nsec -= 1000000000L; \ + } \ + } while (0) +#endif + +#ifndef timespecsub +#define timespecsub(tsp, usp, vsp) \ + do { \ + (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \ + (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \ + if ((vsp)->tv_nsec < 0) { \ + (vsp)->tv_sec--; \ + (vsp)->tv_nsec += 1000000000L; \ + } \ + } while (0) +#endif + static inline time_t monotime(struct timeval *tvo) { struct timespec ts; @@ -132,6 +160,53 @@ static inline const char *frrtime_to_interval(time_t t, char *buf, return buf; } +enum { + /* n/a - input was seconds precision, don't print any fractional */ + TIMEFMT_SECONDS = (1 << 0), + /* caller is directly invoking printfrr_time and has pre-specified + * I/Iu/Is/M/Mu/Ms/R/Ru/Rs (for printing timers) + */ + TIMEFMT_PRESELECT = (1 << 1), + /* don't print any output - this is needed for invoking printfrr_time + * from another printfrr extensions to skip over flag characters + */ + TIMEFMT_SKIP = (1 << 2), + /* use spaces in appropriate places */ + TIMEFMT_SPACE = (1 << 3), + + /* input interpretations: */ + TIMEFMT_REALTIME = (1 << 8), + TIMEFMT_MONOTONIC = (1 << 9), + TIMEFMT_SINCE = (1 << 10), + TIMEFMT_UNTIL = (1 << 11), + + TIMEFMT_ABSOLUTE = TIMEFMT_REALTIME | TIMEFMT_MONOTONIC, + TIMEFMT_ANCHORS = TIMEFMT_SINCE | TIMEFMT_UNTIL, + + /* calendaric formats: */ + TIMEFMT_ISO8601 = (1 << 16), + + /* interval formats: */ + /* 't' - use [t]raditional 3-block format */ + TIMEFMT_BASIC = (1 << 24), + /* 'm' - select mm:ss */ + TIMEFMT_MMSS = (1 << 25), + /* 'h' - select hh:mm:ss */ + TIMEFMT_HHMMSS = (1 << 26), + /* 'd' - print as decimal number of seconds */ + TIMEFMT_DECIMAL = (1 << 27), + /* 'mx'/'hx' - replace zero value with "--:--" or "--:--:--" */ + TIMEFMT_DASHES = (1 << 31), + + /* helpers for reference */ + TIMEFMT_TIMER_DEADLINE = + TIMEFMT_PRESELECT | TIMEFMT_MONOTONIC | TIMEFMT_UNTIL, + TIMEFMT_TIMER_INTERVAL = TIMEFMT_PRESELECT, +}; + +extern ssize_t printfrr_time(struct fbuf *buf, struct printfrr_eargs *ea, + const struct timespec *ts, unsigned int flags); + #ifdef __cplusplus } #endif diff --git a/lib/nexthop.c b/lib/nexthop.c index 2e09cb4bcc..e17eeb8303 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -943,7 +943,7 @@ int nexthop_str2backups(const char *str, int *num_backups, * eth0 * (0-length if no interface present) */ -printfrr_ext_autoreg_p("NH", printfrr_nh) +printfrr_ext_autoreg_p("NH", printfrr_nh); static ssize_t printfrr_nh(struct fbuf *buf, struct printfrr_eargs *ea, const void *ptr) { diff --git a/lib/prefix.c b/lib/prefix.c index d3e8a612eb..90ab48a13b 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -1353,7 +1353,7 @@ char *evpn_es_df_alg2str(uint8_t df_alg, char *buf, int buf_len) return buf; } -printfrr_ext_autoreg_p("EA", printfrr_ea) +printfrr_ext_autoreg_p("EA", printfrr_ea); static ssize_t printfrr_ea(struct fbuf *buf, struct printfrr_eargs *ea, const void *ptr) { @@ -1368,47 +1368,93 @@ static ssize_t printfrr_ea(struct fbuf *buf, struct printfrr_eargs *ea, return bputs(buf, cbuf); } -printfrr_ext_autoreg_p("IA", printfrr_ia) +printfrr_ext_autoreg_p("IA", printfrr_ia); static ssize_t printfrr_ia(struct fbuf *buf, struct printfrr_eargs *ea, const void *ptr) { const struct ipaddr *ipa = ptr; char cbuf[INET6_ADDRSTRLEN]; + bool use_star = false; + + if (ea->fmt[0] == 's') { + use_star = true; + ea->fmt++; + } if (!ipa) return bputs(buf, "(null)"); + if (use_star) { + struct in_addr zero4 = {}; + struct in6_addr zero6 = {}; + + switch (ipa->ipa_type) { + case IPADDR_V4: + if (!memcmp(&ipa->ip.addr, &zero4, sizeof(zero4))) + return bputch(buf, '*'); + break; + + case IPADDR_V6: + if (!memcmp(&ipa->ip.addr, &zero6, sizeof(zero6))) + return bputch(buf, '*'); + break; + + default: + break; + } + } + ipaddr2str(ipa, cbuf, sizeof(cbuf)); return bputs(buf, cbuf); } -printfrr_ext_autoreg_p("I4", printfrr_i4) +printfrr_ext_autoreg_p("I4", printfrr_i4); static ssize_t printfrr_i4(struct fbuf *buf, struct printfrr_eargs *ea, const void *ptr) { char cbuf[INET_ADDRSTRLEN]; + bool use_star = false; + struct in_addr zero = {}; + + if (ea->fmt[0] == 's') { + use_star = true; + ea->fmt++; + } if (!ptr) return bputs(buf, "(null)"); + if (use_star && !memcmp(ptr, &zero, sizeof(zero))) + return bputch(buf, '*'); + inet_ntop(AF_INET, ptr, cbuf, sizeof(cbuf)); return bputs(buf, cbuf); } -printfrr_ext_autoreg_p("I6", printfrr_i6) +printfrr_ext_autoreg_p("I6", printfrr_i6); static ssize_t printfrr_i6(struct fbuf *buf, struct printfrr_eargs *ea, const void *ptr) { char cbuf[INET6_ADDRSTRLEN]; + bool use_star = false; + struct in6_addr zero = {}; + + if (ea->fmt[0] == 's') { + use_star = true; + ea->fmt++; + } if (!ptr) return bputs(buf, "(null)"); + if (use_star && !memcmp(ptr, &zero, sizeof(zero))) + return bputch(buf, '*'); + inet_ntop(AF_INET6, ptr, cbuf, sizeof(cbuf)); return bputs(buf, cbuf); } -printfrr_ext_autoreg_p("FX", printfrr_pfx) +printfrr_ext_autoreg_p("FX", printfrr_pfx); static ssize_t printfrr_pfx(struct fbuf *buf, struct printfrr_eargs *ea, const void *ptr) { @@ -1421,7 +1467,7 @@ static ssize_t printfrr_pfx(struct fbuf *buf, struct printfrr_eargs *ea, return bputs(buf, cbuf); } -printfrr_ext_autoreg_p("PSG4", printfrr_psg) +printfrr_ext_autoreg_p("PSG4", printfrr_psg); static ssize_t printfrr_psg(struct fbuf *buf, struct printfrr_eargs *ea, const void *ptr) { diff --git a/lib/printf/glue.c b/lib/printf/glue.c index 1147901236..6e39c2d9cf 100644 --- a/lib/printf/glue.c +++ b/lib/printf/glue.c @@ -256,7 +256,7 @@ ssize_t printfrr_exti(struct fbuf *buf, struct printfrr_eargs *ea, return -1; } -printfrr_ext_autoreg_p("FB", printfrr_fb) +printfrr_ext_autoreg_p("FB", printfrr_fb); static ssize_t printfrr_fb(struct fbuf *out, struct printfrr_eargs *ea, const void *ptr) { @@ -278,7 +278,7 @@ static ssize_t printfrr_fb(struct fbuf *out, struct printfrr_eargs *ea, return in->pos - in->buf; } -printfrr_ext_autoreg_p("VA", printfrr_va) +printfrr_ext_autoreg_p("VA", printfrr_va); static ssize_t printfrr_va(struct fbuf *buf, struct printfrr_eargs *ea, const void *ptr) { diff --git a/lib/printfrr.h b/lib/printfrr.h index 37f1f9c8cd..a2d113ba1e 100644 --- a/lib/printfrr.h +++ b/lib/printfrr.h @@ -209,10 +209,11 @@ void printfrr_ext_reg(const struct printfrr_ext *); .print_ptr = print_fn, \ }; \ static void _printreg_##print_fn(void) __attribute__((constructor)); \ - static void _printreg_##print_fn(void) { \ + static void _printreg_##print_fn(void) \ + { \ printfrr_ext_reg(&_printext_##print_fn); \ } \ - /* end */ + MACRO_REQUIRE_SEMICOLON() #define printfrr_ext_autoreg_i(matchs, print_fn) \ static ssize_t print_fn(struct fbuf *, struct printfrr_eargs *, \ @@ -222,10 +223,11 @@ void printfrr_ext_reg(const struct printfrr_ext *); .print_int = print_fn, \ }; \ static void _printreg_##print_fn(void) __attribute__((constructor)); \ - static void _printreg_##print_fn(void) { \ + static void _printreg_##print_fn(void) \ + { \ printfrr_ext_reg(&_printext_##print_fn); \ } \ - /* end */ + MACRO_REQUIRE_SEMICOLON() /* fbuf helper functions - note all 3 of these return the length that would * be written regardless of how much space was available in the buffer, as @@ -286,6 +288,10 @@ struct va_format { #pragma FRR printfrr_ext "%pSE" (char *) #pragma FRR printfrr_ext "%pSQ" (char *) + +#pragma FRR printfrr_ext "%pTS" (struct timespec *) +#pragma FRR printfrr_ext "%pTV" (struct timeval *) +#pragma FRR printfrr_ext "%pTT" (time_t *) #endif /* when using non-ISO-C compatible extension specifiers... */ diff --git a/lib/sockunion.c b/lib/sockunion.c index c7af458e9e..006ac142aa 100644 --- a/lib/sockunion.c +++ b/lib/sockunion.c @@ -662,7 +662,7 @@ void sockunion_init(union sockunion *su) memset(su, 0, sizeof(union sockunion)); } -printfrr_ext_autoreg_p("SU", printfrr_psu) +printfrr_ext_autoreg_p("SU", printfrr_psu); static ssize_t printfrr_psu(struct fbuf *buf, struct printfrr_eargs *ea, const void *ptr) { @@ -752,7 +752,7 @@ int sockunion_is_null(const union sockunion *su) } } -printfrr_ext_autoreg_i("PF", printfrr_pf) +printfrr_ext_autoreg_i("PF", printfrr_pf); static ssize_t printfrr_pf(struct fbuf *buf, struct printfrr_eargs *ea, uintmax_t val) { @@ -775,7 +775,7 @@ static ssize_t printfrr_pf(struct fbuf *buf, struct printfrr_eargs *ea, return bprintfrr(buf, "AF_(%ju)", val); } -printfrr_ext_autoreg_i("SO", printfrr_so) +printfrr_ext_autoreg_i("SO", printfrr_so); static ssize_t printfrr_so(struct fbuf *buf, struct printfrr_eargs *ea, uintmax_t val) { diff --git a/lib/srcdest_table.c b/lib/srcdest_table.c index d2e0682e95..828cf2cce0 100644 --- a/lib/srcdest_table.c +++ b/lib/srcdest_table.c @@ -306,7 +306,7 @@ const char *srcdest_rnode2str(const struct route_node *rn, char *str, int size) return srcdest2str(dst_p, (const struct prefix_ipv6 *)src_p, str, size); } -printfrr_ext_autoreg_p("RN", printfrr_rn) +printfrr_ext_autoreg_p("RN", printfrr_rn); static ssize_t printfrr_rn(struct fbuf *buf, struct printfrr_eargs *ea, const void *ptr) { diff --git a/lib/strformat.c b/lib/strformat.c index 431e573a0c..a420ba553a 100644 --- a/lib/strformat.c +++ b/lib/strformat.c @@ -22,10 +22,12 @@ #include <string.h> #include <ctype.h> +#include <time.h> #include "printfrr.h" +#include "monotime.h" -printfrr_ext_autoreg_p("HX", printfrr_hexdump) +printfrr_ext_autoreg_p("HX", printfrr_hexdump); static ssize_t printfrr_hexdump(struct fbuf *buf, struct printfrr_eargs *ea, const void *ptr) { @@ -56,7 +58,7 @@ static ssize_t printfrr_hexdump(struct fbuf *buf, struct printfrr_eargs *ea, /* string analog for hexdumps / the "this." in ("74 68 69 73 0a |this.|") */ -printfrr_ext_autoreg_p("HS", printfrr_hexdstr) +printfrr_ext_autoreg_p("HS", printfrr_hexdstr); static ssize_t printfrr_hexdstr(struct fbuf *buf, struct printfrr_eargs *ea, const void *ptr) { @@ -199,7 +201,7 @@ static ssize_t bquote(struct fbuf *buf, const uint8_t *pos, size_t len, return ret; } -printfrr_ext_autoreg_p("SE", printfrr_escape) +printfrr_ext_autoreg_p("SE", printfrr_escape); static ssize_t printfrr_escape(struct fbuf *buf, struct printfrr_eargs *ea, const void *vptr) { @@ -224,7 +226,7 @@ static ssize_t printfrr_escape(struct fbuf *buf, struct printfrr_eargs *ea, return bquote(buf, ptr, len, ESC_ALL); } -printfrr_ext_autoreg_p("SQ", printfrr_quote) +printfrr_ext_autoreg_p("SQ", printfrr_quote); static ssize_t printfrr_quote(struct fbuf *buf, struct printfrr_eargs *ea, const void *vptr) { @@ -270,3 +272,326 @@ static ssize_t printfrr_quote(struct fbuf *buf, struct printfrr_eargs *ea, ret += bputch(buf, '"'); return ret; } + +static ssize_t printfrr_abstime(struct fbuf *buf, struct printfrr_eargs *ea, + const struct timespec *ts, unsigned int flags); +static ssize_t printfrr_reltime(struct fbuf *buf, struct printfrr_eargs *ea, + const struct timespec *ts, unsigned int flags); + +ssize_t printfrr_time(struct fbuf *buf, struct printfrr_eargs *ea, + const struct timespec *ts, unsigned int flags) +{ + bool have_abs, have_anchor; + + if (!(flags & TIMEFMT_PRESELECT)) { + switch (ea->fmt[0]) { + case 'I': + /* no bit set */ + break; + case 'M': + flags |= TIMEFMT_MONOTONIC; + break; + case 'R': + flags |= TIMEFMT_REALTIME; + break; + default: + return bputs(buf, + "{invalid time format input specifier}"); + } + ea->fmt++; + + if (ea->fmt[0] == 's') { + flags |= TIMEFMT_SINCE; + ea->fmt++; + } else if (ea->fmt[0] == 'u') { + flags |= TIMEFMT_UNTIL; + ea->fmt++; + } + } + + have_abs = !!(flags & TIMEFMT_ABSOLUTE); + have_anchor = !!(flags & TIMEFMT_ANCHORS); + + if (have_abs ^ have_anchor) + return printfrr_abstime(buf, ea, ts, flags); + else + return printfrr_reltime(buf, ea, ts, flags); +} + +static ssize_t do_subsec(struct fbuf *buf, const struct timespec *ts, + int precision, unsigned int flags) +{ + unsigned long long frac; + + if (precision <= 0 || (flags & TIMEFMT_SECONDS)) + return 0; + + frac = ts->tv_nsec; + if (precision > 9) + precision = 9; + for (int i = precision; i < 9; i++) + frac /= 10; + return bprintfrr(buf, ".%0*llu", precision, frac); +} + +static ssize_t printfrr_abstime(struct fbuf *buf, struct printfrr_eargs *ea, + const struct timespec *ts, unsigned int flags) +{ + struct timespec real_ts[1]; + struct tm tm; + char cbuf[32] = ""; /* manpage says 26 for ctime_r */ + ssize_t ret = 0; + int precision = ea->precision; + + while (ea->fmt[0]) { + char ch = *ea->fmt++; + + switch (ch) { + case 'p': + flags |= TIMEFMT_SPACE; + continue; + case 'i': + flags |= TIMEFMT_ISO8601; + continue; + } + + ea->fmt--; + break; + } + + if (flags & TIMEFMT_SKIP) + return 0; + + if (flags & TIMEFMT_REALTIME) + *real_ts = *ts; + else if (flags & TIMEFMT_MONOTONIC) { + struct timespec mono_now[1]; + + clock_gettime(CLOCK_REALTIME, real_ts); + clock_gettime(CLOCK_MONOTONIC, mono_now); + + timespecsub(real_ts, mono_now, real_ts); + timespecadd(real_ts, ts, real_ts); + } else { + clock_gettime(CLOCK_REALTIME, real_ts); + + if (flags & TIMEFMT_SINCE) + timespecsub(real_ts, ts, real_ts); + else /* flags & TIMEFMT_UNTIL */ + timespecadd(real_ts, ts, real_ts); + } + + localtime_r(&real_ts->tv_sec, &tm); + + if (flags & TIMEFMT_ISO8601) { + if (flags & TIMEFMT_SPACE) + strftime(cbuf, sizeof(cbuf), "%Y-%m-%d %H:%M:%S", &tm); + else + strftime(cbuf, sizeof(cbuf), "%Y-%m-%dT%H:%M:%S", &tm); + ret += bputs(buf, cbuf); + + if (precision == -1) + precision = 3; + ret += do_subsec(buf, real_ts, precision, flags); + } else { + size_t len; + + asctime_r(&tm, cbuf); + + len = strlen(cbuf); + if (!len) + /* WTF. */ + return 0; + if (cbuf[len - 1] == '\n') + cbuf[len - 1] = '\0'; + + ret += bputs(buf, cbuf); + } + return ret; +} + +static ssize_t printfrr_reltime(struct fbuf *buf, struct printfrr_eargs *ea, + const struct timespec *ts, unsigned int flags) +{ + struct timespec real_ts[1]; + ssize_t ret = 0; + const char *space = ""; + const char *dashes = "-"; + int precision = ea->precision; + + while (ea->fmt[0]) { + char ch = *ea->fmt++; + + switch (ch) { + case 'p': + flags |= TIMEFMT_SPACE; + space = " "; + continue; + case 't': + flags |= TIMEFMT_BASIC; + continue; + case 'd': + flags |= TIMEFMT_DECIMAL; + continue; + case 'm': + flags |= TIMEFMT_MMSS; + dashes = "--:--"; + continue; + case 'h': + flags |= TIMEFMT_HHMMSS; + dashes = "--:--:--"; + continue; + case 'x': + flags |= TIMEFMT_DASHES; + continue; + } + + ea->fmt--; + break; + } + + if (flags & TIMEFMT_SKIP) + return 0; + + if (flags & TIMEFMT_ABSOLUTE) { + struct timespec anchor[1]; + + if (flags & TIMEFMT_REALTIME) + clock_gettime(CLOCK_REALTIME, anchor); + else + clock_gettime(CLOCK_MONOTONIC, anchor); + if (flags & TIMEFMT_UNTIL) + timespecsub(ts, anchor, real_ts); + else /* flags & TIMEFMT_SINCE */ + timespecsub(anchor, ts, real_ts); + } else + *real_ts = *ts; + + if (real_ts->tv_sec == 0 && real_ts->tv_nsec == 0 && + (flags & TIMEFMT_DASHES)) + return bputs(buf, dashes); + + if (real_ts->tv_sec < 0) { + if (flags & TIMEFMT_DASHES) + return bputs(buf, dashes); + + /* -0.3s is { -1s + 700ms } */ + real_ts->tv_sec = -real_ts->tv_sec - 1; + real_ts->tv_nsec = 1000000000L - real_ts->tv_nsec; + if (real_ts->tv_nsec >= 1000000000L) { + real_ts->tv_sec++; + real_ts->tv_nsec -= 1000000000L; + } + + /* all formats have a - make sense in front */ + ret += bputch(buf, '-'); + } + + if (flags & TIMEFMT_DECIMAL) { + ret += bprintfrr(buf, "%lld", (long long)real_ts->tv_sec); + if (precision == -1) + precision = 3; + ret += do_subsec(buf, real_ts, precision, flags); + return ret; + } + + /* these divisions may be slow on embedded boxes, hence only do the + * ones we need, plus the ?: zero check to hopefully skip zeros fast + */ + lldiv_t min_sec = lldiv(real_ts->tv_sec, 60); + + if (flags & TIMEFMT_MMSS) { + ret += bprintfrr(buf, "%02lld:%02lld", min_sec.quot, + min_sec.rem); + ret += do_subsec(buf, real_ts, precision, flags); + return ret; + } + + lldiv_t hour_min = min_sec.quot ? lldiv(min_sec.quot, 60) : (lldiv_t){}; + + if (flags & TIMEFMT_HHMMSS) { + ret += bprintfrr(buf, "%02lld:%02lld:%02lld", hour_min.quot, + hour_min.rem, min_sec.rem); + ret += do_subsec(buf, real_ts, precision, flags); + return ret; + } + + lldiv_t day_hour = + hour_min.quot ? lldiv(hour_min.quot, 24) : (lldiv_t){}; + lldiv_t week_day = + day_hour.quot ? lldiv(day_hour.quot, 7) : (lldiv_t){}; + + /* if sub-second precision is not supported, return */ + if (flags & TIMEFMT_BASIC) { + /* match frrtime_to_interval (without space flag) */ + if (week_day.quot) + ret += bprintfrr(buf, "%lldw%s%lldd%s%02lldh", + week_day.quot, space, week_day.rem, + space, day_hour.rem); + else if (day_hour.quot) + ret += bprintfrr(buf, "%lldd%s%02lldh%s%02lldm", + day_hour.quot, space, day_hour.rem, + space, hour_min.rem); + else + ret += bprintfrr(buf, "%02lld:%02lld:%02lld", + hour_min.quot, hour_min.rem, + min_sec.rem); + /* no sub-seconds here */ + return ret; + } + + /* default format */ + if (week_day.quot) + ret += bprintfrr(buf, "%lldw%s", week_day.quot, space); + if (week_day.rem || week_day.quot) + ret += bprintfrr(buf, "%lldd%s", week_day.rem, space); + + ret += bprintfrr(buf, "%02lld:%02lld:%02lld", day_hour.rem, + hour_min.rem, min_sec.rem); + + if (precision == -1) + precision = 3; + ret += do_subsec(buf, real_ts, precision, flags); + return ret; +} + +printfrr_ext_autoreg_p("TS", printfrr_ts); +static ssize_t printfrr_ts(struct fbuf *buf, struct printfrr_eargs *ea, + const void *vptr) +{ + const struct timespec *ts = vptr; + + if (!ts) + return bputs(buf, "(null)"); + return printfrr_time(buf, ea, ts, 0); +} + +printfrr_ext_autoreg_p("TV", printfrr_tv); +static ssize_t printfrr_tv(struct fbuf *buf, struct printfrr_eargs *ea, + const void *vptr) +{ + const struct timeval *tv = vptr; + struct timespec ts; + + if (!tv) + return bputs(buf, "(null)"); + + ts.tv_sec = tv->tv_sec; + ts.tv_nsec = tv->tv_usec * 1000; + return printfrr_time(buf, ea, &ts, 0); +} + +printfrr_ext_autoreg_p("TT", printfrr_tt); +static ssize_t printfrr_tt(struct fbuf *buf, struct printfrr_eargs *ea, + const void *vptr) +{ + const time_t *tt = vptr; + struct timespec ts; + + if (!tt) + return bputs(buf, "(null)"); + + ts.tv_sec = *tt; + ts.tv_nsec = 0; + return printfrr_time(buf, ea, &ts, TIMEFMT_SECONDS); +} diff --git a/lib/thread.c b/lib/thread.c index 7b223ed6de..77e34f48f3 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -2056,3 +2056,70 @@ bool thread_is_scheduled(struct thread *thread) return true; } + +static ssize_t printfrr_thread_dbg(struct fbuf *buf, struct printfrr_eargs *ea, + const struct thread *thread) +{ + static const char * const types[] = { + [THREAD_READ] = "read", + [THREAD_WRITE] = "write", + [THREAD_TIMER] = "timer", + [THREAD_EVENT] = "event", + [THREAD_READY] = "ready", + [THREAD_UNUSED] = "unused", + [THREAD_EXECUTE] = "exec", + }; + ssize_t rv = 0; + char info[16] = ""; + + if (!thread) + return bputs(buf, "{(thread *)NULL}"); + + rv += bprintfrr(buf, "{(thread *)%p arg=%p", thread, thread->arg); + + if (thread->type < array_size(types) && types[thread->type]) + rv += bprintfrr(buf, " %-6s", types[thread->type]); + else + rv += bprintfrr(buf, " INVALID(%u)", thread->type); + + switch (thread->type) { + case THREAD_READ: + case THREAD_WRITE: + snprintfrr(info, sizeof(info), "fd=%d", thread->u.fd); + break; + + case THREAD_TIMER: + snprintfrr(info, sizeof(info), "r=%pTVMud", &thread->u.sands); + break; + } + + rv += bprintfrr(buf, " %-12s %s() %s from %s:%d}", info, + thread->xref->funcname, thread->xref->dest, + thread->xref->xref.file, thread->xref->xref.line); + return rv; +} + +printfrr_ext_autoreg_p("TH", printfrr_thread); +static ssize_t printfrr_thread(struct fbuf *buf, struct printfrr_eargs *ea, + const void *ptr) +{ + const struct thread *thread = ptr; + struct timespec remain = {}; + + if (ea->fmt[0] == 'D') { + ea->fmt++; + return printfrr_thread_dbg(buf, ea, thread); + } + + if (!thread) { + /* need to jump over time formatting flag characters in the + * input format string, i.e. adjust ea->fmt! + */ + printfrr_time(buf, ea, &remain, + TIMEFMT_TIMER_DEADLINE | TIMEFMT_SKIP); + return bputch(buf, '-'); + } + + TIMEVAL_TO_TIMESPEC(&thread->u.sands, &remain); + return printfrr_time(buf, ea, &remain, TIMEFMT_TIMER_DEADLINE); +} diff --git a/lib/thread.h b/lib/thread.h index 39f21da11d..49a70696d0 100644 --- a/lib/thread.h +++ b/lib/thread.h @@ -128,6 +128,10 @@ struct thread { pthread_mutex_t mtx; /* mutex for thread.c functions */ }; +#ifdef _FRR_ATTRIBUTE_PRINTFRR +#pragma FRR printfrr_ext "%pTH" (struct thread *) +#endif + struct cpu_thread_history { int (*func)(struct thread *); atomic_size_t total_cpu_warn; diff --git a/pimd/pim_addr.c b/pimd/pim_addr.c index 825f38274a..8d89b57141 100644 --- a/pimd/pim_addr.c +++ b/pimd/pim_addr.c @@ -24,7 +24,7 @@ #include "prefix.h" -printfrr_ext_autoreg_p("PA", printfrr_pimaddr) +printfrr_ext_autoreg_p("PA", printfrr_pimaddr); static ssize_t printfrr_pimaddr(struct fbuf *buf, struct printfrr_eargs *ea, const void *vptr) { @@ -56,7 +56,7 @@ static ssize_t printfrr_pimaddr(struct fbuf *buf, struct printfrr_eargs *ea, #endif } -printfrr_ext_autoreg_p("SG", printfrr_sgaddr) +printfrr_ext_autoreg_p("SG", printfrr_sgaddr); static ssize_t printfrr_sgaddr(struct fbuf *buf, struct printfrr_eargs *ea, const void *vptr) { diff --git a/tests/lib/test_printfrr.c b/tests/lib/test_printfrr.c index 7694077574..8f9d637afd 100644 --- a/tests/lib/test_printfrr.c +++ b/tests/lib/test_printfrr.c @@ -186,6 +186,10 @@ int main(int argc, char **argv) test_va("VA [192.168.1.2 1234] --", "%pI4 %u", &ip, 1234); + inet_aton("0.0.0.0", &ip); + printchk("0.0.0.0", "%pI4", &ip); + printchk("*", "%pI4s", &ip); + snprintfrr(buf, sizeof(buf), "test%s", "#1"); csnprintfrr(buf, sizeof(buf), "test%s", "#2"); assert(strcmp(buf, "test#1test#2") == 0); @@ -274,5 +278,115 @@ int main(int argc, char **argv) inet_pton(AF_INET6, "fe2c::34", &nh.gate.ipv6); printchk("fe2c::34", "%pNHcg", &nh); + /* time printing */ + + /* need a non-UTC timezone for testing */ + setenv("TZ", "TEST-01:00", 1); + tzset(); + + struct timespec ts; + struct timeval tv; + time_t tt; + + ts.tv_sec = tv.tv_sec = tt = 1642015880; + ts.tv_nsec = 123456789; + tv.tv_usec = 234567; + + printchk("Wed Jan 12 20:31:20 2022", "%pTSR", &ts); + printchk("Wed Jan 12 20:31:20 2022", "%pTVR", &tv); + printchk("Wed Jan 12 20:31:20 2022", "%pTTR", &tt); + + FMT_NSTD(printchk("Wed Jan 12 20:31:20 2022", "%.3pTSR", &ts)); + + printchk("2022-01-12T20:31:20.123", "%pTSRi", &ts); + printchk("2022-01-12 20:31:20.123", "%pTSRip", &ts); + printchk("2022-01-12 20:31:20.123", "%pTSRpi", &ts); + FMT_NSTD(printchk("2022-01-12T20:31:20", "%.0pTSRi", &ts)); + FMT_NSTD(printchk("2022-01-12T20:31:20.123456789", "%.9pTSRi", &ts)); + FMT_NSTD(printchk("2022-01-12T20:31:20", "%.3pTTRi", &tt)); + + ts.tv_sec = tv.tv_sec = tt = 9 * 86400 + 12345; + + printchk("1w 2d 03:25:45.123", "%pTSIp", &ts); + printchk("1w2d03:25:45.123", "%pTSI", &ts); + printchk("1w2d03:25:45.234", "%pTVI", &tv); + printchk("1w2d03:25:45", "%pTTI", &tt); + + printchk("1w 2d 03h", "%pTVItp", &tv); + printchk("1w2d03h", "%pTSIt", &ts); + + printchk("219:25:45", "%pTVIh", &tv); + printchk("13165:45", "%pTVIm", &tv); + + ts.tv_sec = tv.tv_sec = tt = 1 * 86400 + 12345; + + printchk("1d 03:25:45.123", "%pTSIp", &ts); + printchk("1d03:25:45.234", "%pTVI", &tv); + + printchk("1d 03h 25m", "%pTVItp", &tv); + printchk("1d03h25m", "%pTSIt", &ts); + + printchk("98745.234", "%pTVId", &tv); + + printchk("27:25:45", "%pTVIh", &tv); + printchk("1645:45", "%pTVIm", &tv); + + ts.tv_sec = tv.tv_sec = tt = 12345; + + printchk("03:25:45.123", "%pTSIp", &ts); + printchk("03:25:45.123", "%pTSI", &ts); + printchk("03:25:45.234", "%pTVI", &tv); + printchk("03:25:45", "%pTTI", &tt); + + printchk("03:25:45", "%pTSItp", &ts); + printchk("03:25:45", "%pTVIt", &tv); + + printchk("12345.234", "%pTVId", &tv); + + printchk("03:25:45", "%pTVIh", &tv); + printchk("205:45", "%pTVIm", &tv); + + ts.tv_sec = tv.tv_sec = tt = 0; + + printchk("00:00:00.123", "%pTSIp", &ts); + printchk("00:00:00.123", "%pTSI", &ts); + printchk("00:00:00.234", "%pTVI", &tv); + printchk("00:00:00", "%pTTI", &tt); + + printchk("00:00:00", "%pTVItp", &tv); + printchk("00:00:00", "%pTSIt", &ts); + + printchk("0.234", "%pTVId", &tv); + printchk("0.234", "%pTVIdx", &tv); + printchk("-", "%pTTIdx", &tt); + + printchk("00:00:00", "%pTVIhx", &tv); + printchk("--:--:--", "%pTTIhx", &tt); + printchk("00:00", "%pTVImx", &tv); + printchk("--:--", "%pTTImx", &tt); + + ts.tv_sec = tv.tv_sec = tt = -10; + + printchk("-00:00:09.876", "%pTSIp", &ts); + printchk("-00:00:09.876", "%pTSI", &ts); + printchk("-00:00:09.765", "%pTVI", &tv); + printchk("-00:00:10", "%pTTI", &tt); + + printchk("-00:00:09", "%pTSItp", &ts); + printchk("-00:00:09", "%pTSIt", &ts); + printchk("-00:00:09", "%pTVIt", &tv); + printchk("-00:00:10", "%pTTIt", &tt); + + printchk("-9.765", "%pTVId", &tv); + printchk("-", "%pTVIdx", &tv); + + printchk("-00:00:09", "%pTSIh", &ts); + printchk("--:--:--", "%pTVIhx", &tv); + printchk("--:--:--", "%pTTIhx", &tt); + + printchk("-00:09", "%pTSIm", &ts); + printchk("--:--", "%pTVImx", &tv); + printchk("--:--", "%pTTImx", &tt); + return !!errors; } diff --git a/tools/checkpatch.pl b/tools/checkpatch.pl index d2eb20ce5b..db6460f343 100755 --- a/tools/checkpatch.pl +++ b/tools/checkpatch.pl @@ -5789,7 +5789,7 @@ sub process { } # check for vsprintf extension %p<foo> misuses - if ($^V && $^V ge 5.10.0 && + if (0 && $^V && $^V ge 5.10.0 && defined $stat && $stat =~ /^\+(?![^\{]*\{\s*).*\b(\w+)\s*\(.*$String\s*,/s && $1 !~ /^_*volatile_*$/) { |
