Skip to content

Conversation

1599-web
Copy link

@1599-web 1599-web commented Aug 4, 2025

JFR 文件分析功能:前后端实现

本次 PR 实现了对 .jfr 文件的分析能力,包括前后端服务的完整集成。

模块说明

  • 后端模块arthas-jfr-backend

    • 使用 Spring Boot 实现 REST 服务
    • 支持 .jfr 文件上传、解析、火焰图等数据接口
    • 使用 JMC(Java Mission Control)依赖进行 JFR 分析
    • 本模块要求使用 JDK 17 编译
  • 前端模块arthas-jfr-frontend

    • 实现用户界面,用于上传 .jfr 文件并展示火焰图
    • 技术栈:Vue / React(根据实际情况填写)

使用说明

在 Arthas 命令行中执行:

jfr-analyze

相关 Issue

Closes #3014

@CLAassistant
Copy link

CLAassistant commented Aug 4, 2025

CLA assistant check
All committers have signed the CLA.

@hengyunabc hengyunabc requested a review from Copilot August 5, 2025 02:13
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements a comprehensive JFR (Java Flight Recorder) file analysis feature with both frontend and backend components, enabling users to upload, analyze, and visualize JFR files through flame graphs.

  • Adds a new Arthas command jfr-analyze to launch the JFR analysis service
  • Implements a Spring Boot backend service for JFR file processing and analysis
  • Creates a React-based frontend application for file management and flame graph visualization

Reviewed Changes

Copilot reviewed 152 out of 156 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
pom.xml Adds new module references for JFR frontend and backend components
core/src/main/java/com/taobao/arthas/core/command/basic1000/JFRansCommand.java New Arthas command to launch JFR analysis service
core/src/main/java/com/taobao/arthas/core/command/BuiltinCommandPack.java Registers the new JFR analysis command
arthas-jfr-backend/* Complete Spring Boot backend implementation with JFR analysis capabilities
arthas-jfr-frontend/* React frontend application for JFR file management and visualization
Files not reviewed (1)
  • arthas-jfr-frontend/package-lock.json: Language not supported

ProcessBuilder builder = new ProcessBuilder(
"java", "-jar", jarPath
);
builder.inheritIO();
Copy link
Preview

Copilot AI Aug 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using inheritIO() will redirect the child process's output to the parent process, which may interfere with Arthas's console output and user interaction. Consider using redirectOutput() and redirectError() with specific files or streams instead.

Suggested change
builder.inheritIO();
// Redirect output and error to log files to avoid interfering with Arthas console
File outputLog = new File(System.getProperty("java.io.tmpdir"), "arthas-jfr-analyze-out.log");
File errorLog = new File(System.getProperty("java.io.tmpdir"), "arthas-jfr-analyze-err.log");
builder.redirectOutput(outputLog);
builder.redirectError(errorLog);

Copilot uses AI. Check for mistakes.

Comment on lines +11 to +27

@Name("jfr-analyze")
@Summary("启动 JFR 分析服务")
@Description("运行一个 HTTP 服务用于 .jfr 文件上传和火焰图展示")
public class JFRansCommand extends AnnotatedCommand {

private static volatile boolean isStarted = false;

@Override
public void process(CommandProcess process) {
try {
if (isStarted) {
process.write("服务已运行,访问 http://localhost:8200\n");
process.end();
return;
}

Copy link
Preview

Copilot AI Aug 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The volatile boolean flag for tracking service state is not thread-safe for the check-then-act pattern. Multiple threads could pass the check simultaneously. Consider using AtomicBoolean with compareAndSet() or proper synchronization.

Suggested change
@Name("jfr-analyze")
@Summary("启动 JFR 分析服务")
@Description("运行一个 HTTP 服务用于 .jfr 文件上传和火焰图展示")
public class JFRansCommand extends AnnotatedCommand {
private static volatile boolean isStarted = false;
@Override
public void process(CommandProcess process) {
try {
if (isStarted) {
process.write("服务已运行,访问 http://localhost:8200\n");
process.end();
return;
}
import java.util.concurrent.atomic.AtomicBoolean;
@Name("jfr-analyze")
@Summary("启动 JFR 分析服务")
@Description("运行一个 HTTP 服务用于 .jfr 文件上传和火焰图展示")
public class JFRansCommand extends AnnotatedCommand {
private static final AtomicBoolean isStarted = new AtomicBoolean(false);
@Override
public void process(CommandProcess process) {
try {
if (isStarted.get()) {
process.write("服务已运行,访问 http://localhost:8200\n");
process.end();
return;
}
// Only one thread should start the service
if (!isStarted.compareAndSet(false, true)) {
process.write("服务已运行,访问 http://localhost:8200\n");
process.end();
return;
}

Copilot uses AI. Check for mistakes.

Comment on lines +9 to +18
private static final PageView<?> EMPTY = new PageView<>(null, 0, Collections.emptyList());

/**
* Return an empty page view.
*
* @param <T> data type
* @return empty page view
*/
@SuppressWarnings("unchecked")
public static <T> PageView<T> empty() { return (PageView<T>) EMPTY;
Copy link
Preview

Copilot AI Aug 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The empty() method returns a raw cast which can lead to unchecked cast warnings. Consider using proper generics or creating type-specific empty instances.

Suggested change
private static final PageView<?> EMPTY = new PageView<>(null, 0, Collections.emptyList());
/**
* Return an empty page view.
*
* @param <T> data type
* @return empty page view
*/
@SuppressWarnings("unchecked")
public static <T> PageView<T> empty() { return (PageView<T>) EMPTY;
/**
* Return an empty page view.
*
* @param <T> data type
* @return empty page view
*/
public static <T> PageView<T> empty() {
return new PageView<>(null, 0, Collections.emptyList());

Copilot uses AI. Check for mistakes.

Comment on lines 114 to 115
System.out.println(fg);
System.out.println(fg.getSymbolTable());
Copy link
Preview

Copilot AI Aug 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using System.out.println for debugging in production code. Consider using proper logging with the existing slf4j logger or removing debug output.

Suggested change
System.out.println(fg);
System.out.println(fg.getSymbolTable());
log.debug("FlameGraph: {}", fg);
log.debug("SymbolTable: {}", fg.getSymbolTable());

Copilot uses AI. Check for mistakes.

@Override
public Metadata getMetadata() {
// 直接复用 JFRAnalyzerImpl 的 metadata 逻辑
return new JFRAnalyzerImpl(null, null, null).metadata();
Copy link
Preview

Copilot AI Aug 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating a JFRAnalyzerImpl with null parameters may cause NullPointerException or unexpected behavior. The constructor expects valid Path and options parameters for proper initialization.

Suggested change
return new JFRAnalyzerImpl(null, null, null).metadata();
// Use a dummy Path and empty options map to avoid NullPointerException
Path dummyPath = Path.of("dummy.jfr");
Map<String, String> emptyOptions = java.util.Collections.emptyMap();
return new JFRAnalyzerImpl(dummyPath, emptyOptions, null).metadata();

Copilot uses AI. Check for mistakes.

@@ -0,0 +1,574 @@
// @ts-nocheck
// eslint-disable-next-line
Copy link
Preview

Copilot AI Aug 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blanket ESLint disable comments should be avoided. Consider fixing specific linting issues or using more targeted disable comments for specific rules.

Suggested change
// eslint-disable-next-line

Copilot uses AI. Check for mistakes.

// file.transferTo(savePath.toFile());
// 使用配置的路径
Path savePath = Paths.get(arthasConfig.getJfrStoragePath(), uniqueName);
Files.createDirectories(savePath.getParent());

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
This path depends on a
user-provided value
.
@Override
public boolean isValidJFRFile(Path path) {
try {
return path != null && Files.exists(path) && Files.isRegularFile(path);

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
@hengyunabc
Copy link
Collaborator

代码模块可以放到 https://github.com/alibaba/arthas/tree/master/labs 目录下。

另外,不需要在 arthas core 里增加命令来启动这个 server 。这个server由用户自己单独部署。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Arthas 支持 JFR 解析
4 participants