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
.osgi
.util
.NLS
;
39 import org
.eclipse
.tracecompass
.internal
.lttng2
.control
.core
.Activator
;
40 import org
.eclipse
.tracecompass
.internal
.lttng2
.control
.core
.model
.IChannelInfo
;
41 import org
.eclipse
.tracecompass
.internal
.lttng2
.control
.core
.model
.IDomainInfo
;
42 import org
.eclipse
.tracecompass
.internal
.lttng2
.control
.core
.model
.IEventInfo
;
43 import org
.eclipse
.tracecompass
.internal
.lttng2
.control
.core
.model
.ISessionInfo
;
44 import org
.eclipse
.tracecompass
.internal
.lttng2
.control
.core
.model
.TraceEnablement
;
45 import org
.eclipse
.tracecompass
.internal
.lttng2
.control
.core
.model
.TraceEventType
;
46 import org
.eclipse
.tracecompass
.internal
.lttng2
.control
.core
.model
.TraceLogLevel
;
47 import org
.eclipse
.tracecompass
.internal
.lttng2
.control
.core
.model
.TraceSessionState
;
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
115 public final class SessionConfigGenerator
{
117 /** The name of the session schema */
118 private static final String SESSION_XSD_FILENAME
= "session.xsd"; //$NON-NLS-1$
120 /** The indent size used for the session configuration XML file */
121 private static final String INDENT_AMOUNT_PROPERTY_NAME
= "{http://xml.apache.org/xslt}indent-amount"; //$NON-NLS-1$
122 private static final String INDENT_AMOUNT_PROPERTY_VALUE
= "4"; //$NON-NLS-1$
125 * Private constructor. The class should not be instantiated.
127 private SessionConfigGenerator() {
130 // ---------------------------------------------------------
131 // Methods to generate session configuration files
132 // ---------------------------------------------------------
135 * Generates a session configuration file from a set of session information.
138 * The session informations
139 * @param sessionFileDestination
140 * The path of the locally saved session configuration file
141 * @return The status of the session configuration generation
143 public static IStatus
generateSessionConfig(Set
<ISessionInfo
> sessions
, IPath sessionFileDestination
) {
144 /* Parameters validation */
145 if (sessions
== null || sessions
.isEmpty()) {
146 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, Messages
.SessionConfigXML_InvalidSessionInfoList
);
147 } else if (sessionFileDestination
== null) {
148 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, Messages
.SessionConfigXML_InvalidTraceSessionPath
);
151 /* Generate the session configuration file */
153 Document sessionConfigDocument
= generateSessionConfig(sessions
);
155 if (sessionConfigDocument
!= null) {
156 saveSessionConfig(sessionConfigDocument
, sessionFileDestination
.toString());
158 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, Messages
.SessionConfigXML_SessionConfigGenerationError
);
160 } catch (TransformerException
| IllegalArgumentException
| ParserConfigurationException e
) {
161 Activator
.getDefault().logError("Error generating the session configuration file: " + sessionFileDestination
.toString(), e
); //$NON-NLS-1$
162 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, e
.getMessage());
165 return Status
.OK_STATUS
;
169 * Generates a session configuration from a set of session informations.
172 * The session informations
173 * @return The document with all session configuration nodes
174 * @throws IllegalArgumentException
175 * On an illegal argument inside sessions
176 * @throws ParserConfigurationException
177 * On an parser configuration error
179 private static Document
generateSessionConfig(Iterable
<ISessionInfo
> sessions
) throws IllegalArgumentException
, ParserConfigurationException
{
180 DocumentBuilderFactory docFactory
= DocumentBuilderFactory
.newInstance();
181 DocumentBuilder docBuilder
= docFactory
.newDocumentBuilder();
183 Document document
= docBuilder
.newDocument();
185 Element rootElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_SESSIONS
);
186 document
.appendChild(rootElement
);
188 for (ISessionInfo session
: sessions
) {
189 /* All elements under "sessions" elements */
190 Element sessionElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_SESSION
);
192 /* Contents of session element */
193 String enabled
= session
.getSessionState().equals(TraceSessionState
.ACTIVE
) ? SessionConfigStrings
.CONFIG_STRING_TRUE
: SessionConfigStrings
.CONFIG_STRING_FALSE
;
195 addElementContent(document
, sessionElement
, SessionConfigStrings
.CONFIG_ELEMENT_NAME
, session
.getName());
196 addElementContent(document
, sessionElement
, SessionConfigStrings
.CONFIG_ELEMENT_STARTED
, enabled
);
198 if (session
.isSnapshotSession()) {
199 /* If it's a snapshot, we must add an attribute telling it is */
200 Element attributesElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_ATTRIBUTES
);
201 addElementContent(document
, attributesElement
, SessionConfigStrings
.CONFIG_ELEMENT_SNAPSHOT_MODE
, SessionConfigStrings
.CONFIG_STRING_TRUE
);
202 sessionElement
.appendChild(attributesElement
);
205 sessionElement
.appendChild(getDomainsElement(document
, session
));
206 sessionElement
.appendChild(getOutputElement(document
, session
));
207 rootElement
.appendChild(sessionElement
);
213 // ---------------------------------------------------------
214 // Getters for each element of the configuration file
215 // ---------------------------------------------------------
218 * Gets the 'domains' element after creating it.
221 * The document in which the nodes are being added
223 * The session informations
224 * @return The domains element as an XML element
226 private static Element
getDomainsElement(Document document
, ISessionInfo session
) {
227 Element domainsElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_DOMAINS
);
229 for (IDomainInfo domain
: session
.getDomains()) {
230 Element domainElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_DOMAIN
);
233 * Add everything specific to a domain
235 * TODO: We suppose here that domain is either kernel or UST. It
236 * will have to change if other domains are supported
238 String domainType
= domain
.isKernel() ? SessionConfigStrings
.CONFIG_DOMAIN_TYPE_KERNEL
: SessionConfigStrings
.CONFIG_DOMAIN_TYPE_UST
;
239 addElementContent(document
, domainElement
, SessionConfigStrings
.CONFIG_ELEMENT_TYPE
, domainType
);
241 String bufferType
= null;
242 switch (domain
.getBufferType()) {
244 bufferType
= SessionConfigStrings
.CONFIG_BUFFER_TYPE_PER_UID
;
247 bufferType
= SessionConfigStrings
.CONFIG_BUFFER_TYPE_PER_PID
;
250 bufferType
= SessionConfigStrings
.CONFIG_BUFFER_TYPE_GLOBAL
;
252 case BUFFER_TYPE_UNKNOWN
:
254 throw new IllegalArgumentException(Messages
.SessionConfigXML_UnknownDomainBufferType
);
256 addElementContent(document
, domainElement
, SessionConfigStrings
.CONFIG_ELEMENT_DOMAIN_BUFFER_TYPE
, bufferType
);
258 /* Add the channels */
259 domainElement
.appendChild(getChannelsElement(document
, domain
.isKernel(), domain
.getChannels()));
260 domainsElement
.appendChild(domainElement
);
263 return domainsElement
;
267 * Gets the 'output' element after creating it. If the session is a
268 * snapshot, it will be composed of a snapshot outputs element. Otherwise,
269 * it will contain the consumer output element.
272 * The document in which the nodes are being added
274 * The session informations
275 * @return The output element as an XML node
277 private static Element
getOutputElement(Document document
, ISessionInfo session
) {
278 Element outputElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_OUTPUT
);
280 if (session
.isSnapshotSession()) {
281 outputElement
.appendChild(getSnapshotOuputsElement(document
, session
));
282 } else if (session
.isStreamedTrace()) {
283 outputElement
.appendChild(getNetOutputElement(document
, session
));
285 outputElement
.appendChild(getConsumerOutputElement(document
, session
));
288 return outputElement
;
292 * Gets the 'channels' element after creating it.
295 * The document in which the nodes are being added
297 * Is it a kernel domain type
299 * The channels to be added as elements
300 * @return The channels element as an XML element
302 private static Element
getChannelsElement(Document document
, boolean isKernel
, IChannelInfo
[] channels
) {
303 Element channelsElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_CHANNELS
);
305 for (IChannelInfo channel
: channels
) {
306 Element channelElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_CHANNEL
);
308 /* Add everything related to a channel */
309 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_NAME
, channel
.getName());
311 String overwriteMode
= channel
.isOverwriteMode() ? SessionConfigStrings
.CONFIG_OVERWRITE_MODE_OVERWRITE
: SessionConfigStrings
.CONFIG_OVERWRITE_MODE_DISCARD
;
312 String enabled
= channel
.getState().equals(TraceEnablement
.ENABLED
) ? SessionConfigStrings
.CONFIG_STRING_TRUE
: SessionConfigStrings
.CONFIG_STRING_FALSE
;
314 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_ENABLED
, enabled
);
315 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_OVERWRITE_MODE
, overwriteMode
);
316 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_SUBBUFFER_SIZE
, channel
.getSubBufferSize());
317 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_SUBBUFFER_COUNT
, channel
.getNumberOfSubBuffers());
318 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_SWITCH_TIMER_INTERVAL
, channel
.getSwitchTimer());
319 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_READ_TIMER_INTERVAL
, channel
.getReadTimer());
321 String outputType
= channel
.getOutputType().getInName().startsWith(SessionConfigStrings
.CONFIG_OUTPUT_TYPE_MMAP
) ?
322 outputType
= SessionConfigStrings
.CONFIG_OUTPUT_TYPE_MMAP
: SessionConfigStrings
.CONFIG_OUTPUT_TYPE_SPLICE
;
323 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_OUTPUT_TYPE
, outputType
);
324 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_TRACEFILE_SIZE
, channel
.getMaxSizeTraceFiles());
325 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_TRACEFILE_COUNT
, channel
.getMaxNumberTraceFiles());
328 * TODO: Replace the 0 value by the channel live timer property from
329 * SessionInfo once live session tracing is supported
331 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_LIVE_TIMER_INTERVAL
, SessionConfigStrings
.CONFIG_STRING_ZERO
);
334 channelElement
.appendChild(getEventsElement(document
, isKernel
, channel
.getEvents()));
335 channelsElement
.appendChild(channelElement
);
338 return channelsElement
;
342 * Gets the 'events' element after creating it. It is composed of the event
343 * informations from a list of IEventInfo.
346 * The document in which the nodes are being added
348 * Is the domain type kernel
350 * The event informations to be added
351 * @return An element containing all the event informations as XML elements
353 private static Element
getEventsElement(Document document
, boolean isKernel
, IEventInfo
[] events
) {
354 Element eventsElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_EVENTS
);
356 for (IEventInfo event
: events
) {
357 Element eventElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_EVENT
);
359 /* Enabled attribute */
360 String enabled
= event
.getState().equals(TraceEnablement
.ENABLED
) ? SessionConfigStrings
.CONFIG_STRING_TRUE
: SessionConfigStrings
.CONFIG_STRING_FALSE
;
362 /* Add the attributes to the event node */
363 addElementContent(document
, eventElement
, SessionConfigStrings
.CONFIG_ELEMENT_NAME
, event
.getName());
364 addElementContent(document
, eventElement
, SessionConfigStrings
.CONFIG_ELEMENT_ENABLED
, enabled
);
365 TraceEventType eventType
= event
.getEventType();
366 if (!eventType
.equals(TraceEventType
.UNKNOWN
)) {
367 addElementContent(document
, eventElement
, SessionConfigStrings
.CONFIG_ELEMENT_TYPE
, eventType
.getInName().toUpperCase());
369 throw new IllegalArgumentException(Messages
.SessionConfigXML_UnknownEventType
);
372 /* Specific to UST session config: the log level */
373 if (!isKernel
&& !event
.getLogLevel().equals(TraceLogLevel
.LEVEL_UNKNOWN
)) {
374 addElementContent(document
, eventElement
, SessionConfigStrings
.CONFIG_ELEMENT_LOGLEVEL
, event
.getLogLevel().ordinal());
377 /* Add the node to the parent node events */
378 eventsElement
.appendChild(eventElement
);
381 return eventsElement
;
385 * Gets the 'consumer_output' element after creating it.
388 * The document in which the nodes are being added
390 * The session informations
391 * @return The consumer output element with his informations as XML elements
393 private static Element
getConsumerOutputElement(Document document
, ISessionInfo session
) {
394 Element consumerOutputElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_CONSUMER_OUTPUT
);
395 Element destinationElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_DESTINATION
);
397 /* Value of consumer output element */
398 addElementContent(document
, consumerOutputElement
, SessionConfigStrings
.CONFIG_ELEMENT_ENABLED
, SessionConfigStrings
.CONFIG_STRING_TRUE
);
400 if (session
.isStreamedTrace()) {
401 /* If it is a streamed session, add the net output element */
402 destinationElement
.appendChild(getNetOutputElement(document
, session
));
404 addElementContent(document
, destinationElement
, SessionConfigStrings
.CONFIG_ELEMENT_PATH
, session
.getSessionPath());
407 consumerOutputElement
.appendChild(destinationElement
);
408 return consumerOutputElement
;
412 * Gets the 'net_output' element after creating it. It is composed of the
413 * control and data URIs.
416 * The document in which the nodes are being added
418 * The session informations
419 * @return The net output element
421 private static Element
getNetOutputElement(Document document
, ISessionInfo session
) {
422 Element netOutputElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_NET_OUTPUT
);
424 String networkUrl
= session
.getNetworkUrl();
425 String controlUri
= networkUrl
== null ? session
.getControlUrl() : networkUrl
;
426 String dataUri
= networkUrl
== null ? session
.getDataUrl() : networkUrl
;
427 addElementContent(document
, netOutputElement
, SessionConfigStrings
.CONFIG_ELEMENT_CONTROL_URI
, controlUri
);
428 addElementContent(document
, netOutputElement
, SessionConfigStrings
.CONFIG_ELEMENT_DATA_URI
, dataUri
);
430 return netOutputElement
;
434 * Gets the 'snapshot_outputs' element after creating it.
437 * The document in which the nodes are being added
439 * The session informations
440 * @return The snapshot outputs element with snapshot informations as XML
443 private static Element
getSnapshotOuputsElement(Document document
, ISessionInfo session
) {
444 Element snapshotOutputsElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_SNAPSHOT_OUTPUTS
);
445 Element outputElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_OUTPUT
);
447 /* Add the name of the snapshot and the max size element */
448 addElementContent(document
, outputElement
, SessionConfigStrings
.CONFIG_ELEMENT_NAME
, session
.getSnapshotInfo().getName());
451 * TODO: find the proper max size value of output element. For now it is
452 * set to the default 0 value which means unlimited for lttng.
454 addElementContent(document
, outputElement
, SessionConfigStrings
.CONFIG_ELEMENT_MAX_SIZE
, SessionConfigStrings
.CONFIG_STRING_ZERO
);
455 outputElement
.appendChild(getConsumerOutputElement(document
, session
));
457 snapshotOutputsElement
.appendChild(outputElement
);
458 return snapshotOutputsElement
;
461 // ---------------------------------------------------------
463 // ---------------------------------------------------------
466 * Validates the session configuration file against its schema.
469 * The session configuration file
470 * @return The status of the validation
472 public static IStatus
sessionValidate(File sessionFile
) {
473 URL url
= SessionConfigGenerator
.class.getResource(SESSION_XSD_FILENAME
);
474 SchemaFactory schemaFactory
= SchemaFactory
.newInstance(XMLConstants
.W3C_XML_SCHEMA_NS_URI
);
475 Source xmlSource
= new StreamSource(sessionFile
);
478 Schema schema
= schemaFactory
.newSchema(url
);
479 Validator validator
= schema
.newValidator();
480 validator
.validate(xmlSource
);
481 } catch (SAXParseException e
) {
482 String error
= NLS
.bind(Messages
.SessionConfigXML_XmlParseError
, e
.getLineNumber(), e
.getLocalizedMessage());
483 Activator
.getDefault().logError(error
);
484 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, error
, e
);
485 } catch (SAXException e
) {
486 String error
= NLS
.bind(Messages
.SessionConfigXML_XmlValidationError
, e
.getLocalizedMessage());
487 Activator
.getDefault().logError(error
);
488 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, error
, e
);
489 } catch (IOException e
) {
490 String error
= Messages
.SessionConfigXML_XmlValidateError
;
491 Activator
.getDefault().logError("IO exception occurred", e
); //$NON-NLS-1$
492 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, error
, e
);
494 return Status
.OK_STATUS
;
498 * Saves the session configuration into a XML file.
501 * The document representing the session configuration file
503 * The path of the locally saved session configuration file
504 * @throws TransformerException
505 * On an transformation process
507 private static void saveSessionConfig(Document document
, String destination
) throws TransformerException
{
508 /* Write the content into a XML file */
509 TransformerFactory transformerFactory
= TransformerFactory
.newInstance();
510 Transformer transformer
= transformerFactory
.newTransformer();
512 transformer
.setOutputProperty(OutputKeys
.INDENT
, "yes"); //$NON-NLS-1$
513 transformer
.setOutputProperty(INDENT_AMOUNT_PROPERTY_NAME
, INDENT_AMOUNT_PROPERTY_VALUE
);
515 DOMSource source
= new DOMSource(document
);
516 StreamResult result
= new StreamResult(new File(destination
));
518 transformer
.transform(source
, result
);
522 * Adds to a parent node an element with his content.
525 * The document in which the nodes are being added
527 * The parent node that contains the element and his content
529 * The element container name
530 * @param elementContent
533 private static void addElementContent(Document document
, Element parent
, String elementName
, Object elementContent
) {
534 Element contentElement
= document
.createElement(elementName
);
535 contentElement
.appendChild(document
.createTextNode(elementContent
.toString()));
536 parent
.appendChild(contentElement
);