| /******************************************************************************* |
| * Copyright (c) 2018 École Polytechnique de Montréal |
| * |
| * All rights reserved. This program and the accompanying materials are |
| * made available under the terms of the Eclipse Public License 2.0 which |
| * accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| *******************************************************************************/ |
| |
| package org.eclipse.tracecompass.analysis.profiling.core.tests.data; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Stack; |
| |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.jdt.annotation.NonNullByDefault; |
| import org.eclipse.tracecompass.analysis.profiling.core.callstack.CallStackStateProvider; |
| import org.eclipse.tracecompass.analysis.profiling.core.tests.stubs.CallStackProviderStub; |
| import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; |
| import org.eclipse.tracecompass.tmf.core.event.ITmfEventField; |
| import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest; |
| import org.eclipse.tracecompass.tmf.core.request.TmfEventRequest; |
| import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; |
| |
| /** |
| * Describe the callstack test data for a big stub trace, randomly generated. It |
| * is mostly to test the algorithms that require accesses to the state system, |
| * the state system here being big enough to have multiple nodes and levels. |
| * |
| * @author Geneviève Bastien |
| */ |
| @NonNullByDefault |
| public class TestDataBigCallStack extends CallStackTestData { |
| |
| private static final String CALLSTACK_FILE = "testfiles/traces/callstackBig.xml"; |
| private boolean fDataInitialized = false; |
| private long fEnd = 0; |
| private long fStart = Long.MAX_VALUE; |
| |
| @Override |
| public @NonNull ITmfTrace getTrace() { |
| // A good moment to initialize the expected data by reading the trace |
| ITmfTrace trace = super.getTrace(); |
| if (!fDataInitialized) { |
| setCallStackData(getExpectedData(trace)); |
| fDataInitialized = true; |
| } |
| return trace; |
| } |
| |
| @Override |
| protected @NonNull String getTraceFile() { |
| return CALLSTACK_FILE; |
| } |
| |
| @Override |
| protected long getStartTime() { |
| return fStart; |
| } |
| |
| @Override |
| protected long getEndTime() { |
| return fEnd; |
| } |
| |
| private List<ExpectedCallStackElement> getExpectedData(ITmfTrace trace) { |
| ExpectedDataBuildingRequest request = new ExpectedDataBuildingRequest(); |
| trace.sendRequest(request); |
| try { |
| request.waitForCompletion(); |
| } catch (InterruptedException e) { |
| throw new IllegalStateException("Request was interrupted"); |
| } |
| return request.getExpectedData(); |
| } |
| |
| private static class OpenedFunction { |
| private final long fFctStart; |
| private final String fFctName; |
| private final List<ExpectedFunction> fChildren = new ArrayList<>(); |
| |
| OpenedFunction(long fctStart, String fctName) { |
| fFctStart = fctStart; |
| fFctName = fctName; |
| } |
| |
| ExpectedFunction closeFunction(long end) { |
| return new ExpectedFunction((int) fFctStart, (int) end, fFctName, fChildren); |
| } |
| } |
| |
| private static class OpenedElement { |
| private final int fPid; |
| private final int fTid; |
| private final List<ExpectedFunction> fCalls = new ArrayList<>(); |
| |
| OpenedElement(int pid, int tid) { |
| fPid = pid; |
| fTid = tid; |
| } |
| } |
| |
| private class ExpectedDataBuildingRequest extends TmfEventRequest { |
| |
| Map<Long, Stack<OpenedFunction>> fOpenedFct = new HashMap<>(); |
| Map<Long, OpenedElement> fOpenedElements = new HashMap<>(); |
| |
| public ExpectedDataBuildingRequest() { |
| super(ITmfEvent.class, 0, ITmfEventRequest.ALL_DATA, ExecutionType.FOREGROUND); |
| } |
| |
| public List<ExpectedCallStackElement> getExpectedData() { |
| List<ExpectedCallStackElement> list = new ArrayList<>(); |
| for (OpenedElement element : fOpenedElements.values()) { |
| list.add(new ExpectedCallStackElement(element.fPid, element.fTid, element.fCalls)); |
| } |
| return list; |
| } |
| |
| @Override |
| public void handleData(ITmfEvent event) { |
| super.handleData(event); |
| long ts = event.getTimestamp().toNanos(); |
| fStart = Math.min(ts, fStart); |
| fEnd = Math.max(ts, fEnd); |
| long tid = CallStackProviderStub.getThreadIdFromEvent(event); |
| if (tid == CallStackStateProvider.UNKNOWN_PID) { |
| return; |
| } |
| Stack<OpenedFunction> stack = fOpenedFct.get(tid); |
| if (stack == null) { |
| stack = new Stack<>(); |
| fOpenedFct.put(tid, stack); |
| } |
| String name = event.getName(); |
| if (name.equals(CallStackProviderStub.ENTRY)) { |
| // Add the opened function to the stack |
| ITmfEventField field = event.getContent().getField(CallStackProviderStub.FIELD_NAME); |
| if (field == null) { |
| return; |
| } |
| stack.push(new OpenedFunction(ts, field.getValue().toString())); |
| } else if (name.equals(CallStackProviderStub.EXIT)) { |
| if (stack.isEmpty()) { |
| return; |
| } |
| OpenedFunction toClose = stack.pop(); |
| ExpectedFunction closedFunction = toClose.closeFunction(ts); |
| if (stack.isEmpty()) { |
| // Add the expected function to the base element |
| // See if we have an opened element for this tid |
| OpenedElement openedElement = fOpenedElements.get(tid); |
| if (openedElement == null) { |
| openedElement = createElementFromEvent(event, tid); |
| } |
| openedElement.fCalls.add(closedFunction); |
| } else { |
| // Add the closed function to the parent |
| OpenedFunction parent = stack.peek(); |
| parent.fChildren.add(closedFunction); |
| } |
| |
| } |
| } |
| |
| private OpenedElement createElementFromEvent(ITmfEvent event, long tid) { |
| int pid = CallStackProviderStub.getProcessIdFromEvent(event); |
| if (pid == CallStackStateProvider.UNKNOWN_PID) { |
| throw new NullPointerException("There should be a pid field"); |
| } |
| OpenedElement openedElement = new OpenedElement(pid, (int) tid); |
| fOpenedElements.put(tid, openedElement); |
| return openedElement; |
| } |
| |
| } |
| |
| } |