Skip to content

Commit 54eefee

Browse files
committed
callStack: add Annotations handlers for instant events
implement IOutputAnnotationProvider and override methods add marker data to tooltips add unit tests (boilerplate tests generated by amazon Q) move Annotations to InstrumentedCallStackAnalysis Signed-off-by: Yassine Ibhir <[email protected]>
1 parent 7f18609 commit 54eefee

File tree

8 files changed

+358
-7
lines changed

8 files changed

+358
-7
lines changed

analysis/org.eclipse.tracecompass.analysis.profiling.core.tests/src/org/eclipse/tracecompass/analysis/profiling/core/tests/FlameChartDataProviderTest.java

Lines changed: 163 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,16 @@
2424
import java.util.List;
2525
import java.util.Map;
2626
import java.util.Objects;
27+
2728
import java.util.Set;
29+
//import java.util.stream.Collectors;
30+
import java.util.stream.Collectors;
2831

2932
import org.eclipse.core.runtime.IProgressMonitor;
3033
import org.eclipse.core.runtime.NullProgressMonitor;
3134
import org.eclipse.jdt.annotation.NonNull;
3235
import org.eclipse.jdt.annotation.Nullable;
36+
import org.eclipse.tracecompass.analysis.profiling.core.instrumented.InstrumentedCallStackAnalysis;
3337
import org.eclipse.tracecompass.analysis.profiling.core.tests.stubs2.CallStackAnalysisStub;
3438
import org.eclipse.tracecompass.internal.analysis.os.linux.core.registry.LinuxStyle;
3539
import org.eclipse.tracecompass.internal.analysis.profiling.core.instrumented.FlameChartDataProvider;
@@ -38,7 +42,10 @@
3842
import org.eclipse.tracecompass.internal.analysis.profiling.core.instrumented.FlameChartEntryModel.EntryType;
3943
import org.eclipse.tracecompass.internal.tmf.core.model.filters.FetchParametersUtils;
4044
import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderDescriptor;
45+
import org.eclipse.tracecompass.tmf.core.model.CommonStatusMessage;
4146
import org.eclipse.tracecompass.tmf.core.model.OutputElementStyle;
47+
import org.eclipse.tracecompass.tmf.core.model.StyleProperties;
48+
import org.eclipse.tracecompass.tmf.core.model.StyleProperties.SymbolType;
4249
import org.eclipse.tracecompass.tmf.core.model.filters.SelectionTimeQueryFilter;
4350
import org.eclipse.tracecompass.tmf.core.model.filters.TimeQueryFilter;
4451
import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphArrow;
@@ -48,6 +55,10 @@
4855
import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphModel;
4956
import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphState;
5057
import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeModel;
58+
import org.eclipse.tracecompass.tmf.core.model.annotations.Annotation;
59+
//import org.eclipse.tracecompass.tmf.core.model.annotations.Annotation;
60+
import org.eclipse.tracecompass.tmf.core.model.annotations.AnnotationCategoriesModel;
61+
import org.eclipse.tracecompass.tmf.core.model.annotations.AnnotationModel;
5162
import org.eclipse.tracecompass.tmf.core.response.ITmfResponse;
5263
import org.eclipse.tracecompass.tmf.core.response.TmfModelResponse;
5364
import org.junit.Test;
@@ -436,10 +447,161 @@ private static void verifyStates(List<ITimeGraphRowModel> rowModels, FlameChartE
436447
}
437448
}
438449

450+
/**
451+
* Test fetching annotation categories
452+
*/
453+
@Test
454+
public void testFetchAnnotationCategories() {
455+
FlameChartDataProvider dataProvider = getDataProvider();
456+
457+
TmfModelResponse<AnnotationCategoriesModel> response = dataProvider.fetchAnnotationCategories(Collections.emptyMap(), MONITOR);
458+
assertEquals(ITmfResponse.Status.COMPLETED, response.getStatus());
459+
assertEquals(CommonStatusMessage.COMPLETED, response.getStatusMessage());
460+
461+
AnnotationCategoriesModel model = response.getModel();
462+
assertNotNull(model);
463+
assertEquals(1, model.getAnnotationCategories().size());
464+
assertEquals(InstrumentedCallStackAnalysis.ANNOTATIONS, model.getAnnotationCategories().get(0));
465+
}
466+
467+
/**
468+
* Test fetchAnnotations with empty result
469+
*/
470+
@Test
471+
public void testFetchAnnotations() {
472+
FlameChartDataProvider dataProvider = getDataProvider();
473+
Map<String, Object> parameters = FetchParametersUtils.selectionTimeQueryToMap(
474+
new SelectionTimeQueryFilter(0, Long.MAX_VALUE, 2, Collections.emptySet()));
475+
TmfModelResponse<AnnotationModel> response = dataProvider.fetchAnnotations(parameters, MONITOR);
476+
assertEquals(ITmfResponse.Status.COMPLETED, response.getStatus());
477+
assertEquals(CommonStatusMessage.COMPLETED, response.getStatusMessage());
478+
479+
AnnotationModel model = response.getModel();
480+
assertNotNull(model);
481+
assertTrue(model.getAnnotations().containsKey(InstrumentedCallStackAnalysis.ANNOTATIONS));
482+
}
483+
/**
484+
* Test fetchAnnotations with null filter returns completed with null model
485+
*/
486+
@Test
487+
public void testFetchAnnotationsNullFilter() {
488+
FlameChartDataProvider dataProvider = getDataProvider();
489+
490+
Map<String, Object> parameters = Collections.emptyMap();
491+
TmfModelResponse<AnnotationModel> response = dataProvider.fetchAnnotations(parameters, MONITOR);
492+
assertEquals(ITmfResponse.Status.COMPLETED, response.getStatus());
493+
assertEquals(CommonStatusMessage.COMPLETED, response.getStatusMessage());
494+
assertNull(response.getModel());
495+
}
496+
497+
/**
498+
* Test fetchAnnotations style
499+
*/
500+
@Test
501+
public void testFetchAnnotationsOutputElementStyle() {
502+
FlameChartDataProvider dataProvider = getDataProvider();
503+
504+
// Get tree to find entry IDs
505+
TmfModelResponse<@NonNull TmfTreeModel<@NonNull FlameChartEntryModel>> treeResponse =
506+
dataProvider.fetchTree(FetchParametersUtils.timeQueryToMap(new TimeQueryFilter(0, Long.MAX_VALUE, 2)), null);
507+
TmfTreeModel<@NonNull FlameChartEntryModel> model = treeResponse.getModel();
508+
509+
if (model != null) {
510+
List<FlameChartEntryModel> entries = model.getEntries();
511+
512+
// Find a function entry that should have annotations
513+
FlameChartEntryModel functionEntry = entries.stream()
514+
.filter(e -> e.getEntryType() == EntryType.FUNCTION)
515+
.findFirst()
516+
.orElse(null);
517+
assertNotNull(functionEntry);
518+
519+
// 15 and 18 are timestamps with instant events
520+
Map<String, Object> fetchParams = FetchParametersUtils.selectionTimeQueryToMap(
521+
new SelectionTimeQueryFilter(15, 18, 2, Collections.singleton(functionEntry.getId())));
522+
523+
// Fetch annotations
524+
TmfModelResponse<AnnotationModel> response = dataProvider.fetchAnnotations(fetchParams, MONITOR);
525+
526+
assertEquals(ITmfResponse.Status.COMPLETED, response.getStatus());
527+
AnnotationModel m = response.getModel();
528+
assertNotNull(m);
529+
Collection<@NonNull Annotation> annotations = m.getAnnotations().get(InstrumentedCallStackAnalysis.ANNOTATIONS);
530+
assertNotNull(annotations);
531+
assertFalse(annotations.isEmpty());
532+
533+
// Verify OutputElementStyle properties
534+
Annotation annotation = annotations.iterator().next();
535+
OutputElementStyle style = annotation.getStyle();
536+
assertNotNull(style);
537+
538+
Map<String, Object> styleMap = style.getStyleValues();
539+
assertEquals("#7D3D31", styleMap.get(StyleProperties.COLOR));
540+
assertEquals(0.33f, styleMap.get(StyleProperties.HEIGHT));
541+
assertEquals(SymbolType.DIAMOND, styleMap.get(StyleProperties.SYMBOL_TYPE));
542+
}
543+
544+
}
545+
546+
/**
547+
* Test annotation model structure and properties
548+
*/
549+
@Test
550+
public void testAnnotationModelStructure() {
551+
FlameChartDataProvider dataProvider = getDataProvider();
552+
553+
Map<String, Object> parameters = FetchParametersUtils.selectionTimeQueryToMap(
554+
new SelectionTimeQueryFilter(0, Long.MAX_VALUE, 2,Collections.emptySet()));
555+
TmfModelResponse<AnnotationModel> response = dataProvider.fetchAnnotations(parameters, MONITOR);
556+
assertEquals(ITmfResponse.Status.COMPLETED, response.getStatus());
557+
558+
AnnotationModel model = response.getModel();
559+
assertNotNull(model);
560+
561+
assertEquals(1, model.getAnnotations().size());
562+
assertTrue(model.getAnnotations().containsKey(InstrumentedCallStackAnalysis.ANNOTATIONS));
563+
assertNotNull(model.getAnnotations().get(InstrumentedCallStackAnalysis.ANNOTATIONS));
564+
}
565+
566+
/**
567+
* Test annotation model structure and properties
568+
*/
569+
@SuppressWarnings("null")
570+
@Test
571+
public void testFetchValidAnnotations() {
572+
FlameChartDataProvider dataProvider = getDataProvider();
573+
TmfModelResponse<@NonNull TmfTreeModel<@NonNull FlameChartEntryModel>> treeResponse =
574+
dataProvider.fetchTree(FetchParametersUtils.timeQueryToMap(new TimeQueryFilter(1, Long.MAX_VALUE, 2)), MONITOR);
575+
assertEquals(ITmfResponse.Status.COMPLETED, treeResponse.getStatus());
576+
577+
TmfTreeModel<@NonNull FlameChartEntryModel> m = treeResponse.getModel();
578+
// Get entry IDs from the same tree response
579+
if(m != null) {
580+
Set<Long> allEntryIds = m.getEntries().stream()
581+
.map(FlameChartEntryModel::getId)
582+
.collect(Collectors.toSet());
583+
584+
585+
Map<String, Object> parameters = FetchParametersUtils.selectionTimeQueryToMap(
586+
new SelectionTimeQueryFilter(1, Long.MAX_VALUE, 2, allEntryIds));
587+
588+
TmfModelResponse<AnnotationModel> response = dataProvider.fetchAnnotations(parameters, MONITOR);
589+
590+
assertNotNull(response);
591+
assertEquals(ITmfResponse.Status.COMPLETED, response.getStatus());
592+
593+
AnnotationModel model = response.getModel();
594+
assertNotNull(model);
595+
596+
Map<String, Collection<Annotation>> annotations = model.getAnnotations();
597+
assertNotNull(annotations );
598+
}
599+
}
600+
439601
private static void verifyArrows(List<ITimeGraphArrow> arrows, List<ITimeGraphArrow> expectedArrows) {
440602
assertEquals(expectedArrows.size(), arrows.size());
441603
for (ITimeGraphArrow expectedArrow : expectedArrows) {
442-
for (ITimeGraphArrow arrow: arrows) {
604+
for (ITimeGraphArrow arrow : arrows) {
443605
if (arrow.getValue() == expectedArrow.getValue()) {
444606
assertEquals("Duration for arrow " + arrow.getValue(), expectedArrow.getDuration(), arrow.getDuration());
445607
assertEquals("Start time for arrow " + arrow.getValue(), expectedArrow.getStartTime(), arrow.getStartTime());

analysis/org.eclipse.tracecompass.analysis.profiling.core.tests/stubs/org/eclipse/tracecompass/analysis/profiling/core/tests/stubs2/CallStackProviderStub.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,20 @@
1111

1212
package org.eclipse.tracecompass.analysis.profiling.core.tests.stubs2;
1313

14+
import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
15+
1416
import org.eclipse.jdt.annotation.NonNull;
1517
import org.eclipse.jdt.annotation.Nullable;
1618
import org.eclipse.tracecompass.analysis.profiling.core.callstack.CallStackStateProvider;
19+
import org.eclipse.tracecompass.analysis.profiling.core.instrumented.InstrumentedCallStackAnalysis;
20+
import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder;
1721
import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue;
1822
import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue;
1923
import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
2024
import org.eclipse.tracecompass.tmf.core.event.ITmfEventField;
2125
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
2226

27+
2328
/**
2429
* A call stack state provider stub
2530
*
@@ -96,4 +101,49 @@ protected long getThreadId(ITmfEvent event) {
96101
}
97102
return CallStackStateProvider.UNKNOWN_PID;
98103
}
104+
105+
@Override
106+
protected void eventHandle(ITmfEvent event) {
107+
if (!considerEvent(event)) {
108+
return;
109+
}
110+
// Handle instant events
111+
ITmfEventField phField = event.getContent().getField("ph");
112+
if (phField != null && "i".equals(phField.getValue())) {
113+
handleInstant(event);
114+
return;
115+
}
116+
// Call parent for regular entry/exit events
117+
super.eventHandle(event);
118+
}
119+
120+
private void handleInstant(@NonNull ITmfEvent event) {
121+
super.addMarker(event);
122+
123+
ITmfStateSystemBuilder ss = checkNotNull(getStateSystemBuilder());
124+
long timestamp = event.getTimestamp().toNanos();
125+
Object contents = event.getContent().getFieldValue( String.class,"name");
126+
String toStore = (contents != null ? contents.toString() : "");
127+
128+
String processName = getProcessName(event);
129+
int processId = getProcessId(event);
130+
if (processName == null) {
131+
processName = (processId == UNKNOWN_PID) ? UNKNOWN : Integer.toString(processId);
132+
}
133+
int processQuark = ss.getQuarkAbsoluteAndAdd(PROCESSES, processName);
134+
ss.updateOngoingState(TmfStateValue.newValueInt(processId), processQuark);
135+
136+
String threadName = getThreadName(event);
137+
long threadId = getThreadId(event);
138+
if (threadName == null) {
139+
threadName = Long.toString(threadId);
140+
}
141+
int threadQuark = ss.getQuarkRelativeAndAdd(processQuark, threadName);
142+
ss.updateOngoingState(TmfStateValue.newValueLong(threadId), threadQuark);
143+
144+
int callStackQuark = ss.getQuarkRelativeAndAdd(threadQuark, InstrumentedCallStackAnalysis.ANNOTATIONS);
145+
ss.pushAttribute(timestamp, toStore, callStackQuark);
146+
return;
147+
148+
}
99149
}

analysis/org.eclipse.tracecompass.analysis.profiling.core.tests/testfiles/traces/callstack2.xml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,30 @@
175175
<field name="pid" type="string" value="5" />
176176
<field name="tid" type="string" value="7" />
177177
</event>
178+
<event timestamp="15" name="instant_event">
179+
<field name="ph" value="i" type="string" />
180+
<field name="tid" value="3" type="string" />
181+
<field name="pid" type="string" value="1" />
182+
<field name="name" value="HistoryTreeBackend:ClosingFile" type="string" />
183+
</event>
184+
<event timestamp="16" name="instant_event">
185+
<field name="ph" value="i" type="string" />
186+
<field name="tid" value="2" type="string" />
187+
<field name="pid" type="string" value="1" />
188+
<field name="name" value="InstantEvent1" type="string" />
189+
</event>
190+
<event timestamp="17" name="instant_event">
191+
<field name="ph" value="i" type="string" />
192+
<field name="tid" value="6" type="string" />
193+
<field name="pid" type="string" value="5" />
194+
<field name="name" value="InstantEvent2" type="string" />
195+
</event>
196+
<event timestamp="18" name="instant_event">
197+
<field name="ph" value="i" type="string" />
198+
<field name="tid" value="2" type="string" />
199+
<field name="pid" type="string" value="1" />
200+
<field name="name" value="InstantEvent3" type="string" />
201+
</event>
178202
<event timestamp="19" name="exit">
179203
<field name="op" type="string" value="op2" />
180204
<field name="pid" type="string" value="5" />

analysis/org.eclipse.tracecompass.analysis.profiling.core/src/org/eclipse/tracecompass/analysis/profiling/core/callstack/CallStackAnalysis.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,13 @@ public abstract class CallStackAnalysis extends TmfStateSystemAnalysisModule imp
5454
*/
5555
public static final String CALL_STACK = "CallStack"; //$NON-NLS-1$
5656

57+
/**
58+
* Annotations string state attribute
59+
*
60+
* @since 2.6
61+
*/
62+
public static final String ANNOTATIONS = "Markers"; //$NON-NLS-1$
63+
5764
private static final String[] DEFAULT_PROCESSES_PATTERN = new String[] { CallStackStateProvider.PROCESSES, "*" }; //$NON-NLS-1$
5865

5966
private static final String[] DEFAULT_THREADS_PATTERN = new String[] { "*" }; //$NON-NLS-1$

analysis/org.eclipse.tracecompass.analysis.profiling.core/src/org/eclipse/tracecompass/analysis/profiling/core/callstack/CallStackStateProvider.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,4 +277,15 @@ public void done() {
277277
/* Override to provide a thread name */
278278
return null;
279279
}
280+
281+
/**
282+
* Add an event as a marker to the Annotations attribute
283+
*
284+
* @param event
285+
* The event to add as a marker
286+
* @since 2.6
287+
*/
288+
protected void addMarker(ITmfEvent event) {
289+
// Do nothing, this is a placeholder for overriding.
290+
}
280291
}

analysis/org.eclipse.tracecompass.analysis.profiling.core/src/org/eclipse/tracecompass/analysis/profiling/core/instrumented/InstrumentedCallStackAnalysis.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@ public abstract class InstrumentedCallStackAnalysis extends TmfStateSystemAnalys
6767
/** CallStack stack-attribute */
6868
public static final String CALL_STACK = "CallStack"; //$NON-NLS-1$
6969

70+
/**
71+
* Annotations string state attribute
72+
*
73+
* @since 2.6
74+
*/
75+
public static final String ANNOTATIONS = "Markers"; //$NON-NLS-1$
76+
7077
private @Nullable CallStackSeries fCallStacks;
7178

7279
private final CallGraphAnalysis fCallGraph;

0 commit comments

Comments
 (0)