diff --git a/src/libcharon/plugins/vici/vici_query.c b/src/libcharon/plugins/vici/vici_query.c index e7c48f4aa..54833abde 100644 --- a/src/libcharon/plugins/vici/vici_query.c +++ b/src/libcharon/plugins/vici/vici_query.c @@ -17,9 +17,13 @@ #include "vici_builder.h" #include +#include #ifndef WIN32 #include #endif +#ifdef HAVE_MALLINFO +#include +#endif #include @@ -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); diff --git a/src/swanctl/Makefile.am b/src/swanctl/Makefile.am index a262a2fa7..385737ad4 100644 --- a/src/swanctl/Makefile.am +++ b/src/swanctl/Makefile.am @@ -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 = \ diff --git a/src/swanctl/command.h b/src/swanctl/command.h index 3c21484cd..8510fa44d 100644 --- a/src/swanctl/command.h +++ b/src/swanctl/command.h @@ -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 index 000000000..b5425f504 --- /dev/null +++ b/src/swanctl/commands/stats.c @@ -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 . + * + * 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 + +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"}, + } + }); +}