Merge branch 'ovs-dev', remote branch 'origin' into bsd-port
[sliver-openvswitch.git] / lib / memory.c
diff --git a/lib/memory.c b/lib/memory.c
new file mode 100644 (file)
index 0000000..1babfe9
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2012 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include "memory.h"
+#include <stdbool.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include "dynamic-string.h"
+#include "poll-loop.h"
+#include "simap.h"
+#include "timeval.h"
+#include "unixctl.h"
+#include "vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(memory);
+
+/* The number of milliseconds before the first report of daemon memory usage,
+ * and the number of milliseconds between checks for daemon memory growth.  */
+#define MEMORY_CHECK_INTERVAL (10 * 1000)
+
+/* When we should next check memory usage and possibly trigger a report. */
+static long long int next_check;
+
+/* The last time at which we reported memory usage, and the usage we reported
+ * at that time. */
+static long long int last_report;
+static unsigned long int last_reported_maxrss;
+
+/* Are we expecting a call to memory_report()? */
+static bool want_report;
+
+/* Unixctl connections waiting for responses. */
+static struct unixctl_conn **conns;
+static size_t n_conns;
+
+static void memory_init(void);
+
+/* Runs the memory monitor.
+ *
+ * The client should call memory_should_report() afterward. */
+void
+memory_run(void)
+{
+    struct rusage usage;
+    long long int now;
+
+    memory_init();
+
+    /* Time for a check? */
+    now = time_msec();
+    if (now < next_check) {
+        return;
+    }
+    next_check = now + MEMORY_CHECK_INTERVAL;
+
+    /* Time for a report? */
+    getrusage(RUSAGE_SELF, &usage);
+    if (!last_reported_maxrss) {
+        VLOG_INFO("%lu kB peak resident set size after %.1f seconds",
+                  (unsigned long int) usage.ru_maxrss,
+                  (now - time_boot_msec()) / 1000.0);
+    } else if (usage.ru_maxrss >= last_reported_maxrss * 1.5) {
+        VLOG_INFO("peak resident set size grew %.0f%% in last %.1f seconds, "
+                  "from %lu kB to %lu kB",
+                  ((double) usage.ru_maxrss / last_reported_maxrss - 1) * 100,
+                  (now - last_report) / 1000.0,
+                  last_reported_maxrss, (unsigned long int) usage.ru_maxrss);
+    } else {
+        return;
+    }
+
+    /* Request a report. */
+    want_report = true;
+    last_report = now;
+    last_reported_maxrss = usage.ru_maxrss;
+}
+
+/* Causes the poll loop to wake up if the memory monitor needs to run. */
+void
+memory_wait(void)
+{
+    if (memory_should_report()) {
+        poll_immediate_wake();
+    }
+}
+
+/* Returns true if the caller should log some information about memory usage
+ * (with memory_report()), false otherwise. */
+bool
+memory_should_report(void)
+{
+    return want_report || n_conns > 0;
+}
+
+static void
+compose_report(const struct simap *usage, struct ds *s)
+{
+    const struct simap_node **nodes = simap_sort(usage);
+    size_t n = simap_count(usage);
+    size_t i;
+
+    for (i = 0; i < n; i++) {
+        const struct simap_node *node = nodes[i];
+
+        ds_put_format(s, "%s:%u ", node->name, node->data);
+    }
+    ds_chomp(s, ' ');
+    free(nodes);
+}
+
+/* Logs the contents of 'usage', as a collection of name-count pairs.
+ *
+ * 'usage' should capture large-scale statistics that one might reasonably
+ * expect to correlate with memory usage.  For example, each OpenFlow flow
+ * requires some memory, so ovs-vswitchd includes the total number of flows in
+ * 'usage'. */
+void
+memory_report(const struct simap *usage)
+{
+    struct ds s;
+    size_t i;
+
+    ds_init(&s);
+    compose_report(usage, &s);
+
+    if (want_report) {
+        VLOG_INFO("%s", ds_cstr(&s));
+        want_report = false;
+    }
+    if (n_conns) {
+        for (i = 0; i < n_conns; i++) {
+            unixctl_command_reply(conns[i], ds_cstr(&s));
+        }
+        free(conns);
+        conns = NULL;
+        n_conns = 0;
+    }
+
+    ds_destroy(&s);
+}
+
+static void
+memory_unixctl_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                    const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
+{
+    conns = xrealloc(conns, (n_conns + 1) * sizeof *conns);
+    conns[n_conns++] = conn;
+}
+
+static void
+memory_init(void)
+{
+    static bool inited = false;
+
+    if (!inited) {
+        inited = true;
+        unixctl_command_register("memory/show", "", 0, 0,
+                                 memory_unixctl_show, NULL);
+
+        next_check = time_boot_msec() + MEMORY_CHECK_INTERVAL;
+    }
+}