-
Notifications
You must be signed in to change notification settings - Fork 13
/
AbstractCriticalPathModule.java
277 lines (245 loc) · 10.3 KB
/
AbstractCriticalPathModule.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
/*******************************************************************************
* Copyright (c) 2022, 2024 École Polytechnique de Montréal and others
*
* 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.graph.core.criticalpath;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.osgi.util.NLS;
import org.eclipse.tracecompass.analysis.graph.core.base.IGraphWorker;
import org.eclipse.tracecompass.analysis.graph.core.building.AbstractTmfGraphBuilderModule;
import org.eclipse.tracecompass.analysis.graph.core.graph.ITmfGraph;
import org.eclipse.tracecompass.analysis.graph.core.graph.ITmfVertex;
import org.eclipse.tracecompass.analysis.graph.core.graph.WorkerSerializer;
import org.eclipse.tracecompass.common.core.NonNullUtils;
import org.eclipse.tracecompass.internal.analysis.graph.core.Activator;
import org.eclipse.tracecompass.internal.analysis.graph.core.criticalpath.CriticalPathModule;
import org.eclipse.tracecompass.internal.analysis.graph.core.criticalpath.Messages;
import org.eclipse.tracecompass.internal.analysis.graph.core.criticalpath.OSCriticalPathAlgorithm;
import org.eclipse.tracecompass.tmf.core.exceptions.TmfAnalysisException;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
import com.google.common.annotations.VisibleForTesting;
/**
* Class to implement the critical path analysis
*
* @author Francis Giraldeau
* @author Geneviève Bastien
* @since 4.0
*/
public abstract class AbstractCriticalPathModule extends CriticalPathModule {
/** Worker_id parameter name */
public static final String PARAM_WORKER = "workerid"; //$NON-NLS-1$
private static final int CRITICAL_PATH_GRAPH_VERSION = 1;
private final AbstractTmfGraphBuilderModule fGraphModule;
private volatile @Nullable ITmfGraph fCriticalPath;
private volatile boolean fScheduleOnParameterChange = true;
/**
* Default constructor
*
* @param graph
* The graph module that will be used to calculate the critical
* path on
* @since 1.1
*/
public AbstractCriticalPathModule(AbstractTmfGraphBuilderModule graph) {
super(graph);
addParameter(PARAM_WORKER);
setId(ANALYSIS_ID);
fGraphModule = graph;
}
/**
* Constructor with the parameter. Can be used by benchmarks, to avoid that
* setting the parameter causes the module to be schedule and run in a job
* which keeps it in memory forever (and thus can cause OOME)
*
* @param graph
* The graph module that will be used to calculate the critical
* path on
* @param worker
* The worker parameter to set
* @since 3.1
*/
@VisibleForTesting
public AbstractCriticalPathModule(AbstractTmfGraphBuilderModule graph, IGraphWorker worker) {
this(graph);
fScheduleOnParameterChange = false;
setParameter(PARAM_WORKER, worker);
}
@Override
protected boolean executeAnalysis(final IProgressMonitor monitor) throws TmfAnalysisException {
/* Get the worker id */
Object workerObj = getParameter(PARAM_WORKER);
if (workerObj == null) {
return false;
}
if (!(workerObj instanceof IGraphWorker)) {
throw new IllegalStateException("Worker parameter must be an IGraphWorker"); //$NON-NLS-1$
}
IGraphWorker worker = (IGraphWorker) workerObj;
/* Get the graph */
AbstractTmfGraphBuilderModule graphModule = fGraphModule;
graphModule.schedule();
monitor.setTaskName(NLS.bind(Messages.CriticalPathModule_waitingForGraph, graphModule.getName()));
if (!graphModule.waitForCompletion(monitor)) {
Activator.getInstance().logInfo("Critical path execution: graph building was cancelled. Results may not be accurate."); //$NON-NLS-1$
return false;
}
ITmfGraph graph = graphModule.getTmfGraph();
if (graph == null) {
throw new TmfAnalysisException("Critical Path analysis: graph " + graphModule.getName() + " is null"); //$NON-NLS-1$//$NON-NLS-2$
}
ITmfVertex head = graph.getHead(worker);
if (head == null) {
/* Nothing happens with this worker, return an empty graph */
fCriticalPath = createGraph();
return true;
}
ICriticalPathAlgorithm cp = getAlgorithm(graph);
try {
ITmfGraph criticalPath = createGraph();
if (criticalPath == null) {
return false;
}
// If the critical path has already been computed previously
if (criticalPath.isDoneBuilding()) {
ITmfVertex previousGraphHead = criticalPath.getHead();
IGraphWorker previousGraphWorker = previousGraphHead == null ? null : criticalPath.getParentOf(previousGraphHead);
IGraphWorker currentWorker = graph.getParentOf(head);
// If it is the correct critical path
boolean isSameWorker = previousGraphWorker != null && compareWorker(previousGraphWorker, currentWorker);
if (isSameWorker) {
fCriticalPath = criticalPath;
return true;
}
// Else delete it
criticalPath.dispose();
getHistoryTreeFilePath().toFile().delete();
// Recreate a new graph with a brand new history tree file
criticalPath = createGraph();
}
criticalPath = cp.computeCriticalPath(criticalPath, head, null);
criticalPath.closeGraph(Long.MAX_VALUE);
fCriticalPath = criticalPath;
return true;
} catch (CriticalPathAlgorithmException e) {
Activator.getInstance().logError(NonNullUtils.nullToEmptyString(e.getMessage()), e);
}
return false;
}
private static boolean compareWorker(IGraphWorker worker1, IGraphWorker worker2) {
if (!worker1.getHostId().equals(worker2.getHostId())) {
return false;
}
for (String key: worker1.getWorkerAspects().keySet()) {
Object value1 = worker1.getWorkerAspects().get(key);
Object value2 = worker2.getWorkerAspects().get(key);
if (value1 == null && value2 == null) {
continue;
}
if (value1 == null || value2 == null || !value1.equals(value2)) {
return false;
}
}
return true;
}
private @Nullable ITmfGraph createGraph() {
Path htFile = getHistoryTreeFilePath();
ITmfTrace trace = fGraphModule.getTrace();
if (trace == null) {
throw new NullPointerException("The graph should not be created if there is no trace set"); //$NON-NLS-1$
}
return createGraphInstance(htFile, fGraphModule.getWorkerSerializer(), trace.getStartTime().toNanos(), CRITICAL_PATH_GRAPH_VERSION);
}
private Path getHistoryTreeFilePath() {
ITmfTrace trace = fGraphModule.getTrace();
if (trace == null) {
throw new NullPointerException("The graph should not be created if there is no trace set"); //$NON-NLS-1$
}
String fileDirectory = TmfTraceManager.getSupplementaryFileDir(trace);
// FIXME Move somewhere where both graph and crit path module can use
String id = fGraphModule.getId() + ".critPath"; //$NON-NLS-1$
Path historyTreeFile = Paths.get(fileDirectory + id + ".ht"); //$NON-NLS-1$
return historyTreeFile;
}
/**
* Create the graph instance.
*
* The method needs to be implemented by children as the graph has to implement factories
* and other methods that are specific to each use of the critical path module.
*
* @param htFile The history tree file used to store the graph
* @param workerSerializer Responsible to read and write htfiles from disk
* @param startTime Start time of the trace
* @param version Version of the critical path graph used
* @return The critical path graph
*
* @since 3.2
*/
protected abstract @Nullable ITmfGraph createGraphInstance(Path htFile, WorkerSerializer workerSerializer, long startTime, int version);
@Override
protected void canceling() {
// Do nothing
}
@Override
protected void parameterChanged(String name) {
closeCriticalPath();
cancel();
resetAnalysis();
if (fScheduleOnParameterChange) {
schedule();
}
}
@Override
public void dispose() {
super.dispose();
closeCriticalPath();
}
private void closeCriticalPath() {
if (fCriticalPath != null) {
fCriticalPath.dispose();
}
fCriticalPath = null;
}
private static ICriticalPathAlgorithm getAlgorithm(ITmfGraph graph) {
return new OSCriticalPathAlgorithm(graph);
}
@Override
public boolean canExecute(ITmfTrace trace) {
/*
* TODO: The critical path executes on a graph, so at least a graph must
* be available for this trace
*/
return true;
}
/**
* Gets the graph for the critical path
*
* @return The critical path graph
*/
@Override
public @Nullable ITmfGraph getCriticalPathGraph() {
return fCriticalPath;
}
@Override
protected @NonNull String getFullHelpText() {
return NonNullUtils.nullToEmptyString(Messages.CriticalPathModule_fullHelpText);
}
@Override
protected @NonNull String getShortHelpText(ITmfTrace trace) {
return getFullHelpText();
}
@Override
protected @NonNull String getTraceCannotExecuteHelpText(@NonNull ITmfTrace trace) {
return NonNullUtils.nullToEmptyString(Messages.CriticalPathModule_cantExecute);
}
}