]> git.proxmox.com Git - mirror_frr.git/commitdiff
lib: add mt-safe debugging facilities
authorQuentin Young <qlyoung@cumulusnetworks.com>
Wed, 28 Feb 2018 21:14:45 +0000 (16:14 -0500)
committerQuentin Young <qlyoung@cumulusnetworks.com>
Thu, 1 Mar 2018 20:01:25 +0000 (15:01 -0500)
The current strategy for fine-grained debugging across FRR is to use
static long int bitfields, in combination with helper macros that are
copy-pasted between daemons, to hold state on what debugging information
should be collected at any given time. This has a couple of problems:

* These bitfields are generally extern'd and accessed everywhere, so
  they are not MT-safe or easy to make MT-safe
* Lots of code duplication from copy-pasting the DEBUG_* macros...
* Code duplication because of the "term" vs "conf" debugging concept

This patch aims to remedy that by providing some infrastructure to work
with debugs. The core concept of using bitfields has been retained, but
the number of these for each debug has been reduced to 1. This allows
easy use of lock-free methods for synchronizing access to debugging
info.

The helper macros have also been retained but they are now collected in
one place and perform exclusively atomic operations.

Finally there is a bit of code that allows daemons to register
callbacks, which I used to implement a command that will toggle all
debugging for any daemons that use these facilities.

Signed-off-by: Quentin Young <qlyoung@cumulusnetworks.com>
lib/debug.c [new file with mode: 0644]
lib/debug.h [new file with mode: 0644]
lib/subdir.am
vtysh/vtysh.c

diff --git a/lib/debug.c b/lib/debug.c
new file mode 100644 (file)
index 0000000..0bacf97
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Debugging utilities.
+ * Copyright (C) 2018  Cumulus Networks, Inc.
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <zebra.h>
+#include "debug.h"
+#include "command.h"
+
+static const struct debug_callbacks *callbacks;
+
+/* All code in this section should be reentrant and MT-safe */
+
+DEFUN_NOSH(debug_all, debug_all_cmd, "[no] debug all",
+          NO_STR DEBUG_STR "Toggle all debugging output\n")
+{
+       bool set = strmatch(argv[0]->text, "no");
+       uint32_t mode = DEBUG_NODE2MODE(vty->node);
+
+       if (callbacks->debug_set_all)
+               callbacks->debug_set_all(mode, set);
+       return CMD_SUCCESS;
+}
+
+/* ------------------------------------------------------------------------- */
+
+void debug_init(const struct debug_callbacks *cb)
+{
+       callbacks = cb;
+       install_element(ENABLE_NODE, &debug_all_cmd);
+       install_element(CONFIG_NODE, &debug_all_cmd);
+}
diff --git a/lib/debug.h b/lib/debug.h
new file mode 100644 (file)
index 0000000..3e6772a
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * Debugging utilities.
+ * Copyright (C) 2018  Cumulus Networks, Inc.
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef _FRRDEBUG_H
+#define _FRRDEBUG_H
+
+#include <zebra.h>
+#include "command.h"
+#include "frratomic.h"
+
+/*
+ * Debugging modes.
+ *
+ * FRR's convention is that a debug statement issued under the vty CONFIG_NODE
+ * persists to the config file, whereas the same debug statement issued from
+ * the ENABLE_NODE only persists for the current session. These are mapped to
+ * DEBUG_MODE_CONF and DEBUG_MODE_TERM respectively.
+ *
+ * They are not mutually exclusive and are placed in the MSB of the flags
+ * field in a debugging record.
+ */
+#define DEBUG_MODE_TERM 0x01000000
+#define DEBUG_MODE_CONF 0x02000000
+#define DEBUG_MODE_ALL (DEBUG_MODE_TERM | DEBUG_MODE_CONF)
+#define DEBUG_MODE_NONE 0x00000000
+#define DEBUG_OPT_ALL 0x00FFFFFF
+#define DEBUG_OPT_NONE 0x00000000
+
+
+/*
+ * Debugging record.
+ *
+ * All operations on this record exposed in this header are MT-safe.
+ *
+ * flags
+ *    A bitfield with the following format (bytes high to low)
+ *    - [0] Debugging mode field (MSB)  | Mode
+ *    - [1] Arbitrary flag field        | Option
+ *    - [2] Arbitrary flag field        | Option
+ *    - [3] Arbitrary flag field (LSB)  | Option
+ *
+ *              ALL THESE BYTES ARE YOURS - EXCEPT MODE.
+ *                      ATTEMPT NO BIT OPS THERE.
+ *
+ *    The MSB of this field determines the debug mode, Use the DEBUG_MODE*
+ *    macros to manipulate this byte.
+ *
+ *    The low 3 bytes of this field may be used to store arbitrary information.
+ *    Usually they are used to store flags that tune how detailed the logging
+ *    for a particular debug record is. Use the DEBUG_OPT* macros to manipulate
+ *    those bytes.
+ *
+ *    All operations performed on this field should be done using the macros
+ *    later in this header file. They are guaranteed to be atomic operations
+ *    with respect to this field. Using anything except the macros to
+ *    manipulate the flags field in a multithreaded environment results in
+ *    undefined behavior.
+ *
+ * desc
+ *    Human-readable description of this debugging record.
+ */
+struct debug {
+       _Atomic uint32_t flags;
+       const char *desc;
+};
+
+/*
+ * Callback set for debugging code.
+ *
+ * debug_set_all
+ *    Function pointer to call when the user requests that all debugs have a
+ *    mode set.
+ */
+struct debug_callbacks {
+       /*
+        * flags
+        *    flags to set on debug flag fields
+        *
+        * set
+        *    true: set flags
+        *    false: unset flags
+        */
+       void (*debug_set_all)(uint32_t flags, bool set);
+};
+
+/*
+ * Check if a mode is set for a debug.
+ *
+ * MT-Safe
+ */
+#define DEBUG_MODE_CHECK(name, type)                                           \
+       CHECK_FLAG_ATOMIC(&(name)->flags, (type)&DEBUG_MODE_ALL)
+
+/*
+ * Check if an option bit is set for a debug.
+ *
+ * MT-Safe
+ */
+#define DEBUG_OPT_CHECK(name, type)                                            \
+       CHECK_FLAG_ATOMIC(&(name)->flags, (type)&DEBUG_OPT_ALL)
+
+/*
+ * Check if bits are set for a debug.
+ *
+ * MT-Safe
+ */
+#define DEBUG_FLAGS_CHECK(name, type) CHECK_FLAG_ATOMIC(&(name)->flags, (type))
+
+/*
+ * Check if any mode is on for a debug.
+ *
+ * MT-Safe
+ */
+#define DEBUG(name) DEBUG_MODE_CHECK((name), DEBUG_MODE_ALL)
+
+/*
+ * Set modes on a debug.
+ *
+ * MT-Safe
+ */
+#define DEBUG_MODE_SET(name, type)                                             \
+       SET_FLAG_ATOMIC(&(name)->flags, (type)&DEBUG_MODE_ALL)
+
+/*
+ * Unset modes on a debug.
+ *
+ * MT-Safe
+ */
+#define DEBUG_MODE_UNSET(name, type)                                           \
+       UNSET_FLAG_ATOMIC(&(name)->flags, (type)&DEBUG_MODE_ALL)
+
+/*
+ * Set options on a debug.
+ *
+ * MT-Safe
+ */
+#define DEBUG_OPT_SET(name, type)                                              \
+       SET_FLAG_ATOMIC(&(name)->flags, (type)&DEBUG_OPT_ALL)
+
+/*
+ * Unset options on a debug.
+ *
+ * MT-Safe
+ */
+#define DEBUG_OPT_UNSET(name, type)                                            \
+       UNSET_FLAG_ATOMIC(&(name)->flags, (type)&DEBUG_OPT_ALL)
+
+/*
+ * Set bits on a debug.
+ *
+ * MT-Safe
+ */
+#define DEBUG_FLAGS_SET(name, type) SET_FLAG_ATOMIC(&(name)->flags, (type))
+
+/*
+ * Unset bits on a debug.
+ *
+ * MT-Safe
+ */
+#define DEBUG_FLAGS_UNSET(name, type) UNSET_FLAG_ATOMIC(&(name)->flags, (type))
+
+/*
+ * Unset all modes and options on a debug.
+ *
+ * MT-Safe
+ */
+#define DEBUG_CLEAR(name) RESET_FLAG_ATOMIC(&(name)->flags)
+
+/*
+ * Set all modes and options on a debug.
+ *
+ * MT-Safe
+ */
+#define DEBUG_ON(name)                                                         \
+       SET_FLAG_ATOMIC(&(name)->flags, DEBUG_MODE_ALL | DEBUG_OPT_ALL)
+
+/*
+ * Map a vty node to the correct debugging mode flags. FRR behaves such that a
+ * debug statement issued under the config node persists to the config file,
+ * whereas the same debug statement issued from the enable node only persists
+ * for the current session.
+ *
+ * MT-Safe
+ */
+#define DEBUG_NODE2MODE(vtynode)                                               \
+       (((vtynode) == CONFIG_NODE) ? DEBUG_MODE_ALL : DEBUG_MODE_TERM)
+
+
+/*
+ * Optional initializer for debugging. Highly recommended.
+ *
+ * This function installs common debugging commands and allows the caller to
+ * specify callbacks to take when these commands are issued, allowing the
+ * caller to respond to events such as a request to turn off all debugs.
+ *
+ * MT-Safe
+ */
+void debug_init(const struct debug_callbacks *cb);
+
+#endif /* _FRRDEBUG_H */
index e292d7a342d0c889f18bb8bd81bcbef225c040ae..c8da5a2a8c18692b1c0fa3a5fb485886b0a1ced6 100644 (file)
@@ -15,6 +15,7 @@ lib_libfrr_la_SOURCES = \
        lib/command_match.c \
        lib/command_parse.y \
        lib/csv.c \
+       lib/debug.c \
        lib/distribute.c \
        lib/event_counter.c \
        lib/ferr.c \
@@ -91,6 +92,7 @@ pkginclude_HEADERS += \
        lib/command_match.h \
        lib/compiler.h \
        lib/csv.h \
+       lib/debug.h \
        lib/distribute.h \
        lib/event_counter.h \
        lib/ferr.h \
index 8719226281f162afa2a9562afb71692229a828f3..23df78f23ec42ae524fd2a7b5014688daf76b97f 100644 (file)
@@ -1971,14 +1971,24 @@ static int show_per_daemon(const char *line, const char *headline)
        return ret;
 }
 
+DEFUNSH_HIDDEN (0x00,
+                vtysh_debug_all,
+                vtysh_debug_all_cmd,
+                "[no] debug all",
+                NO_STR
+                DEBUG_STR
+                "Toggle all debugs on or off\n")
+{
+       return CMD_SUCCESS;
+}
+
 DEFUN (vtysh_show_debugging,
        vtysh_show_debugging_cmd,
        "show debugging",
        SHOW_STR
        DEBUG_STR)
 {
-       return show_per_daemon("do show debugging\n",
-                              "");
+       return show_per_daemon("do show debugging\n", "");
 }
 
 DEFUN (vtysh_show_debugging_hashtable,
@@ -3368,14 +3378,17 @@ void vtysh_init_vty(void)
        install_element(ENABLE_NODE, &vtysh_start_zsh_cmd);
 #endif
 
+       /* debugging */
        install_element(VIEW_NODE, &vtysh_show_debugging_cmd);
        install_element(VIEW_NODE, &vtysh_show_debugging_hashtable_cmd);
+       install_element(VIEW_NODE, &vtysh_debug_all_cmd);
+       install_element(CONFIG_NODE, &vtysh_debug_all_cmd);
+
+       /* misc lib show commands */
        install_element(VIEW_NODE, &vtysh_show_memory_cmd);
        install_element(VIEW_NODE, &vtysh_show_modules_cmd);
-
        install_element(VIEW_NODE, &vtysh_show_work_queues_cmd);
        install_element(VIEW_NODE, &vtysh_show_work_queues_daemon_cmd);
-
        install_element(VIEW_NODE, &vtysh_show_thread_cmd);
 
        /* Logging */