]>
Commit | Line | Data |
---|---|---|
0d085684 | 1 | /* |
b9de4087 | 2 | * Copyright (c) 2012, 2013 Nicira, Inc. |
0d085684 BP |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at: | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | ||
17 | #include <config.h> | |
18 | #include "memory.h" | |
19 | #include <stdbool.h> | |
20 | #include <sys/time.h> | |
21 | #include <sys/resource.h> | |
22 | #include "dynamic-string.h" | |
23 | #include "poll-loop.h" | |
24 | #include "simap.h" | |
25 | #include "timeval.h" | |
26 | #include "unixctl.h" | |
27 | #include "vlog.h" | |
28 | ||
29 | VLOG_DEFINE_THIS_MODULE(memory); | |
30 | ||
31 | /* The number of milliseconds before the first report of daemon memory usage, | |
32 | * and the number of milliseconds between checks for daemon memory growth. */ | |
33 | #define MEMORY_CHECK_INTERVAL (10 * 1000) | |
34 | ||
35 | /* When we should next check memory usage and possibly trigger a report. */ | |
36 | static long long int next_check; | |
37 | ||
38 | /* The last time at which we reported memory usage, and the usage we reported | |
39 | * at that time. */ | |
40 | static long long int last_report; | |
41 | static unsigned long int last_reported_maxrss; | |
42 | ||
43 | /* Are we expecting a call to memory_report()? */ | |
44 | static bool want_report; | |
45 | ||
46 | /* Unixctl connections waiting for responses. */ | |
47 | static struct unixctl_conn **conns; | |
48 | static size_t n_conns; | |
49 | ||
50 | static void memory_init(void); | |
51 | ||
52 | /* Runs the memory monitor. | |
53 | * | |
54 | * The client should call memory_should_report() afterward. */ | |
55 | void | |
56 | memory_run(void) | |
57 | { | |
58 | struct rusage usage; | |
59 | long long int now; | |
60 | ||
61 | memory_init(); | |
62 | ||
63 | /* Time for a check? */ | |
64 | now = time_msec(); | |
65 | if (now < next_check) { | |
66 | return; | |
67 | } | |
68 | next_check = now + MEMORY_CHECK_INTERVAL; | |
69 | ||
70 | /* Time for a report? */ | |
71 | getrusage(RUSAGE_SELF, &usage); | |
72 | if (!last_reported_maxrss) { | |
73 | VLOG_INFO("%lu kB peak resident set size after %.1f seconds", | |
74 | (unsigned long int) usage.ru_maxrss, | |
75 | (now - time_boot_msec()) / 1000.0); | |
76 | } else if (usage.ru_maxrss >= last_reported_maxrss * 1.5) { | |
77 | VLOG_INFO("peak resident set size grew %.0f%% in last %.1f seconds, " | |
78 | "from %lu kB to %lu kB", | |
79 | ((double) usage.ru_maxrss / last_reported_maxrss - 1) * 100, | |
80 | (now - last_report) / 1000.0, | |
81 | last_reported_maxrss, (unsigned long int) usage.ru_maxrss); | |
82 | } else { | |
83 | return; | |
84 | } | |
85 | ||
86 | /* Request a report. */ | |
87 | want_report = true; | |
88 | last_report = now; | |
89 | last_reported_maxrss = usage.ru_maxrss; | |
90 | } | |
91 | ||
92 | /* Causes the poll loop to wake up if the memory monitor needs to run. */ | |
93 | void | |
94 | memory_wait(void) | |
95 | { | |
96 | if (memory_should_report()) { | |
97 | poll_immediate_wake(); | |
98 | } | |
99 | } | |
100 | ||
101 | /* Returns true if the caller should log some information about memory usage | |
102 | * (with memory_report()), false otherwise. */ | |
103 | bool | |
104 | memory_should_report(void) | |
105 | { | |
106 | return want_report || n_conns > 0; | |
107 | } | |
108 | ||
109 | static void | |
110 | compose_report(const struct simap *usage, struct ds *s) | |
111 | { | |
112 | const struct simap_node **nodes = simap_sort(usage); | |
113 | size_t n = simap_count(usage); | |
114 | size_t i; | |
115 | ||
116 | for (i = 0; i < n; i++) { | |
117 | const struct simap_node *node = nodes[i]; | |
118 | ||
119 | ds_put_format(s, "%s:%u ", node->name, node->data); | |
120 | } | |
121 | ds_chomp(s, ' '); | |
d0ff75c6 | 122 | free(nodes); |
0d085684 BP |
123 | } |
124 | ||
125 | /* Logs the contents of 'usage', as a collection of name-count pairs. | |
126 | * | |
127 | * 'usage' should capture large-scale statistics that one might reasonably | |
128 | * expect to correlate with memory usage. For example, each OpenFlow flow | |
129 | * requires some memory, so ovs-vswitchd includes the total number of flows in | |
130 | * 'usage'. */ | |
131 | void | |
132 | memory_report(const struct simap *usage) | |
133 | { | |
134 | struct ds s; | |
135 | size_t i; | |
136 | ||
137 | ds_init(&s); | |
138 | compose_report(usage, &s); | |
139 | ||
1c85959e BP |
140 | if (want_report) { |
141 | if (s.length) { | |
142 | VLOG_INFO("%s", ds_cstr(&s)); | |
143 | } | |
0d085684 BP |
144 | want_report = false; |
145 | } | |
146 | if (n_conns) { | |
147 | for (i = 0; i < n_conns; i++) { | |
148 | unixctl_command_reply(conns[i], ds_cstr(&s)); | |
149 | } | |
150 | free(conns); | |
151 | conns = NULL; | |
152 | n_conns = 0; | |
153 | } | |
154 | ||
155 | ds_destroy(&s); | |
156 | } | |
157 | ||
158 | static void | |
159 | memory_unixctl_show(struct unixctl_conn *conn, int argc OVS_UNUSED, | |
160 | const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) | |
161 | { | |
162 | conns = xrealloc(conns, (n_conns + 1) * sizeof *conns); | |
163 | conns[n_conns++] = conn; | |
164 | } | |
165 | ||
166 | static void | |
167 | memory_init(void) | |
168 | { | |
169 | static bool inited = false; | |
170 | ||
171 | if (!inited) { | |
172 | inited = true; | |
173 | unixctl_command_register("memory/show", "", 0, 0, | |
174 | memory_unixctl_show, NULL); | |
175 | ||
176 | next_check = time_boot_msec() + MEMORY_CHECK_INTERVAL; | |
177 | } | |
178 | } |