-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathMemoryTest.groovy
202 lines (186 loc) · 5.88 KB
/
MemoryTest.groovy
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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
#!/bin/env groovy
import groovy.transform.Field
@Field int interval = 600
@Field String processid = ""
@Field boolean debug = false
@Field int count = 5
@Field float thresholdMultiplier = 1.1
@Field int startValue = -1
@Field Date startTime = new Date()
/*
* Messages for debug mode
*/
void debugOut(message) {
if (debug) {
message = message.trim()
println "${ new Date() }[DEBUG] $message"
}
}
void infoOut(message) {
message = message.trim()
println "${ new Date() }[INFO] $message"
}
void parseOpts(args) {
infoOut "Parsing options"
def cli = new CliBuilder(usage:'groovy MemoryTest.groovy [-h -v -i <interval> -p <processId> -c <count> -t <threshold>]')
cli.with {
h longOpt: 'help', 'Show usage'
v longOpt: 'verbose', 'Verbose mode'
i longOpt: 'interval', args:1, argName:'interval', "Delay between samples (seconds) [600]"
c longOpt: 'count', args:1, argName:'count', "Number of samples to record [5]"
p longOpt: 'processId', args:1, argName:'process', "ID of Zanata server process, eg. 123456"
t longOpt: 'threshold', args:1, argName:'thresholdMultiplier', "Max memory increase (multiplied) before failure [1.1]"
s longOpt: 'startMem', args:1, argName:'startmem', "Starting memory (for script testing)"
}
def options = cli.parse args
if (!options) {
return
}
if (options.h) {
cli.usage()
System.exit 0
}
if (options.v) {
debug = true
}
if (options.i) {
interval = options.i.toInteger()
}
if (options.p) {
processid = options.p
}
if (options.c) {
count = options.c.toInteger()
}
if (options.t) {
thresholdMultiplier = options.t.toFloat()
}
if (options.s) {
startValue = options.s.toInteger()
}
}
/*
* Exit with failure
*/
void exitWithError(message) {
debugOut "Exiting for"
infoOut message
System.exit 1
}
/**
* Verify process is functional
* This uses the system util 'pkill -0', which has the no-op signal of 0
* @param pid Process ID
*/
void verifyProcess(pid) {
try {
command = "kill -0 $pid"
debugOut command
excode = command.execute().waitFor()
debugOut "Kill process check code $excode"
if (excode != 0) {
exitWithError "Unable to verify running process $pid"
}
} catch (IllegalThreadStateException e) {
exitWithError "Error attempting to verify running process $pid : $e"
}
}
/**
* Retrieve the process ID via the process name
* This uses the system util 'pgrep'
* @param processName A string that exists in the name of the Zanata process
*/
Integer getPid(processName) {
pid = processid.toString().trim()
if (pid.isEmpty()) {
command = "pgrep -f $processName"
debugOut command
pid = command.execute().text
debugOut "pgrep: $pid"
if (pid.isEmpty()) {
exitWithError "Error: Could not find running process with name $processName"
}
}
verifyProcess pid
return pid as Integer
}
/**
* Get memory usage from process, with delay
* @param pid Process ID of Server
* @param delay Seconds to wait before sampling
*/
Integer getMemoryUsage(pid, delay) {
String command = "pidstat -p $pid -r $delay 2"
debugOut command
result = command
.execute()
.text
.split('\n')?.
find { it.contains "java" }
debugOut result
return result.split()[5] as Integer
}
/**
* End the test sequence
* @param message Pass/fail message to report
* @param passed Test pass/fail status
* @param endtime Time the test ended
* @param endmemory Total memory used at test end
*/
void finishTest(String message, boolean passed, Date endtime, int endmemory) {
infoOut "\n[END TEST]"
infoOut "Start: $startTime"
infoOut "End: $endtime"
infoOut "Start Memory: $startingMemory"
infoOut "End Memory: $endmemory"
if (!passed) {
exitWithError message
} else {
infoOut "[PASS] $message"
}
}
/**
* The test procedure
* This test locates the running Zanata Server process, takes
* samples - ie. the amount of Virtual Memory allocated - and
* warns if the memory usage increases.
* If the value exceeds the acceptable threshold, the test will
* fail.
*/
void test() {
parseOpts args
debugOut "Verbose mode enabled"
debugOut "Interval is $interval seconds"
debugOut "Count is $count iterations"
debugOut "Threshold Multiplier is $thresholdMultiplier"
// Validate process
String pid = getPid 'org.jboss.as.standalone'
if (pid == null || pid.isEmpty()) {
exitWithError "Process not found, run Zanata server first!"
}
// Get starting memory and prepare test
startingMemory = startValue > 0 ? startValue : getMemoryUsage(pid, 1)
infoOut "Starting memory is $startingMemory"
int oldMemory = startingMemory
int memoryThreshold = (startingMemory * thresholdMultiplier).toInteger()
debugOut "Memory threshold is $memoryThreshold"
String testMessage = ""
// Begin sampling
1.upto(count, {
verifyProcess(pid)
newMemory = getMemoryUsage(pid, interval)
infoOut "Sample $it:$newMemory"
debugOut "[INFO] Start $startingMemory Old $oldMemory New $newMemory Upper $memoryThreshold"
if (newMemory > memoryThreshold) {
finishTest("[FAIL] Memory exceeds threshold ($newMemory > $memoryThreshold)", false, new Date(), newMemory)
} else if (newMemory > oldMemory) {
infoOut "[WARNING] Memory usage increased ($newMemory > $oldMemory)"
oldMemory = newMemory
debugOut "Old memory value updated to $oldMemory"
testMessage = "memory increased to $newMemory (${ ((newMemory - startingMemory)/startingMemory) * 100 }%)"
}
})
finishTest(testMessage, true, new Date(), newMemory)
}
// Execute
test()