]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
add a rndc command to toggle jemalloc profiling
authorAydın Mercan <aydin@isc.org>
Mon, 12 Aug 2024 15:17:05 +0000 (18:17 +0300)
committerAydın Mercan <aydin@isc.org>
Sat, 25 Jan 2025 11:28:41 +0000 (14:28 +0300)
The new command is `rndc memprof`. The memory profiling status is also
reported inside `rndc status`. The status also shows whether named can
toggle memory profiling or not and if the server is built with jemalloc.

bin/named/control.c
bin/named/include/named/control.h
bin/named/include/named/server.h
bin/named/server.c
bin/rndc/rndc.c
bin/rndc/rndc.rst

index 9d7bc6f16717751bcf129b401a787662e3cf0915..4f93d228051aca93400c0b296910676aa898d0e4 100644 (file)
@@ -233,6 +233,8 @@ named_control_docommand(isccc_sexpr_t *message, bool readonly,
                   command_compare(command, NAMED_COMMAND_SIGN))
        {
                result = named_server_rekey(named_g_server, lex, text);
+       } else if (command_compare(command, NAMED_COMMAND_MEMPROF)) {
+               result = named_server_togglememprof(lex);
        } else if (command_compare(command, NAMED_COMMAND_MKEYS)) {
                result = named_server_mkeys(named_g_server, lex, text);
        } else if (command_compare(command, NAMED_COMMAND_NOTIFY)) {
index cb3c69c820316aa92cd4adcc033e56e3e4b92f0e..79f62e6c10c20f5527d4d1a6768c1ad9075f3904 100644 (file)
@@ -43,6 +43,7 @@
 #define NAMED_COMMAND_FREEZE      "freeze"
 #define NAMED_COMMAND_HALT        "halt"
 #define NAMED_COMMAND_LOADKEYS    "loadkeys"
+#define NAMED_COMMAND_MEMPROF     "memprof"
 #define NAMED_COMMAND_MKEYS       "managed-keys"
 #define NAMED_COMMAND_MODZONE     "modzone"
 #define NAMED_COMMAND_NOTIFY      "notify"
index 90375260b8e3776433db7abf19d6d8c27a84fd0d..394b55ad4df3dc98d8af0e765db4a8260acada4b 100644 (file)
@@ -388,3 +388,15 @@ named_server_fetchlimit(named_server_t *server, isc_lex_t *lex,
  */
 isc_result_t
 named_server_skr(named_server_t *server, isc_lex_t *lex, isc_buffer_t **text);
+
+/*%
+ * Toggle memory profiling if supported.
+ */
+isc_result_t
+named_server_togglememprof(isc_lex_t *lex);
+
+/*%
+ * Get status of memory profiling.
+ */
+const char *
+named_server_getmemprof(void);
index 9eab82568bf64b2773e6689765a6da82edbead86..8481cde8ba194972fe1f9f13bc1a8e81132f86b6 100644 (file)
 #include <named/smf_globals.h>
 #endif /* ifdef HAVE_LIBSCF */
 
+/* On DragonFly BSD the header does not provide jemalloc API */
+#if defined(HAVE_MALLOC_NP_H) && !defined(__DragonFly__)
+#include <malloc_np.h>
+#define JEMALLOC_API_SUPPORTED 1
+#elif defined(HAVE_JEMALLOC)
+#include <jemalloc/jemalloc.h>
+#define JEMALLOC_API_SUPPORTED 1
+#endif
+
 #ifdef HAVE_LMDB
 #include <lmdb.h>
 #define configure_newzones configure_newzones_db
@@ -358,6 +367,22 @@ typedef struct {
        isc_result_t result;
 } ns_dzarg_t;
 
+typedef enum {
+       MEMPROF_UNSUPPORTED = 0x00,
+       MEMPROF_INACTIVE = 0x01,
+       MEMPROF_FAILING = 0x02,
+       MEMPROF_OFF = 0x03,
+       MEMPROF_ON = 0x04,
+} memprof_status;
+
+static const char *memprof_status_text[] = {
+       [MEMPROF_UNSUPPORTED] = "UNSUPPORTED",
+       [MEMPROF_INACTIVE] = "INACTIVE",
+       [MEMPROF_FAILING] = "FAILING",
+       [MEMPROF_OFF] = "OFF",
+       [MEMPROF_ON] = "ON",
+};
+
 /*
  * These zones should not leak onto the Internet.
  */
@@ -10058,6 +10083,39 @@ named_server_closelogswanted(void *arg, int signum) {
        isc_async_run(named_g_mainloop, named_server_closelogs, server);
 }
 
+#ifdef JEMALLOC_API_SUPPORTED
+static isc_result_t
+memprof_toggle(bool active) {
+       if (mallctl("prof.active", NULL, NULL, &active, sizeof(active)) != 0) {
+               return ISC_R_FAILURE;
+       }
+
+       return ISC_R_SUCCESS;
+}
+
+static isc_result_t
+memprof_dump(void) {
+       if (mallctl("prof.dump", NULL, NULL, NULL, 0) != 0) {
+               return ISC_R_FAILURE;
+       }
+
+       return ISC_R_SUCCESS;
+}
+#else
+static isc_result_t
+memprof_toggle(bool active) {
+       UNUSED(active);
+
+       return ISC_R_NOTIMPLEMENTED;
+}
+
+static isc_result_t
+memprof_dump(void) {
+       return ISC_R_NOTIMPLEMENTED;
+}
+
+#endif /* JEMALLOC_API_SUPPORTED */
+
 void
 named_server_scan_interfaces(named_server_t *server) {
        isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER,
@@ -11930,6 +11988,10 @@ named_server_status(named_server_t *server, isc_buffer_t **text) {
                         : "OFF");
        CHECK(putstr(text, line));
 
+       snprintf(line, sizeof(line), "memory profiling is %s\n",
+                named_server_getmemprof());
+       CHECK(putstr(text, line));
+
        snprintf(line, sizeof(line), "recursive clients: %u/%u/%u\n",
                 isc_quota_getused(&server->sctx->recursionquota),
                 isc_quota_getsoft(&server->sctx->recursionquota),
@@ -16140,3 +16202,113 @@ cleanup:
 
        return result;
 }
+
+isc_result_t
+named_server_togglememprof(isc_lex_t *lex) {
+       isc_result_t result = ISC_R_FAILURE;
+       bool active;
+       char *ptr;
+
+       /* Skip the command name. */
+       ptr = next_token(lex, NULL);
+       if (ptr == NULL) {
+               return ISC_R_UNEXPECTEDEND;
+       }
+
+       ptr = next_token(lex, NULL);
+       if (ptr == NULL) {
+               return ISC_R_UNEXPECTEDEND;
+       } else if (!strcasecmp(ptr, "dump")) {
+               result = memprof_dump();
+               if (result != ISC_R_SUCCESS) {
+                       isc_log_write(NAMED_LOGCATEGORY_GENERAL,
+                                     NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+                                     "failed to dump memory profile");
+
+               } else {
+                       isc_log_write(NAMED_LOGCATEGORY_GENERAL,
+                                     NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+                                     "memory profile dumped");
+               }
+
+               goto done;
+       } else if (!strcasecmp(ptr, "on") || !strcasecmp(ptr, "yes") ||
+                  !strcasecmp(ptr, "enable") || !strcasecmp(ptr, "true"))
+       {
+               active = true;
+       } else if (!strcasecmp(ptr, "off") || !strcasecmp(ptr, "no") ||
+                  !strcasecmp(ptr, "disable") || !strcasecmp(ptr, "false"))
+       {
+               active = false;
+       } else {
+               return DNS_R_SYNTAX;
+       }
+
+       result = memprof_toggle(active);
+       if (result != ISC_R_SUCCESS) {
+               isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER,
+                             ISC_LOG_ERROR,
+                             "failed to toggle memory profiling");
+       } else {
+               isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER,
+                             ISC_LOG_INFO, "memory profiling %s",
+                             active ? "enabled" : "disabled");
+       }
+
+done:
+       return result;
+}
+
+#ifdef JEMALLOC_API_SUPPORTED
+const char *
+named_server_getmemprof(void) {
+       memprof_status status = MEMPROF_ON;
+       bool is_enabled;
+       size_t len = sizeof(is_enabled);
+
+       if (mallctl("config.prof", &is_enabled, &len, NULL, 0) != 0) {
+               status = MEMPROF_FAILING;
+               goto done;
+       }
+
+       INSIST(len == sizeof(is_enabled));
+
+       if (!is_enabled) {
+               status = MEMPROF_UNSUPPORTED;
+               goto done;
+       }
+
+       if (mallctl("opt.prof", &is_enabled, &len, NULL, 0) != 0) {
+               status = MEMPROF_FAILING;
+               goto done;
+       }
+
+       INSIST(len == sizeof(is_enabled));
+
+       if (!is_enabled) {
+               status = MEMPROF_INACTIVE;
+               goto done;
+       }
+
+       len = sizeof(is_enabled);
+       if (mallctl("prof.active", &is_enabled, &len, NULL, 0) != 0) {
+               status = MEMPROF_FAILING;
+               goto done;
+       }
+
+       INSIST(len == sizeof(is_enabled));
+
+       if (!is_enabled) {
+               status = MEMPROF_OFF;
+       }
+
+done:
+       return memprof_status_text[status];
+}
+
+#else  /* JEMALLOC_API_SUPPORTED */
+const char *
+named_server_getmemprof(void) {
+       return memprof_status_text[MEMPROF_UNSUPPORTED];
+}
+#endif /* JEMALLOC_API_SUPPORTED */
index 9750d5d5373784cd3898b249ee15af2d6008759f..38b1caf7755e406c9827351eac76897cea25e34a 100644 (file)
@@ -143,6 +143,10 @@ command is one of the following:\n\
                Display RFC 5011 managed keys information\n\
   managed-keys sync [class [view]]\n\
                Write RFC 5011 managed keys to disk\n\
+  memprof [ on | off | dump ]\n\
+               Enable / disable memory profiling or dump the profile.\n\
+               Requires named to built with jemalloc and run with the relevant\n\
+               MALLOC_CONF environment variables.\n\
   modzone zone [class [view]] { zone-options }\n\
                Modify a zone's configuration.\n\
                Requires allow-new-zones option.\n\
index 11c35c2d0376c53e8d45292a411168112d08d899..80681c84aee06a0cbd220c2b05cf08666fad9437 100644 (file)
@@ -322,6 +322,19 @@ Currently supported commands are:
       keys in the event of a trust anchor rollover, or as a brute-force
       repair for key maintenance problems.
 
+.. option:: memprof [(on | off | dump)]
+
+   This command controls memory profiling. To have any effect, :iscman:`named` must be
+   built with jemalloc, the library have profiling support enabled and run with the
+   ``prof:true`` allocator configuration. (either via ``MALLOC_CONF`` or ``/etc/malloc.conf``)
+
+   The ``prof_active:false`` option is recommended to ensure the profiling overhead does
+   not affect :iscman:`named` when not needed.
+
+   The ``on`` and ``off`` options will start and stop the jemalloc memory profiling respectively.
+   When run with the `dump` option, :iscman:`named` will dump the profile to the working
+   directory. The name will be chosen automatically by jemalloc.
+
 .. option:: modzone zone [class [view]] configuration
 
    This command modifies the configuration of a zone while the server is running. This