--- /dev/null
+/*
+ * 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, ' ');
+}
+
+/* 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;
+ }
+}