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
.IChannelInfo
;
42 import org
.eclipse
.tracecompass
.internal
.lttng2
.control
.core
.model
.IDomainInfo
;
43 import org
.eclipse
.tracecompass
.internal
.lttng2
.control
.core
.model
.IEventInfo
;
44 import org
.eclipse
.tracecompass
.internal
.lttng2
.control
.core
.model
.ISessionInfo
;
45 import org
.eclipse
.tracecompass
.internal
.lttng2
.control
.core
.model
.TraceEnablement
;
46 import org
.eclipse
.tracecompass
.internal
.lttng2
.control
.core
.model
.TraceEventType
;
47 import org
.eclipse
.tracecompass
.internal
.lttng2
.control
.core
.model
.TraceLogLevel
;
48 import org
.eclipse
.tracecompass
.internal
.lttng2
.control
.core
.model
.TraceSessionState
;
49 import org
.w3c
.dom
.Document
;
50 import org
.w3c
.dom
.Element
;
51 import org
.xml
.sax
.SAXException
;
52 import org
.xml
.sax
.SAXParseException
;
55 * Class for generating a session configuration file. A session configuration is
56 * used to configure a trace session. It is a XML formatted file that contains
57 * values defining the behavior of that specific trace session.
59 * Kernel session configuration example:
65 * <name>test_kernel</name>
69 * <buffer_type>GLOBAL</buffer_type>
72 * <name>channel0</name>
73 * <enabled>false</enabled>
74 * <overwrite_mode>DISCARD</overwrite_mode>
75 * <subbuffer_size>262144</subbuffer_size>
76 * <subbuffer_count>4</subbuffer_count>
77 * <switch_timer_interval>0</switch_timer_interval>
78 * <read_timer_interval>200000</read_timer_interval>
79 * <output_type>SPLICE</output_type>
80 * <tracefile_size>0</tracefile_size>
81 * <tracefile_count>0</tracefile_count>
82 * <live_timer_interval>0</live_timer_interval>
85 * <enabled>true</enabled>
86 * <type>SYSCALL</type>
89 * <name>snd_soc_cache_sync</name>
90 * <enabled>true</enabled>
91 * <type>TRACEPOINT</type>
98 * <started>false</started>
101 * <enabled>true</enabled>
103 * <path>/home/user/lttng-traces/test_kernel</path>
114 * @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
);
155 saveSessionConfig(sessionConfigDocument
, sessionFileDestination
.toString());
156 } catch (TransformerException
| IllegalArgumentException
| ParserConfigurationException e
) {
157 Activator
.getDefault().logError("Error generating the session configuration file: " + sessionFileDestination
.toString(), e
); //$NON-NLS-1$
158 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, e
.getMessage());
161 return Status
.OK_STATUS
;
165 * Generates a session configuration from a set of session informations.
168 * The session informations
169 * @return The document with all session configuration nodes
170 * @throws IllegalArgumentException
171 * On an illegal argument inside sessions
172 * @throws ParserConfigurationException
173 * On an parser configuration error
175 private static @NonNull Document
generateSessionConfig(Iterable
<ISessionInfo
> sessions
) throws IllegalArgumentException
, ParserConfigurationException
{
176 DocumentBuilderFactory docFactory
= DocumentBuilderFactory
.newInstance();
177 DocumentBuilder docBuilder
= docFactory
.newDocumentBuilder();
179 Document document
= docBuilder
.newDocument();
181 Element rootElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_SESSIONS
);
182 document
.appendChild(rootElement
);
184 for (ISessionInfo session
: sessions
) {
185 /* All elements under "sessions" elements */
186 Element sessionElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_SESSION
);
188 /* Contents of session element */
189 String enabled
= session
.getSessionState().equals(TraceSessionState
.ACTIVE
) ? SessionConfigStrings
.CONFIG_STRING_TRUE
: SessionConfigStrings
.CONFIG_STRING_FALSE
;
191 addElementContent(document
, sessionElement
, SessionConfigStrings
.CONFIG_ELEMENT_NAME
, session
.getName());
192 addElementContent(document
, sessionElement
, SessionConfigStrings
.CONFIG_ELEMENT_STARTED
, enabled
);
194 if (session
.isSnapshotSession()) {
195 /* If it's a snapshot, we must add an attribute telling it is */
196 Element attributesElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_ATTRIBUTES
);
197 addElementContent(document
, attributesElement
, SessionConfigStrings
.CONFIG_ELEMENT_SNAPSHOT_MODE
, SessionConfigStrings
.CONFIG_STRING_TRUE
);
198 sessionElement
.appendChild(attributesElement
);
201 sessionElement
.appendChild(getDomainsElement(document
, session
));
202 sessionElement
.appendChild(getOutputElement(document
, session
));
203 rootElement
.appendChild(sessionElement
);
209 // ---------------------------------------------------------
210 // Getters for each element of the configuration file
211 // ---------------------------------------------------------
214 * Gets the 'domains' element after creating it.
217 * The document in which the nodes are being added
219 * The session informations
220 * @return The domains element as an XML element
222 private static Element
getDomainsElement(Document document
, ISessionInfo session
) {
223 Element domainsElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_DOMAINS
);
225 for (IDomainInfo domain
: session
.getDomains()) {
226 Element domainElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_DOMAIN
);
229 * Add everything specific to a domain
231 * TODO: We suppose here that domain is either kernel or UST. It
232 * will have to change if other domains are supported
234 String domainType
= domain
.isKernel() ? SessionConfigStrings
.CONFIG_DOMAIN_TYPE_KERNEL
: SessionConfigStrings
.CONFIG_DOMAIN_TYPE_UST
;
235 addElementContent(document
, domainElement
, SessionConfigStrings
.CONFIG_ELEMENT_TYPE
, domainType
);
237 String bufferType
= null;
238 switch (domain
.getBufferType()) {
240 bufferType
= SessionConfigStrings
.CONFIG_BUFFER_TYPE_PER_UID
;
243 bufferType
= SessionConfigStrings
.CONFIG_BUFFER_TYPE_PER_PID
;
246 bufferType
= SessionConfigStrings
.CONFIG_BUFFER_TYPE_GLOBAL
;
248 case BUFFER_TYPE_UNKNOWN
:
250 throw new IllegalArgumentException(Messages
.SessionConfigXML_UnknownDomainBufferType
);
252 addElementContent(document
, domainElement
, SessionConfigStrings
.CONFIG_ELEMENT_DOMAIN_BUFFER_TYPE
, bufferType
);
254 /* Add the channels */
255 domainElement
.appendChild(getChannelsElement(document
, domain
.isKernel(), domain
.getChannels()));
256 domainsElement
.appendChild(domainElement
);
259 return domainsElement
;
263 * Gets the 'output' element after creating it. If the session is a
264 * snapshot, it will be composed of a snapshot outputs element. Otherwise,
265 * it will contain the consumer output element.
268 * The document in which the nodes are being added
270 * The session informations
271 * @return The output element as an XML node
273 private static Element
getOutputElement(Document document
, ISessionInfo session
) {
274 Element outputElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_OUTPUT
);
276 if (session
.isSnapshotSession()) {
277 outputElement
.appendChild(getSnapshotOuputsElement(document
, session
));
278 } else if (session
.isStreamedTrace()) {
279 outputElement
.appendChild(getNetOutputElement(document
, session
));
281 outputElement
.appendChild(getConsumerOutputElement(document
, session
));
284 return outputElement
;
288 * Gets the 'channels' element after creating it.
291 * The document in which the nodes are being added
293 * Is it a kernel domain type
295 * The channels to be added as elements
296 * @return The channels element as an XML element
298 private static Element
getChannelsElement(Document document
, boolean isKernel
, IChannelInfo
[] channels
) {
299 Element channelsElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_CHANNELS
);
301 for (IChannelInfo channel
: channels
) {
302 Element channelElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_CHANNEL
);
304 /* Add everything related to a channel */
305 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_NAME
, channel
.getName());
307 String overwriteMode
= channel
.isOverwriteMode() ? SessionConfigStrings
.CONFIG_OVERWRITE_MODE_OVERWRITE
: SessionConfigStrings
.CONFIG_OVERWRITE_MODE_DISCARD
;
308 String enabled
= channel
.getState().equals(TraceEnablement
.ENABLED
) ? SessionConfigStrings
.CONFIG_STRING_TRUE
: SessionConfigStrings
.CONFIG_STRING_FALSE
;
310 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_ENABLED
, enabled
);
311 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_OVERWRITE_MODE
, overwriteMode
);
312 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_SUBBUFFER_SIZE
, channel
.getSubBufferSize());
313 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_SUBBUFFER_COUNT
, channel
.getNumberOfSubBuffers());
314 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_SWITCH_TIMER_INTERVAL
, channel
.getSwitchTimer());
315 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_READ_TIMER_INTERVAL
, channel
.getReadTimer());
317 String outputType
= channel
.getOutputType().getInName().startsWith(SessionConfigStrings
.CONFIG_OUTPUT_TYPE_MMAP
) ?
318 outputType
= SessionConfigStrings
.CONFIG_OUTPUT_TYPE_MMAP
: SessionConfigStrings
.CONFIG_OUTPUT_TYPE_SPLICE
;
319 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_OUTPUT_TYPE
, outputType
);
320 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_TRACEFILE_SIZE
, channel
.getMaxSizeTraceFiles());
321 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_TRACEFILE_COUNT
, channel
.getMaxNumberTraceFiles());
324 * TODO: Replace the 0 value by the channel live timer property from
325 * SessionInfo once live session tracing is supported
327 addElementContent(document
, channelElement
, SessionConfigStrings
.CONFIG_ELEMENT_LIVE_TIMER_INTERVAL
, SessionConfigStrings
.CONFIG_STRING_ZERO
);
330 channelElement
.appendChild(getEventsElement(document
, isKernel
, channel
.getEvents()));
331 channelsElement
.appendChild(channelElement
);
334 return channelsElement
;
338 * Gets the 'events' element after creating it. It is composed of the event
339 * informations from a list of IEventInfo.
342 * The document in which the nodes are being added
344 * Is the domain type kernel
346 * The event informations to be added
347 * @return An element containing all the event informations as XML elements
349 private static Element
getEventsElement(Document document
, boolean isKernel
, IEventInfo
[] events
) {
350 Element eventsElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_EVENTS
);
352 for (IEventInfo event
: events
) {
353 Element eventElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_EVENT
);
355 /* Enabled attribute */
356 String enabled
= event
.getState().equals(TraceEnablement
.ENABLED
) ? SessionConfigStrings
.CONFIG_STRING_TRUE
: SessionConfigStrings
.CONFIG_STRING_FALSE
;
358 /* Add the attributes to the event node */
359 addElementContent(document
, eventElement
, SessionConfigStrings
.CONFIG_ELEMENT_NAME
, event
.getName());
360 addElementContent(document
, eventElement
, SessionConfigStrings
.CONFIG_ELEMENT_ENABLED
, enabled
);
361 TraceEventType eventType
= event
.getEventType();
362 if (!eventType
.equals(TraceEventType
.UNKNOWN
)) {
363 addElementContent(document
, eventElement
, SessionConfigStrings
.CONFIG_ELEMENT_TYPE
, eventType
.getInName().toUpperCase());
365 throw new IllegalArgumentException(Messages
.SessionConfigXML_UnknownEventType
);
368 /* Specific to UST session config: the log level */
369 if (!isKernel
&& !event
.getLogLevel().equals(TraceLogLevel
.LEVEL_UNKNOWN
)) {
370 addElementContent(document
, eventElement
, SessionConfigStrings
.CONFIG_ELEMENT_LOGLEVEL
, event
.getLogLevel().ordinal());
373 /* Add the node to the parent node events */
374 eventsElement
.appendChild(eventElement
);
377 return eventsElement
;
381 * Gets the 'consumer_output' element after creating it.
384 * The document in which the nodes are being added
386 * The session informations
387 * @return The consumer output element with his informations as XML elements
389 private static Element
getConsumerOutputElement(Document document
, ISessionInfo session
) {
390 Element consumerOutputElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_CONSUMER_OUTPUT
);
391 Element destinationElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_DESTINATION
);
393 /* Value of consumer output element */
394 addElementContent(document
, consumerOutputElement
, SessionConfigStrings
.CONFIG_ELEMENT_ENABLED
, SessionConfigStrings
.CONFIG_STRING_TRUE
);
396 if (session
.isStreamedTrace()) {
397 /* If it is a streamed session, add the net output element */
398 destinationElement
.appendChild(getNetOutputElement(document
, session
));
400 addElementContent(document
, destinationElement
, SessionConfigStrings
.CONFIG_ELEMENT_PATH
, session
.getSessionPath());
403 consumerOutputElement
.appendChild(destinationElement
);
404 return consumerOutputElement
;
408 * Gets the 'net_output' element after creating it. It is composed of the
409 * control and data URIs.
412 * The document in which the nodes are being added
414 * The session informations
415 * @return The net output element
417 private static Element
getNetOutputElement(Document document
, ISessionInfo session
) {
418 Element netOutputElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_NET_OUTPUT
);
420 String networkUrl
= session
.getNetworkUrl();
421 String controlUri
= networkUrl
== null ? session
.getControlUrl() : networkUrl
;
422 String dataUri
= networkUrl
== null ? session
.getDataUrl() : networkUrl
;
423 addElementContent(document
, netOutputElement
, SessionConfigStrings
.CONFIG_ELEMENT_CONTROL_URI
, controlUri
);
424 addElementContent(document
, netOutputElement
, SessionConfigStrings
.CONFIG_ELEMENT_DATA_URI
, dataUri
);
426 return netOutputElement
;
430 * Gets the 'snapshot_outputs' element after creating it.
433 * The document in which the nodes are being added
435 * The session informations
436 * @return The snapshot outputs element with snapshot informations as XML
439 private static Element
getSnapshotOuputsElement(Document document
, ISessionInfo session
) {
440 Element snapshotOutputsElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_SNAPSHOT_OUTPUTS
);
441 Element outputElement
= document
.createElement(SessionConfigStrings
.CONFIG_ELEMENT_OUTPUT
);
443 /* Add the name of the snapshot and the max size element */
444 addElementContent(document
, outputElement
, SessionConfigStrings
.CONFIG_ELEMENT_NAME
, session
.getSnapshotInfo().getName());
447 * TODO: find the proper max size value of output element. For now it is
448 * set to the default 0 value which means unlimited for lttng.
450 addElementContent(document
, outputElement
, SessionConfigStrings
.CONFIG_ELEMENT_MAX_SIZE
, SessionConfigStrings
.CONFIG_STRING_ZERO
);
451 outputElement
.appendChild(getConsumerOutputElement(document
, session
));
453 snapshotOutputsElement
.appendChild(outputElement
);
454 return snapshotOutputsElement
;
457 // ---------------------------------------------------------
459 // ---------------------------------------------------------
462 * Validates the session configuration file against its schema.
465 * The session configuration file
466 * @return The status of the validation
468 public static IStatus
sessionValidate(File sessionFile
) {
469 URL url
= SessionConfigGenerator
.class.getResource(SESSION_XSD_FILENAME
);
470 SchemaFactory schemaFactory
= SchemaFactory
.newInstance(XMLConstants
.W3C_XML_SCHEMA_NS_URI
);
471 Source xmlSource
= new StreamSource(sessionFile
);
474 Schema schema
= schemaFactory
.newSchema(url
);
475 Validator validator
= schema
.newValidator();
476 validator
.validate(xmlSource
);
477 } catch (SAXParseException e
) {
478 String error
= NLS
.bind(Messages
.SessionConfigXML_XmlParseError
, e
.getLineNumber(), e
.getLocalizedMessage());
479 Activator
.getDefault().logError(error
);
480 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, error
, e
);
481 } catch (SAXException e
) {
482 String error
= NLS
.bind(Messages
.SessionConfigXML_XmlValidationError
, e
.getLocalizedMessage());
483 Activator
.getDefault().logError(error
);
484 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, error
, e
);
485 } catch (IOException e
) {
486 String error
= Messages
.SessionConfigXML_XmlValidateError
;
487 Activator
.getDefault().logError("IO exception occurred", e
); //$NON-NLS-1$
488 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, error
, e
);
490 return Status
.OK_STATUS
;
494 * Saves the session configuration into a XML file.
497 * The document representing the session configuration file
499 * The path of the locally saved session configuration file
500 * @throws TransformerException
501 * On an transformation process
503 private static void saveSessionConfig(Document document
, String destination
) throws TransformerException
{
504 /* Write the content into a XML file */
505 TransformerFactory transformerFactory
= TransformerFactory
.newInstance();
506 Transformer transformer
= transformerFactory
.newTransformer();
508 transformer
.setOutputProperty(OutputKeys
.INDENT
, "yes"); //$NON-NLS-1$
509 transformer
.setOutputProperty(INDENT_AMOUNT_PROPERTY_NAME
, INDENT_AMOUNT_PROPERTY_VALUE
);
511 DOMSource source
= new DOMSource(document
);
512 StreamResult result
= new StreamResult(new File(destination
));
514 transformer
.transform(source
, result
);
518 * Adds to a parent node an element with his content.
521 * The document in which the nodes are being added
523 * The parent node that contains the element and his content
525 * The element container name
526 * @param elementContent
529 private static void addElementContent(Document document
, Element parent
, String elementName
, Object elementContent
) {
530 Element contentElement
= document
.createElement(elementName
);
531 contentElement
.appendChild(document
.createTextNode(elementContent
.toString()));
532 parent
.appendChild(contentElement
);