1 /*******************************************************************************
2 * Copyright (c) 2016 Ericsson
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *******************************************************************************/
10 package org
.eclipse
.tracecompass
.internal
.analysis
.timing
.core
.callgraph
;
12 import java
.util
.ArrayList
;
13 import java
.util
.Arrays
;
14 import java
.util
.Collections
;
15 import java
.util
.List
;
16 import java
.util
.stream
.Collectors
;
18 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
19 import org
.eclipse
.core
.runtime
.ListenerList
;
20 import org
.eclipse
.jdt
.annotation
.NonNull
;
21 import org
.eclipse
.jdt
.annotation
.Nullable
;
22 import org
.eclipse
.tracecompass
.analysis
.timing
.core
.segmentstore
.IAnalysisProgressListener
;
23 import org
.eclipse
.tracecompass
.analysis
.timing
.core
.segmentstore
.ISegmentStoreProvider
;
24 import org
.eclipse
.tracecompass
.common
.core
.StreamUtils
;
25 import org
.eclipse
.tracecompass
.internal
.analysis
.timing
.core
.Activator
;
26 import org
.eclipse
.tracecompass
.segmentstore
.core
.ISegment
;
27 import org
.eclipse
.tracecompass
.segmentstore
.core
.ISegmentStore
;
28 import org
.eclipse
.tracecompass
.segmentstore
.core
.SegmentStoreFactory
;
29 import org
.eclipse
.tracecompass
.segmentstore
.core
.SegmentStoreFactory
.SegmentStoreType
;
30 import org
.eclipse
.tracecompass
.statesystem
.core
.ITmfStateSystem
;
31 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.AttributeNotFoundException
;
32 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.StateSystemDisposedException
;
33 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.TimeRangeException
;
34 import org
.eclipse
.tracecompass
.statesystem
.core
.interval
.ITmfStateInterval
;
35 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.ITmfStateValue
;
36 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.ITmfStateValue
.Type
;
37 import org
.eclipse
.tracecompass
.tmf
.core
.analysis
.IAnalysisModule
;
38 import org
.eclipse
.tracecompass
.tmf
.core
.analysis
.TmfAbstractAnalysisModule
;
39 import org
.eclipse
.tracecompass
.tmf
.core
.callstack
.CallStackAnalysis
;
40 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTrace
;
41 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceManager
;
42 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceUtils
;
44 import com
.google
.common
.annotations
.VisibleForTesting
;
45 import com
.google
.common
.collect
.ImmutableList
;
48 * Call stack analysis used to create a segment for each call function from an
49 * entry/exit event. It builds a segment tree from the state system. An example
50 * taken from the Fibonacci trace's callStack shows the structure of the segment
51 * tree given by this analysis:
63 * @author Sonia Farrah
65 public abstract class CallGraphAnalysis
extends TmfAbstractAnalysisModule
implements ISegmentStoreProvider
{
67 // ------------------------------------------------------------------------
69 // ------------------------------------------------------------------------
74 private final ISegmentStore
<@NonNull ISegment
> fStore
;
79 private final ListenerList fListeners
= new ListenerList(ListenerList
.IDENTITY
);
82 * The Trace's root functions list
84 private final List
<ICalledFunction
> fRootFunctions
= new ArrayList
<>();
87 * The sub attributes of a certain thread
89 private List
<Integer
> fCurrentQuarks
= Collections
.emptyList();
91 * The List of thread nodes. Each thread has a virtual node having the root
92 * function as children
94 private List
<ThreadNode
> fThreadNodes
= new ArrayList
<>();
99 public CallGraphAnalysis() {
101 fStore
= SegmentStoreFactory
.createSegmentStore(SegmentStoreType
.Fast
);
105 public @NonNull String
getHelpText() {
106 String msg
= Messages
.CallGraphAnalysis_Description
;
107 return (msg
!= null) ? msg
: super.getHelpText();
111 public @NonNull String
getHelpText(@NonNull ITmfTrace trace
) {
112 return getHelpText();
116 public boolean canExecute(ITmfTrace trace
) {
118 * FIXME: change to !Iterables.isEmpty(getDependentAnalyses()) when
119 * analysis dependencies work better
125 protected Iterable
<IAnalysisModule
> getDependentAnalyses() {
126 return TmfTraceManager
.getTraceSet(getTrace()).stream()
127 .flatMap(trace
-> StreamUtils
.getStream(TmfTraceUtils
.getAnalysisModulesOfClass(trace
, CallStackAnalysis
.class)))
128 .distinct().collect(Collectors
.toList());
132 protected boolean executeAnalysis(@Nullable IProgressMonitor monitor
) {
133 ITmfTrace trace
= getTrace();
134 if (monitor
== null || trace
== null) {
137 Iterable
<IAnalysisModule
> dependentAnalyses
= getDependentAnalyses();
138 for (IAnalysisModule module
: dependentAnalyses
) {
139 if (!(module
instanceof CallStackAnalysis
)) {
144 // TODO:Look at updates while the state system's being built
145 dependentAnalyses
.forEach((t
) -> t
.waitForCompletion(monitor
));
146 for (IAnalysisModule module
: dependentAnalyses
) {
147 CallStackAnalysis callstackModule
= (CallStackAnalysis
) module
;
148 String
[] threadsPattern
= callstackModule
.getThreadsPattern();
149 String
[] processesPattern
= callstackModule
.getProcessesPattern();
150 String
[] callStackPath
= callstackModule
.getCallStackPath();
151 ITmfStateSystem ss
= callstackModule
.getStateSystem();
152 if (!iterateOverStateSystem(ss
, threadsPattern
, processesPattern
, callStackPath
, monitor
)) {
163 * Iterate over the process of the state system,then iterate over the
164 * different threads of each process.
168 * @param threadsPattern
169 * The threads pattern
170 * @param processesPattern
171 * The processes pattern
172 * @param callStackPath
173 * The call stack path
179 protected boolean iterateOverStateSystem(@Nullable ITmfStateSystem ss
, String
[] threadsPattern
, String
[] processesPattern
, String
[] callStackPath
, IProgressMonitor monitor
) {
183 List
<Integer
> processQuarks
= ss
.getQuarks(processesPattern
);
184 for (int processQuark
: processQuarks
) {
185 int processId
= getProcessId(ss
, processQuark
, ss
.getCurrentEndTime());
186 for (int threadQuark
: ss
.getQuarks(processQuark
, threadsPattern
)) {
187 if (!iterateOverQuark(ss
, processId
, threadQuark
, callStackPath
, monitor
)) {
197 * Iterate over functions with the same quark,search for their callees then
198 * add them to the segment store
203 * The process ID of the traced application
206 * @param subAttributePath
207 * sub-Attributes path
212 private boolean iterateOverQuark(ITmfStateSystem stateSystem
, int processId
, int threadQuark
, String
[] subAttributePath
, IProgressMonitor monitor
) {
213 String threadName
= stateSystem
.getAttributeName(threadQuark
);
215 ITmfStateInterval interval
= null;
217 interval
= stateSystem
.querySingleState(stateSystem
.getStartTime(), threadQuark
);
218 ITmfStateValue threadStateValue
= interval
.getStateValue();
219 if (threadStateValue
.getType() == Type
.LONG
|| threadStateValue
.getType() == Type
.INTEGER
) {
220 threadId
= threadStateValue
.unboxLong();
223 threadId
= Long
.parseLong(threadName
);
224 } catch (NumberFormatException e
) {
225 /* use default threadId */
228 } catch (StateSystemDisposedException error
) {
229 Activator
.getInstance().logError(Messages
.QueringStateSystemError
, error
);
232 long curTime
= stateSystem
.getStartTime();
233 long limit
= stateSystem
.getCurrentEndTime();
234 AbstractCalledFunction initSegment
= CalledFunctionFactory
.create(0, 0, 0, threadName
, processId
, null);
235 ThreadNode init
= new ThreadNode(initSegment
, 0, threadId
);
236 while (curTime
< limit
) {
237 if (monitor
.isCanceled()) {
240 int callStackQuark
= stateSystem
.getQuarkRelative(threadQuark
, subAttributePath
);
241 fCurrentQuarks
= stateSystem
.getSubAttributes(callStackQuark
, false);
242 if (fCurrentQuarks
.isEmpty()) {
246 int quarkParent
= fCurrentQuarks
.get(depth
);
247 interval
= stateSystem
.querySingleState(curTime
, quarkParent
);
248 ITmfStateValue stateValue
= interval
.getStateValue();
250 if (!stateValue
.isNull()) {
251 long intervalStart
= interval
.getStartTime();
252 long intervalEnd
= interval
.getEndTime();
253 // Create the segment for the first call event.
254 AbstractCalledFunction segment
= CalledFunctionFactory
.create(intervalStart
, intervalEnd
+ 1, depth
, stateValue
, processId
, null);
255 fRootFunctions
.add(segment
);
256 AggregatedCalledFunction firstNode
= new AggregatedCalledFunction(segment
, fCurrentQuarks
.size());
257 if (!findChildren(segment
, depth
, stateSystem
, fCurrentQuarks
.size() + fCurrentQuarks
.get(depth
), firstNode
, processId
, monitor
)) {
260 init
.addChild(firstNode
);
263 curTime
= interval
.getEndTime() + 1;
265 fThreadNodes
.add(init
);
266 } catch (AttributeNotFoundException
| StateSystemDisposedException
| TimeRangeException e
) {
267 Activator
.getInstance().logError(Messages
.QueringStateSystemError
, e
);
274 * Find the functions called by a parent function in a call stack then add
275 * segments for each child, updating the self times of each node
279 * The segment of the stack call event(the parent) callStackQuark
281 * The depth of the parent function
283 * The quark of the segment parent ss The actual state system
285 * The last quark in the state system
286 * @param aggregatedCalledFunction
287 * A node in the aggregation tree
289 * The process ID of the traced application
291 * The progress monitor The progress monitor TODO: if stack size
292 * is an issue, convert to a stack instead of recursive function
294 private boolean findChildren(AbstractCalledFunction node
, int depth
, ITmfStateSystem ss
, int maxQuark
, AggregatedCalledFunction aggregatedCalledFunction
, int processId
, IProgressMonitor monitor
) {
296 long curTime
= node
.getStart();
297 long limit
= node
.getEnd();
298 ITmfStateInterval interval
= null;
299 while (curTime
< limit
) {
300 if (monitor
.isCanceled()) {
304 if (depth
+ 1 < fCurrentQuarks
.size()) {
305 interval
= ss
.querySingleState(curTime
, fCurrentQuarks
.get(depth
+ 1));
309 } catch (StateSystemDisposedException e
) {
310 Activator
.getInstance().logError(Messages
.QueringStateSystemError
, e
);
313 ITmfStateValue stateValue
= interval
.getStateValue();
314 if (!stateValue
.isNull()) {
315 long intervalStart
= interval
.getStartTime();
316 long intervalEnd
= interval
.getEndTime();
317 if (intervalStart
< node
.getStart() || intervalEnd
> limit
) {
320 AbstractCalledFunction segment
= CalledFunctionFactory
.create(intervalStart
, intervalEnd
+ 1, node
.getDepth() + 1, stateValue
, processId
, node
);
321 AggregatedCalledFunction childNode
= new AggregatedCalledFunction(segment
, aggregatedCalledFunction
);
322 // Search for the children with the next quark.
323 findChildren(segment
, depth
+ 1, ss
, maxQuark
, childNode
, processId
, monitor
);
324 aggregatedCalledFunction
.addChild(childNode
);
325 node
.addChild(segment
);
327 curTime
= interval
.getEndTime() + 1;
333 public void addListener(@NonNull IAnalysisProgressListener listener
) {
334 fListeners
.add(listener
);
338 public void removeListener(@NonNull IAnalysisProgressListener listener
) {
339 fListeners
.remove(listener
);
343 protected void canceling() {
348 public @Nullable ISegmentStore
<@NonNull ISegment
> getSegmentStore() {
358 protected void sendUpdate(final ISegmentStore
<@NonNull ISegment
> store
) {
359 getListeners().forEach(listener
-> listener
.onComplete(this, store
));
365 * @return The listeners
367 protected Iterable
<IAnalysisProgressListener
> getListeners() {
368 return Arrays
.stream(fListeners
.getListeners())
369 .filter(listener
-> listener
instanceof IAnalysisProgressListener
)
370 .map(listener
-> (IAnalysisProgressListener
) listener
)
371 .collect(Collectors
.toList());
375 * The functions of the first level
377 * @return Functions of the first level
379 public List
<ICalledFunction
> getRootFunctions() {
380 return ImmutableList
.copyOf(fRootFunctions
);
384 * List of thread nodes. Each thread has a virtual node having the root
385 * functions called as children.
387 * @return The thread nodes
389 public List
<ThreadNode
> getThreadNodes() {
390 return ImmutableList
.copyOf(fThreadNodes
);
393 private static int getProcessId(ITmfStateSystem ss
, int processQuark
, long curTime
) {
395 if (processQuark
!= ITmfStateSystem
.ROOT_ATTRIBUTE
) {
397 ITmfStateInterval interval
= ss
.querySingleState(curTime
, processQuark
);
398 String processName
= ss
.getAttributeName(processQuark
);
399 ITmfStateValue processStateValue
= interval
.getStateValue();
400 if (processStateValue
.getType() == Type
.INTEGER
) {
401 processId
= processStateValue
.unboxInt();
404 processId
= Integer
.parseInt(processName
);
405 } catch (NumberFormatException e
) {
406 /* use default processId */
409 } catch (StateSystemDisposedException e
) {