2 * Copyright (C) 2016 - EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
4 * This library is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License, version 2.1 only,
6 * as published by the Free Software Foundation.
8 * This library is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library; if not, write to the Free Software Foundation,
15 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 package org
.lttng
.ust
.agent
.context
;
20 import java
.io
.IOException
;
21 import java
.nio
.ByteBuffer
;
22 import java
.nio
.ByteOrder
;
23 import java
.nio
.charset
.Charset
;
24 import java
.util
.Collection
;
27 import org
.lttng
.ust
.agent
.utils
.LttngUstAgentLogger
;
30 * This class is used to serialize the list of "context info" objects to pass
33 * The protocol expects a single byte array parameter. This byte array consists
34 * of a series of fixed-size entries, where each entry contains the following
35 * elements (with their size in bytes in parenthesis):
38 * <li>The full context name, like "$app.myprovider:mycontext" (256)</li>
39 * <li>The context value type (1)</li>
40 * <li>The context value itself(256)</li>
43 * So the total size of each entry is 513 bytes. All unused bytes will be
46 * @author Alexandre Montplaisir
48 public class ContextInfoSerializer
{
50 private enum DataType
{
61 private final byte value
;
63 private DataType(int value
) {
64 this.value
= (byte) value
;
67 public byte getValue() {
72 private static final String UST_APP_CTX_PREFIX
= "$app.";
73 private static final int ELEMENT_LENGTH
= 256;
74 private static final int ENTRY_LENGTH
= 513;
75 private static final ByteOrder NATIVE_ORDER
= ByteOrder
.nativeOrder();
76 private static final Charset UTF8_CHARSET
= Charset
.forName("UTF-8");
77 private static final byte[] EMPTY_ARRAY
= new byte[0];
80 * From the list of requested contexts in the tracing session, look them up
81 * in the {@link ContextInfoManager}, retrieve the available ones, and
82 * serialize them into a byte array.
84 * @param enabledContexts
85 * The contexts that are enabled in the tracing session (indexed
86 * first by retriever name, then by index names). Should come
87 * from the LTTng Agent.
88 * @return The byte array representing the intersection of the requested and
91 public static byte[] queryAndSerializeRequestedContexts(Collection
<Map
.Entry
<String
, Map
<String
, Integer
>>> enabledContexts
) {
92 if (enabledContexts
.isEmpty()) {
93 /* Early return if there is no requested context information */
97 /* Compute the total number of contexts (flatten the map) */
98 int totalArraySize
= 0;
99 for (Map
.Entry
<String
, Map
<String
, Integer
>> contexts
: enabledContexts
) {
100 totalArraySize
+= contexts
.getValue().size() * ENTRY_LENGTH
;
103 ContextInfoManager contextManager
;
105 contextManager
= ContextInfoManager
.getInstance();
106 } catch (IOException e
) {
108 * The JNI library is not available, do not send any context
109 * information. No retriever could have been defined anyways.
114 ByteBuffer buffer
= ByteBuffer
.allocate(totalArraySize
);
115 buffer
.order(NATIVE_ORDER
);
118 for (Map
.Entry
<String
, Map
<String
, Integer
>> entry
: enabledContexts
) {
119 String requestedRetrieverName
= entry
.getKey();
120 Map
<String
, Integer
> requestedContexts
= entry
.getValue();
122 IContextInfoRetriever retriever
= contextManager
.getContextInfoRetriever(requestedRetrieverName
);
124 for (String requestedContext
: requestedContexts
.keySet()) {
126 if (retriever
== null) {
129 contextInfo
= retriever
.retrieveContextInfo(requestedContext
);
131 * 'contextInfo' can still be null here, which would
132 * indicate the retriever does not supply this context. We
133 * will still write this information so that the tracer can
138 /* Serialize the result to the buffer */
139 // FIXME Eventually pass the retriever name only once?
140 String fullContextName
= (UST_APP_CTX_PREFIX
+ requestedRetrieverName
+ ':' + requestedContext
);
141 byte[] strArray
= fullContextName
.getBytes(UTF8_CHARSET
);
142 int remainingBytes
= ELEMENT_LENGTH
- strArray
.length
;
143 // FIXME Handle case where name is too long...
144 buffer
.put(strArray
);
145 buffer
.position(buffer
.position() + remainingBytes
);
147 LttngUstAgentLogger
.log(ContextInfoSerializer
.class,
148 "ContextInfoSerializer: Context to be sent through JNI: " + fullContextName
+ '=' +
149 (contextInfo
== null ?
"null" : contextInfo
.toString()));
151 serializeContextInfo(buffer
, contextInfo
);
154 return buffer
.array();
157 private static void serializeContextInfo(ByteBuffer buffer
, Object contextInfo
) {
159 if (contextInfo
== null) {
160 buffer
.put(DataType
.NULL
.getValue());
161 remainingBytes
= ELEMENT_LENGTH
;
163 } else if (contextInfo
instanceof Integer
) {
164 buffer
.put(DataType
.INTEGER
.getValue());
165 buffer
.putInt(((Integer
) contextInfo
).intValue());
166 remainingBytes
= ELEMENT_LENGTH
- 4;
168 } else if (contextInfo
instanceof Long
) {
169 buffer
.put(DataType
.LONG
.getValue());
170 buffer
.putLong(((Long
) contextInfo
).longValue());
171 remainingBytes
= ELEMENT_LENGTH
- 8;
173 } else if (contextInfo
instanceof Double
) {
174 buffer
.put(DataType
.DOUBLE
.getValue());
175 buffer
.putDouble(((Double
) contextInfo
).doubleValue());
176 remainingBytes
= ELEMENT_LENGTH
- 8;
178 } else if (contextInfo
instanceof Float
) {
179 buffer
.put(DataType
.FLOAT
.getValue());
180 buffer
.putFloat(((Float
) contextInfo
).floatValue());
181 remainingBytes
= ELEMENT_LENGTH
- 4;
183 } else if (contextInfo
instanceof Byte
) {
184 buffer
.put(DataType
.BYTE
.getValue());
185 buffer
.put(((Byte
) contextInfo
).byteValue());
186 remainingBytes
= ELEMENT_LENGTH
- 1;
188 } else if (contextInfo
instanceof Short
) {
189 buffer
.put(DataType
.SHORT
.getValue());
190 buffer
.putShort(((Short
) contextInfo
).shortValue());
191 remainingBytes
= ELEMENT_LENGTH
- 2;
193 } else if (contextInfo
instanceof Boolean
) {
194 buffer
.put(DataType
.BOOLEAN
.getValue());
195 boolean b
= ((Boolean
) contextInfo
).booleanValue();
196 /* Converted to one byte, write 1 for true, 0 for false */
197 buffer
.put((byte) (b ?
1 : 0));
198 remainingBytes
= ELEMENT_LENGTH
- 1;
201 /* We'll write the object as a string. Also includes the case of Character. */
202 String str
= contextInfo
.toString();
203 byte[] strArray
= str
.getBytes(UTF8_CHARSET
);
205 buffer
.put(DataType
.STRING
.getValue());
206 if (strArray
.length
>= ELEMENT_LENGTH
) {
207 /* Trim the string to the max allowed length */
208 buffer
.put(strArray
, 0, ELEMENT_LENGTH
);
211 buffer
.put(strArray
);
212 remainingBytes
= ELEMENT_LENGTH
- strArray
.length
;
215 buffer
.position(buffer
.position() + remainingBytes
);