Skip to content

Conversation

@siwei-zhang-work
Copy link
Contributor

@siwei-zhang-work siwei-zhang-work commented Sep 3, 2025

What it does

Adds the generic XY view as introduced in eclipse-cdt-cloud/trace-server-protocol#114.

Note on Selection Display:

The initial design intended to show selection data in the bar chart (similar to the data tree view). This was revised during implementation because the legacy XY endpoint does not show selection, and adding extra bars for selection would be visually confusing with a time-based x-axis. Consequently, selection is no longer displayed in this view.

Dependencies

This change requires the updates from eclipse-cdt-cloud/tsp-typescript-client#138.

Related Merged Pull Requests

The corresponding server and protocol updates are already merged in:

How to test

The non-time x-axis with bar chart can be tested with a lttng-ust trace using function density view.

For other axis and type there's no existing view for now, but can be tested by doing small modifications in the code:

Testing other view types

AbstractTreeGenericXYCommonXDataProvider#getDisplayType() can be modified to return DisplayType.LINE/SCATTER.

Testing time-based x-axis

The following patch can be addressed in org.eclipse.tracecompass for a temporary test.

diff --git a/analysis/org.eclipse.tracecompass.analysis.profiling.core/src/org/eclipse/tracecompass/internal/analysis/profiling/core/callstack/provider/CallStackFunctionDensityDataProvider.java b/analysis/org.eclipse.tracecompass.analysis.profiling.core/src/org/eclipse/tracecompass/internal/analysis/profiling/core/callstack/provider/CallStackFunctionDensityDataProvider.java
index ea30625dd..f4bb31238 100644
--- a/analysis/org.eclipse.tracecompass.analysis.profiling.core/src/org/eclipse/tracecompass/internal/analysis/profiling/core/callstack/provider/CallStackFunctionDensityDataProvider.java
+++ b/analysis/org.eclipse.tracecompass.analysis.profiling.core/src/org/eclipse/tracecompass/internal/analysis/profiling/core/callstack/provider/CallStackFunctionDensityDataProvider.java
@@ -245,7 +245,14 @@ public class CallStackFunctionDensityDataProvider extends AbstractTreeGenericXYC
             yModels.add(new YModel(entryId, getNameFromPID(pid), bins, Y_AXIS_DESCRIPTION));
         }
 
-        return new Pair<>(sampling, yModels);
+        long[] times = new long[nbSamples];
+        long stepSize = (filter.getEnd() - filter.getStart()) / nbSamples;
+        for(int i=0 ; i<times.length ; i++) {
+            times[i] = filter.getStart() + stepSize * i;
+        }
+
+        return new Pair<>(new ISampling.Timestamps(times), yModels);
+//        return new Pair<>(sampling, yModels);
     }
 
     private static ISampling.@Nullable Ranges createEvenlyDistributedRanges(long start, long end, int samples) {

Follow-ups

No follow-ups.

Review checklist

  • [Y] As an author, I have thoroughly tested my changes and carefully followed the instructions in this template

@siwei-zhang-work siwei-zhang-work force-pushed the generic-xy branch 2 times, most recently from 2ff9cd1 to bb02fa2 Compare September 3, 2025 08:40
@siwei-zhang-work siwei-zhang-work changed the title [WIP] Generic xy Add the generic XY view Sep 3, 2025
@siwei-zhang-work siwei-zhang-work changed the title Add the generic XY view [WIP] Add the generic XY view Sep 3, 2025
@siwei-zhang-work siwei-zhang-work force-pushed the generic-xy branch 3 times, most recently from c28017c to edf4f4f Compare September 3, 2025 12:05
@siwei-zhang-work siwei-zhang-work changed the title [WIP] Add the generic XY view Add generic XY view Sep 3, 2025
@siwei-zhang-work
Copy link
Contributor Author

siwei-zhang-work commented Sep 4, 2025

The build will require the changes from eclipse-cdt-cloud/tsp-typescript-client#138 to succeed.

Copy link
Contributor

@bhufmann bhufmann left a comment

Choose a reason for hiding this comment

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

Thanks for contributing the generic xy widget and also to extracting common code in a shared utility class. I did a first review and some testing. I was able to see the Function Density view with Trace Compass server. I also tried other chart types and the time axis as described in the description.

I added some review comments and findings. Please have a look

}

function buildColumns(headers?: HeaderSpec[]): ColumnSpec[] {
if (headers && headers.length) {
Copy link
Contributor

Choose a reason for hiding this comment

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

if (headers?.length)

{
display: false,
gridLines: { display: false, drawBorder: false, drawTicks: false },
ticks: yTickFix as any
Copy link
Contributor

Choose a reason for hiding this comment

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

Here a suggestion for another, separate PR. I think it would be too much for this PR.

So, for the Function Duration Density chart of the Trace Compass server, I noticed very high and low numbers, so that in a linear scale the low numbers are not visible. For those cases logarithmic scale would be better. Since the viewer doesn't allow for users to change UI settings and modify the scale, I wonder if we should set it y-scale for bar charts either by default to logarithmic or if the difference between lowest and highest number exceeds a certain threshold. When using logarithmic scale we should probably enable the horizontal gridlines so that it's obvious for the user. Since the y-axis is our custom implementation, then it needs to be updated as well for logarithmic scale. Even better would be to support view customizations in FE.

WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes that would be a nice improvement. But I fear that treating bar chart as a special citizen would over-complicate the code. What about using logarithmic y-scale for all xy chart types (line/scatter/bar) if the difference between lowest and highest number exceeds a certain threshold?

Copy link
Contributor

Choose a reason for hiding this comment

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

That would be a good start. Do this in a separate PR maybe.

export function computeYRange(
datasets: DatasetLike[] | undefined,
isScatter = false,
pad = 0.01
Copy link
Contributor

Choose a reason for hiding this comment

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

What is this padding for? Ok found it, It was already in the original code.

}

export class ColorAllocator {
private map = new Map<string, number>();
Copy link
Contributor

Choose a reason for hiding this comment

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

can be readonly

isScatter = false,
pad = 0.01
): { min: number; max: number } {
if (!datasets || !datasets.length) return { min: 0, max: 1 };
Copy link
Contributor

Choose a reason for hiding this comment

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

if (!datasets?.length)

const newStartFloat = Number(this.mousePanningStart) - xNow / this.resolution;

const min = BigInt(0);
const max = this.props.unitController.absoluteRange - this.props.unitController.viewRangeLength;
Copy link
Contributor

Choose a reason for hiding this comment

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

unitcontroller is for the common time axis

if (ev.shiftKey && !ev.ctrlKey && this.props.unitController.selectionRange) {
this.isSelecting = true;
this.setState({ cursor: 'crosshair' });
this.props.unitController.selectionRange = {
Copy link
Contributor

Choose a reason for hiding this comment

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

unitcontroller is for the common time axis

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed the selectionRange feature.


private updateViewRange(start: bigint, end: bigint): void {
const [s, e] = start < end ? [start, end] : [end, start];
this.props.unitController.viewRange = { start: s, end: e };
Copy link
Contributor

Choose a reason for hiding this comment

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

unitcontroller is for the common time axis. It can't be updated from this class unless x is the time axis

}

private zoom(isZoomIn: boolean): void {
if (this.props.unitController.viewRangeLength >= 1) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here: unitcontroller is for the common time axis

private pan(left: boolean): void {
const vr = this.props.unitController.viewRange;
const abs = this.props.unitController.absoluteRange;
this.props.unitController.viewRange = panRange(vr, abs, left);
Copy link
Contributor

Choose a reason for hiding this comment

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

same here: unitcontroller is for the common time axis

Copy link
Contributor Author

@siwei-zhang-work siwei-zhang-work left a comment

Choose a reason for hiding this comment

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

Thanks for the review, I've applied the comments.

return getCollapsedNodesFromAutoExpandLevel(listToTree(entries, columns as any), autoExpandLevel);
}

export type DefaultCheckMode = 'strict-default-flag' | 'leaf-if-has-data';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I feel that adding this flag over-complicate things. I removed it and kept the behavior the same.

return { min: 0, max: 1 };
}
const range = localMax - localMin;
return { min: localMin - range * pad, max: localMax + range * pad };
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I meant to keep it the same, changed it back.

{
display: false,
gridLines: { display: false, drawBorder: false, drawTicks: false },
ticks: yTickFix as any
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes that would be a nice improvement. But I fear that treating bar chart as a special citizen would over-complicate the code. What about using logarithmic y-scale for all xy chart types (line/scatter/bar) if the difference between lowest and highest number exceeds a certain threshold?

}

private computeDesiredSampleNb(): number {
// TODO: Currently the number of samples is calculated only for bars.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is needed because the bars of each series are drawn beside each other, right?

Yes, this is needed because the bars of each series are drawn beside each other so we can't keep the original calculation of sample numbers.

So, we would have to add options to the tree model, that are specific for xy, timegraph, data tree etc.

I don't fully understand what you mean here. I was thinking to add the display type in XyEntry?

}

private endSelection = (_e: MouseEvent): void => {
if (this.clickedMouseButton === 2) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This feature was what I wanted to achieve in the initial commit in the server by enabling the xValues in request parameters. It requires adding XYValuesDescription to the tree model response to let server reply with available xValues ranges. However, all the /tree endpoint in the server currently is using the same function. So it'll require quite a big change in the APIs. I've now disabled the zoom for non-time x-axis.

const rawLabel = labels[index];

if (this.isTimeAxis) {
// If it's a time axis, the label is a bigint timestamp. Format it as seconds.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Here it is always timestamp if this.isTimeAxis is true. Modified to do this transformation while creating the labels.

if (ev.shiftKey && !ev.ctrlKey && this.props.unitController.selectionRange) {
this.isSelecting = true;
this.setState({ cursor: 'crosshair' });
this.props.unitController.selectionRange = {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed the selectionRange feature.

@bhufmann
Copy link
Contributor

So, we would have to add options to the tree model, that are specific for xy, timegraph, data tree etc.

I don't fully understand what you mean here. I was thinking to add the display type in XyEntry?

That's pretty much what I mean, XyEntry has this special information, while other entries (e.g. for timegraph) don't have that.

Copy link
Contributor

@bhufmann bhufmann left a comment

Choose a reason for hiding this comment

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

I did a second review I have only some minor comments. The functionality looks good for this PR and my previous comment are addressed:

  • I'll release the latest tsp-typescript-client which you can use in this PR. I'll let you know when it's done tsp-typescript-client v0.8.0 is released
  • I noticed that the progress wheel in the view is not shown when analysis is running (message in the view)
  • Please rebase to latest master (there were some changes brought in from the traceviewer-libs, i.e. mainly linter related updates.

@PatrickTasse could you please have look into this PR as well? Thanks

@siwei-zhang-work
Copy link
Contributor Author

Thanks for the review. Rebased on master and added the progress wheel that I accidentally deleted.

Extract util methods from abstract-xy-output-component so
that later they can be shared with generic xy view.

Signed-off-by: Siwei Zhang <[email protected]>
],
"dependencies": {
"tsp-typescript-client": "^0.7.0"
"tsp-typescript-client": "^0.8.0"
Copy link
Contributor

Choose a reason for hiding this comment

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

the version has to be updated in the following files as well:

  • local-libs/traceviewer-libs/react-components/package.json
  • vscode-trace-common/package.json

Implement the new generic xy view to support non-time
x-axis xy view. This new view uses a new  endpoint in
the tsp introduced in:

eclipse-cdt-cloud/trace-server-protocol#114

Signed-off-by: Siwei Zhang <[email protected]>
Copy link
Contributor

@bhufmann bhufmann left a comment

Choose a reason for hiding this comment

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

Looks good to me. Thank you very much for the contribution It enhances the application to be able to provide density views. We are much closer to feature parity to Eclipse Trace Compass.

@bhufmann bhufmann merged commit 29b32fb into eclipse-cdt-cloud:master Sep 18, 2025
8 checks passed
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.

3 participants