Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
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 "coverage.h"
19 : : #include <inttypes.h>
20 : : #include <stdlib.h>
21 : : #include "openvswitch/dynamic-string.h"
22 : : #include "hash.h"
23 : : #include "svec.h"
24 : : #include "timeval.h"
25 : : #include "unixctl.h"
26 : : #include "util.h"
27 : : #include "openvswitch/vlog.h"
28 : :
29 : 53956 : VLOG_DEFINE_THIS_MODULE(coverage);
30 : :
31 : : /* The coverage counters. */
32 : : static struct coverage_counter **coverage_counters = NULL;
33 : : static size_t n_coverage_counters = 0;
34 : : static size_t allocated_coverage_counters = 0;
35 : :
36 : : static struct ovs_mutex coverage_mutex = OVS_MUTEX_INITIALIZER;
37 : :
38 : 2420152 : DEFINE_STATIC_PER_THREAD_DATA(long long int, coverage_clear_time, LLONG_MIN);
39 : : static long long int coverage_run_time = LLONG_MIN;
40 : :
41 : : /* Index counter used to compute the moving average array's index. */
42 : : static unsigned int idx_count = 0;
43 : :
44 : : static void coverage_read(struct svec *);
45 : : static unsigned int coverage_array_sum(const unsigned int *arr,
46 : : const unsigned int len);
47 : :
48 : : /* Registers a coverage counter with the coverage core */
49 : : void
50 : 885607 : coverage_counter_register(struct coverage_counter* counter)
51 : : {
52 [ + + ]: 885607 : if (n_coverage_counters >= allocated_coverage_counters) {
53 : 157954 : coverage_counters = x2nrealloc(coverage_counters,
54 : : &allocated_coverage_counters,
55 : : sizeof(struct coverage_counter*));
56 : : }
57 : 885607 : coverage_counters[n_coverage_counters++] = counter;
58 : 885607 : }
59 : :
60 : : static void
61 : 0 : coverage_unixctl_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
62 : : const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
63 : : {
64 : : struct svec lines;
65 : : char *reply;
66 : :
67 : 0 : svec_init(&lines);
68 : 0 : coverage_read(&lines);
69 : 0 : reply = svec_join(&lines, "\n", "\n");
70 : 0 : unixctl_command_reply(conn, reply);
71 : 0 : free(reply);
72 : 0 : svec_destroy(&lines);
73 : 0 : }
74 : :
75 : : void
76 : 16217 : coverage_init(void)
77 : : {
78 : 16217 : unixctl_command_register("coverage/show", "", 0, 0,
79 : : coverage_unixctl_show, NULL);
80 : 16217 : }
81 : :
82 : : /* Sorts coverage counters in descending order by total, within equal
83 : : * totals alphabetically by name. */
84 : : static int
85 : 9867 : compare_coverage_counters(const void *a_, const void *b_)
86 : : {
87 : 9867 : const struct coverage_counter *const *ap = a_;
88 : 9867 : const struct coverage_counter *const *bp = b_;
89 : 9867 : const struct coverage_counter *a = *ap;
90 : 9867 : const struct coverage_counter *b = *bp;
91 [ + + ]: 9867 : if (a->total != b->total) {
92 [ + + ]: 3271 : return a->total < b->total ? 1 : -1;
93 : : } else {
94 : 6596 : return strcmp(a->name, b->name);
95 : : }
96 : : }
97 : :
98 : : static uint32_t
99 : 21 : coverage_hash(void)
100 : : {
101 : : struct coverage_counter **c;
102 : 21 : uint32_t hash = 0;
103 : : int n_groups, i;
104 : :
105 : : /* Sort coverage counters into groups with equal totals. */
106 : 21 : c = xmalloc(n_coverage_counters * sizeof *c);
107 : 21 : ovs_mutex_lock(&coverage_mutex);
108 [ + + ]: 1972 : for (i = 0; i < n_coverage_counters; i++) {
109 : 1951 : c[i] = coverage_counters[i];
110 : : }
111 : 21 : ovs_mutex_unlock(&coverage_mutex);
112 : 21 : qsort(c, n_coverage_counters, sizeof *c, compare_coverage_counters);
113 : :
114 : : /* Hash the names in each group along with the rank. */
115 : 21 : n_groups = 0;
116 [ + - ]: 334 : for (i = 0; i < n_coverage_counters; ) {
117 : : int j;
118 : :
119 [ + + ]: 334 : if (!c[i]->total) {
120 : 21 : break;
121 : : }
122 : 313 : n_groups++;
123 : 313 : hash = hash_int(i, hash);
124 [ + - ]: 929 : for (j = i; j < n_coverage_counters; j++) {
125 [ + + ]: 929 : if (c[j]->total != c[i]->total) {
126 : 313 : break;
127 : : }
128 : 616 : hash = hash_string(c[j]->name, hash);
129 : : }
130 : 313 : i = j;
131 : : }
132 : :
133 : 21 : free(c);
134 : :
135 : 21 : return hash_int(n_groups, hash);
136 : : }
137 : :
138 : : static bool
139 : 11 : coverage_hit(uint32_t hash)
140 : : {
141 : : enum { HIT_BITS = 1024, BITS_PER_WORD = 32 };
142 : : static uint32_t hit[HIT_BITS / BITS_PER_WORD];
143 : : BUILD_ASSERT_DECL(IS_POW2(HIT_BITS));
144 : :
145 : : static long long int next_clear = LLONG_MIN;
146 : :
147 : 11 : unsigned int bit_index = hash & (HIT_BITS - 1);
148 : 11 : unsigned int word_index = bit_index / BITS_PER_WORD;
149 : 11 : unsigned int word_mask = 1u << (bit_index % BITS_PER_WORD);
150 : :
151 : : /* Expire coverage hash suppression once a day. */
152 [ + + ]: 11 : if (time_msec() >= next_clear) {
153 : 10 : memset(hit, 0, sizeof hit);
154 : 10 : next_clear = time_msec() + 60 * 60 * 24 * 1000LL;
155 : : }
156 : :
157 [ + + ]: 11 : if (hit[word_index] & word_mask) {
158 : 1 : return true;
159 : : } else {
160 : 10 : hit[word_index] |= word_mask;
161 : 10 : return false;
162 : : }
163 : : }
164 : :
165 : : /* Logs the coverage counters, unless a similar set of events has already been
166 : : * logged.
167 : : *
168 : : * This function logs at log level VLL_INFO. Use care before adjusting this
169 : : * level, because depending on its configuration, syslogd can write changes
170 : : * synchronously, which can cause the coverage messages to take several seconds
171 : : * to write. */
172 : : void
173 : 11 : coverage_log(void)
174 : : {
175 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 3);
176 : :
177 [ + - ]: 11 : if (!VLOG_DROP_INFO(&rl)) {
178 : 11 : uint32_t hash = coverage_hash();
179 [ + + ]: 11 : if (coverage_hit(hash)) {
180 [ + - ]: 1 : VLOG_INFO("Skipping details of duplicate event coverage for "
181 : : "hash=%08"PRIx32, hash);
182 : : } else {
183 : : struct svec lines;
184 : : const char *line;
185 : : size_t i;
186 : :
187 : 10 : svec_init(&lines);
188 : 10 : coverage_read(&lines);
189 [ + + ][ + + ]: 324 : SVEC_FOR_EACH (i, line, &lines) {
190 [ + - ]: 314 : VLOG_INFO("%s", line);
191 : : }
192 : 10 : svec_destroy(&lines);
193 : : }
194 : : }
195 : 11 : }
196 : :
197 : : /* Adds coverage counter information to 'lines'. */
198 : : static void
199 : 10 : coverage_read(struct svec *lines)
200 : : {
201 : 10 : struct coverage_counter **c = coverage_counters;
202 : : unsigned long long int *totals;
203 : : size_t n_never_hit;
204 : : uint32_t hash;
205 : : size_t i;
206 : :
207 : 10 : hash = coverage_hash();
208 : :
209 : 10 : n_never_hit = 0;
210 : 10 : svec_add_nocopy(lines,
211 : : xasprintf("Event coverage, avg rate over last: %d "
212 : : "seconds, last minute, last hour, "
213 : : "hash=%08"PRIx32":",
214 : : COVERAGE_RUN_INTERVAL/1000, hash));
215 : :
216 : 10 : totals = xmalloc(n_coverage_counters * sizeof *totals);
217 : 10 : ovs_mutex_lock(&coverage_mutex);
218 [ + + ]: 935 : for (i = 0; i < n_coverage_counters; i++) {
219 : 925 : totals[i] = c[i]->total;
220 : : }
221 : 10 : ovs_mutex_unlock(&coverage_mutex);
222 : :
223 [ + + ]: 935 : for (i = 0; i < n_coverage_counters; i++) {
224 [ + + ]: 925 : if (totals[i]) {
225 : : /* Shows the averaged per-second rates for the last
226 : : * COVERAGE_RUN_INTERVAL interval, the last minute and
227 : : * the last hour. */
228 : 294 : svec_add_nocopy(lines,
229 : : xasprintf("%-24s %5.1f/sec %9.3f/sec "
230 : : "%13.4f/sec total: %llu",
231 : 294 : c[i]->name,
232 : 294 : (c[i]->min[(idx_count - 1) % MIN_AVG_LEN]
233 : 294 : * 1000.0 / COVERAGE_RUN_INTERVAL),
234 : 294 : coverage_array_sum(c[i]->min, MIN_AVG_LEN) / 60.0,
235 : 294 : coverage_array_sum(c[i]->hr, HR_AVG_LEN) / 3600.0,
236 : 294 : totals[i]));
237 : : } else {
238 : 631 : n_never_hit++;
239 : : }
240 : : }
241 : :
242 : 10 : svec_add_nocopy(lines, xasprintf("%"PRIuSIZE" events never hit", n_never_hit));
243 : 10 : free(totals);
244 : 10 : }
245 : :
246 : : /* Runs approximately every COVERAGE_CLEAR_INTERVAL amount of time to
247 : : * synchronize per-thread counters with global counters. Every thread maintains
248 : : * a separate timer to ensure all counters are periodically aggregated.
249 : : *
250 : : * Uses 'ovs_mutex_trylock()' if 'trylock' is true. This is to prevent
251 : : * multiple performance-critical threads contending over the 'coverage_mutex'.
252 : : *
253 : : * */
254 : : static void
255 : 605075 : coverage_clear__(bool trylock)
256 : : {
257 : : long long int now, *thread_time;
258 : :
259 : 605075 : now = time_msec();
260 : 605039 : thread_time = coverage_clear_time_get();
261 : :
262 : : /* Initialize the coverage_clear_time. */
263 [ + + ]: 605032 : if (*thread_time == LLONG_MIN) {
264 : 18836 : *thread_time = now + COVERAGE_CLEAR_INTERVAL;
265 : : }
266 : :
267 [ + + ]: 605032 : if (now >= *thread_time) {
268 : : size_t i;
269 : :
270 [ + + ]: 14681 : if (trylock) {
271 : : /* Returns if cannot acquire lock. */
272 [ - + ]: 36 : if (ovs_mutex_trylock(&coverage_mutex)) {
273 : 0 : return;
274 : : }
275 : : } else {
276 : 14645 : ovs_mutex_lock(&coverage_mutex);
277 : : }
278 : :
279 [ + + ]: 1266051 : for (i = 0; i < n_coverage_counters; i++) {
280 : 1251370 : struct coverage_counter *c = coverage_counters[i];
281 : 1251370 : c->total += c->count();
282 : : }
283 : 14681 : ovs_mutex_unlock(&coverage_mutex);
284 : 14681 : *thread_time = now + COVERAGE_CLEAR_INTERVAL;
285 : : }
286 : : }
287 : :
288 : : void
289 : 344812 : coverage_clear(void)
290 : : {
291 : 344812 : coverage_clear__(false);
292 : 344812 : }
293 : :
294 : : void
295 : 260279 : coverage_try_clear(void)
296 : : {
297 : 260279 : coverage_clear__(true);
298 : 260285 : }
299 : :
300 : : /* Runs approximately every COVERAGE_RUN_INTERVAL amount of time to update the
301 : : * coverage counters' 'min' and 'hr' array. 'min' array is for cumulating
302 : : * per second counts into per minute count. 'hr' array is for cumulating per
303 : : * minute counts into per hour count. Every thread may call this function. */
304 : : void
305 : 344812 : coverage_run(void)
306 : : {
307 : 344812 : struct coverage_counter **c = coverage_counters;
308 : : long long int now;
309 : :
310 : 344812 : ovs_mutex_lock(&coverage_mutex);
311 : 344812 : now = time_msec();
312 : : /* Initialize the coverage_run_time. */
313 [ + + ]: 344812 : if (coverage_run_time == LLONG_MIN) {
314 : 15692 : coverage_run_time = now + COVERAGE_RUN_INTERVAL;
315 : : }
316 : :
317 [ + + ]: 344812 : if (now >= coverage_run_time) {
318 : : size_t i, j;
319 : : /* Computes the number of COVERAGE_RUN_INTERVAL slots, since
320 : : * it is possible that the actual run interval is multiple of
321 : : * COVERAGE_RUN_INTERVAL. */
322 : 1405 : int slots = (now - coverage_run_time) / COVERAGE_RUN_INTERVAL + 1;
323 : :
324 [ + + ]: 83633 : for (i = 0; i < n_coverage_counters; i++) {
325 : : unsigned int count, portion;
326 : 82228 : unsigned int idx = idx_count;
327 : :
328 : : /* Computes the differences between the current total and the one
329 : : * recorded in last invocation of coverage_run(). */
330 : 82228 : count = c[i]->total - c[i]->last_total;
331 : 82228 : c[i]->last_total = c[i]->total;
332 : : /* The count over the time interval is evenly distributed
333 : : * among slots by calculating the portion. */
334 : 82228 : portion = count / slots;
335 : :
336 [ + + ]: 168767 : for (j = 0; j < slots; j++) {
337 : : /* Updates the index variables. */
338 : : /* The m_idx is increased from 0 to MIN_AVG_LEN - 1. Every
339 : : * time the m_idx finishes a cycle (a cycle is one minute),
340 : : * the h_idx is incremented by 1. */
341 : 86539 : unsigned int m_idx = idx % MIN_AVG_LEN;
342 : 86539 : unsigned int h_idx = idx / MIN_AVG_LEN;
343 : :
344 : 173078 : c[i]->min[m_idx] = portion + (j == (slots - 1)
345 [ + + ]: 86539 : ? count % slots : 0);
346 : 173078 : c[i]->hr[h_idx] = m_idx == 0
347 : 24835 : ? c[i]->min[m_idx]
348 [ + + ]: 86539 : : (c[i]->hr[h_idx] + c[i]->min[m_idx]);
349 : : /* This is to guarantee that h_idx ranges from 0 to 59. */
350 : 86539 : idx = (idx + 1) % (MIN_AVG_LEN * HR_AVG_LEN);
351 : : }
352 : : }
353 : :
354 : : /* Updates the global index variables. */
355 : 1405 : idx_count = (idx_count + slots) % (MIN_AVG_LEN * HR_AVG_LEN);
356 : : /* Updates the run time. */
357 : 1405 : coverage_run_time = now + COVERAGE_RUN_INTERVAL;
358 : : }
359 : 344812 : ovs_mutex_unlock(&coverage_mutex);
360 : 344812 : }
361 : :
362 : : static unsigned int
363 : 588 : coverage_array_sum(const unsigned int *arr, const unsigned int len)
364 : : {
365 : 588 : unsigned int sum = 0;
366 : : size_t i;
367 : :
368 : 588 : ovs_mutex_lock(&coverage_mutex);
369 [ + + ]: 21756 : for (i = 0; i < len; i++) {
370 : 21168 : sum += arr[i];
371 : : }
372 : 588 : ovs_mutex_unlock(&coverage_mutex);
373 : 588 : return sum;
374 : : }
|