Merge branch 'vici-stats'
authorMartin Willi <martin@revosec.ch>
Tue, 17 Jun 2014 15:56:05 +0000 (17:56 +0200)
committerMartin Willi <martin@revosec.ch>
Tue, 17 Jun 2014 15:56:05 +0000 (17:56 +0200)
Add a vici/swanctl "stats" command to print daemon info, similar to the header
shown in "ipsec statusall".

src/libcharon/plugins/vici/vici_query.c
src/swanctl/Makefile.am
src/swanctl/command.h
src/swanctl/commands/stats.c [new file with mode: 0644]

index e7c48f4..54833ab 100644 (file)
 #include "vici_builder.h"
 
 #include <inttypes.h>
+#include <time.h>
 #ifndef WIN32
 #include <sys/utsname.h>
 #endif
+#ifdef HAVE_MALLINFO
+#include <malloc.h>
+#endif
 
 #include <daemon.h>
 
@@ -39,6 +43,11 @@ struct private_vici_query_t {
         * Dispatcher
         */
        vici_dispatcher_t *dispatcher;
+
+       /**
+        * Daemon startup timestamp
+        */
+       time_t uptime;
 };
 
 /**
@@ -832,6 +841,152 @@ CALLBACK(version, vici_message_t*,
        return b->finalize(b);
 }
 
+/**
+ * Callback function for memusage summary
+ */
+CALLBACK(sum_usage, void,
+       vici_builder_t *b, int count, size_t bytes, int whitelisted)
+{
+       b->begin_section(b, "mem");
+       b->add_kv(b, "total", "%zu", bytes);
+       b->add_kv(b, "allocs", "%d", count);
+       b->end_section(b);
+}
+
+CALLBACK(stats, vici_message_t*,
+       private_vici_query_t *this, char *name, u_int id, vici_message_t *request)
+{
+       vici_builder_t *b;
+       enumerator_t *enumerator;
+       plugin_t *plugin;
+       time_t since, now;
+       int i;
+
+       b = vici_builder_create();
+
+       now = time_monotonic(NULL);
+       since = time(NULL) - (now - this->uptime);
+
+       b->begin_section(b, "uptime");
+       b->add_kv(b, "running", "%V", &now, &this->uptime);
+       b->add_kv(b, "since", "%T", &since, FALSE);
+       b->end_section(b);
+
+       b->begin_section(b, "workers");
+       b->add_kv(b, "total", "%d",
+               lib->processor->get_total_threads(lib->processor));
+       b->add_kv(b, "idle", "%d",
+               lib->processor->get_idle_threads(lib->processor));
+       b->begin_section(b, "active");
+       for (i = 0; i < JOB_PRIO_MAX; i++)
+       {
+               b->add_kv(b, enum_to_name(job_priority_names, i), "%d",
+                       lib->processor->get_working_threads(lib->processor, i));
+       }
+       b->end_section(b);
+       b->end_section(b);
+
+       b->begin_section(b, "queues");
+       for (i = 0; i < JOB_PRIO_MAX; i++)
+       {
+               b->add_kv(b, enum_to_name(job_priority_names, i), "%d",
+                       lib->processor->get_job_load(lib->processor, i));
+       }
+       b->end_section(b);
+
+       b->add_kv(b, "scheduled", "%d",
+               lib->scheduler->get_job_load(lib->scheduler));
+
+       b->begin_section(b, "ikesas");
+       b->add_kv(b, "total", "%u",
+               charon->ike_sa_manager->get_count(charon->ike_sa_manager));
+       b->add_kv(b, "half-open", "%u",
+               charon->ike_sa_manager->get_half_open_count(charon->ike_sa_manager,
+                                                                                                       NULL));
+       b->end_section(b);
+
+       b->begin_list(b, "plugins");
+       enumerator = lib->plugins->create_plugin_enumerator(lib->plugins);
+       while (enumerator->enumerate(enumerator, &plugin, NULL))
+       {
+               b->add_li(b, "%s", plugin->get_name(plugin));
+       }
+       enumerator->destroy(enumerator);
+       b->end_list(b);
+
+       if (lib->leak_detective)
+       {
+               lib->leak_detective->usage(lib->leak_detective, NULL, sum_usage, b);
+       }
+#ifdef WIN32
+       else
+       {
+               DWORD lasterr = ERROR_INVALID_HANDLE;
+               HANDLE heaps[32];
+               int i, count;
+               char buf[16];
+               size_t total = 0;
+               int allocs = 0;
+
+               b->begin_section(b, "mem");
+               count = GetProcessHeaps(countof(heaps), heaps);
+               for (i = 0; i < count; i++)
+               {
+                       PROCESS_HEAP_ENTRY entry = {};
+                       size_t heap_total = 0;
+                       int heap_allocs = 0;
+
+                       if (HeapLock(heaps[i]))
+                       {
+                               while (HeapWalk(heaps[i], &entry))
+                               {
+                                       if (entry.wFlags & PROCESS_HEAP_ENTRY_BUSY)
+                                       {
+                                               heap_total += entry.cbData;
+                                               heap_allocs++;
+                                       }
+                               }
+                               lasterr = GetLastError();
+                               HeapUnlock(heaps[i]);
+                       }
+                       if (lasterr != ERROR_NO_MORE_ITEMS)
+                       {
+                               break;
+                       }
+                       snprintf(buf, sizeof(buf), "heap-%d", i);
+                       b->begin_section(b, buf);
+                       b->add_kv(b, "total", "%zu", heap_total);
+                       b->add_kv(b, "allocs", "%d", heap_allocs);
+                       b->end_section(b);
+
+                       total += heap_total;
+                       allocs += heap_allocs;
+               }
+               if (lasterr == ERROR_NO_MORE_ITEMS)
+               {
+                       b->add_kv(b, "total", "%zu", total);
+                       b->add_kv(b, "allocs", "%d", allocs);
+               }
+               b->end_section(b);
+       }
+#endif
+
+#ifdef HAVE_MALLINFO
+       {
+               struct mallinfo mi = mallinfo();
+
+               b->begin_section(b, "mallinfo");
+               b->add_kv(b, "sbrk", "%d", mi.arena);
+               b->add_kv(b, "mmap", "%d", mi.hblkhd);
+               b->add_kv(b, "used", "%d", mi.uordblks);
+               b->add_kv(b, "free", "%d", mi.fordblks);
+               b->end_section(b);
+       }
+#endif /* HAVE_MALLINFO */
+
+       return b->finalize(b);
+}
+
 static void manage_command(private_vici_query_t *this,
                                                   char *name, vici_command_cb_t cb, bool reg)
 {
@@ -853,6 +1008,7 @@ static void manage_commands(private_vici_query_t *this, bool reg)
        manage_command(this, "list-conns", list_conns, reg);
        manage_command(this, "list-certs", list_certs, reg);
        manage_command(this, "version", version, reg);
+       manage_command(this, "stats", stats, reg);
 }
 
 METHOD(vici_query_t, destroy, void,
@@ -874,6 +1030,7 @@ vici_query_t *vici_query_create(vici_dispatcher_t *dispatcher)
                        .destroy = _destroy,
                },
                .dispatcher = dispatcher,
+               .uptime = time_monotonic(NULL),
        );
 
        manage_commands(this, TRUE);
index a262a2f..385737a 100644 (file)
@@ -15,6 +15,7 @@ swanctl_SOURCES = \
        commands/load_pools.c \
        commands/log.c \
        commands/version.c \
+       commands/stats.c \
        swanctl.c swanctl.h
 
 swanctl_LDADD = \
index 3c21484..8510fa4 100644 (file)
@@ -27,7 +27,7 @@
 /**
  * Maximum number of commands (+1).
  */
-#define MAX_COMMANDS 15
+#define MAX_COMMANDS 16
 
 /**
  * Maximum number of options in a command (+3)
diff --git a/src/swanctl/commands/stats.c b/src/swanctl/commands/stats.c
new file mode 100644 (file)
index 0000000..b5425f5
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2014 Martin Willi
+ * Copyright (C) 2014 revosec AG
+ *
+ * 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.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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.
+ */
+
+#include "command.h"
+
+#include <errno.h>
+
+static int stats(vici_conn_t *conn)
+{
+       vici_req_t *req;
+       vici_res_t *res;
+       char *arg;
+       command_format_options_t format = COMMAND_FORMAT_NONE;
+
+       while (TRUE)
+       {
+               switch (command_getopt(&arg))
+               {
+                       case 'h':
+                               return command_usage(NULL);
+                       case 'P':
+                               format |= COMMAND_FORMAT_PRETTY;
+                               /* fall through to raw */
+                       case 'r':
+                               format |= COMMAND_FORMAT_RAW;
+                               continue;
+                       case EOF:
+                               break;
+                       default:
+                               return command_usage("invalid --stats option");
+               }
+               break;
+       }
+
+       req = vici_begin("stats");
+       res = vici_submit(req, conn);
+       if (!res)
+       {
+               fprintf(stderr, "stats request failed: %s\n", strerror(errno));
+               return errno;
+       }
+       if (format & COMMAND_FORMAT_RAW)
+       {
+               vici_dump(res, "stats reply", format & COMMAND_FORMAT_PRETTY, stdout);
+       }
+       else
+       {
+               printf("uptime: %s, since %s\n",
+                       vici_find_str(res, "", "uptime.running"),
+                       vici_find_str(res, "", "uptime.since"));
+
+               printf("worker threads: %s total, %s idle, working: %s/%s/%s/%s\n",
+                       vici_find_str(res, "", "workers.total"),
+                       vici_find_str(res, "", "workers.idle"),
+                       vici_find_str(res, "", "workers.active.critical"),
+                       vici_find_str(res, "", "workers.active.high"),
+                       vici_find_str(res, "", "workers.active.medium"),
+                       vici_find_str(res, "", "workers.active.low"));
+
+               printf("job queues: %s/%s/%s/%s\n",
+                       vici_find_str(res, "", "queues.critical"),
+                       vici_find_str(res, "", "queues.high"),
+                       vici_find_str(res, "", "queues.medium"),
+                       vici_find_str(res, "", "queues.low"));
+
+               printf("jobs scheduled: %s\n",
+                       vici_find_str(res, "", "scheduled"));
+
+               printf("IKE_SAs: %s total, %s half-open\n",
+                       vici_find_str(res, "", "ikesas.total"),
+                       vici_find_str(res, "", "ikesas.half-open"));
+
+               if (vici_find_str(res, NULL, "mem.total"))
+               {
+                       printf("memory usage: %s bytes, %s allocations\n",
+                               vici_find_str(res, "", "mem.total"),
+                               vici_find_str(res, "", "mem.allocs"));
+               }
+               if (vici_find_str(res, NULL, "mallinfo.sbrk"))
+               {
+                       printf("mallinfo: sbrk %s, mmap %s, used %s, free %s\n",
+                               vici_find_str(res, "", "mallinfo.sbrk"),
+                               vici_find_str(res, "", "mallinfo.mmap"),
+                               vici_find_str(res, "", "mallinfo.used"),
+                               vici_find_str(res, "", "mallinfo.free"));
+               }
+       }
+       vici_free_res(res);
+       return 0;
+}
+
+/**
+ * Register the command.
+ */
+static void __attribute__ ((constructor))reg()
+{
+       command_register((command_t) {
+               stats, 'S', "stats", "show daemon stats information",
+               {"[--raw|--pretty]"},
+               {
+                       {"help",                'h', 0, "show usage information"},
+                       {"raw",                 'r', 0, "dump raw response message"},
+                       {"pretty",              'P', 0, "dump raw response message in pretty print"},
+               }
+       });
+}