-
Notifications
You must be signed in to change notification settings - Fork 7.6k
Feat/jfr analyzer #3046
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat/jfr analyzer #3046
Conversation
There was a problem hiding this 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
core/src/main/java/com/taobao/arthas/core/command/basic1000/JFRansCommand.java
Outdated
Show resolved
Hide resolved
ProcessBuilder builder = new ProcessBuilder( | ||
"java", "-jar", jarPath | ||
); | ||
builder.inheritIO(); |
There was a problem hiding this comment.
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.
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.
|
||
@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; | ||
} | ||
|
There was a problem hiding this comment.
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.
@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.
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; |
There was a problem hiding this comment.
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.
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.
...s-jfr-backend/src/main/java/org/example/jfranalyzerbackend/service/impl/JFRAnalyzerImpl.java
Outdated
Show resolved
Hide resolved
System.out.println(fg); | ||
System.out.println(fg.getSymbolTable()); |
There was a problem hiding this comment.
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.
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(); |
There was a problem hiding this comment.
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.
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 |
There was a problem hiding this comment.
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.
// 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
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
user-provided value
…RansCommand.java Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
…d/service/impl/JFRAnalyzerImpl.java Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
代码模块可以放到 https://github.com/alibaba/arthas/tree/master/labs 目录下。 另外,不需要在 arthas core 里增加命令来启动这个 server 。这个server由用户自己单独部署。 |
JFR 文件分析功能:前后端实现
本次 PR 实现了对
.jfr
文件的分析能力,包括前后端服务的完整集成。模块说明
后端模块:
arthas-jfr-backend
.jfr
文件上传、解析、火焰图等数据接口前端模块:
arthas-jfr-frontend
.jfr
文件并展示火焰图使用说明
在 Arthas 命令行中执行:
相关 Issue
Closes #3014