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
|
#include "test-lib.h"
#include "lib-signals.h"
#include "guid.h"
#include "time-util.h"
#include "cpu-limit.h"
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/resource.h>
/* The CPU limits aren't exact. Allow this much leniency in the time
comparisons. Note that system CPU usage can grow very large on loaded
systems, so we're not checking its upper limit at all. */
#define ALLOW_MSECS_BELOW 500
#define ALLOW_MSECS_ABOVE 3000
static const char *const test_path = ".test.cpulimit";
static struct timeval get_cpu_time(enum cpu_limit_type type)
{
struct rusage rusage;
struct timeval cpu_usage = { 0, 0 };
/* Query cpu usage so far */
if (getrusage(RUSAGE_SELF, &rusage) < 0)
i_fatal("getrusage() failed: %m");
if ((type & CPU_LIMIT_TYPE_USER) != 0)
timeval_add(&cpu_usage, &rusage.ru_utime);
if ((type & CPU_LIMIT_TYPE_SYSTEM) != 0)
timeval_add(&cpu_usage, &rusage.ru_stime);
return cpu_usage;
}
static void test_cpu_loop_once(void)
{
guid_128_t guid;
/* consume some user CPU */
for (unsigned int i = 0; i < 10000; i++)
guid_128_generate(guid);
/* consume some system CPU */
int fd = creat(test_path, 0600);
if (fd == -1)
i_fatal("creat(%s) failed: %m", test_path);
if (write(fd, guid, sizeof(guid)) < 0)
i_fatal("write(%s) failed: %m", test_path);
i_close_fd(&fd);
}
static void
test_cpu_limit_simple(enum cpu_limit_type type, const char *type_str)
{
struct cpu_limit *climit;
struct timeval usage, cpu;
int diff_msecs;
test_begin(t_strdup_printf("cpu limit - simple (%s)", type_str));
lib_signals_init();
climit = cpu_limit_init(2, type);
usage = get_cpu_time(type);
while (!cpu_limit_exceeded(climit))
test_cpu_loop_once();
cpu_limit_deinit(&climit);
cpu = get_cpu_time(type);
diff_msecs = timeval_diff_msecs(&cpu, &usage);
test_assert_cmp(diff_msecs, >=, 2000 - ALLOW_MSECS_BELOW);
lib_signals_deinit();
test_end();
}
static void test_cpu_limit_nested(enum cpu_limit_type type, const char *type_str)
{
struct cpu_limit *climit1, *climit2;
struct timeval usage1, cpu;
unsigned int n;
int diff_msecs;
test_begin(t_strdup_printf("cpu limit - nested (%s)", type_str));
lib_signals_init();
climit1 = cpu_limit_init(3, type);
usage1 = get_cpu_time(type);
while (!cpu_limit_exceeded(climit1) && !test_has_failed()) {
climit2 = cpu_limit_init(1, type);
while (!cpu_limit_exceeded(climit2) && !test_has_failed())
test_cpu_loop_once();
cpu_limit_deinit(&climit2);
}
cpu_limit_deinit(&climit1);
cpu = get_cpu_time(type);
diff_msecs = timeval_diff_msecs(&cpu, &usage1);
test_assert_cmp(diff_msecs, >=, 3000 - ALLOW_MSECS_BELOW);
lib_signals_deinit();
test_end();
test_begin(t_strdup_printf("cpu limit - nested2 (%s)", type_str));
lib_signals_init();
climit1 = cpu_limit_init(3, type);
usage1 = get_cpu_time(type);
n = 0;
while (!cpu_limit_exceeded(climit1) && !test_has_failed()) {
if (++n >= 3) {
/* Consume last second in top cpu limit */
test_cpu_loop_once();
continue;
}
climit2 = cpu_limit_init(1, type);
while (!cpu_limit_exceeded(climit2) && !test_has_failed())
test_cpu_loop_once();
cpu_limit_deinit(&climit2);
}
cpu_limit_deinit(&climit1);
cpu = get_cpu_time(type);
diff_msecs = timeval_diff_msecs(&cpu, &usage1);
test_assert_cmp(diff_msecs, >=, 3000 - ALLOW_MSECS_BELOW);
i_unlink_if_exists(test_path);
lib_signals_deinit();
test_end();
}
void test_cpu_limit(void)
{
test_cpu_limit_simple(CPU_LIMIT_TYPE_USER, "user");
test_cpu_limit_simple(CPU_LIMIT_TYPE_SYSTEM, "system");
test_cpu_limit_simple(CPU_LIMIT_TYPE_ALL, "all");
test_cpu_limit_nested(CPU_LIMIT_TYPE_USER, "user");
test_cpu_limit_nested(CPU_LIMIT_TYPE_SYSTEM, "system");
test_cpu_limit_nested(CPU_LIMIT_TYPE_ALL, "all");
}
|