1 /**********************************************************************
2 * Copyright (c) 2014 École Polytechnique de Montréal
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
10 * Guilliano Molaire - Initial API and implementation
11 *********************************************************************/
12 package org
.eclipse
.tracecompass
.lttng2
.control
.core
.session
;
15 import java
.io
.IOException
;
19 import javax
.xml
.XMLConstants
;
20 import javax
.xml
.parsers
.DocumentBuilder
;
21 import javax
.xml
.parsers
.DocumentBuilderFactory
;
22 import javax
.xml
.parsers
.ParserConfigurationException
;
23 import javax
.xml
.transform
.OutputKeys
;
24 import javax
.xml
.transform
.Source
;
25 import javax
.xml
.transform
.Transformer
;
26 import javax
.xml
.transform
.TransformerException
;
27 import javax
.xml
.transform
.TransformerFactory
;
28 import javax
.xml
.transform
.dom
.DOMSource
;
29 import javax
.xml
.transform
.stream
.StreamResult
;
30 import javax
.xml
.transform
.stream
.StreamSource
;
31 import javax
.xml
.validation
.Schema
;
32 import javax
.xml
.validation
.SchemaFactory
;
33 import javax
.xml
.validation
.Validator
;
35 import org
.eclipse
.core
.runtime
.IPath
;
36 import org
.eclipse
.core
.runtime
.IStatus
;
37 import org
.eclipse
.core
.runtime
.Status
;
38 import org
.eclipse
.jdt
.annotation
.NonNull
;
39 import org
.eclipse
.osgi
.util
.NLS
;
40 import org
.eclipse
.tracecompass
.internal
.lttng2
.control
.core
.Activator
;
41 import org
.eclipse
.tracecompass
.internal
.lttng2
.control
.core
.model
.TraceDomainType
;
42 import org
.eclipse
.tracecompass
.internal
.lttng2
.control
.core
.model
.IChannelInfo
;
43 import org
.eclipse
.tracecompass
.internal
.lttng2
.control
.core
.model
.IDomainInfo
;
44 import org
.eclipse
.tracecompass
.internal
.lttng2
.control
.core
.model
.IEventInfo
;
45 import org
.eclipse
.tracecompass
.internal
.lttng2
.control
.core
.model
.ISessionInfo
;
46 import org
.eclipse
.tracecompass
.internal
.lttng2
.control
.core
.model
.TraceEnablement
;
47 import org
.eclipse
.tracecompass
.internal
.lttng2
.control
.core
.model
.TraceEventType
;
48 import org
.eclipse
.tracecompass
.internal
.lttng2
.control
.core
.model
.TraceLogLevel
;
49 import org
.eclipse
.tracecompass
.internal
.lttng2
.control
.core
.model
.TraceSessionState
;
50 import org
.w3c
.dom
.Document
;
51 import org
.w3c
.dom
.Element
;
52 import org
.xml
.sax
.SAXException
;
53 import org
.xml
.sax
.SAXParseException
;
56 * Class for generating a session configuration file. A session configuration is
57 * used to configure a trace session. It is a XML formatted file that contains
58 * values defining the behavior of that specific trace session.
60 * Kernel session configuration example:
66 * <name>test_kernel</name>
70 * <buffer_type>GLOBAL</buffer_type>
73 * <name>channel0</name>
74 * <enabled>false</enabled>
75 * <overwrite_mode>DISCARD</overwrite_mode>
76 * <subbuffer_size>262144</subbuffer_size>
77 * <subbuffer_count>4</subbuffer_count>
78 * <switch_timer_interval>0</switch_timer_interval>
79 * <read_timer_interval>200000</read_timer_interval>
80 * <output_type>SPLICE</output_type>
81 * <tracefile_size>0</tracefile_size>
82 * <tracefile_count>0</tracefile_count>
83 * <live_timer_interval>0</live_timer_interval>
86 * <enabled>true</enabled>
87 * <type>SYSCALL</type>
90 * <name>snd_soc_cache_sync</name>
91 * <enabled>true</enabled>
92 * <type>TRACEPOINT</type>
99 * <started>false</started>
102 * <enabled>true</enabled>
104 * <path>/home/user/lttng-traces/test_kernel</path>
115 * @author Guilliano Molaire
117 public final class SessionConfigGenerator
{
119 /** The name of the session schema */
120 private static final String SESSION_XSD_FILENAME
= "session.xsd"; //$NON-NLS-1$
122 /** The indent size used for the session configuration XML file */
123 private static final String INDENT_AMOUNT_PROPERTY_NAME
= "{http://xml.apache.org/xslt}indent-amount"; //$NON-NLS-1$
124 private static final String INDENT_AMOUNT_PROPERTY_VALUE
= "4"; //$NON-NLS-1$
127 * Private constructor. The class should not be instantiated.
129 private SessionConfigGenerator() {
132 // ---------------------------------------------------------
133 // Methods to generate session configuration files
134 // ---------------------------------------------------------
137 * Generates a session configuration file from a set of session information.
140 * The session informations
141 * @param sessionFileDestination
142 * The path of the locally saved session configuration file
143 * @return The status of the session configuration generation
145 public static IStatus
generateSessionConfig(Set
<ISessionInfo
> sessions
, IPath sessionFileDestination
) {
146 /* Parameters validation */
147 if (sessions
== null || sessions
.isEmpty()) {
148 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, Messages
.SessionConfigXML_InvalidSessionInfoList
);
149 } else if (sessionFileDestination
== null) {
150 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, Messages
.SessionConfigXML_InvalidTraceSessionPath
);
153 /* Generate the session configuration file */
155 Document sessionConfigDocument
= generateSessionConfig(sessions
);
156 saveSessionConfig(sessionConfigDocument
, sessionFileDestination
.toString());
157 } catch (TransformerException
| IllegalArgumentException
| ParserConfigurationException e
) {
158 Activator
.getDefault().logError("Error generating the session configuration file: " + sessionFileDestination
.toString(), e
); //$NON-NLS-1$
159 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, e
.getMessage());
162 return Status
.OK_STATUS
;
166 * Generates a session configuration from a set of session informations.
169 * The session informations
170 * @return The document with all session configuration nodes
171 * @throws IllegalArgumentException
172 * On an illegal argument inside sessions
173 * @throws ParserConfigurationException
174 * On an parser configuration error
176 private static @NonNull Document
generateSessionConfig(Iterable
<ISessionInfo
> sessions
) throws IllegalArgumentException
, ParserConfigurationException
{
177 DocumentBuilderFactory docFactory
= DocumentBuilderFactory
.newInstance();
178 DocumentBuilder docBuilder
= docFactory
.newDocumentBuilder();
180 Document document
= docBuilder
.newDocument();
182 Element rootElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_SESSIONS
);
183 document
.appendChild(rootElement
);
185 for (ISessionInfo session
: sessions
) {
186 /* All elements under "sessions" elements */
187 Element sessionElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_SESSION
);
189 /* Contents of session element */
190 String enabled
= session
.getSessionState().equals(TraceSessionState
.ACTIVE
) ? SessionConfigStrings
.CONFIG_STRING_TRUE
: SessionConfigStrings
.CONFIG_STRING_FALSE
;
192 addElementContent(document
, sessionElement
, SessionConfigStrings
.CONFIG_ELEMENT_NAME
, session
.getName());
193 addElementContent(document
, sessionElement
, SessionConfigStrings
.CONFIG_ELEMENT_STARTED
, enabled
);
195 if (session
.isSnapshotSession()) {
196 /* If it's a snapshot, we must add an attribute telling it is */
197 Element attributesElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_ATTRIBUTES
);
198 addElementContent(document
, attributesElement
, SessionConfigStrings
.CONFIG_ELEMENT_SNAPSHOT_MODE
, SessionConfigStrings
.CONFIG_STRING_TRUE
);
199 sessionElement
.appendChild(attributesElement
);
202 sessionElement
.appendChild(getDomainsElement(document
, session
));
203 sessionElement
.appendChild(getOutputElement(document
, session
));
204 rootElement
.appendChild(sessionElement
);
210 // ---------------------------------------------------------
211 // Getters for each element of the configuration file
212 // ---------------------------------------------------------
215 * Gets the 'domains' element after creating it.
218 * The document in which the nodes are being added
220 * The session informations
221 * @return The domains element as an XML element
223 private static Element
getDomainsElement(Document document
, ISessionInfo session
) {
224 Element domainsElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_DOMAINS
);
226 for (IDomainInfo domain
: session
.getDomains()) {
227 Element domainElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_DOMAIN
);
229 String domainType
= null;
230 switch (domain
.getDomain()) {
232 domainType
= SessionConfigStrings
.CONFIG_DOMAIN_TYPE_KERNEL
;
235 domainType
= SessionConfigStrings
.CONFIG_DOMAIN_TYPE_UST
;
237 // TODO: Add the following cases when more domains are supported
246 addElementContent(document
, domainElement
, SessionConfigStrings
.CONFIG_ELEMENT_TYPE
, domainType
);
248 String bufferType
= null;
249 switch (domain
.getBufferType()) {
251 bufferType
= SessionConfigStrings
.CONFIG_BUFFER_TYPE_PER_UID
;
254 bufferType
= SessionConfigStrings
.CONFIG_BUFFER_TYPE_PER_PID
;
257 bufferType
= SessionConfigStrings
.CONFIG_BUFFER_TYPE_GLOBAL
;
259 case BUFFER_TYPE_UNKNOWN
:
261 throw new IllegalArgumentException(Messages
.SessionConfigXML_UnknownDomainBufferType
);
263 addElementContent(document
, domainElement
, SessionConfigStrings
.CONFIG_ELEMENT_DOMAIN_BUFFER_TYPE
, bufferType
);
265 /* Add the channels */
266 domainElement
.appendChild(getChannelsElement(document
, domain
.getDomain(), domain
.getChannels()));
267 domainsElement
.appendChild(domainElement
);
270 return domainsElement
;
274 * Gets the 'output' element after creating it. If the session is a
275 * snapshot, it will be composed of a snapshot outputs element. Otherwise,
276 * it will contain the consumer output element.
279 * The document in which the nodes are being added
281 * The session informations
282 * @return The output element as an XML node
284 private static Element
getOutputElement(Document document
, ISessionInfo session
) {
285 Element outputElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_OUTPUT
);
287 if (session
.isSnapshotSession()) {
288 outputElement
.appendChild(getSnapshotOuputsElement(document
, session
));
289 } else if (session
.isStreamedTrace()) {
290 outputElement
.appendChild(getNetOutputElement(document
, session
));
292 outputElement
.appendChild(getConsumerOutputElement(document
, session
));
295 return outputElement
;
299 * Gets the 'channels' element after creating it.
302 * The document in which the nodes are being added
304 * The domain type ({@link TraceDomainType})
306 * The channels to be added as elements
307 * @return The channels element as an XML element
309 private static Element
getChannelsElement(Document document
, TraceDomainType domain
, IChannelInfo
[] channels
) {
310 Element channelsElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_CHANNELS
);
312 for (IChannelInfo channel
: channels
) {
313 Element channelElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_CHANNEL
);
315 /* Add everything related to a channel */
316 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_NAME
, channel
.getName());
318 String overwriteMode
= channel
.isOverwriteMode() ? SessionConfigStrings
.CONFIG_OVERWRITE_MODE_OVERWRITE
: SessionConfigStrings
.CONFIG_OVERWRITE_MODE_DISCARD
;
319 String enabled
= channel
.getState().equals(TraceEnablement
.ENABLED
) ? SessionConfigStrings
.CONFIG_STRING_TRUE
: SessionConfigStrings
.CONFIG_STRING_FALSE
;
321 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_ENABLED
, enabled
);
322 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_OVERWRITE_MODE
, overwriteMode
);
323 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_SUBBUFFER_SIZE
, channel
.getSubBufferSize());
324 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_SUBBUFFER_COUNT
, channel
.getNumberOfSubBuffers());
325 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_SWITCH_TIMER_INTERVAL
, channel
.getSwitchTimer());
326 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_READ_TIMER_INTERVAL
, channel
.getReadTimer());
328 String outputType
= channel
.getOutputType().getInName().startsWith(SessionConfigStrings
.CONFIG_OUTPUT_TYPE_MMAP
) ?
329 outputType
= SessionConfigStrings
.CONFIG_OUTPUT_TYPE_MMAP
: SessionConfigStrings
.CONFIG_OUTPUT_TYPE_SPLICE
;
330 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_OUTPUT_TYPE
, outputType
);
331 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_TRACEFILE_SIZE
, channel
.getMaxSizeTraceFiles());
332 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_TRACEFILE_COUNT
, channel
.getMaxNumberTraceFiles());
335 * TODO: Replace the 0 value by the channel live timer property from
336 * SessionInfo once live session tracing is supported
338 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_LIVE_TIMER_INTERVAL
, SessionConfigStrings
.CONFIG_STRING_ZERO
);
341 channelElement
.appendChild(getEventsElement(document
, domain
, channel
.getEvents()));
342 channelsElement
.appendChild(channelElement
);
345 return channelsElement
;
349 * Gets the 'events' element after creating it. It is composed of the event
350 * informations from a list of IEventInfo.
353 * The document in which the nodes are being added
355 * The domain type ({@link TraceDomainType})
357 * The event informations to be added
358 * @return An element containing all the event informations as XML elements
360 private static Element
getEventsElement(Document document
, TraceDomainType domain
, IEventInfo
[] events
) {
361 Element eventsElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_EVENTS
);
363 for (IEventInfo event
: events
) {
364 Element eventElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_EVENT
);
366 /* Enabled attribute */
367 String enabled
= event
.getState().equals(TraceEnablement
.ENABLED
) ? SessionConfigStrings
.CONFIG_STRING_TRUE
: SessionConfigStrings
.CONFIG_STRING_FALSE
;
369 /* Add the attributes to the event node */
370 addElementContent(document
, eventElement
, SessionConfigStrings
.CONFIG_ELEMENT_NAME
, event
.getName());
371 addElementContent(document
, eventElement
, SessionConfigStrings
.CONFIG_ELEMENT_ENABLED
, enabled
);
372 TraceEventType eventType
= event
.getEventType();
373 if (!eventType
.equals(TraceEventType
.UNKNOWN
)) {
374 addElementContent(document
, eventElement
, SessionConfigStrings
.CONFIG_ELEMENT_TYPE
, eventType
.getInName().toUpperCase());
376 throw new IllegalArgumentException(Messages
.SessionConfigXML_UnknownEventType
);
379 /* Specific to UST session config: the log level */
380 if (domain
.equals(TraceDomainType
.UST
) && !event
.getLogLevel().equals(TraceLogLevel
.LEVEL_UNKNOWN
)) {
381 addElementContent(document
, eventElement
, SessionConfigStrings
.CONFIG_ELEMENT_LOGLEVEL
, event
.getLogLevel().ordinal());
384 /* Add the node to the parent node events */
385 eventsElement
.appendChild(eventElement
);
388 return eventsElement
;
392 * Gets the 'consumer_output' element after creating it.
395 * The document in which the nodes are being added
397 * The session informations
398 * @return The consumer output element with his informations as XML elements
400 private static Element
getConsumerOutputElement(Document document
, ISessionInfo session
) {
401 Element consumerOutputElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_CONSUMER_OUTPUT
);
402 Element destinationElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_DESTINATION
);
404 /* Value of consumer output element */
405 addElementContent(document
, consumerOutputElement
, SessionConfigStrings
.CONFIG_ELEMENT_ENABLED
, SessionConfigStrings
.CONFIG_STRING_TRUE
);
407 if (session
.isStreamedTrace()) {
408 /* If it is a streamed session, add the net output element */
409 destinationElement
.appendChild(getNetOutputElement(document
, session
));
411 addElementContent(document
, destinationElement
, SessionConfigStrings
.CONFIG_ELEMENT_PATH
, session
.getSessionPath());
414 consumerOutputElement
.appendChild(destinationElement
);
415 return consumerOutputElement
;
419 * Gets the 'net_output' element after creating it. It is composed of the
420 * control and data URIs.
423 * The document in which the nodes are being added
425 * The session informations
426 * @return The net output element
428 private static Element
getNetOutputElement(Document document
, ISessionInfo session
) {
429 Element netOutputElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_NET_OUTPUT
);
431 String networkUrl
= session
.getNetworkUrl();
432 String controlUri
= networkUrl
== null ? session
.getControlUrl() : networkUrl
;
433 String dataUri
= networkUrl
== null ? session
.getDataUrl() : networkUrl
;
434 addElementContent(document
, netOutputElement
, SessionConfigStrings
.CONFIG_ELEMENT_CONTROL_URI
, controlUri
);
435 addElementContent(document
, netOutputElement
, SessionConfigStrings
.CONFIG_ELEMENT_DATA_URI
, dataUri
);
437 return netOutputElement
;
441 * Gets the 'snapshot_outputs' element after creating it.
444 * The document in which the nodes are being added
446 * The session informations
447 * @return The snapshot outputs element with snapshot informations as XML
450 private static Element
getSnapshotOuputsElement(Document document
, ISessionInfo session
) {
451 Element snapshotOutputsElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_SNAPSHOT_OUTPUTS
);
452 Element outputElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_OUTPUT
);
454 /* Add the name of the snapshot and the max size element */
455 addElementContent(document
, outputElement
, SessionConfigStrings
.CONFIG_ELEMENT_NAME
, session
.getSnapshotInfo().getName());
458 * TODO: find the proper max size value of output element. For now it is
459 * set to the default 0 value which means unlimited for lttng.
461 addElementContent(document
, outputElement
, SessionConfigStrings
.CONFIG_ELEMENT_MAX_SIZE
, SessionConfigStrings
.CONFIG_STRING_ZERO
);
462 outputElement
.appendChild(getConsumerOutputElement(document
, session
));
464 snapshotOutputsElement
.appendChild(outputElement
);
465 return snapshotOutputsElement
;
468 // ---------------------------------------------------------
470 // ---------------------------------------------------------
473 * Validates the session configuration file against its schema.
476 * The session configuration file
477 * @return The status of the validation
479 public static IStatus
sessionValidate(File sessionFile
) {
480 URL url
= SessionConfigGenerator
.class.getResource(SESSION_XSD_FILENAME
);
481 SchemaFactory schemaFactory
= SchemaFactory
.newInstance(XMLConstants
.W3C_XML_SCHEMA_NS_URI
);
482 Source xmlSource
= new StreamSource(sessionFile
);
485 Schema schema
= schemaFactory
.newSchema(url
);
486 Validator validator
= schema
.newValidator();
487 validator
.validate(xmlSource
);
488 } catch (SAXParseException e
) {
489 String error
= NLS
.bind(Messages
.SessionConfigXML_XmlParseError
, e
.getLineNumber(), e
.getLocalizedMessage());
490 Activator
.getDefault().logError(error
);
491 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, error
, e
);
492 } catch (SAXException e
) {
493 String error
= NLS
.bind(Messages
.SessionConfigXML_XmlValidationError
, e
.getLocalizedMessage());
494 Activator
.getDefault().logError(error
);
495 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, error
, e
);
496 } catch (IOException e
) {
497 String error
= Messages
.SessionConfigXML_XmlValidateError
;
498 Activator
.getDefault().logError("IO exception occurred", e
); //$NON-NLS-1$
499 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, error
, e
);
501 return Status
.OK_STATUS
;
505 * Saves the session configuration into a XML file.
508 * The document representing the session configuration file
510 * The path of the locally saved session configuration file
511 * @throws TransformerException
512 * On an transformation process
514 private static void saveSessionConfig(Document document
, String destination
) throws TransformerException
{
515 /* Write the content into a XML file */
516 TransformerFactory transformerFactory
= TransformerFactory
.newInstance();
517 Transformer transformer
= transformerFactory
.newTransformer();
519 transformer
.setOutputProperty(OutputKeys
.INDENT
, "yes"); //$NON-NLS-1$
520 transformer
.setOutputProperty(INDENT_AMOUNT_PROPERTY_NAME
, INDENT_AMOUNT_PROPERTY_VALUE
);
522 DOMSource source
= new DOMSource(document
);
523 StreamResult result
= new StreamResult(new File(destination
));
525 transformer
.transform(source
, result
);
529 * Adds to a parent node an element with his content.
532 * The document in which the nodes are being added
534 * The parent node that contains the element and his content
536 * The element container name
537 * @param elementContent
540 private static void addElementContent(Document document
, Element parent
, String elementName
, Object elementContent
) {
541 Element contentElement
= document
.createElement(elementName
);
542 contentElement
.appendChild(document
.createTextNode(elementContent
.toString()));
543 parent
.appendChild(contentElement
);