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
|
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.maintenance.servicedump;
import com.yahoo.vespa.hosted.node.admin.task.util.fs.ContainerPath;
import com.yahoo.yolean.concurrent.Sleeper;
import java.time.Duration;
import java.util.List;
import static com.yahoo.vespa.hosted.node.admin.maintenance.servicedump.Artifact.Classification.CONFIDENTIAL;
import static com.yahoo.vespa.hosted.node.admin.maintenance.servicedump.Artifact.Classification.INTERNAL;
/**
* @author bjorncs
*/
class JvmDumper {
private JvmDumper() {}
static class HeapDump implements ArtifactProducer {
@Override public String artifactName() { return "jvm-heap-dump"; }
@Override public String description() { return "JVM heap dump"; }
@Override
public List<Artifact> produceArtifacts(Context ctx) {
ContainerPath heapDumpFile = ctx.outputContainerPath().resolve("jvm-heap-dump.hprof");
List<String> cmd = List.of(
"jmap", "-dump:live,format=b,file=" + heapDumpFile.pathInContainer(), Integer.toString(ctx.servicePid()));
ctx.executeCommandInNode(cmd, true);
return List.of(
Artifact.newBuilder().classification(CONFIDENTIAL).file(heapDumpFile).compressOnUpload().build());
}
}
static class Jmap implements ArtifactProducer {
@Override public String artifactName() { return "jvm-jmap"; }
@Override public String description() { return "JVM jmap output"; }
@Override
public List<Artifact> produceArtifacts(Context ctx) {
ContainerPath jmapReport = ctx.outputContainerPath().resolve("jvm-jmap.txt");
List<String> cmd = List.of("bash", "-c", "jhsdb jmap --heap --pid " + ctx.servicePid() + " > " + jmapReport.pathInContainer());
ctx.executeCommandInNode(cmd, true);
return List.of(Artifact.newBuilder().classification(INTERNAL).file(jmapReport).build());
}
}
static class Jstat implements ArtifactProducer {
@Override public String artifactName() { return "jvm-jstat"; }
@Override public String description() { return "JVM jstat output"; }
@Override
public List<Artifact> produceArtifacts(Context ctx) {
ContainerPath jstatReport = ctx.outputContainerPath().resolve("jvm-jstat.txt");
List<String> cmd = List.of("bash", "-c", "jstat -gcutil " + ctx.servicePid() + " > " + jstatReport.pathInContainer());
ctx.executeCommandInNode(cmd, true);
return List.of(Artifact.newBuilder().classification(INTERNAL).file(jstatReport).build());
}
}
static class Jstack implements ArtifactProducer {
@Override public String artifactName() { return "jvm-jstack"; }
@Override public String description() { return "JVM jstack output"; }
@Override
public List<Artifact> produceArtifacts(Context ctx) {
ContainerPath jstackReport = ctx.outputContainerPath().resolve("jvm-jstack.txt");
ctx.executeCommandInNode(List.of("bash", "-c", "jstack " + ctx.servicePid() + " > " + jstackReport.pathInContainer()), true);
return List.of(Artifact.newBuilder().classification(INTERNAL).file(jstackReport).build());
}
}
static class JavaFlightRecorder implements ArtifactProducer {
private final Sleeper sleeper;
JavaFlightRecorder(Sleeper sleeper) { this.sleeper = sleeper; }
@Override public String artifactName() { return "jvm-jfr"; }
@Override public String description() { return "Java Flight Recorder recording"; }
@Override
public List<Artifact> produceArtifacts(ArtifactProducer.Context ctx) {
int seconds = (int) (ctx.options().duration().orElse(30.0));
ContainerPath outputFile = ctx.outputContainerPath().resolve("recording.jfr");
List<String> startCommand = List.of("jcmd", Integer.toString(ctx.servicePid()), "JFR.start", "name=host-admin",
"path-to-gc-roots=true", "settings=profile", "filename=" + outputFile.pathInContainer(), "duration=" + seconds + "s");
ctx.executeCommandInNode(startCommand, true);
sleeper.sleep(Duration.ofSeconds(seconds).plusSeconds(1));
int maxRetries = 10;
List<String> checkCommand = List.of("jcmd", Integer.toString(ctx.servicePid()), "JFR.check", "name=host-admin");
for (int i = 0; i < maxRetries; i++) {
boolean stillRunning = ctx.executeCommandInNode(checkCommand, true).getOutputLines().stream()
.anyMatch(l -> l.contains("name=host-admin") && l.contains("running"));
if (!stillRunning) {
Artifact a = Artifact.newBuilder()
.classification(CONFIDENTIAL).file(outputFile).compressOnUpload().build();
return List.of(a);
}
sleeper.sleep(Duration.ofSeconds(1));
}
throw new RuntimeException("Failed to wait for JFR dump to complete after " + maxRetries + " retries");
}
}
}
|