1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
|
/*
* Copyright (c) 2012, 2013 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.
*
* This function, and the remainder of this module's interface, should be
* called from only a single thread. */
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) {
if (s.length) {
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;
}
}
|