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
.linuxtools
.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
.linuxtools
.internal
.lttng2
.control
.core
.Activator
;
39 import org
.eclipse
.linuxtools
.internal
.lttng2
.control
.core
.model
.IChannelInfo
;
40 import org
.eclipse
.linuxtools
.internal
.lttng2
.control
.core
.model
.IDomainInfo
;
41 import org
.eclipse
.linuxtools
.internal
.lttng2
.control
.core
.model
.IEventInfo
;
42 import org
.eclipse
.linuxtools
.internal
.lttng2
.control
.core
.model
.ISessionInfo
;
43 import org
.eclipse
.linuxtools
.internal
.lttng2
.control
.core
.model
.TraceEnablement
;
44 import org
.eclipse
.linuxtools
.internal
.lttng2
.control
.core
.model
.TraceEventType
;
45 import org
.eclipse
.linuxtools
.internal
.lttng2
.control
.core
.model
.TraceLogLevel
;
46 import org
.eclipse
.linuxtools
.internal
.lttng2
.control
.core
.model
.TraceSessionState
;
47 import org
.eclipse
.osgi
.util
.NLS
;
48 import org
.w3c
.dom
.Document
;
49 import org
.w3c
.dom
.Element
;
50 import org
.xml
.sax
.SAXException
;
51 import org
.xml
.sax
.SAXParseException
;
54 * Class for generating a session configuration file. A session configuration is
55 * used to configure a trace session. It is a XML formatted file that contains
56 * values defining the behavior of that specific trace session.
58 * Kernel session configuration example:
64 * <name>test_kernel</name>
68 * <buffer_type>GLOBAL</buffer_type>
71 * <name>channel0</name>
72 * <enabled>false</enabled>
73 * <overwrite_mode>DISCARD</overwrite_mode>
74 * <subbuffer_size>262144</subbuffer_size>
75 * <subbuffer_count>4</subbuffer_count>
76 * <switch_timer_interval>0</switch_timer_interval>
77 * <read_timer_interval>200000</read_timer_interval>
78 * <output_type>SPLICE</output_type>
79 * <tracefile_size>0</tracefile_size>
80 * <tracefile_count>0</tracefile_count>
81 * <live_timer_interval>0</live_timer_interval>
84 * <enabled>true</enabled>
85 * <type>SYSCALL</type>
88 * <name>snd_soc_cache_sync</name>
89 * <enabled>true</enabled>
90 * <type>TRACEPOINT</type>
97 * <started>false</started>
100 * <enabled>true</enabled>
102 * <path>/home/user/lttng-traces/test_kernel</path>
113 * @author Guilliano Molaire
116 public final class SessionConfigGenerator
{
118 /** The name of the session schema */
119 private static final String SESSION_XSD_FILENAME
= "session.xsd"; //$NON-NLS-1$
121 /** The indent size used for the session configuration XML file */
122 private static final String INDENT_AMOUNT_PROPERTY_NAME
= "{http://xml.apache.org/xslt}indent-amount"; //$NON-NLS-1$
123 private static final String INDENT_AMOUNT_PROPERTY_VALUE
= "4"; //$NON-NLS-1$
126 * Private constructor. The class should not be instantiated.
128 private SessionConfigGenerator() {
131 // ---------------------------------------------------------
132 // Methods to generate session configuration files
133 // ---------------------------------------------------------
136 * Generates a session configuration file from a set of session information.
139 * The session informations
140 * @param sessionFileDestination
141 * The path of the locally saved session configuration file
142 * @return The status of the session configuration generation
144 public static IStatus
generateSessionConfig(Set
<ISessionInfo
> sessions
, IPath sessionFileDestination
) {
145 /* Parameters validation */
146 if (sessions
== null || sessions
.isEmpty()) {
147 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, Messages
.SessionConfigXML_InvalidSessionInfoList
);
148 } else if (sessionFileDestination
== null) {
149 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, Messages
.SessionConfigXML_InvalidTraceSessionPath
);
152 /* Generate the session configuration file */
154 Document sessionConfigDocument
= generateSessionConfig(sessions
);
156 if (sessionConfigDocument
!= null) {
157 saveSessionConfig(sessionConfigDocument
, sessionFileDestination
.toString());
159 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, Messages
.SessionConfigXML_SessionConfigGenerationError
);
161 } catch (TransformerException
| IllegalArgumentException
| ParserConfigurationException e
) {
162 Activator
.getDefault().logError("Error generating the session configuration file: " + sessionFileDestination
.toString(), e
); //$NON-NLS-1$
163 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, e
.getMessage());
166 return Status
.OK_STATUS
;
170 * Generates a session configuration from a set of session informations.
173 * The session informations
174 * @return The document with all session configuration nodes
175 * @throws IllegalArgumentException
176 * On an illegal argument inside sessions
177 * @throws ParserConfigurationException
178 * On an parser configuration error
180 private static Document
generateSessionConfig(Iterable
<ISessionInfo
> sessions
) throws IllegalArgumentException
, ParserConfigurationException
{
181 DocumentBuilderFactory docFactory
= DocumentBuilderFactory
.newInstance();
182 DocumentBuilder docBuilder
= docFactory
.newDocumentBuilder();
184 Document document
= docBuilder
.newDocument();
186 Element rootElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_SESSIONS
);
187 document
.appendChild(rootElement
);
189 for (ISessionInfo session
: sessions
) {
190 /* All elements under "sessions" elements */
191 Element sessionElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_SESSION
);
193 /* Contents of session element */
194 String enabled
= session
.getSessionState().equals(TraceSessionState
.ACTIVE
) ? SessionConfigStrings
.CONFIG_STRING_TRUE
: SessionConfigStrings
.CONFIG_STRING_FALSE
;
196 addElementContent(document
, sessionElement
, SessionConfigStrings
.CONFIG_ELEMENT_NAME
, session
.getName());
197 addElementContent(document
, sessionElement
, SessionConfigStrings
.CONFIG_ELEMENT_STARTED
, enabled
);
199 if (session
.isSnapshotSession()) {
200 /* If it's a snapshot, we must add an attribute telling it is */
201 Element attributesElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_ATTRIBUTES
);
202 addElementContent(document
, attributesElement
, SessionConfigStrings
.CONFIG_ELEMENT_SNAPSHOT_MODE
, SessionConfigStrings
.CONFIG_STRING_TRUE
);
203 sessionElement
.appendChild(attributesElement
);
206 sessionElement
.appendChild(getDomainsElement(document
, session
));
207 sessionElement
.appendChild(getOutputElement(document
, session
));
208 rootElement
.appendChild(sessionElement
);
214 // ---------------------------------------------------------
215 // Getters for each element of the configuration file
216 // ---------------------------------------------------------
219 * Gets the 'domains' element after creating it.
222 * The document in which the nodes are being added
224 * The session informations
225 * @return The domains element as an XML element
227 private static Element
getDomainsElement(Document document
, ISessionInfo session
) {
228 Element domainsElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_DOMAINS
);
230 for (IDomainInfo domain
: session
.getDomains()) {
231 Element domainElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_DOMAIN
);
234 * Add everything specific to a domain
236 * TODO: We suppose here that domain is either kernel or UST. It
237 * will have to change if other domains are supported
239 String domainType
= domain
.isKernel() ? SessionConfigStrings
.CONFIG_DOMAIN_TYPE_KERNEL
: SessionConfigStrings
.CONFIG_DOMAIN_TYPE_UST
;
240 addElementContent(document
, domainElement
, SessionConfigStrings
.CONFIG_ELEMENT_TYPE
, domainType
);
242 String bufferType
= null;
243 switch (domain
.getBufferType()) {
245 bufferType
= SessionConfigStrings
.CONFIG_BUFFER_TYPE_PER_UID
;
248 bufferType
= SessionConfigStrings
.CONFIG_BUFFER_TYPE_PER_PID
;
251 bufferType
= SessionConfigStrings
.CONFIG_BUFFER_TYPE_GLOBAL
;
253 case BUFFER_TYPE_UNKNOWN
:
255 throw new IllegalArgumentException(Messages
.SessionConfigXML_UnknownDomainBufferType
);
257 addElementContent(document
, domainElement
, SessionConfigStrings
.CONFIG_ELEMENT_DOMAIN_BUFFER_TYPE
, bufferType
);
259 /* Add the channels */
260 domainElement
.appendChild(getChannelsElement(document
, domain
.isKernel(), domain
.getChannels()));
261 domainsElement
.appendChild(domainElement
);
264 return domainsElement
;
268 * Gets the 'output' element after creating it. If the session is a
269 * snapshot, it will be composed of a snapshot outputs element. Otherwise,
270 * it will contain the consumer output element.
273 * The document in which the nodes are being added
275 * The session informations
276 * @return The output element as an XML node
278 private static Element
getOutputElement(Document document
, ISessionInfo session
) {
279 Element outputElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_OUTPUT
);
281 if (session
.isSnapshotSession()) {
282 outputElement
.appendChild(getSnapshotOuputsElement(document
, session
));
283 } else if (session
.isStreamedTrace()) {
284 outputElement
.appendChild(getNetOutputElement(document
, session
));
286 outputElement
.appendChild(getConsumerOutputElement(document
, session
));
289 return outputElement
;
293 * Gets the 'channels' element after creating it.
296 * The document in which the nodes are being added
298 * Is it a kernel domain type
300 * The channels to be added as elements
301 * @return The channels element as an XML element
303 private static Element
getChannelsElement(Document document
, boolean isKernel
, IChannelInfo
[] channels
) {
304 Element channelsElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_CHANNELS
);
306 for (IChannelInfo channel
: channels
) {
307 Element channelElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_CHANNEL
);
309 /* Add everything related to a channel */
310 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_NAME
, channel
.getName());
312 String overwriteMode
= channel
.isOverwriteMode() ? SessionConfigStrings
.CONFIG_OVERWRITE_MODE_OVERWRITE
: SessionConfigStrings
.CONFIG_OVERWRITE_MODE_DISCARD
;
313 String enabled
= channel
.getState().equals(TraceEnablement
.ENABLED
) ? SessionConfigStrings
.CONFIG_STRING_TRUE
: SessionConfigStrings
.CONFIG_STRING_FALSE
;
315 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_ENABLED
, enabled
);
316 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_OVERWRITE_MODE
, overwriteMode
);
317 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_SUBBUFFER_SIZE
, channel
.getSubBufferSize());
318 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_SUBBUFFER_COUNT
, channel
.getNumberOfSubBuffers());
319 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_SWITCH_TIMER_INTERVAL
, channel
.getSwitchTimer());
320 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_READ_TIMER_INTERVAL
, channel
.getReadTimer());
322 String outputType
= channel
.getOutputType().getInName().startsWith(SessionConfigStrings
.CONFIG_OUTPUT_TYPE_MMAP
) ?
323 outputType
= SessionConfigStrings
.CONFIG_OUTPUT_TYPE_MMAP
: SessionConfigStrings
.CONFIG_OUTPUT_TYPE_SPLICE
;
324 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_OUTPUT_TYPE
, outputType
);
325 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_TRACEFILE_SIZE
, channel
.getMaxSizeTraceFiles());
326 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_TRACEFILE_COUNT
, channel
.getMaxNumberTraceFiles());
329 * TODO: Replace the 0 value by the channel live timer property from
330 * SessionInfo once live session tracing is supported
332 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_LIVE_TIMER_INTERVAL
, SessionConfigStrings
.CONFIG_STRING_ZERO
);
335 channelElement
.appendChild(getEventsElement(document
, isKernel
, channel
.getEvents()));
336 channelsElement
.appendChild(channelElement
);
339 return channelsElement
;
343 * Gets the 'events' element after creating it. It is composed of the event
344 * informations from a list of IEventInfo.
347 * The document in which the nodes are being added
349 * Is the domain type kernel
351 * The event informations to be added
352 * @return An element containing all the event informations as XML elements
354 private static Element
getEventsElement(Document document
, boolean isKernel
, IEventInfo
[] events
) {
355 Element eventsElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_EVENTS
);
357 for (IEventInfo event
: events
) {
358 Element eventElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_EVENT
);
360 /* Enabled attribute */
361 String enabled
= event
.getState().equals(TraceEnablement
.ENABLED
) ? SessionConfigStrings
.CONFIG_STRING_TRUE
: SessionConfigStrings
.CONFIG_STRING_FALSE
;
363 /* Add the attributes to the event node */
364 addElementContent(document
, eventElement
, SessionConfigStrings
.CONFIG_ELEMENT_NAME
, event
.getName());
365 addElementContent(document
, eventElement
, SessionConfigStrings
.CONFIG_ELEMENT_ENABLED
, enabled
);
366 TraceEventType eventType
= event
.getEventType();
367 if (!eventType
.equals(TraceEventType
.UNKNOWN
)) {
368 addElementContent(document
, eventElement
, SessionConfigStrings
.CONFIG_ELEMENT_TYPE
, eventType
.getInName().toUpperCase());
370 throw new IllegalArgumentException(Messages
.SessionConfigXML_UnknownEventType
);
373 /* Specific to UST session config: the log level */
374 if (!isKernel
&& !event
.getLogLevel().equals(TraceLogLevel
.LEVEL_UNKNOWN
)) {
375 addElementContent(document
, eventElement
, SessionConfigStrings
.CONFIG_ELEMENT_LOGLEVEL
, event
.getLogLevel().ordinal());
378 /* Add the node to the parent node events */
379 eventsElement
.appendChild(eventElement
);
382 return eventsElement
;
386 * Gets the 'consumer_output' element after creating it.
389 * The document in which the nodes are being added
391 * The session informations
392 * @return The consumer output element with his informations as XML elements
394 private static Element
getConsumerOutputElement(Document document
, ISessionInfo session
) {
395 Element consumerOutputElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_CONSUMER_OUTPUT
);
396 Element destinationElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_DESTINATION
);
398 /* Value of consumer output element */
399 addElementContent(document
, consumerOutputElement
, SessionConfigStrings
.CONFIG_ELEMENT_ENABLED
, SessionConfigStrings
.CONFIG_STRING_TRUE
);
401 if (session
.isStreamedTrace()) {
402 /* If it is a streamed session, add the net output element */
403 destinationElement
.appendChild(getNetOutputElement(document
, session
));
405 addElementContent(document
, destinationElement
, SessionConfigStrings
.CONFIG_ELEMENT_PATH
, session
.getSessionPath());
408 consumerOutputElement
.appendChild(destinationElement
);
409 return consumerOutputElement
;
413 * Gets the 'net_output' element after creating it. It is composed of the
414 * control and data URIs.
417 * The document in which the nodes are being added
419 * The session informations
420 * @return The net output element
422 private static Element
getNetOutputElement(Document document
, ISessionInfo session
) {
423 Element netOutputElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_NET_OUTPUT
);
425 String networkUrl
= session
.getNetworkUrl();
426 String controlUri
= networkUrl
== null ? session
.getControlUrl() : networkUrl
;
427 String dataUri
= networkUrl
== null ? session
.getDataUrl() : networkUrl
;
428 addElementContent(document
, netOutputElement
, SessionConfigStrings
.CONFIG_ELEMENT_CONTROL_URI
, controlUri
);
429 addElementContent(document
, netOutputElement
, SessionConfigStrings
.CONFIG_ELEMENT_DATA_URI
, dataUri
);
431 return netOutputElement
;
435 * Gets the 'snapshot_outputs' element after creating it.
438 * The document in which the nodes are being added
440 * The session informations
441 * @return The snapshot outputs element with snapshot informations as XML
444 private static Element
getSnapshotOuputsElement(Document document
, ISessionInfo session
) {
445 Element snapshotOutputsElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_SNAPSHOT_OUTPUTS
);
446 Element outputElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_OUTPUT
);
448 /* Add the name of the snapshot and the max size element */
449 addElementContent(document
, outputElement
, SessionConfigStrings
.CONFIG_ELEMENT_NAME
, session
.getSnapshotInfo().getName());
452 * TODO: find the proper max size value of output element. For now it is
453 * set to the default 0 value which means unlimited for lttng.
455 addElementContent(document
, outputElement
, SessionConfigStrings
.CONFIG_ELEMENT_MAX_SIZE
, SessionConfigStrings
.CONFIG_STRING_ZERO
);
456 outputElement
.appendChild(getConsumerOutputElement(document
, session
));
458 snapshotOutputsElement
.appendChild(outputElement
);
459 return snapshotOutputsElement
;
462 // ---------------------------------------------------------
464 // ---------------------------------------------------------
467 * Validates the session configuration file against its schema.
470 * The session configuration file
471 * @return The status of the validation
473 public static IStatus
sessionValidate(File sessionFile
) {
474 URL url
= SessionConfigGenerator
.class.getResource(SESSION_XSD_FILENAME
);
475 SchemaFactory schemaFactory
= SchemaFactory
.newInstance(XMLConstants
.W3C_XML_SCHEMA_NS_URI
);
476 Source xmlSource
= new StreamSource(sessionFile
);
479 Schema schema
= schemaFactory
.newSchema(url
);
480 Validator validator
= schema
.newValidator();
481 validator
.validate(xmlSource
);
482 } catch (SAXParseException e
) {
483 String error
= NLS
.bind(Messages
.SessionConfigXML_XmlParseError
, e
.getLineNumber(), e
.getLocalizedMessage());
484 Activator
.getDefault().logError(error
);
485 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, error
, e
);
486 } catch (SAXException e
) {
487 String error
= NLS
.bind(Messages
.SessionConfigXML_XmlValidationError
, e
.getLocalizedMessage());
488 Activator
.getDefault().logError(error
);
489 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, error
, e
);
490 } catch (IOException e
) {
491 String error
= Messages
.SessionConfigXML_XmlValidateError
;
492 Activator
.getDefault().logError("IO exception occurred", e
); //$NON-NLS-1$
493 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, error
, e
);
495 return Status
.OK_STATUS
;
499 * Saves the session configuration into a XML file.
502 * The document representing the session configuration file
504 * The path of the locally saved session configuration file
505 * @throws TransformerException
506 * On an transformation process
508 private static void saveSessionConfig(Document document
, String destination
) throws TransformerException
{
509 /* Write the content into a XML file */
510 TransformerFactory transformerFactory
= TransformerFactory
.newInstance();
511 Transformer transformer
= transformerFactory
.newTransformer();
513 transformer
.setOutputProperty(OutputKeys
.INDENT
, "yes"); //$NON-NLS-1$
514 transformer
.setOutputProperty(INDENT_AMOUNT_PROPERTY_NAME
, INDENT_AMOUNT_PROPERTY_VALUE
);
516 DOMSource source
= new DOMSource(document
);
517 StreamResult result
= new StreamResult(new File(destination
));
519 transformer
.transform(source
, result
);
523 * Adds to a parent node an element with his content.
526 * The document in which the nodes are being added
528 * The parent node that contains the element and his content
530 * The element container name
531 * @param elementContent
534 private static void addElementContent(Document document
, Element parent
, String elementName
, Object elementContent
) {
535 Element contentElement
= document
.createElement(elementName
);
536 contentElement
.appendChild(document
.createTextNode(elementContent
.toString()));
537 parent
.appendChild(contentElement
);