Thread dump viewer - #14
Conversation
There was a problem hiding this comment.
Pull Request Overview
Adds a grid-based thread dump viewer to the Jifa UI and exposes a new endpoint in the analyzer to supply row data.
- New
ThreadViewerGrid.vuecomponent renders threads in a sortable table and emits selections. - Updated
ThreadDump.vueto fetch thread rows, integrate the grid view, and display code snippets for the selected thread. - Backend support via
ThreadDumpRowmodel androws()method inThreadDumpAnalyzerto supply thread metadata and line ranges.
Reviewed Changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| frontend/src/components/threaddump/ThreadViewerGrid.vue | New Vue component for grid-based thread listing and selection |
| frontend/src/components/threaddump/ThreadDump.vue | Integrated grid view, fetch logic, and details panel |
| analysis/thread-dump/src/main/java/org/eclipse/jifa/tda/model/ThreadDumpRow.java | Added data model for a thread dump row |
| analysis/thread-dump/src/main/java/org/eclipse/jifa/tda/ThreadDumpAnalyzer.java | Added rows() to expose thread row data; adjusted imports |
| 'javaMonitors', | ||
| 'callSiteTree' | ||
| 'callSiteTree', | ||
| 'Table View', |
There was a problem hiding this comment.
activeNames includes the display title 'Table View', but the corresponding <el-collapse-item> uses the name 'emptyBox'. This mismatch prevents the grid tab from activating. Update activeNames to use 'emptyBox' or rename the collapse-item to 'Table View'.
|
|
||
| console.log(thread) | ||
|
|
||
| if (lineNumberStart && lineNumberEnd) { |
There was a problem hiding this comment.
Checking these values with a truthiness test fails when lineNumberStart is 0 (falsy). Use explicit checks like if (lineNumberStart != null && lineNumberEnd != null) { to handle zero-based lines.
| if (lineNumberStart && lineNumberEnd) { | |
| if (lineNumberStart != null && lineNumberEnd != null) { |
| /** | ||
| * @return rows of a thread dump table | ||
| */ | ||
| public List<ThreadDumpRow> rows(PagingRequest paging) { |
There was a problem hiding this comment.
The paging parameter is currently unused. Either implement pagination logic inside this method or remove the parameter to keep the API signature accurate.
| import java.util.*; | ||
| import java.util.stream.Collectors; |
There was a problem hiding this comment.
[nitpick] Avoid wildcard imports; explicitly import only the needed classes to improve readability and prevent accidental dependencies.
| import java.util.*; | |
| import java.util.stream.Collectors; | |
| import java.util.ArrayList; | |
| import java.util.HashMap; | |
| import java.util.List; | |
| import java.util.Map; | |
| import java.util.Optional; | |
| import java.util.Set; | |
| import java.util.function.Function; | |
| import java.util.stream.Collectors; | |
| import java.util.stream.Collectors; |
| import lombok.Data; | ||
|
|
||
| @Data |
There was a problem hiding this comment.
Because there's an explicit all-args constructor, Lombok won’t generate a no-argument constructor. Add @NoArgsConstructor (or a manual default constructor) if your JSON serialization framework requires it.
| import lombok.Data; | |
| @Data | |
| import lombok.Data; | |
| import lombok.NoArgsConstructor; | |
| @Data | |
| @NoArgsConstructor |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
| import org.eclipse.jifa.tda.model.Monitor; | ||
| import org.eclipse.jifa.tda.model.RawMonitor; | ||
| import org.eclipse.jifa.tda.model.Snapshot; | ||
| import org.eclipse.jifa.tda.model.*; |
| import org.eclipse.jifa.common.domain.vo.PageView; | ||
| import org.eclipse.jifa.common.util.PageViewBuilder; | ||
| import org.eclipse.jifa.tda.enums.MonitorState; | ||
| import org.eclipse.jifa.tda.enums.OSTreadState; |
There was a problem hiding this comment.
nit: typo
We should submit a separate PR upstream to rename it.
| */ | ||
| public List<ThreadDumpRow> rows(PagingRequest paging) { | ||
|
|
||
| List<ThreadDumpRow> threadDumpRows = new ArrayList<>(snapshot.getJavaThreads().stream().map(t -> { |
There was a problem hiding this comment.
style:
We can add static functions and/or local functions somewhere to do this conversion. For example, a ThreadDumpRow(JavaThread javaThread) constructor or a static ThreadDumpRow fromJavaThread(JavaThread javaThread).
Though, it's a bit clunky having that in the Model, so maybe somewhere here.
| return new ThreadDumpRow(t.getName(), t.getId(), t.getOsThreadState(), 0, new Frame[]{}, t.getElapsed(), t.getCpu(), t.getLineStart(), t.getLineEnd()); | ||
| }).toList(); | ||
|
|
||
| threadDumpRows.addAll(nonJavaThreadDumpRows); |
There was a problem hiding this comment.
You should be able to simply construct the paging component using PageViewer, see the function buildVThreadPageView for an example.
| List<ThreadDumpRow> threadDumpRows = new ArrayList<>(snapshot.getJavaThreads().stream().map(t -> { | ||
|
|
||
| Frame[] frames = null; | ||
| int length = 0; | ||
|
|
||
| if (t.getTrace() != null) { | ||
| frames = t.getTrace().getFrames(); | ||
| length = frames.length; | ||
| } | ||
|
|
||
| return new ThreadDumpRow(t.getName(), t.getId(), t.getOsThreadState(), length, frames, t.getElapsed(), t.getCpu(), t.getLineStart(), t.getLineEnd()); | ||
| }).toList()); | ||
|
|
||
| List<ThreadDumpRow> nonJavaThreadDumpRows = snapshot.getNonJavaThreads().stream().map(t -> { | ||
| return new ThreadDumpRow(t.getName(), t.getId(), t.getOsThreadState(), 0, new Frame[]{}, t.getElapsed(), t.getCpu(), t.getLineStart(), t.getLineEnd()); | ||
| }).toList(); | ||
|
|
||
| threadDumpRows.addAll(nonJavaThreadDumpRows); |
There was a problem hiding this comment.
This approach generates quite a bit of intermediary list garbage. Consider something like this instead:
Stream<ThreadDumpRow> javaRows = snapshot.getJavaThreads().stream().map(....);
Stream<ThreadDumpRow> nonJavaRows = snapshot.getNonJavaThreads().stream().map(....);
Stream<ThreadDumpRow> allRows = Stream.concat(javaRows, nonJavaRows);
// consider implementing paging here on the stream
List<ThreadRumpRow> threadDumpRows = allRows.toList();
| 'javaMonitors', | ||
| 'callSiteTree' | ||
| 'callSiteTree', | ||
| 'Table View', |
There was a problem hiding this comment.
Table View doesn't have the java camelCase approach.
|
|
||
| console.log(thread) | ||
|
|
||
| if (lineNumberStart && lineNumberEnd) { |
| }); | ||
|
|
||
| request('rows').then((threadsData) => { | ||
| console.log(threadsData); |
| if (selectedThread.value) { | ||
| return row === selectedThread.value ? 'selected-row' : ''; | ||
| } else { | ||
| return rowIndex === 0 ? 'selected-row' : ''; |
There was a problem hiding this comment.
Maybe not clear what the intent is here, are we defaulting to first-row selected deliberately?
Once our npm cache comes backup ill post a picture, but this adds a "grid" thread dump viewer to jifa