| /******************************************************************************* |
| * Copyright (c) 2018 Ericsson |
| * |
| * All rights reserved. This program and the accompanying materials are |
| * made available under the terms of the Eclipse Public License v1.0 which |
| * accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| *******************************************************************************/ |
| |
| package org.eclipse.tracecompass.incubator.internal.opentracing.core.analysis.spanlife; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.function.Predicate; |
| |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.tracecompass.incubator.internal.opentracing.core.analysis.spanlife.SpanLifeEntryModel.LogEvent; |
| import org.eclipse.tracecompass.incubator.internal.opentracing.core.event.IOpenTracingConstants; |
| import org.eclipse.tracecompass.internal.tmf.core.model.filters.FetchParametersUtils; |
| import org.eclipse.tracecompass.internal.tmf.core.model.timegraph.AbstractTimeGraphDataProvider; |
| import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem; |
| import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException; |
| import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException; |
| import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException; |
| import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval; |
| import org.eclipse.tracecompass.tmf.core.dataprovider.DataProviderParameterUtils; |
| import org.eclipse.tracecompass.tmf.core.model.CommonStatusMessage; |
| import org.eclipse.tracecompass.tmf.core.model.filters.SelectionTimeQueryFilter; |
| import org.eclipse.tracecompass.tmf.core.model.filters.TimeQueryFilter; |
| import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphArrow; |
| import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphRowModel; |
| import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphState; |
| import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphEntryModel; |
| import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphModel; |
| import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphRowModel; |
| import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphState; |
| import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeModel; |
| import org.eclipse.tracecompass.tmf.core.response.ITmfResponse; |
| import org.eclipse.tracecompass.tmf.core.response.TmfModelResponse; |
| import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; |
| import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; |
| |
| import com.google.common.collect.ImmutableList.Builder; |
| import com.google.common.collect.Multimap; |
| import com.google.common.collect.TreeMultimap; |
| |
| /** |
| * Data provider that will show the object lifespans. |
| * |
| * @author Katherine Nadeau |
| * |
| */ |
| @SuppressWarnings("restriction") |
| public class SpanLifeDataProvider extends AbstractTimeGraphDataProvider<@NonNull SpanLifeAnalysis, @NonNull TimeGraphEntryModel> { |
| |
| private static final int MARKER_SIZE = 500; |
| |
| private static final String ERROR = "error"; //$NON-NLS-1$ |
| private static final String EVENT = "event"; //$NON-NLS-1$ |
| private static final String MESSAGE = "message"; //$NON-NLS-1$ |
| private static final String STACK = "stack"; //$NON-NLS-1$ |
| private static final String OTHER = "other"; //$NON-NLS-1$ |
| |
| /** |
| * Suffix for dataprovider ID |
| */ |
| public static final String SUFFIX = ".dataprovider"; //$NON-NLS-1$ |
| |
| /** |
| * Constructor |
| * |
| * @param trace |
| * the trace this provider represents |
| * @param analysisModule |
| * the analysis encapsulated by this provider |
| */ |
| public SpanLifeDataProvider(ITmfTrace trace, SpanLifeAnalysis analysisModule) { |
| super(trace, analysisModule); |
| } |
| |
| @Override |
| public @NonNull TmfModelResponse<@NonNull List<@NonNull ITimeGraphArrow>> fetchArrows(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) { |
| return new TmfModelResponse<>(null, ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED); |
| } |
| |
| @Override |
| public @NonNull TmfModelResponse<@NonNull Map<@NonNull String, @NonNull String>> fetchTooltip(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) { |
| ITmfStateSystem ss = getAnalysisModule().getStateSystem(); |
| SelectionTimeQueryFilter filter = FetchParametersUtils.createSelectionTimeQuery(fetchParameters); |
| if (filter == null) { |
| return new TmfModelResponse<>(null, ITmfResponse.Status.FAILED, CommonStatusMessage.INCORRECT_QUERY_PARAMETERS); |
| } |
| Map<@NonNull Long, @NonNull Integer> entries = getSelectedEntries(filter); |
| Collection<@NonNull Integer> quarks = entries.values(); |
| long startTime = filter.getStart(); |
| long hoverTime = filter.getTimesRequested()[1]; |
| long endTime = filter.getEnd(); |
| if (ss == null || quarks.size() != 1 || !getAnalysisModule().isQueryable(hoverTime)) { |
| return new TmfModelResponse<>(null, ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED); |
| } |
| |
| int traceLogsQuark = ITmfStateSystem.INVALID_ATTRIBUTE; |
| try { |
| String traceId = ss.getFullAttributePathArray(quarks.iterator().next())[0]; |
| traceLogsQuark = ss.getQuarkRelative(ss.getQuarkAbsolute(traceId), IOpenTracingConstants.LOGS); |
| } catch (AttributeNotFoundException e) { |
| return new TmfModelResponse<>(null, ITmfResponse.Status.CANCELLED, CommonStatusMessage.TASK_CANCELLED); |
| } |
| |
| int spanLogQuark = getLogQuark(ss, ss.getAttributeName(quarks.iterator().next()), ss.getSubAttributes(traceLogsQuark, false)); |
| |
| try { |
| Map<@NonNull String, @NonNull String> retMap = new HashMap<>(); |
| if (spanLogQuark != ITmfStateSystem.INVALID_ATTRIBUTE) { |
| Long ssStartTime = startTime == Long.MIN_VALUE ? ss.getStartTime() : startTime; |
| Long ssEndTime = endTime == Long.MIN_VALUE ? ss.getCurrentEndTime() : endTime; |
| Long deviationAccepted = (ssEndTime - ssStartTime) / MARKER_SIZE; |
| for (ITmfStateInterval state : ss.query2D(Collections.singletonList(spanLogQuark), Math.max(hoverTime - deviationAccepted, ssStartTime), Math.min(hoverTime + deviationAccepted, ssEndTime))) { |
| Object object = state.getValue(); |
| if (object instanceof String) { |
| String logs = (String) object; |
| String timestamp = TmfTimestamp.fromNanos(state.getStartTime()).toString(); |
| if (timestamp != null) { |
| retMap.put("log timestamp", timestamp); //$NON-NLS-1$ |
| } |
| String[] fields = logs.split("~"); //$NON-NLS-1$ |
| for (String field : fields) { |
| retMap.put(field.substring(0, field.indexOf(':')), field.substring(field.indexOf(':') + 1)); |
| } |
| return new TmfModelResponse<>(retMap, ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED); |
| } |
| } |
| } |
| return new TmfModelResponse<>(retMap, ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED); |
| } catch (StateSystemDisposedException e) { |
| return new TmfModelResponse<>(null, ITmfResponse.Status.CANCELLED, CommonStatusMessage.TASK_CANCELLED); |
| } |
| } |
| |
| @Override |
| public @NonNull String getId() { |
| return getAnalysisModule().getId() + SUFFIX; |
| } |
| |
| @Override |
| protected @Nullable TimeGraphModel getRowModel(@NonNull ITmfStateSystem ss, Map<String, Object> parameters, @Nullable IProgressMonitor monitor) throws StateSystemDisposedException { |
| TreeMultimap<Integer, ITmfStateInterval> intervals = TreeMultimap.create(Comparator.naturalOrder(), |
| Comparator.comparing(ITmfStateInterval::getStartTime)); |
| SelectionTimeQueryFilter filter = FetchParametersUtils.createSelectionTimeQuery(parameters); |
| Map<@NonNull Long, @NonNull Integer> entries = getSelectedEntries(filter); |
| Collection<Long> times = getTimes(filter, ss.getStartTime(), ss.getCurrentEndTime()); |
| /* Do the actual query */ |
| for (ITmfStateInterval interval : ss.query2D(entries.values(), times)) { |
| if (monitor != null && monitor.isCanceled()) { |
| return new TimeGraphModel(Collections.emptyList()); |
| } |
| intervals.put(interval.getAttribute(), interval); |
| } |
| Map<@NonNull Integer, @NonNull Predicate<@NonNull Multimap<@NonNull String, @NonNull Object>>> predicates = new HashMap<>(); |
| Multimap<@NonNull Integer, @NonNull String> regexesMap = DataProviderParameterUtils.extractRegexFilter(parameters); |
| if (regexesMap != null) { |
| predicates.putAll(computeRegexPredicate(regexesMap)); |
| } |
| List<@NonNull ITimeGraphRowModel> rows = new ArrayList<>(); |
| for (Map.Entry<@NonNull Long, @NonNull Integer> entry : entries.entrySet()) { |
| if (monitor != null && monitor.isCanceled()) { |
| return new TimeGraphModel(Collections.emptyList()); |
| } |
| |
| List<ITimeGraphState> eventList = new ArrayList<>(); |
| for (ITmfStateInterval interval : intervals.get(entry.getValue())) { |
| long startTime = interval.getStartTime(); |
| long duration = interval.getEndTime() - startTime + 1; |
| Object state = interval.getValue(); |
| TimeGraphState value = new TimeGraphState(startTime, duration, state == null ? Integer.MIN_VALUE : 0); |
| applyFilterAndAddState(eventList, value, entry.getKey(), predicates, monitor); |
| } |
| rows.add(new TimeGraphRowModel(entry.getKey(), eventList)); |
| |
| } |
| return new TimeGraphModel(rows); |
| } |
| |
| @Override |
| protected boolean isCacheable() { |
| return true; |
| } |
| |
| @Override |
| protected @NonNull TmfTreeModel<@NonNull TimeGraphEntryModel> getTree(@NonNull ITmfStateSystem ss, Map<String, Object> parameters, @Nullable IProgressMonitor monitor) throws StateSystemDisposedException { |
| Builder<@NonNull TimeGraphEntryModel> builder = new Builder<>(); |
| long rootId = getId(ITmfStateSystem.ROOT_ATTRIBUTE); |
| builder.add(new TimeGraphEntryModel(rootId, -1, Collections.singletonList(String.valueOf(getTrace().getName())), ss.getStartTime(), ss.getCurrentEndTime())); |
| |
| for (int traceQuark : ss.getSubAttributes(ITmfStateSystem.ROOT_ATTRIBUTE, false)) { |
| addTrace(ss, builder, traceQuark, rootId); |
| } |
| |
| return new TmfTreeModel<>(Collections.emptyList(), builder.build()); |
| } |
| |
| private void addTrace(ITmfStateSystem ss, Builder<@NonNull TimeGraphEntryModel> builder, int quark, long parentId) { |
| List<@NonNull Integer> logsQuarks; |
| try { |
| int logsQuark = ss.getQuarkRelative(quark, IOpenTracingConstants.LOGS); |
| logsQuarks = ss.getSubAttributes(logsQuark, false); |
| } catch (AttributeNotFoundException e) { |
| logsQuarks = new ArrayList<>(); |
| } |
| |
| int openTracingSpansQuark; |
| try { |
| openTracingSpansQuark = ss.getQuarkRelative(quark, SpanLifeStateProvider.OPEN_TRACING_ATTRIBUTE); |
| } catch (AttributeNotFoundException e) { |
| return; |
| } |
| |
| long traceQuarkId = getId(quark); |
| builder.add(new TimeGraphEntryModel(traceQuarkId, parentId, Collections.singletonList(ss.getAttributeName(quark)), ss.getStartTime(), ss.getCurrentEndTime())); |
| |
| int ustSpansQuark; |
| try { |
| ustSpansQuark = ss.getQuarkRelative(quark, SpanLifeStateProvider.UST_ATTRIBUTE); |
| } catch (AttributeNotFoundException e) { |
| addChildren(ss, builder, openTracingSpansQuark, traceQuarkId, logsQuarks); |
| return; |
| } |
| addUstChildren(ss, builder, openTracingSpansQuark, ustSpansQuark, traceQuarkId, logsQuarks); |
| } |
| |
| private void addChildren(ITmfStateSystem ss, Builder<@NonNull TimeGraphEntryModel> builder, int quark, long parentId, List<Integer> logsQuarks) { |
| for (Integer child : ss.getSubAttributes(quark, false)) { |
| long childId = getId(child); |
| String childName = ss.getAttributeName(child); |
| if (!childName.equals(IOpenTracingConstants.LOGS)) { |
| List<LogEvent> logs = new ArrayList<>(); |
| int logQuark = getLogQuark(ss, childName, logsQuarks); |
| try { |
| for (ITmfStateInterval interval : ss.query2D(Collections.singletonList(logQuark), ss.getStartTime(), ss.getCurrentEndTime())) { |
| if (!interval.getStateValue().isNull()) { |
| logs.add(new LogEvent(interval.getStartTime(), getLogType(String.valueOf(interval.getValue())))); |
| } |
| } |
| } catch (IndexOutOfBoundsException | TimeRangeException | StateSystemDisposedException e) { |
| } |
| builder.add(new SpanLifeEntryModel(childId, parentId, Collections.singletonList(getSpanName(childName)), ss.getStartTime(), ss.getCurrentEndTime(), logs, getErrorTag(childName), getProcessName(childName))); |
| addChildren(ss, builder, child, childId, logsQuarks); |
| } |
| } |
| } |
| |
| private void addUstChildren(ITmfStateSystem ss, Builder<@NonNull TimeGraphEntryModel> builder, int openTracingQuark, int ustQuark, long parentId, List<Integer> logsQuarks) { |
| for (Integer child : ss.getSubAttributes(openTracingQuark, false)) { |
| String childName = ss.getAttributeName(child); |
| |
| List<LogEvent> logs = new ArrayList<>(); |
| int logQuark = getLogQuark(ss, childName, logsQuarks); |
| try { |
| for (ITmfStateInterval interval : ss.query2D(Collections.singletonList(logQuark), ss.getStartTime(), ss.getCurrentEndTime())) { |
| if (!interval.getStateValue().isNull()) { |
| logs.add(new LogEvent(interval.getStartTime(), getLogType(String.valueOf(interval.getValue())))); |
| } |
| } |
| } catch (IndexOutOfBoundsException | TimeRangeException | StateSystemDisposedException e) { |
| } |
| |
| String spanId = getSpanId(childName); |
| |
| int ustSpan; |
| try { |
| ustSpan = ss.getQuarkRelative(ustQuark, spanId); |
| } catch (AttributeNotFoundException e) { |
| return; |
| } |
| long childId = getId(ustSpan); |
| builder.add(new SpanLifeEntryModel(childId, parentId, Collections.singletonList(getSpanName(childName)), ss.getStartTime(), ss.getCurrentEndTime(), logs, getErrorTag(childName), getProcessName(childName))); |
| addUstChildren(ss, builder, child, ustQuark, childId, logsQuarks); |
| } |
| } |
| |
| private static int getLogQuark(ITmfStateSystem ss, String spanName, List<Integer> logsQuarks) { |
| for (int logsQuark : logsQuarks) { |
| if (ss.getAttributeName(logsQuark).equals(getSpanId(spanName))) { |
| return logsQuark; |
| } |
| } |
| return ITmfStateSystem.INVALID_ATTRIBUTE; |
| } |
| |
| private static String getSpanName(String attributeName) { |
| int slashPos = attributeName.lastIndexOf('/'); |
| slashPos = attributeName.lastIndexOf('/', slashPos); |
| slashPos = attributeName.lastIndexOf('/', slashPos); |
| return attributeName.substring(0, Math.max(0, slashPos)); |
| } |
| |
| private static String getSpanId(String attributeName) { |
| String[] attributeInfo = attributeName.split("/"); //$NON-NLS-1$ |
| return attributeInfo[attributeInfo.length - 3]; |
| } |
| |
| private static Boolean getErrorTag(String attributeName) { |
| String[] attributeInfo = attributeName.split("/"); //$NON-NLS-1$ |
| return attributeInfo[attributeInfo.length - 2].equals("true"); //$NON-NLS-1$ |
| } |
| |
| private static String getProcessName(String attributeName) { |
| return attributeName.substring(attributeName.lastIndexOf('/') + 1); |
| } |
| |
| private static String getLogType(String logs) { |
| String[] logsArray = logs.split("~"); //$NON-NLS-1$ |
| StringBuilder builder = new StringBuilder(); |
| for (int i = 0; i < logsArray.length; i++) { |
| builder.append(logsArray[i].substring(0, logsArray[i].indexOf(':'))); |
| } |
| String types = builder.toString(); |
| |
| if (types.contains(ERROR)) { |
| return ERROR; |
| } else if (types.contains(EVENT)) { |
| return EVENT; |
| } else if (types.contains(MESSAGE)) { |
| return MESSAGE; |
| } else if (types.contains(STACK)) { |
| return STACK; |
| } else { |
| return OTHER; |
| } |
| } |
| |
| @Deprecated |
| @Override |
| public TmfModelResponse<List<ITimeGraphArrow>> fetchArrows(TimeQueryFilter filter, @Nullable IProgressMonitor monitor) { |
| Map<String, Object> parameters = FetchParametersUtils.timeQueryToMap(filter); |
| return fetchArrows(parameters, monitor); |
| } |
| |
| @Deprecated |
| @Override |
| public TmfModelResponse<Map<String, String>> fetchTooltip(SelectionTimeQueryFilter filter, @Nullable IProgressMonitor monitor) { |
| Map<String, Object> parameters = FetchParametersUtils.selectionTimeQueryToMap(filter); |
| return fetchTooltip(parameters, monitor); |
| } |
| } |