lttng.control: Replacing isKernel with an enum for the domain type
[deliverable/tracecompass.git] / lttng / org.eclipse.tracecompass.lttng2.control.ui / src / org / eclipse / tracecompass / internal / lttng2 / control / ui / views / service / LTTngControlServiceMI.java
CommitLineData
0df4af5f 1/**********************************************************************
ed902a2b 2 * Copyright (c) 2014, 2015 Ericsson
0df4af5f
JRJ
3 *
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 *
9 * Contributors:
10 * Jonathan Rajotte - Initial support for machine interface lttng 2.6
e5237dc5 11 * Bernd Hufmann - Fix check for live session
0df4af5f
JRJ
12 **********************************************************************/
13
9bc60be7 14package org.eclipse.tracecompass.internal.lttng2.control.ui.views.service;
0df4af5f 15
364dcfaf
BH
16import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
17
0df4af5f
JRJ
18import java.io.IOException;
19import java.io.StringReader;
6651c1b4 20import java.math.BigInteger;
0df4af5f
JRJ
21import java.net.URL;
22import java.util.ArrayList;
23import java.util.List;
24import java.util.regex.Matcher;
25
b2a6cbac 26import javax.xml.XMLConstants;
0df4af5f
JRJ
27import javax.xml.parsers.DocumentBuilder;
28import javax.xml.parsers.DocumentBuilderFactory;
29import javax.xml.parsers.ParserConfigurationException;
b2a6cbac 30import javax.xml.validation.SchemaFactory;
0df4af5f
JRJ
31
32import org.eclipse.core.commands.ExecutionException;
33import org.eclipse.core.runtime.IProgressMonitor;
d2e26145 34import org.eclipse.core.runtime.Platform;
364dcfaf
BH
35import org.eclipse.jdt.annotation.NonNull;
36import org.eclipse.jdt.annotation.Nullable;
67f8cadd 37import org.eclipse.osgi.util.NLS;
843f1eb2 38import org.eclipse.tracecompass.common.core.NonNullUtils;
1bc37054 39import org.eclipse.tracecompass.internal.lttng2.control.core.model.TraceDomainType;
9bc60be7
AM
40import org.eclipse.tracecompass.internal.lttng2.control.core.model.IBaseEventInfo;
41import org.eclipse.tracecompass.internal.lttng2.control.core.model.IChannelInfo;
42import org.eclipse.tracecompass.internal.lttng2.control.core.model.IDomainInfo;
43import org.eclipse.tracecompass.internal.lttng2.control.core.model.IEventInfo;
44import org.eclipse.tracecompass.internal.lttng2.control.core.model.IFieldInfo;
45import org.eclipse.tracecompass.internal.lttng2.control.core.model.IProbeEventInfo;
46import org.eclipse.tracecompass.internal.lttng2.control.core.model.ISessionInfo;
47import org.eclipse.tracecompass.internal.lttng2.control.core.model.ISnapshotInfo;
48import org.eclipse.tracecompass.internal.lttng2.control.core.model.IUstProviderInfo;
49import org.eclipse.tracecompass.internal.lttng2.control.core.model.LogLevelType;
9bc60be7
AM
50import org.eclipse.tracecompass.internal.lttng2.control.core.model.TraceEnablement;
51import org.eclipse.tracecompass.internal.lttng2.control.core.model.TraceEventType;
52import org.eclipse.tracecompass.internal.lttng2.control.core.model.TraceLogLevel;
53import org.eclipse.tracecompass.internal.lttng2.control.core.model.impl.BaseEventInfo;
54import org.eclipse.tracecompass.internal.lttng2.control.core.model.impl.BufferType;
55import org.eclipse.tracecompass.internal.lttng2.control.core.model.impl.ChannelInfo;
56import org.eclipse.tracecompass.internal.lttng2.control.core.model.impl.DomainInfo;
57import org.eclipse.tracecompass.internal.lttng2.control.core.model.impl.EventInfo;
58import org.eclipse.tracecompass.internal.lttng2.control.core.model.impl.FieldInfo;
59import org.eclipse.tracecompass.internal.lttng2.control.core.model.impl.ProbeEventInfo;
60import org.eclipse.tracecompass.internal.lttng2.control.core.model.impl.SessionInfo;
61import org.eclipse.tracecompass.internal.lttng2.control.core.model.impl.SnapshotInfo;
62import org.eclipse.tracecompass.internal.lttng2.control.core.model.impl.UstProviderInfo;
d2e26145 63import org.eclipse.tracecompass.internal.lttng2.control.ui.Activator;
9bc60be7
AM
64import org.eclipse.tracecompass.internal.lttng2.control.ui.views.handlers.XmlMiValidationErrorHandler;
65import org.eclipse.tracecompass.internal.lttng2.control.ui.views.messages.Messages;
364dcfaf 66import org.eclipse.tracecompass.tmf.remote.core.shell.ICommandInput;
ec619615
BH
67import org.eclipse.tracecompass.tmf.remote.core.shell.ICommandResult;
68import org.eclipse.tracecompass.tmf.remote.core.shell.ICommandShell;
0df4af5f
JRJ
69import org.w3c.dom.Document;
70import org.w3c.dom.Node;
71import org.w3c.dom.NodeList;
72import org.xml.sax.InputSource;
73import org.xml.sax.SAXException;
74
75/**
76 * Service for sending LTTng trace control commands to remote host via machine
77 * interface mode.
78 *
79 * @author Jonathan Rajotte
80 */
81public class LTTngControlServiceMI extends LTTngControlService {
82
d2e26145
MAL
83 /**
84 * The tracing key (.options) and System property to control whether or not schema validation should be used.
85 */
86 public static final String MI_SCHEMA_VALIDATION_KEY = Activator.PLUGIN_ID + "/mi/schema-validation"; //$NON-NLS-1$
87
0df4af5f
JRJ
88 // ------------------------------------------------------------------------
89 // Attributes
90 // ------------------------------------------------------------------------
91
92 private final DocumentBuilder fDocumentBuilder;
93
94 // ------------------------------------------------------------------------
95 // Constructors
96 // ------------------------------------------------------------------------
97
98 /**
99 * Constructor
100 *
101 * @param shell
102 * the command shell implementation to use
ce709731
MAL
103 * @param version
104 * the lttng version
0df4af5f
JRJ
105 * @throws ExecutionException
106 * if the creation of the Schema and DocumentBuilder objects
107 * fails
108 */
ce709731 109 public LTTngControlServiceMI(@NonNull ICommandShell shell, @Nullable LttngVersion version) throws ExecutionException {
0df4af5f 110 super(shell);
ce709731 111 setVersion(version);
0df4af5f
JRJ
112
113 DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
114 docBuilderFactory.setValidating(false);
115
d2e26145 116 if (isSchemaValidationEnabled()) {
ce709731 117 if (version != null) {
d2e26145
MAL
118 SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
119 try {
ce709731
MAL
120 URL xsdUrl = LTTngControlService.class.getResource(LTTngControlServiceConstants.MI_XSD_FILENAME);
121 if (version.compareTo(new LttngVersion(2, 8, 0, null, null, null, null, null, null)) >= 0) {
122 xsdUrl = LTTngControlService.class.getResource(LTTngControlServiceConstants.MI3_XSD_FILENAME);
123 // MI 3.0 added name spaces. It will fail to validate if this is not set to true.
124 docBuilderFactory.setNamespaceAware(true);
125 }
d2e26145
MAL
126 docBuilderFactory.setSchema(schemaFactory.newSchema(xsdUrl));
127 } catch (SAXException e) {
128 throw new ExecutionException(Messages.TraceControl_InvalidSchemaError, e);
129 }
b2a6cbac
AM
130 }
131 }
132
0df4af5f
JRJ
133 try {
134 fDocumentBuilder = docBuilderFactory.newDocumentBuilder();
135 } catch (ParserConfigurationException e) {
136 throw new ExecutionException(Messages.TraceControl_XmlDocumentBuilderError, e);
137 }
138
139 fDocumentBuilder.setErrorHandler(new XmlMiValidationErrorHandler());
140
141 }
142
d2e26145
MAL
143 private static boolean isSchemaValidationEnabled() {
144 String schemaValidationKey = Platform.getDebugOption(MI_SCHEMA_VALIDATION_KEY);
145 String systemProperty = System.getProperty(MI_SCHEMA_VALIDATION_KEY);
146 return schemaValidationKey != null && Boolean.parseBoolean(schemaValidationKey) || systemProperty != null && Boolean.parseBoolean(systemProperty);
147 }
148
0df4af5f 149 /**
cbc46cc9 150 * Generate a Document object from an list of Strings.
0df4af5f
JRJ
151 *
152 * @param xmlStrings
cbc46cc9 153 * list of strings representing an xml input
ce709731
MAL
154 * @param documentBuilder
155 * the builder used to get the document
0df4af5f
JRJ
156 * @return Document generated from strings input
157 * @throws ExecutionException
158 * when parsing has failed
159 */
ce709731 160 private static Document getDocumentFromStrings(List<String> xmlStrings, DocumentBuilder documentBuilder) throws ExecutionException {
0df4af5f
JRJ
161 StringBuilder concatenedString = new StringBuilder();
162 for (String string : xmlStrings) {
163 concatenedString.append(string);
164 }
165 InputSource stream = new InputSource(new StringReader(concatenedString.toString()));
166
167 Document document;
168 try {
ce709731 169 document = documentBuilder.parse(stream);
0df4af5f 170 } catch (SAXException | IOException e) {
b2a6cbac 171 throw new ExecutionException(Messages.TraceControl_XmlParsingError + ':' + e.toString(), e);
0df4af5f
JRJ
172 }
173 return document;
174
175 }
176
177 /**
ce709731 178 * Parse LTTng version from a MI command result
91dc1c3e 179 *
ce709731
MAL
180 * @param commandResult
181 * the result obtained from a MI command
182 * @return the LTTng version
0df4af5f
JRJ
183 * @throws ExecutionException
184 * when xml extraction fail
185 */
ce709731
MAL
186 public static LttngVersion parseVersion(ICommandResult commandResult) throws ExecutionException {
187 DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
188 DocumentBuilder documentBuilder;
189 try {
190 documentBuilder = docBuilderFactory.newDocumentBuilder();
191 } catch (ParserConfigurationException e) {
192 throw new ExecutionException(Messages.TraceControl_XmlDocumentBuilderError, e);
193 }
194
195 Document doc = getDocumentFromStrings(commandResult.getOutput(), documentBuilder);
0df4af5f 196 NodeList element = doc.getElementsByTagName(MIStrings.VERSION);
ce709731
MAL
197 if (element.getLength() != 1) {
198 throw new ExecutionException(Messages.TraceControl_UnsupportedVersionError);
199 }
200
0df4af5f
JRJ
201 int major = 0;
202 int minor = 0;
203 int patchLevel = 0;
204 String license = ""; //$NON-NLS-1$
205 String commit = ""; //$NON-NLS-1$
206 String name = ""; //$NON-NLS-1$
207 String description = ""; //$NON-NLS-1$
208 String url = ""; //$NON-NLS-1$
209 String fullVersion = ""; //$NON-NLS-1$
ce709731
MAL
210 NodeList child = element.item(0).getChildNodes();
211 // Get basic information
212 for (int i = 0; i < child.getLength(); i++) {
213 Node node = child.item(i);
214 switch (node.getNodeName()) {
215 case MIStrings.VERSION_MAJOR:
216 major = Integer.parseInt(node.getTextContent());
217 break;
218 case MIStrings.VERSION_MINOR:
219 minor = Integer.parseInt(node.getTextContent());
220 break;
221 case MIStrings.VERSION_PATCH_LEVEL:
222 patchLevel = Integer.parseInt(node.getTextContent());
223 break;
224 case MIStrings.VERSION_COMMIT:
225 commit = node.getTextContent();
226 break;
227 case MIStrings.VERSION_DESCRIPTION:
228 description = node.getTextContent();
229 break;
230 case MIStrings.VERSION_LICENSE:
231 license = node.getTextContent();
232 break;
233 case MIStrings.VERSION_NAME:
234 name = node.getTextContent();
235 break;
236 case MIStrings.VERSION_STR:
237 fullVersion = node.getTextContent();
238 break;
239 case MIStrings.VERSION_WEB:
240 url = node.getTextContent();
241 break;
242 default:
243 break;
0df4af5f 244 }
0df4af5f 245 }
ce709731 246 return new LttngVersion(major, minor, patchLevel, license, commit, name, description, url, fullVersion);
0df4af5f
JRJ
247 }
248
249 @Override
cbc46cc9 250 public List<String> getSessionNames(IProgressMonitor monitor) throws ExecutionException {
364dcfaf 251 ICommandInput command = createCommand(LTTngControlServiceConstants.COMMAND_LIST);
774a7993 252 ICommandResult result = executeCommand(command, monitor);
0df4af5f 253
ce709731 254 Document doc = getDocumentFromStrings(result.getOutput(), fDocumentBuilder);
0df4af5f
JRJ
255
256 NodeList elements = doc.getElementsByTagName(MIStrings.NAME);
257
258 ArrayList<String> retArray = new ArrayList<>();
259 for (int i = 0; i < elements.getLength(); i++) {
260 Node node = elements.item(i);
261 if (node.getParentNode().getNodeName().equalsIgnoreCase(MIStrings.SESSION)) {
262 retArray.add(node.getTextContent());
263 }
264 }
cbc46cc9 265 return retArray;
0df4af5f
JRJ
266 }
267
268 @Override
269 public ISessionInfo getSession(String sessionName, IProgressMonitor monitor) throws ExecutionException {
364dcfaf 270 ICommandInput command = createCommand(LTTngControlServiceConstants.COMMAND_LIST, sessionName);
774a7993 271 ICommandResult result = executeCommand(command, monitor);
0df4af5f
JRJ
272
273 ISessionInfo sessionInfo = new SessionInfo(sessionName);
ce709731 274 Document document = getDocumentFromStrings(result.getOutput(), fDocumentBuilder);
0df4af5f
JRJ
275
276 NodeList sessionsNode = document.getElementsByTagName(MIStrings.SESSION);
277 // There should be only one session
278 if (sessionsNode.getLength() != 1) {
517c0261 279 throw new ExecutionException(NLS.bind(Messages.TraceControl_MiInvalidNumberOfElementError, MIStrings.SESSION));
0df4af5f
JRJ
280 }
281
282 // Populate session information
67f8cadd
JRJ
283 Node rawSession = sessionsNode.item(0);
284 parseSession(sessionInfo, rawSession);
285
286 // Fetch the snapshot info
287 if (sessionInfo.isSnapshotSession()) {
288 ISnapshotInfo snapshot = getSnapshotInfo(sessionName, monitor);
289 sessionInfo.setSnapshotInfo(snapshot);
290 }
291
292 return sessionInfo;
293 }
294
295 /**
296 * @param sessionInfo
297 * @param rawSession
298 * @throws ExecutionException
299 */
300 private void parseSession(ISessionInfo sessionInfo, Node rawSession) throws ExecutionException {
301 if (!rawSession.getNodeName().equalsIgnoreCase(MIStrings.SESSION)) {
302 throw new ExecutionException(Messages.TraceControl_MiInvalidElementError);
303 }
304 NodeList rawSessionInfos = rawSession.getChildNodes();
0df4af5f
JRJ
305 for (int i = 0; i < rawSessionInfos.getLength(); i++) {
306 Node rawInfo = rawSessionInfos.item(i);
307 switch (rawInfo.getNodeName()) {
67f8cadd
JRJ
308 case MIStrings.NAME:
309 sessionInfo.setName(rawInfo.getTextContent());
310 break;
0df4af5f
JRJ
311 case MIStrings.PATH:
312 sessionInfo.setSessionPath(rawInfo.getTextContent());
313 break;
314 case MIStrings.ENABLED:
315 sessionInfo.setSessionState(rawInfo.getTextContent());
316 break;
317 case MIStrings.SNAPSHOT_MODE:
318 if (rawInfo.getTextContent().equals(LTTngControlServiceConstants.TRUE_NUMERICAL)) {
319 // real name will be set later
320 ISnapshotInfo snapshotInfo = new SnapshotInfo(""); //$NON-NLS-1$
321 sessionInfo.setSnapshotInfo(snapshotInfo);
322 }
323 break;
324 case MIStrings.LIVE_TIMER_INTERVAL:
ae02b8eb 325 long liveDelay = Long.parseLong(rawInfo.getTextContent());
e5237dc5 326 if ((liveDelay > 0 && (liveDelay <= LTTngControlServiceConstants.MAX_LIVE_TIMER_INTERVAL))) {
ae02b8eb
MAL
327 sessionInfo.setLive(true);
328 sessionInfo.setLiveUrl(SessionInfo.DEFAULT_LIVE_NETWORK_URL);
329 sessionInfo.setLivePort(SessionInfo.DEFAULT_LIVE_PORT);
330 sessionInfo.setLiveDelay(liveDelay);
331 }
0df4af5f
JRJ
332 break;
333 case MIStrings.DOMAINS:
334 // Extract the domains node
335 NodeList rawDomains = rawInfo.getChildNodes();
336 IDomainInfo domain = null;
337 for (int j = 0; j < rawDomains.getLength(); j++) {
338 if (rawDomains.item(j).getNodeName().equalsIgnoreCase(MIStrings.DOMAIN)) {
339 domain = parseDomain(rawDomains.item(j));
340 sessionInfo.addDomain(domain);
341 }
342 }
343 break;
344 default:
345 break;
346 }
347 }
348
349 if (!sessionInfo.isSnapshotSession()) {
350 Matcher matcher = LTTngControlServiceConstants.TRACE_NETWORK_PATTERN.matcher(sessionInfo.getSessionPath());
351 if (matcher.matches()) {
352 sessionInfo.setStreamedTrace(true);
353 }
354 }
0df4af5f
JRJ
355 }
356
357 /**
f4401569
JRJ
358 * Parse a raw domain XML node to a IDomainInfo object
359 *
360 * @param rawDomain
0df4af5f 361 * a domain xml node
f4401569
JRJ
362 * @return a populated {@link DomainInfo} object
363 * @throws ExecutionException
364 * when missing required xml element (type)
0df4af5f 365 */
f4401569
JRJ
366 protected IDomainInfo parseDomain(Node rawDomain) throws ExecutionException {
367 IDomainInfo domain = null;
368 // Get the type
369 Node rawType = getFirstOf(rawDomain.getChildNodes(), MIStrings.TYPE);
370 if (rawType == null) {
371 throw new ExecutionException(Messages.TraceControl_MiMissingRequiredError);
372 }
373 String rawTypeString = rawType.getTextContent().toLowerCase();
374 TraceDomainType domainType = TraceDomainType.valueOfString(rawTypeString);
375 switch (domainType) {
376 case KERNEL:
377 domain = new DomainInfo(Messages.TraceControl_KernelProviderDisplayName);
1bc37054 378 domain.setDomain(TraceDomainType.KERNEL);
f4401569
JRJ
379 break;
380 case UST:
381 domain = new DomainInfo(Messages.TraceControl_UstGlobalDomainDisplayName);
1bc37054 382 domain.setDomain(TraceDomainType.UST);
f4401569
JRJ
383 break;
384 case JUL:
385 /**
386 * TODO: Support for JUL JUL substructure and semantic is not the
387 * same as a regular UST or Kernel Domain There is no channel under
388 * JUL domain only events. The channel is activated in UST Channel
389 */
390 domain = new DomainInfo(Messages.TraceControl_JULDomainDisplayName);
1bc37054 391 domain.setDomain(TraceDomainType.JUL);
f4401569
JRJ
392 break;
393 case UNKNOWN:
394 domain = new DomainInfo(Messages.TraceControl_UnknownDomainDisplayName);
1bc37054 395 domain.setDomain(TraceDomainType.UNKNOWN);
f4401569 396 break;
1bc37054 397 //$CASES-OMITTED$
f4401569
JRJ
398 default:
399 throw new ExecutionException(Messages.TraceControl_MiInvalidElementError);
400 }
401
402 NodeList rawInfos = rawDomain.getChildNodes();
403 for (int i = 0; i < rawInfos.getLength(); i++) {
404 Node rawInfo = rawInfos.item(i);
405 switch (rawInfo.getNodeName()) {
406 case MIStrings.BUFFER_TYPE:
407 BufferType bufferType = BufferType.valueOfString(rawInfo.getTextContent());
408 domain.setBufferType(bufferType);
409 break;
410 case MIStrings.CHANNELS:
411 ArrayList<IChannelInfo> channels = new ArrayList<>();
412 parseChannels(rawInfo.getChildNodes(), channels);
413 if (channels.size() > 0) {
414 domain.setChannels(channels);
415 }
416 break;
417 default:
418 break;
419 }
420 }
421
422 return domain;
423 }
424
425 /**
426 * Parse a list of raw channel XML node into an ArrayList of IChannelInfo
427 *
428 * @param rawChannes
429 * List of raw channel XML node
430 * @param channels
431 * the parsed channels list
432 * @throws ExecutionException
433 * when missing required xml element (type)
434 */
435 private static void parseChannels(NodeList rawChannels, ArrayList<IChannelInfo> channels) throws ExecutionException {
436 IChannelInfo channel = null;
437 for (int i = 0; i < rawChannels.getLength(); i++) {
438 Node rawChannel = rawChannels.item(i);
439 if (rawChannel.getNodeName().equalsIgnoreCase(MIStrings.CHANNEL)) {
440 channel = new ChannelInfo(""); //$NON-NLS-1$
441
442 // Populate the channel
443 NodeList rawInfos = rawChannel.getChildNodes();
444 Node rawInfo = null;
445 for (int j = 0; j < rawInfos.getLength(); j++) {
446 rawInfo = rawInfos.item(j);
447 switch (rawInfo.getNodeName()) {
448 case MIStrings.NAME:
449 channel.setName(rawInfo.getTextContent());
450 break;
451 case MIStrings.ENABLED:
452 channel.setState(TraceEnablement.valueOfString(rawInfo.getTextContent()));
453 break;
454 case MIStrings.EVENTS:
455 List<IEventInfo> events = new ArrayList<>();
456 getEventInfo(rawInfo.getChildNodes(), events);
457 channel.setEvents(events);
458 break;
459 case MIStrings.ATTRIBUTES:
460 NodeList rawAttributes = rawInfo.getChildNodes();
461 for (int k = 0; k < rawAttributes.getLength(); k++) {
462 Node attribute = rawAttributes.item(k);
463 switch (attribute.getNodeName()) {
464 case MIStrings.OVERWRITE_MODE:
465 channel.setOverwriteMode(!LTTngControlServiceConstants.OVERWRITE_MODE_ATTRIBUTE_FALSE_MI.equalsIgnoreCase(attribute.getTextContent()));
466 break;
467 case MIStrings.SUBBUF_SIZE:
468 channel.setSubBufferSize(Long.valueOf(attribute.getTextContent()));
469 break;
470 case MIStrings.NUM_SUBBUF:
471 channel.setNumberOfSubBuffers(Integer.valueOf(attribute.getTextContent()));
472 break;
473 case MIStrings.SWITCH_TIMER_INTERVAL:
474 channel.setSwitchTimer(Long.valueOf(attribute.getTextContent()));
475 break;
476 case MIStrings.READ_TIMER_INTERVAL:
477 channel.setReadTimer(Long.valueOf(attribute.getTextContent()));
478 break;
479 case MIStrings.OUTPUT_TYPE:
480 channel.setOutputType(attribute.getTextContent());
481 break;
482 case MIStrings.TRACEFILE_SIZE:
6f40b641 483 channel.setMaxSizeTraceFiles(Long.parseLong(attribute.getTextContent()));
f4401569
JRJ
484 break;
485 case MIStrings.TRACEFILE_COUNT:
486 channel.setMaxNumberTraceFiles(Integer.parseInt(attribute.getTextContent()));
487 break;
488 case MIStrings.LIVE_TIMER_INTERVAL:
489 // TODO: currently not supported by tmf
490 break;
491 default:
492 break;
493 }
494 }
495 break;
496 default:
497 break;
498 }
499 }
500 channels.add(channel);
501 }
502 }
503
0df4af5f
JRJ
504 }
505
506 @Override
507 public ISnapshotInfo getSnapshotInfo(String sessionName, IProgressMonitor monitor) throws ExecutionException {
4f9e6a03
JRJ
508 // TODO A session can have multiple snapshot output. This need to be
509 // supported in the future.
510 // Currently the SessionInfo object does not support multiple snashot
511 // output.
512 // For now only keep the last one.
364dcfaf 513 ICommandInput command = createCommand(LTTngControlServiceConstants.COMMAND_SNAPSHOT, LTTngControlServiceConstants.COMMAND_LIST_SNAPSHOT_OUTPUT, LTTngControlServiceConstants.OPTION_SESSION, sessionName);
774a7993 514 ICommandResult result = executeCommand(command, monitor);
ce709731 515 Document doc = getDocumentFromStrings(result.getOutput(), fDocumentBuilder);
4f9e6a03
JRJ
516 NodeList rawSnapshotsOutputs = doc.getElementsByTagName(MIStrings.SNAPSHOT_OUTPUTS);
517
518 ISnapshotInfo snapshotInfo = new SnapshotInfo(""); //$NON-NLS-1$
519
520 // TODO: tmf does not have a notion of a ctrl url.
521 for (int i = 0; i < rawSnapshotsOutputs.getLength(); i++) {
522 NodeList rawSnapshotOutput = rawSnapshotsOutputs.item(i).getChildNodes();
523 for (int j = 0; j < rawSnapshotOutput.getLength(); j++) {
524 Node rawInfo = rawSnapshotOutput.item(j);
525 switch (rawInfo.getNodeName()) {
526 case MIStrings.ID:
527 snapshotInfo.setId(Integer.parseInt(rawInfo.getTextContent()));
528 break;
529 case MIStrings.NAME:
530 snapshotInfo.setName(rawInfo.getTextContent());
531 break;
532 case MIStrings.SNAPSHOT_CTRL_URL:
533 // The use of the ctrl_url for the snapshot path is to assure
534 // basic support. Refactoring is necessary in lttng and
535 // tmf side.
536 // See http://bugs.lttng.org/issues/828 (+comment)
537 snapshotInfo.setSnapshotPath(rawInfo.getTextContent());
538 break;
539 default:
540 break;
541 }
542 }
543 }
544
545 // Check if the snapshot output is Streamed
546 Matcher matcher2 = LTTngControlServiceConstants.TRACE_NETWORK_PATTERN.matcher(snapshotInfo.getSnapshotPath());
547 if (matcher2.matches()) {
548 snapshotInfo.setStreamedSnapshot(true);
549 }
550
551 return snapshotInfo;
0df4af5f
JRJ
552 }
553
554 @Override
555 public List<IBaseEventInfo> getKernelProvider(IProgressMonitor monitor) throws ExecutionException {
364dcfaf 556 ICommandInput command = createCommand(LTTngControlServiceConstants.COMMAND_LIST, LTTngControlServiceConstants.OPTION_KERNEL);
774a7993 557 ICommandResult result = executeCommand(command, monitor, false);
0df4af5f
JRJ
558 List<IBaseEventInfo> events = new ArrayList<>();
559
13729cbc 560 if (isError(result)) {
517c0261
JRJ
561 // Ignore the following 2 cases:
562 // Spawning a session daemon
563 // Error: Unable to list kernel events
564 // or:
565 // Error: Unable to list kernel events
566 if (ignoredPattern(result.getErrorOutput(), LTTngControlServiceConstants.LIST_KERNEL_NO_KERNEL_PROVIDER_PATTERN)) {
567 return events;
568 }
364dcfaf 569 throw new ExecutionException(Messages.TraceControl_CommandError + command.toString());
0df4af5f
JRJ
570 }
571
ce709731 572 Document document = getDocumentFromStrings(result.getOutput(), fDocumentBuilder);
0df4af5f 573 NodeList rawEvents = document.getElementsByTagName(MIStrings.EVENT);
f4401569 574 getBaseEventInfo(rawEvents, events);
0df4af5f
JRJ
575 return events;
576 }
577
578 @Override
579 public List<IUstProviderInfo> getUstProvider(IProgressMonitor monitor) throws ExecutionException {
364dcfaf 580 ICommandInput command = createCommand(LTTngControlServiceConstants.COMMAND_LIST, LTTngControlServiceConstants.OPTION_UST);
0df4af5f 581 // Get the field to
774a7993 582 command.add(LTTngControlServiceConstants.OPTION_FIELDS);
0df4af5f
JRJ
583
584 // Execute
774a7993 585 ICommandResult result = executeCommand(command, monitor, false);
0df4af5f
JRJ
586 List<IUstProviderInfo> allProviders = new ArrayList<>();
587
13729cbc 588 if (isError(result)) {
517c0261
JRJ
589 // Ignore the following 2 cases:
590 // Spawning a session daemon
591 // Error: Unable to list UST events: Listing UST events failed
592 // or:
593 // Error: Unable to list UST events: Listing UST events failed
594 if (ignoredPattern(result.getErrorOutput(), LTTngControlServiceConstants.LIST_UST_NO_UST_PROVIDER_PATTERN)) {
595 return allProviders;
596 }
364dcfaf 597 throw new ExecutionException(Messages.TraceControl_CommandError + command.toString());
0df4af5f
JRJ
598 }
599
ce709731 600 Document document = getDocumentFromStrings(result.getOutput(), fDocumentBuilder);
0df4af5f
JRJ
601 NodeList rawProviders = document.getElementsByTagName(MIStrings.PID);
602
603 IUstProviderInfo providerInfo = null;
604
605 for (int i = 0; i < rawProviders.getLength(); i++) {
606 Node provider = rawProviders.item(i);
607 Node name = getFirstOf(provider.getChildNodes(), MIStrings.NAME);
608 if (name == null) {
609 throw new ExecutionException(Messages.TraceControl_MiInvalidProviderError);
610 }
611 providerInfo = new UstProviderInfo(name.getTextContent());
612
613 // Populate provider
614 NodeList infos = provider.getChildNodes();
615 for (int j = 0; j < infos.getLength(); j++) {
616 Node info = infos.item(j);
617 switch (info.getNodeName()) {
618 case MIStrings.PID_ID:
619 providerInfo.setPid(Integer.parseInt(info.getTextContent()));
620 break;
621 case MIStrings.EVENTS:
622 List<IBaseEventInfo> events = new ArrayList<>();
623 NodeList rawEvents = info.getChildNodes();
f4401569 624 getBaseEventInfo(rawEvents, events);
0df4af5f
JRJ
625 providerInfo.setEvents(events);
626 break;
627 default:
628 break;
629 }
630 }
631 allProviders.add(providerInfo);
632 }
633
634 return allProviders;
635 }
636
637 @Override
638 public ISessionInfo createSession(ISessionInfo sessionInfo, IProgressMonitor monitor) throws ExecutionException {
67f8cadd
JRJ
639 if (sessionInfo.isStreamedTrace()) {
640 return createStreamedSession(sessionInfo, monitor);
641 }
642
364dcfaf 643 ICommandInput command = prepareSessionCreationCommand(sessionInfo);
774a7993 644 ICommandResult result = executeCommand(command, monitor);
67f8cadd 645
ce709731 646 Document document = getDocumentFromStrings(result.getOutput(), fDocumentBuilder);
67f8cadd
JRJ
647 NodeList sessions = document.getElementsByTagName(MIStrings.SESSION);
648
649 // Number of session should be equal to 1
650 if (sessions.getLength() != 1) {
651 throw new ExecutionException(Messages.TraceControl_CommandError + " " + command + "\n" //$NON-NLS-1$//$NON-NLS-2$
652 + NLS.bind(Messages.TraceControl_UnexpectedNumberOfElementError, MIStrings.SESSION) + " " + sessions.getLength()); //$NON-NLS-1$
653 }
654
655 // Fetch a session from output
656 ISessionInfo outputSession = new SessionInfo(""); //$NON-NLS-1$
657 parseSession(outputSession, sessions.item(0));
658
659 // Verify session name
660 if ((outputSession.getName().equals("")) || (!"".equals(sessionInfo.getName()) && !outputSession.getName().equals(sessionInfo.getName()))) { //$NON-NLS-1$ //$NON-NLS-2$
661 // Unexpected name returned
662 throw new ExecutionException(Messages.TraceControl_CommandError + " " + command + "\n" + //$NON-NLS-1$ //$NON-NLS-2$
663 Messages.TraceControl_UnexpectedNameError + ": " + outputSession.getName()); //$NON-NLS-1$
664 }
665
666 // Verify session path
667 if (!sessionInfo.isSnapshotSession() &&
668 ((outputSession.getSessionPath() == null) || ((sessionInfo.getSessionPath() != null) && (!outputSession.getSessionPath().contains(sessionInfo.getSessionPath()))))) {
669 // Unexpected path
670 throw new ExecutionException(Messages.TraceControl_CommandError + " " + command + "\n" + //$NON-NLS-1$ //$NON-NLS-2$
671 Messages.TraceControl_UnexpectedPathError + ": " + outputSession.getName()); //$NON-NLS-1$
672 }
673
674 if (sessionInfo.isSnapshotSession()) {
675 // Make it a snapshot session - content of snapshot info need to
676 // set afterwards using getSession() or getSnapshotInfo()
677 outputSession.setSnapshotInfo(new SnapshotInfo("")); //$NON-NLS-1$
678 }
679
680 return outputSession;
681 }
682
364dcfaf 683 private @NonNull ISessionInfo createStreamedSession(ISessionInfo sessionInfo, IProgressMonitor monitor) throws ExecutionException {
67f8cadd 684
364dcfaf 685 ICommandInput command = prepareStreamedSessionCreationCommand(sessionInfo);
67f8cadd 686
774a7993 687 ICommandResult result = executeCommand(command, monitor);
67f8cadd 688
ce709731 689 Document document = getDocumentFromStrings(result.getOutput(), fDocumentBuilder);
67f8cadd
JRJ
690 NodeList sessions = document.getElementsByTagName(MIStrings.SESSION);
691
692 // Number of session should be equal to 1
693 if (sessions.getLength() != 1) {
694 throw new ExecutionException(Messages.TraceControl_CommandError + " " + command + "\n" //$NON-NLS-1$//$NON-NLS-2$
695 + NLS.bind(Messages.TraceControl_UnexpectedNumberOfElementError, MIStrings.SESSION) + " " + sessions.getLength()); //$NON-NLS-1$
696 }
697
698 // Fetch a session from output
699 ISessionInfo outputSession = new SessionInfo(""); //$NON-NLS-1$
700 parseSession(outputSession, sessions.item(0));
701
702 // Verify session name
703 if ((outputSession.getName().equals("")) || (!"".equals(sessionInfo.getName()) && !outputSession.getName().equals(sessionInfo.getName()))) { //$NON-NLS-1$ //$NON-NLS-2$
704 // Unexpected name returned
705 throw new ExecutionException(Messages.TraceControl_CommandError + " " + command + "\n" + //$NON-NLS-1$ //$NON-NLS-2$
706 Messages.TraceControl_UnexpectedNameError + ": " + outputSession.getName()); //$NON-NLS-1$
707 }
708
709 sessionInfo.setName(outputSession.getName());
710 sessionInfo.setStreamedTrace(true);
711
712 // Verify session path
713 if (sessionInfo.getNetworkUrl() != null) {
714 if (!sessionInfo.isSnapshotSession() && (outputSession.getSessionPath() == null)) {
715 // Unexpected path
716 throw new ExecutionException(Messages.TraceControl_CommandError + " " + command + "\n" + //$NON-NLS-1$ //$NON-NLS-2$
717 Messages.TraceControl_UnexpectedPathError + ": " + outputSession.getName()); //$NON-NLS-1$
718 }
719
720 if (sessionInfo.isSnapshotSession()) {
721 sessionInfo.setStreamedTrace(false);
722 } else {
723 sessionInfo.setSessionPath(outputSession.getSessionPath());
724 // Check file protocol
725 Matcher matcher = LTTngControlServiceConstants.TRACE_FILE_PROTOCOL_PATTERN.matcher(outputSession.getSessionPath());
726 if (matcher.matches()) {
727 sessionInfo.setStreamedTrace(false);
728 }
729 }
730 }
731
732 // When using controlUrl and dataUrl the full session path is not known
733 // yet
734 // and will be set later on when listing the session
735 return sessionInfo;
0df4af5f
JRJ
736 }
737
738 @Override
739 public void destroySession(String sessionName, IProgressMonitor monitor) throws ExecutionException {
364dcfaf 740 ICommandInput command = createCommand(LTTngControlServiceConstants.COMMAND_DESTROY_SESSION, sessionName);
0df4af5f 741
774a7993 742 ICommandResult result = executeCommand(command, monitor, false);
cbc46cc9 743 List<String> errorOutput = result.getErrorOutput();
0df4af5f 744
13729cbc 745 if (isError(result)) {
517c0261
JRJ
746 // Don't treat this as an error
747 if (ignoredPattern(errorOutput, LTTngControlServiceConstants.SESSION_NOT_FOUND_ERROR_PATTERN)) {
748 return;
0df4af5f 749
517c0261 750 }
364dcfaf 751 throw new ExecutionException(Messages.TraceControl_CommandError + " " + command.toString() + "\n" + result.toString()); //$NON-NLS-1$ //$NON-NLS-2$
517c0261
JRJ
752 }
753
754 // Check for action effect
ce709731 755 Document doc = getDocumentFromStrings(result.getOutput(), fDocumentBuilder);
517c0261
JRJ
756 NodeList sessions = doc.getElementsByTagName(MIStrings.SESSION);
757 if (sessions.getLength() != 1) {
758 throw new ExecutionException(NLS.bind(Messages.TraceControl_MiInvalidNumberOfElementError, MIStrings.SESSION));
759 }
0df4af5f 760
517c0261
JRJ
761 Node rawSessionName = getFirstOf(sessions.item(0).getChildNodes(), MIStrings.NAME);
762 if (rawSessionName == null) {
763 throw new ExecutionException(Messages.TraceControl_MiMissingRequiredError);
764 }
765
766 // Validity check
767 if (!rawSessionName.getTextContent().equals(sessionName)) {
768 throw new ExecutionException(NLS.bind(Messages.TraceControl_UnexpectedValueError, rawSessionName.getTextContent(), sessionName));
769 }
0df4af5f
JRJ
770 }
771
0df4af5f 772 @Override
364dcfaf
BH
773 protected ICommandInput createCommand(String... strings) {
774 ICommandInput command = getCommandShell().createCommand();
774a7993 775 command.add(LTTngControlServiceConstants.CONTROL_COMMAND);
aa353506 776 List<@NonNull String> groupOption = getTracingGroupOption();
774a7993 777 if (!groupOption.isEmpty()) {
11b78e10 778 command.addAll(groupOption);
774a7993 779 }
11b78e10
BH
780 command.add(LTTngControlServiceConstants.CONTROL_COMMAND_MI_OPTION);
781 command.add(LTTngControlServiceConstants.CONTROL_COMMAND_MI_XML);
0df4af5f 782 for (String string : strings) {
364dcfaf 783 command.add(checkNotNull(string));
0df4af5f
JRJ
784 }
785 return command;
786 }
787
788 /**
f4401569
JRJ
789 * @param xmlBaseEvents
790 * a Node list of base xml event element
0df4af5f
JRJ
791 * @param events
792 * list of event generated by the parsing of the xml event
793 * element
794 * @throws ExecutionException
795 * when a raw event is not a complete/valid xml event
796 */
f4401569 797 private static void getBaseEventInfo(NodeList xmlBaseEvents, List<IBaseEventInfo> events) throws ExecutionException {
0df4af5f 798 IBaseEventInfo eventInfo = null;
f4401569
JRJ
799 for (int i = 0; i < xmlBaseEvents.getLength(); i++) {
800 NodeList rawInfos = xmlBaseEvents.item(i).getChildNodes();
0df4af5f 801 // Search for name
f4401569 802 if (xmlBaseEvents.item(i).getNodeName().equalsIgnoreCase(MIStrings.EVENT)) {
0df4af5f
JRJ
803 Node rawName = getFirstOf(rawInfos, MIStrings.NAME);
804 if (rawName == null) {
805 throw new ExecutionException(Messages.TraceControl_MiMissingRequiredError);
806 }
807 eventInfo = new BaseEventInfo(rawName.getTextContent());
808
809 // Populate the event
810 for (int j = 0; j < rawInfos.getLength(); j++) {
811 Node infoNode = rawInfos.item(j);
812 switch (infoNode.getNodeName()) {
813 case MIStrings.TYPE:
814 eventInfo.setEventType(infoNode.getTextContent());
815 break;
816 case MIStrings.LOGLEVEL:
817 eventInfo.setLogLevel(infoNode.getTextContent());
818 break;
819 case MIStrings.EVENT_FIELDS:
820 List<IFieldInfo> fields = new ArrayList<>();
821 getFieldInfo(infoNode.getChildNodes(), fields);
822 eventInfo.setFields(fields);
823 break;
824 default:
825 break;
826 }
827 }
828 events.add(eventInfo);
829 }
830 }
831 }
832
f4401569
JRJ
833 /**
834 * @param xmlBaseEvents
835 * a Node list of xml event element linked to a session
836 * @param events
837 * list of event generated by the parsing of the xml event
838 * element
839 * @throws ExecutionException
840 * when a raw event is not a complete/valid xml event
841 */
842 static void getEventInfo(NodeList xmlEvents, List<IEventInfo> events) throws ExecutionException {
843 IEventInfo eventInfo = null;
844 for (int i = 0; i < xmlEvents.getLength(); i++) {
845 NodeList rawInfos = xmlEvents.item(i).getChildNodes();
846 // Search for name
847 if (xmlEvents.item(i).getNodeName().equalsIgnoreCase(MIStrings.EVENT)) {
848 Node rawName = getFirstOf(rawInfos, MIStrings.NAME);
849 if (rawName == null) {
850 throw new ExecutionException(Messages.TraceControl_MiMissingRequiredError);
851 }
852
853 eventInfo = new EventInfo(rawName.getTextContent());
854
855 // Basic information
856 for (int j = 0; j < rawInfos.getLength(); j++) {
857 Node infoNode = rawInfos.item(j);
858 switch (infoNode.getNodeName()) {
859 case MIStrings.TYPE:
860 eventInfo.setEventType(infoNode.getTextContent());
861 break;
862 case MIStrings.LOGLEVEL_TYPE:
863 eventInfo.setLogLevelType(LogLevelType.valueOfString(infoNode.getTextContent()));
864 break;
865 case MIStrings.LOGLEVEL:
866 eventInfo.setLogLevel(TraceLogLevel.valueOfString(infoNode.getTextContent()));
867 break;
868 case MIStrings.ENABLED:
869 eventInfo.setState(TraceEnablement.valueOfString(infoNode.getTextContent()));
870 break;
871 case MIStrings.FILTER:
adecf0a7 872 // Before LTTng 2.8: We emulate the non-mi behavior and simply put
f4401569 873 // "with filter"
3b5e96d8
BH
874 if (Boolean.TRUE.toString().equals(infoNode.getTextContent())) {
875 eventInfo.setFilterExpression(Messages.TraceControl_DefaultEventFilterString);
876 }
f4401569 877 break;
adecf0a7
MAL
878 case MIStrings.FILTER_EXPRESSION:
879 eventInfo.setFilterExpression(infoNode.getTextContent());
880 break;
f4401569 881 case MIStrings.EXCLUSION:
91dc1c3e
BR
882 // Before LTTng 2.8: We emulate the non-mi behavior and simply put
883 // "with exclude"
884 if (Boolean.TRUE.toString().equals(infoNode.getTextContent())) {
885 eventInfo.setExcludedEvents(Messages.TraceControl_DefaultEventExcludeString);
886 }
887 break;
888 case MIStrings.EXCLUSIONS:
889 StringBuilder tmpString = new StringBuilder();
890 // If there is multiple events excluded.
891 for (int k = 0; k < infoNode.getChildNodes().getLength(); k++) {
892 if (k > 0) {
893 tmpString.append(", "); //$NON-NLS-1$
894 }
895 tmpString.append(infoNode.getChildNodes().item(k).getTextContent());
896 }
897 eventInfo.setExcludedEvents(tmpString.toString());
f4401569
JRJ
898 break;
899 default:
900 break;
901 }
902 }
903
904 boolean isProbeFunction = (eventInfo.getEventType().equals(TraceEventType.PROBE)) || (eventInfo.getEventType().equals(TraceEventType.FUNCTION));
905 if (isProbeFunction) {
906 IProbeEventInfo probeEvent = new ProbeEventInfo(eventInfo);
907 eventInfo = probeEvent;
f4401569
JRJ
908
909 Node rawDataNode = null;
910 switch (probeEvent.getEventType()) {
f4401569 911 case FUNCTION:
6651c1b4
JRJ
912 case PROBE: {
913 // get attributes
914 Node rawAttributes = getFirstOf(rawInfos, MIStrings.ATTRIBUTES);
915 if (rawAttributes == null) {
916 throw new ExecutionException(Messages.TraceControl_MiMissingRequiredError);
917 }
918 rawDataNode = getFirstOf(rawAttributes.getChildNodes(), MIStrings.PROBE_ATTRIBUTES);
f4401569 919 break;
6651c1b4 920 }
f4401569
JRJ
921 case SYSCALL:
922 case TRACEPOINT:
923 case UNKNOWN:
924 default:
925 throw new ExecutionException(Messages.TraceControl_MiInvalidElementError);
926 }
927
928 if (rawDataNode == null) {
929 throw new ExecutionException(Messages.TraceControl_MiInvalidElementError);
930 }
931
932 // Extract info
933 NodeList rawDatas = rawDataNode.getChildNodes();
934 for (int j = 0; j < rawDatas.getLength(); j++) {
935 Node rawData = rawDatas.item(j);
936 switch (rawData.getNodeName()) {
937 case MIStrings.SYMBOL_NAME:
938 probeEvent.setSymbol(rawData.getTextContent());
939 break;
940 case MIStrings.ADDRESS:
6651c1b4 941 probeEvent.setAddress(String.format("%#016x", new BigInteger(rawData.getTextContent()))); //$NON-NLS-1$
f4401569
JRJ
942 break;
943 case MIStrings.OFFSET:
6651c1b4 944 probeEvent.setOffset(String.format("%#016x", new BigInteger(rawData.getTextContent()))); //$NON-NLS-1$
f4401569
JRJ
945 break;
946 default:
947 break;
948 }
949 }
950 }
951
f4401569
JRJ
952 // Add the event
953 events.add(eventInfo);
954 }
955 }
956 }
957
0df4af5f
JRJ
958 /**
959 * @param fieldsList
960 * a list of xml event_field element
961 * @param fields
962 * a list of field generated by xml parsing
963 * @throws ExecutionException
964 * when parsing fail or required elements are missing
965 */
966 private static void getFieldInfo(NodeList fieldsList, List<IFieldInfo> fields) throws ExecutionException {
967 IFieldInfo fieldInfo = null;
968 for (int i = 0; i < fieldsList.getLength(); i++) {
969 Node field = fieldsList.item(i);
970 if (field.getNodeName().equalsIgnoreCase(MIStrings.EVENT_FIELD)) {
971 // Get name
972 Node name = getFirstOf(field.getChildNodes(), MIStrings.NAME);
973 if (name == null) {
974 throw new ExecutionException(Messages.TraceControl_MiMissingRequiredError);
975 }
976 fieldInfo = new FieldInfo(name.getTextContent());
977
978 // Populate the field information
979 NodeList infos = field.getChildNodes();
980 for (int j = 0; j < infos.getLength(); j++) {
981 Node info = infos.item(j);
982 switch (info.getNodeName()) {
983 case MIStrings.TYPE:
984 fieldInfo.setFieldType(info.getTextContent());
985 break;
986 default:
987 break;
988 }
989 }
990 fields.add(fieldInfo);
991 }
992 }
993 }
994
995 /**
996 * Retrieve the fist instance of a given node with tag name equal to tagName
997 * parameter
998 *
999 * @param nodeList
1000 * the list of Node to search against
1001 * @param tagName
1002 * the tag name of the desired node
1003 * @return the first occurrence of a node with a tag name equals to tagName
1004 */
364dcfaf 1005 private static @Nullable Node getFirstOf(NodeList nodeList, String tagName) {
0df4af5f
JRJ
1006 Node node = null;
1007 for (int i = 0; i < nodeList.getLength(); i++) {
843f1eb2 1008 if (NonNullUtils.equalsNullable(nodeList.item(i).getNodeName(), tagName)) {
0df4af5f
JRJ
1009 node = nodeList.item(i);
1010 break;
1011 }
1012 }
1013 return node;
1014 }
1015
182e9b8f
MAL
1016 @Override
1017 public @NonNull List<String> getContextList(IProgressMonitor monitor) throws ExecutionException {
1018 if (!isVersionSupported("2.8.0")) { //$NON-NLS-1$)
1019 return super.getContextList(monitor);
1020 }
1021
1022 ICommandInput command = createCommand(LTTngControlServiceConstants.COMMAND_ADD_CONTEXT, LTTngControlServiceConstants.OPTION_LIST);
1023 ICommandResult result = executeCommand(command, monitor);
1024 return result.getOutput();
1025 }
0df4af5f 1026}
This page took 0.115173 seconds and 5 git commands to generate.