Skip to content

Commit

Permalink
#86 vjtop的交互体验优化,显示界面优化,增加进程层面的线程数显示
Browse files Browse the repository at this point in the history
  • Loading branch information
calvin1978 committed Aug 14, 2018
1 parent 7f413ed commit 988970b
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 73 deletions.
6 changes: 3 additions & 3 deletions vjtop/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ JVM进程信息,一次拉取了JVM在操作系统层面和JVM层面的所有
### 2.21 进程区数据来源

* 从/proc/PID/* 文件中获取进程数据
* 从JDK的PerfData文件中获取JVM数据(JDK每秒写入/tmp/hsperfxxxx文件的统计数据)
* 从JDK的PerfData文件中获取JVM数据(JDK每秒写入/tmp/hsperfdata_$userid/$pid文件的统计数据)
* 使用目标JVM的JMX中获取JVM数据(如果目标JVM还没启动JMX,通过attach方式动态加载)

如果数据同时在PerfData和JMX存在,优先使用PerfData,除非PerfData被屏蔽。
Expand Down Expand Up @@ -98,11 +98,11 @@ JVM进程信息,一次拉取了JVM在操作系统层面和JVM层面的所有

* `rss`: `Resident Set Size`, 该进程在内存中的页的数量。该数据从/proc/\<pid\>/status中获取, 含义与[proc filesystem](http://man7.org/linux/man-pages/man5/proc.5.html)中一致。
* `swap`: 被交换出去的虚存大小。该数据从/proc/\<pid\>/status中获取, 含义与[proc filesystem](http://man7.org/linux/man-pages/man5/proc.5.html)中一致。
* `rchar/wchar`: 通过系统调用的读/写的字节数。该数据从/proc/\<pid\>/io中获取,含义与[proc filesystem](http://man7.org/linux/man-pages/man5/proc.5.html)中一致。
* `rchar/wchar`: 通过系统调用的读/写的字节数。包含从PageCache的读写,该数据从/proc/\<pid\>/io中获取,含义与[proc filesystem](http://man7.org/linux/man-pages/man5/proc.5.html)中一致。
* `read_bytes/write_bytes`: 真正达到存储层的读/写的字节数。该数据从/proc/\<pid\>/io中获取,含义与[proc filesystem](http://man7.org/linux/man-pages/man5/proc.5.html)中一致。
* `codeCache`: JIT编译的二进制代码的存放区,满后将不能编译新的代码。
* `direct`: 堆外内存,但注意新版Netty不经过JDK API所分配的堆外内存未能纪录。
* `SAFE-POINT`: PerfData开启时可用,JVM真正的停顿次数及停顿时间
* `SAFE-POINT`: PerfData开启时可用,JVM真正的停顿次数及停顿时间


线程区数据解释:
Expand Down
19 changes: 12 additions & 7 deletions vjtop/src/main/java/com/vip/vjtools/vjtop/InteractiveTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,10 @@ private void changeDisplayMode() {
tty.println(" Nothing be changed");
} else {
app.view.mode = detailMode;
tty.println(" Display mode changed to " + app.view.mode + " for next flush (" + app.nextFlushTime()
+ "s later)");
if (app.nextFlushTime() > 1) {
tty.println(" Display mode changed to " + app.view.mode + " for next flush (" + app.nextFlushTime()
+ "s later)");
}
}
app.continueFlush();
}
Expand All @@ -122,8 +124,10 @@ private void changeInterval() {
try {
int interval = Integer.parseInt(intervalStr);
if (interval != app.interval) {
tty.println(" Flush interval changed to " + interval + " seconds for next flush (" + app.nextFlushTime()
+ "s later)");
if (app.nextFlushTime() > 1) {
tty.println(" Flush interval changed to " + interval + " seconds for next flush ("
+ app.nextFlushTime() + "s later)");
}
app.interval = interval;
} else {
tty.println(" Nothing be changed");
Expand All @@ -135,7 +139,6 @@ private void changeInterval() {
}
}


private void changeThreadLimit() {
app.preventFlush();
tty.print(" Input number of threads to display(current " + app.view.threadLimit + "):");
Expand All @@ -144,8 +147,10 @@ private void changeThreadLimit() {
int threadLimit = Integer.parseInt(threadLimitStr);
if (threadLimit != app.view.threadLimit) {
app.view.threadLimit = threadLimit;
tty.println(" Number of threads to display changed to " + threadLimit + "for next flush ("
+ app.nextFlushTime() + "s later)");
if (app.nextFlushTime() > 1) {
tty.println(" Number of threads to display changed to " + threadLimit + "for next flush ("
+ app.nextFlushTime() + "s later)");
}
} else {
tty.println(" Nothing be changed");
}
Expand Down
33 changes: 18 additions & 15 deletions vjtop/src/main/java/com/vip/vjtools/vjtop/VJTop.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,30 +77,30 @@ public static void main(String[] args) {
System.exit(0);
}

// 2. create vminfo & view
// 2. create vminfo
String pid = parsePid(parser, optionSet);

VMDetailView.DetailMode displayMode = parseDisplayMode(optionSet);

Integer width = null;
if (optionSet.hasArgument("width")) {
width = (Integer) optionSet.valueOf("width");
}

VMInfo vminfo = VMInfo.processNewVM(pid);
if (vminfo.state != VMInfoState.ATTACHED) {
System.out.println("\nERROR: Could not attach to process, please find reason in README\n");
return;
}

// 3. create view
VMDetailView.DetailMode displayMode = parseDisplayMode(optionSet);
Integer width = null;
if (optionSet.hasArgument("width")) {
width = (Integer) optionSet.valueOf("width");
}

VMDetailView view = new VMDetailView(vminfo, displayMode, width);

if (optionSet.hasArgument("limit")) {
Integer limit = (Integer) optionSet.valueOf("limit");
view.threadLimit = limit;
}

// 3. create main application
// 4. create main application
VJTop app = new VJTop();
app.mainThread = Thread.currentThread();
app.view = view;
Expand All @@ -119,12 +119,12 @@ public static void main(String[] args) {
app.maxIterations = iterations;
}

// 4. start thread to get user input
// 5. start thread to get user input
Thread interactiveThread = new Thread(new InteractiveTask(app), "InteractiveThread");
interactiveThread.setDaemon(true);
interactiveThread.start();

// 5. run views
// 6. run app
app.run(view);
} catch (Exception e) {
e.printStackTrace(System.out);
Expand All @@ -140,6 +140,7 @@ private void run(VMDetailView view) throws Exception {
int iterations = 0;
while (!view.shouldExit()) {

// 非只打印一次的场景
if (maxIterations > 1 || maxIterations == -1) {
waitForInput();
clearTerminal();
Expand All @@ -150,15 +151,17 @@ private void run(VMDetailView view) throws Exception {
System.out.flush();

if (maxIterations > 0 && iterations >= maxIterations) {
System.out.println("");
System.out.flush();
break;
}

// 第一次只等待1秒
int sleepTime = iterations == 0 ? 1 : interval;
// 第一次最多只等待2秒
int sleepSeconds = (iterations == 0) ? Math.min(2, interval) : interval;

++iterations;
iterations++;
sleepStartTime = System.currentTimeMillis();
Utils.sleep(sleepTime * 1000);
Utils.sleep(sleepSeconds * 1000);
}
} catch (NoClassDefFoundError e) {
e.printStackTrace(System.out);
Expand Down
67 changes: 40 additions & 27 deletions vjtop/src/main/java/com/vip/vjtools/vjtop/VMDetailView.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,31 +68,6 @@ public void printView() throws Exception {
System.out.print(" Input command (h for help):");
}

/**
* 打印单条线程的stack strace
*/
public void printStack(long tid) throws IOException {
ThreadInfo info = vmInfo.getThreadMXBean().getThreadInfo(tid, 20);
if (info == null) {
System.err.println(" TID not exist:" + tid);
return;
}
StackTraceElement[] trace = info.getStackTrace();
System.out.println(" " + info.getThreadId() + ":" + info.getThreadName());
for (StackTraceElement traceElement : trace) {
System.out.println("\tat " + traceElement);
}
System.out.flush();
}

public void printAllThreads() throws IOException {
long tids[] = vmInfo.getThreadMXBean().getAllThreadIds();
ThreadInfo[] threadInfos = vmInfo.getThreadMXBean().getThreadInfo(tids);
for (ThreadInfo info : threadInfos) {
System.out.println(" " + info.getThreadId() + "\t:" + info.getThreadName());
}
System.out.flush();
}

private boolean checkState() {
if (vmInfo.state == VMInfoState.ATTACHED_UPDATE_ERROR) {
Expand All @@ -117,9 +92,10 @@ private void printJvmInfo() {
vmInfo.cpuLoad * 100, vmInfo.processors);

if (vmInfo.isLinux) {
System.out.printf(", %4s rss, %4s swap%n", Utils.toMB(vmInfo.rss), Utils.toMB(vmInfo.swap));
System.out.printf(", %4s rss, %4s swap, %d thread%n", Utils.toMB(vmInfo.rss), Utils.toMB(vmInfo.swap),
vmInfo.processThreads);

System.out.printf(" IO: %4s rchar, %4s wchar, %4s read, %4s write, NET: %4s recv, %4s send",
System.out.printf(" IO: %4s rchar, %4s wchar, DISK: %4sB read, %4sB write, NET: %4sB recv, %4sB send",
Utils.toSizeUnit(vmInfo.rchar.getRate()), Utils.toSizeUnit(vmInfo.wchar.getRate()),
Utils.toSizeUnit(vmInfo.readBytes.getRate()), Utils.toSizeUnit(vmInfo.writeBytes.getRate()),
Utils.toSizeUnit(vmInfo.receiveBytes.getRate()), Utils.toSizeUnit(vmInfo.sendBytes.getRate()));
Expand Down Expand Up @@ -361,13 +337,50 @@ private void printTopMemoryThreads(DetailMode mode) throws IOException {

private void printWelcome() {
if (firstTime) {
if (!vmInfo.isLinux) {
System.out.printf("%n OS isn't linux, Process's MEMORY, IO, DISK, NET data will be skipped.%n");
}

if (!vmInfo.perfDataSupport) {
System.out.printf("%n Perfdata doesn't support, SAFE-POINT data will be skipped.%n");
}

System.out.printf("%n VMARGS: %s%n%n", vmInfo.vmArgs);
firstTime = false;
}
System.out.printf("%n Collecting data, please wait ......%n%n");
collectingData = true;
}

/**
* 打印单条线程的stack strace,不造成停顿
*/
public void printStack(long tid) throws IOException {
ThreadInfo info = vmInfo.getThreadMXBean().getThreadInfo(tid, 20);
if (info == null) {
System.err.println(" TID not exist:" + tid);
return;
}
StackTraceElement[] trace = info.getStackTrace();
System.out.println(" " + info.getThreadId() + ":" + info.getThreadName());
for (StackTraceElement traceElement : trace) {
System.out.println("\tat " + traceElement);
}
System.out.flush();
}

/**
* 打印所有线程,只获取名称不获取stack,不造成停顿
*/
public void printAllThreads() throws IOException {
long tids[] = vmInfo.getThreadMXBean().getAllThreadIds();
ThreadInfo[] threadInfos = vmInfo.getThreadMXBean().getThreadInfo(tids);
for (ThreadInfo info : threadInfos) {
System.out.println(" " + info.getThreadId() + "\t:" + info.getThreadName());
}
System.out.flush();
}

private static double getThreadCPUUtilization(Long deltaThreadCpuTime, long totalTime, double factor) {
if (deltaThreadCpuTime == null) {
return 0;
Expand Down
36 changes: 20 additions & 16 deletions vjtop/src/main/java/com/vip/vjtools/vjtop/VMInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,12 @@ public class VMInfo {
public boolean threadMemoryAllocatedSupported;

// 动态数据//
public Rate upTimeMills = new Rate();
public Rate cpuTimeNanos = new Rate();

public long rss;
public long swap;
public long processThreads;

public Rate rchar = new Rate();
public Rate wchar = new Rate();
Expand All @@ -54,19 +58,14 @@ public class VMInfo {
public Rate receiveBytes = new Rate();
public Rate sendBytes = new Rate();

public Rate upTimeMills = new Rate();
public Rate cpuTimeNanos = new Rate();

public double cpuLoad = 0.0;
public double singleCoreCpuLoad = 0.0;

public Rate ygcCount = new Rate();
public Rate fullgcCount = new Rate();

public Rate ygcTimeMills = new Rate();
public Rate fullgcCount = new Rate();
public Rate fullgcTimeMills = new Rate();


public long threadActive;
public long threadDaemon;
public long threadPeak;
Expand Down Expand Up @@ -144,8 +143,7 @@ private void init() throws IOException {
try {
perfData = PerfData.connect(Integer.parseInt(pid));
perfDataSupport = true;
} catch (Exception e) {
System.err.println("PerfData not support");
} catch (Throwable ignored) {
}

if (perfDataSupport) {
Expand Down Expand Up @@ -188,8 +186,9 @@ public void update() throws IOException {
updateCpu();

if (isLinux) {
updateMemory();
updateProcessStatus();
updateIO();
updateNet();
}

updateThreads();
Expand All @@ -212,10 +211,11 @@ private void updateCpu() throws IOException {
singleCoreCpuLoad = Utils.calcLoad(cpuTimeNanos.delta / Utils.NANOS_TO_MILLS, upTimeMills.delta, 1);
}

private void updateMemory() {
private void updateProcessStatus() {
Map<String, String> procStatus = ProcFileData.getProcStatus(pid);
rss = Utils.parseFromSize(procStatus.get("VmRSS"));
swap = Utils.parseFromSize(procStatus.get("VmSwap"));
processThreads = Long.parseLong(procStatus.get("Threads"));
}

private void updateIO() {
Expand All @@ -234,19 +234,20 @@ private void updateIO() {
wchar.caculateRate(upTimeMills.delta);
readBytes.caculateRate(upTimeMills.delta);
writeBytes.caculateRate(upTimeMills.delta);
}

private void updateNet() {
Map<String, Long> procNet = ProcFileData.getProcNet(pid);

receiveBytes.current = procNet.get("receiveBytes");
sendBytes.current = procNet.get("sendBytes");
receiveBytes.current = procNet.get(ProcFileData.RECEIVE_BYTES);
sendBytes.current = procNet.get(ProcFileData.SEND_BYTES);

receiveBytes.update();
sendBytes.update();
receiveBytes.caculateRate(upTimeMills.delta);
sendBytes.caculateRate(upTimeMills.delta);
}


private void updateThreads() throws IOException {
if (perfDataSupport) {
threadActive = (Long) perfCounters.get("java.threads.live").getValue();
Expand All @@ -259,6 +260,8 @@ private void updateThreads() throws IOException {
threadPeak = jmxClient.getThreadMXBean().getPeakThreadCount();
threadStarted = jmxClient.getThreadMXBean().getTotalStartedThreadCount();
}


}

private void updateClassLoad() throws IOException {
Expand Down Expand Up @@ -369,7 +372,7 @@ public static class Rate {
private long last = -1;
private long current = -1;
private long delta = -1;
private long ratePerSecond;
private long ratePerSecond = -1;

public void update() {
if (last != -1) {
Expand All @@ -379,7 +382,9 @@ public void update() {
}

public void caculateRate(long deltaTimeMills) {
ratePerSecond = delta * 1000 / deltaTimeMills;
if (delta != -1) {
ratePerSecond = delta * 1000 / deltaTimeMills;
}
}

public long getDelta() {
Expand All @@ -393,7 +398,6 @@ public long getRate() {
public long getCurrent() {
return current;
}

}

public static class Usage {
Expand Down
4 changes: 1 addition & 3 deletions vjtop/src/main/java/com/vip/vjtools/vjtop/data/PerfData.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@ public static PerfData connect(long pid) {
throw e;
} catch (OutOfMemoryError e) {
throw e;
} catch (Error e) {
throw new RuntimeException("Cannot perf data for process " + pid + " - " + e.toString());
} catch (Exception e) {
} catch (Throwable e) {
throw new RuntimeException("Cannot perf data for process " + pid + " - " + e.toString());
}
}
Expand Down
Loading

0 comments on commit 988970b

Please sign in to comment.