Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/test/hotspot/jtreg/containers/docker/TestJcmdWithSideCar.java
40948 views
1
/*
2
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation.
8
*
9
* This code is distributed in the hope that it will be useful, but WITHOUT
10
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12
* version 2 for more details (a copy is included in the LICENSE file that
13
* accompanied this code).
14
*
15
* You should have received a copy of the GNU General Public License version
16
* 2 along with this work; if not, write to the Free Software Foundation,
17
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18
*
19
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20
* or visit www.oracle.com if you need additional information or have any
21
* questions.
22
*/
23
24
25
/*
26
* @test
27
* @summary Test JCMD with side car pattern.
28
* Sidecar is a common pattern used in the cloud environments for monitoring
29
* and other uses. In side car pattern the main application/service container
30
* is paired with a sidecar container by sharing certain aspects of container
31
* namespace such as PID namespace, specific sub-directories, IPC and more.
32
* @requires docker.support
33
* @requires vm.flagless
34
* @modules java.base/jdk.internal.misc
35
* java.management
36
* jdk.jartool/sun.tools.jar
37
* @library /test/lib
38
* @build EventGeneratorLoop
39
* @run driver TestJcmdWithSideCar
40
*/
41
import java.nio.file.Paths;
42
import java.util.Arrays;
43
import java.util.ArrayList;
44
import java.util.List;
45
import java.util.concurrent.TimeUnit;
46
import java.util.function.Consumer;
47
import java.util.stream.Collectors;
48
import jdk.test.lib.Container;
49
import jdk.test.lib.Utils;
50
import jdk.test.lib.containers.docker.Common;
51
import jdk.test.lib.containers.docker.DockerRunOptions;
52
import jdk.test.lib.containers.docker.DockerTestUtils;
53
import jdk.test.lib.process.OutputAnalyzer;
54
import jdk.test.lib.process.ProcessTools;
55
56
57
public class TestJcmdWithSideCar {
58
private static final String IMAGE_NAME = Common.imageName("jfr-jcmd");
59
private static final int TIME_TO_RUN_MAIN_PROCESS = (int) (30 * Utils.TIMEOUT_FACTOR); // seconds
60
private static final long TIME_TO_WAIT_FOR_MAIN_METHOD_START = 50 * 1000; // milliseconds
61
private static final String MAIN_CONTAINER_NAME = "test-container-main";
62
63
public static void main(String[] args) throws Exception {
64
if (!DockerTestUtils.canTestDocker()) {
65
return;
66
}
67
68
DockerTestUtils.buildJdkDockerImage(IMAGE_NAME, "Dockerfile-BasicTest", "jdk-docker");
69
70
try {
71
// Start the loop process in the "main" container, then run test cases
72
// using a sidecar container.
73
MainContainer mainContainer = new MainContainer();
74
mainContainer.start();
75
mainContainer.waitForMainMethodStart(TIME_TO_WAIT_FOR_MAIN_METHOD_START);
76
77
long mainProcPid = testCase01();
78
79
// Excluding the test case below until JDK-8228850 is fixed
80
// JDK-8228850: jhsdb jinfo fails with ClassCastException:
81
// s.j.h.oops.TypeArray cannot be cast to s.j.h.oops.Instance
82
// mainContainer.assertIsAlive();
83
// testCase02(mainProcPid);
84
85
// JCMD does not work in sidecar configuration, except for "jcmd -l".
86
// Including this test case to assist in reproduction of the problem.
87
// mainContainer.assertIsAlive();
88
// testCase03(mainProcPid);
89
90
mainContainer.waitForAndCheck(TIME_TO_RUN_MAIN_PROCESS * 1000);
91
} finally {
92
DockerTestUtils.removeDockerImage(IMAGE_NAME);
93
}
94
}
95
96
97
// Run "jcmd -l" in a sidecar container, find a target process.
98
private static long testCase01() throws Exception {
99
OutputAnalyzer out = runSideCar(MAIN_CONTAINER_NAME, "/jdk/bin/jcmd", "-l")
100
.shouldHaveExitValue(0)
101
.shouldContain("sun.tools.jcmd.JCmd");
102
long pid = findProcess(out, "EventGeneratorLoop");
103
if (pid == -1) {
104
throw new RuntimeException("Could not find specified process");
105
}
106
107
return pid;
108
}
109
110
// run jhsdb jinfo <PID> (jhsdb uses PTRACE)
111
private static void testCase02(long pid) throws Exception {
112
runSideCar(MAIN_CONTAINER_NAME, "/jdk/bin/jhsdb", "jinfo", "--pid", "" + pid)
113
.shouldHaveExitValue(0)
114
.shouldContain("Java System Properties")
115
.shouldContain("VM Flags");
116
}
117
118
// test jcmd with some commands (help, start JFR recording)
119
// JCMD will use signal mechanism and Unix Socket
120
private static void testCase03(long pid) throws Exception {
121
runSideCar(MAIN_CONTAINER_NAME, "/jdk/bin/jcmd", "" + pid, "help")
122
.shouldHaveExitValue(0)
123
.shouldContain("VM.version");
124
runSideCar(MAIN_CONTAINER_NAME, "/jdk/bin/jcmd", "" + pid, "JFR.start")
125
.shouldHaveExitValue(0)
126
.shouldContain("Started recording");
127
}
128
129
130
// JCMD relies on the attach mechanism (com.sun.tools.attach),
131
// which in turn relies on JVMSTAT mechanism, which puts its mapped
132
// buffers in /tmp directory (hsperfdata_<user>). Thus, in sidecar
133
// we mount /tmp via --volumes-from from the main container.
134
private static OutputAnalyzer runSideCar(String mainContainerName, String whatToRun,
135
String... args) throws Exception {
136
List<String> cmd = new ArrayList<>();
137
String[] command = new String[] {
138
Container.ENGINE_COMMAND, "run",
139
"--tty=true", "--rm",
140
"--cap-add=SYS_PTRACE", "--sig-proxy=true",
141
"--pid=container:" + mainContainerName,
142
"--volumes-from", mainContainerName,
143
IMAGE_NAME, whatToRun
144
};
145
146
cmd.addAll(Arrays.asList(command));
147
cmd.addAll(Arrays.asList(args));
148
return DockerTestUtils.execute(cmd);
149
}
150
151
// Returns PID of a matching process, or -1 if not found.
152
private static long findProcess(OutputAnalyzer out, String name) throws Exception {
153
List<String> l = out.asLines()
154
.stream()
155
.filter(s -> s.contains(name))
156
.collect(Collectors.toList());
157
if (l.isEmpty()) {
158
return -1;
159
}
160
String psInfo = l.get(0);
161
System.out.println("findProcess(): psInfo: " + psInfo);
162
String pid = psInfo.substring(0, psInfo.indexOf(' '));
163
System.out.println("findProcess(): pid: " + pid);
164
return Long.parseLong(pid);
165
}
166
167
private static DockerRunOptions commonDockerOpts(String className) {
168
return new DockerRunOptions(IMAGE_NAME, "/jdk/bin/java", className)
169
.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/")
170
.addJavaOpts("-cp", "/test-classes/");
171
}
172
173
private static void sleep(long delay) {
174
try {
175
Thread.sleep(delay);
176
} catch (InterruptedException e) {
177
System.out.println("InterruptedException" + e.getMessage());
178
}
179
}
180
181
182
static class MainContainer {
183
boolean mainMethodStarted;
184
Process p;
185
186
private Consumer<String> outputConsumer = s -> {
187
if (!mainMethodStarted && s.contains(EventGeneratorLoop.MAIN_METHOD_STARTED)) {
188
System.out.println("MainContainer: setting mainMethodStarted");
189
mainMethodStarted = true;
190
}
191
};
192
193
public Process start() throws Exception {
194
// start "main" container (the observee)
195
DockerRunOptions opts = commonDockerOpts("EventGeneratorLoop");
196
opts.addDockerOpts("--cap-add=SYS_PTRACE")
197
.addDockerOpts("--name", MAIN_CONTAINER_NAME)
198
.addDockerOpts("--volume", "/tmp")
199
.addDockerOpts("--volume", Paths.get(".").toAbsolutePath() + ":/workdir/")
200
.addJavaOpts("-XX:+UsePerfData")
201
.addClassOptions("" + TIME_TO_RUN_MAIN_PROCESS);
202
// avoid large Xmx
203
opts.appendTestJavaOptions = false;
204
205
List<String> cmd = DockerTestUtils.buildJavaCommand(opts);
206
ProcessBuilder pb = new ProcessBuilder(cmd);
207
p = ProcessTools.startProcess("main-container-process",
208
pb,
209
outputConsumer);
210
return p;
211
}
212
213
public void waitForMainMethodStart(long howLong) {
214
long expiration = System.currentTimeMillis() + howLong;
215
216
do {
217
if (mainMethodStarted) {
218
return;
219
}
220
sleep(200);
221
} while (System.currentTimeMillis() < expiration);
222
223
throw new RuntimeException("Timed out while waiting for main() to start");
224
}
225
226
public void assertIsAlive() throws Exception {
227
if (!p.isAlive()) {
228
throw new RuntimeException("Main container process stopped unexpectedly, exit value: "
229
+ p.exitValue());
230
}
231
}
232
233
public void waitFor(long timeout) throws Exception {
234
p.waitFor(timeout, TimeUnit.MILLISECONDS);
235
}
236
237
public void waitForAndCheck(long timeout) throws Exception {
238
int exitValue = -1;
239
int retryCount = 3;
240
241
do {
242
waitFor(timeout);
243
try {
244
exitValue = p.exitValue();
245
} catch(IllegalThreadStateException ex) {
246
System.out.println("IllegalThreadStateException occured when calling exitValue()");
247
retryCount--;
248
}
249
} while (exitValue == -1 && retryCount > 0);
250
251
if (exitValue != 0) {
252
throw new RuntimeException("DockerThread stopped unexpectedly, non-zero exit value is " + exitValue);
253
}
254
}
255
256
}
257
258
}
259
260