Skip to content

Commit e0758bf

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 amozon Q) move Annotations to InstrumentedCallStackAnalysis Signed-off-by: Yassine Ibhir <[email protected]>
1 parent 7f18609 commit e0758bf

File tree

5 files changed

+256
-7
lines changed

5 files changed

+256
-7
lines changed

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

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@
2424
import java.util.List;
2525
import java.util.Map;
2626
import java.util.Objects;
27+
import java.util.Optional;
2728
import java.util.Set;
2829

2930
import org.eclipse.core.runtime.IProgressMonitor;
3031
import org.eclipse.core.runtime.NullProgressMonitor;
3132
import org.eclipse.jdt.annotation.NonNull;
3233
import org.eclipse.jdt.annotation.Nullable;
34+
import org.eclipse.tracecompass.analysis.profiling.core.instrumented.InstrumentedCallStackAnalysis;
3335
import org.eclipse.tracecompass.analysis.profiling.core.tests.stubs2.CallStackAnalysisStub;
3436
import org.eclipse.tracecompass.internal.analysis.os.linux.core.registry.LinuxStyle;
3537
import org.eclipse.tracecompass.internal.analysis.profiling.core.instrumented.FlameChartDataProvider;
@@ -38,6 +40,7 @@
3840
import org.eclipse.tracecompass.internal.analysis.profiling.core.instrumented.FlameChartEntryModel.EntryType;
3941
import org.eclipse.tracecompass.internal.tmf.core.model.filters.FetchParametersUtils;
4042
import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderDescriptor;
43+
import org.eclipse.tracecompass.tmf.core.model.CommonStatusMessage;
4144
import org.eclipse.tracecompass.tmf.core.model.OutputElementStyle;
4245
import org.eclipse.tracecompass.tmf.core.model.filters.SelectionTimeQueryFilter;
4346
import org.eclipse.tracecompass.tmf.core.model.filters.TimeQueryFilter;
@@ -48,6 +51,8 @@
4851
import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphModel;
4952
import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphState;
5053
import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeModel;
54+
import org.eclipse.tracecompass.tmf.core.model.annotations.AnnotationCategoriesModel;
55+
import org.eclipse.tracecompass.tmf.core.model.annotations.AnnotationModel;
5156
import org.eclipse.tracecompass.tmf.core.response.ITmfResponse;
5257
import org.eclipse.tracecompass.tmf.core.response.TmfModelResponse;
5358
import org.junit.Test;
@@ -436,10 +441,117 @@ private static void verifyStates(List<ITimeGraphRowModel> rowModels, FlameChartE
436441
}
437442
}
438443

444+
/**
445+
* Test fetching annotation categories
446+
*/
447+
@Test
448+
public void testFetchAnnotationCategories() {
449+
FlameChartDataProvider dataProvider = getDataProvider();
450+
451+
TmfModelResponse<AnnotationCategoriesModel> response = dataProvider.fetchAnnotationCategories(Collections.emptyMap(), MONITOR);
452+
assertEquals(ITmfResponse.Status.COMPLETED, response.getStatus());
453+
assertEquals(CommonStatusMessage.COMPLETED, response.getStatusMessage());
454+
455+
AnnotationCategoriesModel model = response.getModel();
456+
assertNotNull(model);
457+
assertEquals(1, model.getAnnotationCategories().size());
458+
assertEquals(InstrumentedCallStackAnalysis.ANNOTATIONS, model.getAnnotationCategories().get(0));
459+
}
460+
461+
/**
462+
* Test fetchAnnotations with empty result
463+
*/
464+
@Test
465+
public void testFetchAnnotations() {
466+
FlameChartDataProvider dataProvider = getDataProvider();
467+
468+
Map<String, Object> parameters = FetchParametersUtils.selectionTimeQueryToMap(
469+
new SelectionTimeQueryFilter(0, Long.MAX_VALUE, 2, Collections.emptySet()));
470+
TmfModelResponse<AnnotationModel> response = dataProvider.fetchAnnotations(parameters, MONITOR);
471+
assertEquals(ITmfResponse.Status.COMPLETED, response.getStatus());
472+
assertEquals(CommonStatusMessage.COMPLETED, response.getStatusMessage());
473+
474+
AnnotationModel model = response.getModel();
475+
assertNotNull(model);
476+
assertTrue(model.getAnnotations().containsKey(InstrumentedCallStackAnalysis.ANNOTATIONS));
477+
}
478+
/**
479+
* Test fetchAnnotations with null filter returns completed with null model
480+
*/
481+
@Test
482+
public void testFetchAnnotationsNullFilter() {
483+
FlameChartDataProvider dataProvider = getDataProvider();
484+
485+
Map<String, Object> parameters = Collections.emptyMap();
486+
TmfModelResponse<AnnotationModel> response = dataProvider.fetchAnnotations(parameters, MONITOR);
487+
assertEquals(ITmfResponse.Status.COMPLETED, response.getStatus());
488+
assertEquals(CommonStatusMessage.COMPLETED, response.getStatusMessage());
489+
assertNull(response.getModel());
490+
}
491+
492+
/**
493+
* Test fetchAnnotations with specific entries and time range
494+
*/
495+
@Test
496+
public void testFetchAnnotationsWithSelectedEntries() {
497+
FlameChartDataProvider dataProvider = getDataProvider();
498+
499+
Set<Long> selectedIds = getFunctionEntryIds() ;
500+
Map<String, Object> parameters = FetchParametersUtils.selectionTimeQueryToMap(
501+
new SelectionTimeQueryFilter(0, 100, 2, selectedIds));
502+
TmfModelResponse<AnnotationModel> response = dataProvider.fetchAnnotations(parameters, MONITOR);
503+
assertEquals(ITmfResponse.Status.COMPLETED, response.getStatus());
504+
505+
AnnotationModel model = response.getModel();
506+
assertNotNull(model);
507+
assertTrue(model.getAnnotations().containsKey(InstrumentedCallStackAnalysis.ANNOTATIONS));
508+
509+
}
510+
/**
511+
* Test annotation model structure and properties
512+
*/
513+
@Test
514+
public void testAnnotationModelStructure() {
515+
FlameChartDataProvider dataProvider = getDataProvider();
516+
517+
Map<String, Object> parameters = FetchParametersUtils.selectionTimeQueryToMap(
518+
new SelectionTimeQueryFilter(0, Long.MAX_VALUE, 2,Collections.emptySet()));
519+
TmfModelResponse<AnnotationModel> response = dataProvider.fetchAnnotations(parameters, MONITOR);
520+
assertEquals(ITmfResponse.Status.COMPLETED, response.getStatus());
521+
522+
AnnotationModel model = response.getModel();
523+
assertNotNull(model);
524+
525+
assertEquals(1, model.getAnnotations().size());
526+
assertTrue(model.getAnnotations().containsKey(InstrumentedCallStackAnalysis.ANNOTATIONS));
527+
assertNotNull(model.getAnnotations().get(InstrumentedCallStackAnalysis.ANNOTATIONS));
528+
}
529+
530+
private Set<Long> getFunctionEntryIds() {
531+
FlameChartDataProvider dataProvider = getDataProvider();
532+
533+
TmfModelResponse<@NonNull TmfTreeModel<@NonNull FlameChartEntryModel>> treeResponse =
534+
dataProvider.fetchTree(FetchParametersUtils.timeQueryToMap(new TimeQueryFilter(0, Long.MAX_VALUE, 2)), MONITOR);
535+
assertEquals(ITmfResponse.Status.COMPLETED, treeResponse.getStatus());
536+
537+
TmfTreeModel<@NonNull FlameChartEntryModel> treeModel = treeResponse.getModel();
538+
assertNotNull(treeModel);
539+
540+
Optional<@NonNull FlameChartEntryModel> functionEntry = treeModel.getEntries().stream()
541+
.filter(entry -> entry.getEntryType() == EntryType.FUNCTION)
542+
.findFirst();
543+
544+
if (functionEntry.isPresent()) {
545+
return Collections.singleton(functionEntry.get().getId());
546+
}
547+
return Collections.emptySet();
548+
}
549+
550+
439551
private static void verifyArrows(List<ITimeGraphArrow> arrows, List<ITimeGraphArrow> expectedArrows) {
440552
assertEquals(expectedArrows.size(), arrows.size());
441553
for (ITimeGraphArrow expectedArrow : expectedArrows) {
442-
for (ITimeGraphArrow arrow: arrows) {
554+
for (ITimeGraphArrow arrow : arrows) {
443555
if (arrow.getValue() == expectedArrow.getValue()) {
444556
assertEquals("Duration for arrow " + arrow.getValue(), expectedArrow.getDuration(), arrow.getDuration());
445557
assertEquals("Start time for arrow " + arrow.getValue(), expectedArrow.getStartTime(), arrow.getStartTime());

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;

analysis/org.eclipse.tracecompass.analysis.profiling.core/src/org/eclipse/tracecompass/internal/analysis/profiling/core/instrumented/FlameChartDataProvider.java

Lines changed: 117 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,16 @@
4343
import org.eclipse.tracecompass.analysis.profiling.core.callstack2.CallStackSeries;
4444
import org.eclipse.tracecompass.analysis.profiling.core.instrumented.EdgeStateValue;
4545
import org.eclipse.tracecompass.analysis.profiling.core.instrumented.IFlameChartProvider;
46+
import org.eclipse.tracecompass.analysis.profiling.core.instrumented.InstrumentedCallStackAnalysis;
4647
import org.eclipse.tracecompass.analysis.profiling.core.model.IHostModel;
4748
import org.eclipse.tracecompass.common.core.log.TraceCompassLog;
4849
import org.eclipse.tracecompass.internal.analysis.profiling.core.instrumented.FlameChartEntryModel.EntryType;
4950
import org.eclipse.tracecompass.internal.analysis.profiling.core.model.ModelManager;
5051
import org.eclipse.tracecompass.internal.analysis.profiling.core.model.ProcessStatusInterval;
52+
import org.eclipse.tracecompass.internal.tmf.core.model.filters.FetchParametersUtils;
5153
import org.eclipse.tracecompass.segmentstore.core.ISegment;
54+
import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
55+
import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
5256
import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
5357
import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
5458
import org.eclipse.tracecompass.tmf.core.dataprovider.DataProviderParameterUtils;
@@ -57,6 +61,13 @@
5761
import org.eclipse.tracecompass.tmf.core.model.IOutputStyleProvider;
5862
import org.eclipse.tracecompass.tmf.core.model.OutputElementStyle;
5963
import org.eclipse.tracecompass.tmf.core.model.OutputStyleModel;
64+
import org.eclipse.tracecompass.tmf.core.model.StyleProperties;
65+
import org.eclipse.tracecompass.tmf.core.model.StyleProperties.SymbolType;
66+
import org.eclipse.tracecompass.tmf.core.model.annotations.Annotation;
67+
import org.eclipse.tracecompass.tmf.core.model.annotations.AnnotationCategoriesModel;
68+
import org.eclipse.tracecompass.tmf.core.model.annotations.AnnotationModel;
69+
import org.eclipse.tracecompass.tmf.core.model.annotations.IOutputAnnotationProvider;
70+
import org.eclipse.tracecompass.tmf.core.model.filters.SelectionTimeQueryFilter;
6071
import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphArrow;
6172
import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphDataProvider;
6273
import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphRowModel;
@@ -70,6 +81,7 @@
7081
import org.eclipse.tracecompass.tmf.core.response.ITmfResponse;
7182
import org.eclipse.tracecompass.tmf.core.response.ITmfResponse.Status;
7283
import org.eclipse.tracecompass.tmf.core.response.TmfModelResponse;
84+
import org.eclipse.tracecompass.tmf.core.statesystem.TmfStateSystemAnalysisModule;
7385
import org.eclipse.tracecompass.tmf.core.symbols.ISymbolProvider;
7486
import org.eclipse.tracecompass.tmf.core.symbols.SymbolProviderManager;
7587
import org.eclipse.tracecompass.tmf.core.symbols.SymbolProviderUtils;
@@ -84,6 +96,7 @@
8496
import com.google.common.collect.BiMap;
8597
import com.google.common.collect.HashBiMap;
8698
import com.google.common.collect.ImmutableList;
99+
import com.google.common.collect.ImmutableMap;
87100
import com.google.common.collect.ImmutableList.Builder;
88101
import com.google.common.collect.ImmutableMultimap;
89102
import com.google.common.collect.Multimap;
@@ -95,7 +108,7 @@
95108
*
96109
* @author Geneviève Bastien
97110
*/
98-
public class FlameChartDataProvider extends AbstractTmfTraceDataProvider implements ITimeGraphDataProvider<FlameChartEntryModel>, IOutputStyleProvider {
111+
public class FlameChartDataProvider extends AbstractTmfTraceDataProvider implements ITimeGraphDataProvider<FlameChartEntryModel>, IOutputAnnotationProvider, IOutputStyleProvider {
99112

100113
/**
101114
* Provider ID.
@@ -283,6 +296,34 @@ public TmfModelResponse<Map<String, String>> fetchTooltip(Map<String, Object> fe
283296
if (selectedDepth == null) {
284297
return null;
285298
}
299+
Map<String, String> tooltips = new HashMap<>();
300+
if (fFcProvider instanceof TmfStateSystemAnalysisModule) {
301+
ITmfStateSystem ss = ((TmfStateSystemAnalysisModule) fFcProvider).getStateSystem();
302+
if (ss != null) {
303+
try {
304+
int parentQuark = ss.getParentAttributeQuark(ss.getParentAttributeQuark(selectedDepth.getQuark()));
305+
int annotQuark = ss.optQuarkRelative(parentQuark, InstrumentedCallStackAnalysis.ANNOTATIONS);
306+
if (annotQuark != ITmfStateSystem.INVALID_ATTRIBUTE) {
307+
ITmfStateInterval interval = ss.querySingleState(time, annotQuark);
308+
if (!interval.getStateValue().isNull()) {
309+
String annotValue = interval.getStateValue().unboxStr();
310+
String cleaned = annotValue.replace("{", "").replace("}", "");
311+
String[] pairs = cleaned.split(", ");
312+
for (String pair : pairs) {
313+
String[] keyValue = pair.split("=", 2);
314+
if (keyValue.length == 2) {
315+
tooltips.put(keyValue[0].trim(), keyValue[1].trim());
316+
}
317+
}
318+
}
319+
}
320+
return tooltips;
321+
322+
} catch (StateSystemDisposedException e) {
323+
return null;
324+
}
325+
}
326+
}
286327
Multimap<CallStackDepth, ISegment> csFunctions = fFcProvider.queryCallStacks(Collections.singleton(selectedDepth), Collections.singleton(time));
287328
Collection<ISegment> functions = csFunctions.get(selectedDepth);
288329
if (functions.isEmpty()) {
@@ -293,7 +334,6 @@ public TmfModelResponse<Map<String, String>> fetchTooltip(Map<String, Object> fe
293334
return null;
294335
}
295336
ICalledFunction currentFct = (ICalledFunction) next;
296-
Map<String, String> tooltips = new HashMap<>();
297337
int threadId = currentFct.getThreadId();
298338
if (threadId > 0) {
299339
tooltips.put(String.valueOf(Messages.FlameChartDataProvider_ThreadId), String.valueOf(threadId));
@@ -345,7 +385,8 @@ public TmfModelResponse<TmfTreeModel<FlameChartEntryModel>> fetchTree(Map<String
345385
boolean complete = fcProvider.isComplete();
346386
CallStackSeries callstack = fcProvider.getCallStackSeries();
347387
if (callstack == null) {
348-
// Explicitly tell the client that the analysis is completed to prevent further polling
388+
// Explicitly tell the client that the analysis is completed to
389+
// prevent further polling
349390
if (complete) {
350391
return new TmfModelResponse<>(new TmfTreeModel<>(Collections.emptyList(), Collections.emptyList()), ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
351392
}
@@ -710,4 +751,77 @@ public TmfModelResponse<OutputStyleModel> fetchStyle(Map<String, Object> fetchPa
710751
Map<String, OutputElementStyle> styles = FlameWithKernelPalette.getInstance().getStyles();
711752
return new TmfModelResponse<>(new OutputStyleModel(styles), ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
712753
}
754+
755+
@Override
756+
public TmfModelResponse<AnnotationCategoriesModel> fetchAnnotationCategories(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
757+
return new TmfModelResponse<>(new AnnotationCategoriesModel(Collections.singletonList( InstrumentedCallStackAnalysis.ANNOTATIONS)), ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
758+
}
759+
760+
@Override
761+
public TmfModelResponse<AnnotationModel> fetchAnnotations(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
762+
763+
if (!(fFcProvider instanceof TmfStateSystemAnalysisModule)) {
764+
return new TmfModelResponse<>(null, ITmfResponse.Status.FAILED, CommonStatusMessage.ANALYSIS_INITIALIZATION_FAILED);
765+
}
766+
767+
TmfStateSystemAnalysisModule csa = (TmfStateSystemAnalysisModule) fFcProvider;
768+
ITmfStateSystem ss = csa.getStateSystem();
769+
if (ss == null) {
770+
return new TmfModelResponse<>(null, ITmfResponse.Status.FAILED, CommonStatusMessage.STATE_SYSTEM_FAILED);
771+
}
772+
773+
SelectionTimeQueryFilter filter = FetchParametersUtils.createSelectionTimeQuery(fetchParameters);
774+
if (filter == null) {
775+
return new TmfModelResponse<>(null, ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
776+
}
777+
778+
Map<Long, FlameChartEntryModel> selectedEntries = getSelectedEntries(fetchParameters);
779+
List<Annotation> annotations = new ArrayList<>();
780+
try {
781+
for (Entry<@NonNull Long, @NonNull FlameChartEntryModel> entry : selectedEntries.entrySet()) {
782+
long csId = entry.getValue().getId();
783+
CallStackDepth cs = fIdToCallstack.get(csId);
784+
if (cs == null) {
785+
continue;
786+
}
787+
int parentQuark = ss.getParentAttributeQuark(ss.getParentAttributeQuark(cs.getQuark()));
788+
int markersQuark = ss.optQuarkRelative(parentQuark, InstrumentedCallStackAnalysis.ANNOTATIONS);
789+
if (markersQuark != ITmfStateSystem.INVALID_ATTRIBUTE) {
790+
List<Long> times = DataProviderParameterUtils.extractTimeRequested(fetchParameters);
791+
for (ITmfStateInterval markerInterval : ss.query2D(Collections.singleton(markersQuark), times)) {
792+
if (!markerInterval.getStateValue().isNull()) {
793+
long startTime = markerInterval.getStartTime();
794+
// Find callstack depth at marker start time
795+
int depth = 0;
796+
for (int i = 1; i <= ss.getNbAttributes(); i++) {
797+
int depthQuark = ss.optQuarkRelative(cs.getQuark(), String.valueOf(i));
798+
if (depthQuark != ITmfStateSystem.INVALID_ATTRIBUTE) {
799+
ITmfStateInterval depthInterval = ss.querySingleState(startTime, depthQuark);
800+
if (!depthInterval.getStateValue().isNull()) {
801+
depth = i;
802+
}
803+
}
804+
}
805+
long entryId = entry.getKey();
806+
long rowId = (depth > 0) ? entryId + depth : entryId;
807+
String annotValue = markerInterval.getStateValue().unboxStr();
808+
String cleaned = annotValue.replace("{", "").replace("}", "");
809+
String[] pairs = cleaned.split(", ");
810+
if (pairs.length > 0) {
811+
OutputElementStyle style = new OutputElementStyle(null, ImmutableMap.of(
812+
StyleProperties.COLOR, "#7D3D31", //$NON-NLS-1$
813+
StyleProperties.HEIGHT, 0.33f,
814+
StyleProperties.SYMBOL_TYPE, SymbolType.DIAMOND));
815+
annotations.add(new Annotation(startTime, 1, rowId, pairs[0], style));
816+
}
817+
}
818+
}
819+
}
820+
}
821+
} catch (StateSystemDisposedException e) {
822+
return new TmfModelResponse<>(null, ITmfResponse.Status.FAILED, e.getMessage());
823+
}
824+
825+
return new TmfModelResponse<>(new AnnotationModel(Collections.singletonMap( InstrumentedCallStackAnalysis.ANNOTATIONS, annotations)), ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
826+
}
713827
}

0 commit comments

Comments
 (0)