be613043920af9b63290962df6c076879c43aa43
[lttng-ust.git] /
1 /*
2 * Copyright (C) 2016 - EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
3 *
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.
7 *
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
11 * for more details.
12 *
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
16 */
17
18 package org.lttng.ust.agent.context;
19
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;
25 import java.util.Map;
26
27 import org.lttng.ust.agent.utils.LttngUstAgentLogger;
28
29 /**
30 * This class is used to serialize the list of "context info" objects to pass
31 * through JNI.
32 *
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):
36 *
37 * <ul>
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>
41 * </ul>
42 *
43 * So the total size of each entry is 513 bytes. All unused bytes will be
44 * zero'ed.
45 *
46 * @author Alexandre Montplaisir
47 */
48 public class ContextInfoSerializer {
49
50 private enum DataType {
51 NULL(0),
52 INTEGER(1),
53 LONG(2),
54 DOUBLE(3),
55 FLOAT(4),
56 BYTE(5),
57 SHORT(6),
58 BOOLEAN(7),
59 STRING(8);
60
61 private final byte value;
62
63 private DataType(int value) {
64 this.value = (byte) value;
65 }
66
67 public byte getValue() {
68 return value;
69 }
70 }
71
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];
78
79 /**
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.
83 *
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
89 * available contexts.
90 */
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 */
94 return EMPTY_ARRAY;
95 }
96
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;
101 }
102
103 ContextInfoManager contextManager;
104 try {
105 contextManager = ContextInfoManager.getInstance();
106 } catch (IOException e) {
107 /*
108 * The JNI library is not available, do not send any context
109 * information. No retriever could have been defined anyways.
110 */
111 return EMPTY_ARRAY;
112 }
113
114 ByteBuffer buffer = ByteBuffer.allocate(totalArraySize);
115 buffer.order(NATIVE_ORDER);
116 buffer.clear();
117
118 for (Map.Entry<String, Map<String, Integer>> entry : enabledContexts) {
119 String requestedRetrieverName = entry.getKey();
120 Map<String, Integer> requestedContexts = entry.getValue();
121
122 IContextInfoRetriever retriever = contextManager.getContextInfoRetriever(requestedRetrieverName);
123
124 for (String requestedContext : requestedContexts.keySet()) {
125 Object contextInfo;
126 if (retriever == null) {
127 contextInfo = null;
128 } else {
129 contextInfo = retriever.retrieveContextInfo(requestedContext);
130 /*
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
134 * know about it.
135 */
136 }
137
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);
146
147 LttngUstAgentLogger.log(ContextInfoSerializer.class,
148 "ContextInfoSerializer: Context to be sent through JNI: " + fullContextName + '=' +
149 (contextInfo == null ? "null" : contextInfo.toString()));
150
151 serializeContextInfo(buffer, contextInfo);
152 }
153 }
154 return buffer.array();
155 }
156
157 private static void serializeContextInfo(ByteBuffer buffer, Object contextInfo) {
158 int remainingBytes;
159 if (contextInfo == null) {
160 buffer.put(DataType.NULL.getValue());
161 remainingBytes = ELEMENT_LENGTH;
162
163 } else if (contextInfo instanceof Integer) {
164 buffer.put(DataType.INTEGER.getValue());
165 buffer.putInt(((Integer) contextInfo).intValue());
166 remainingBytes = ELEMENT_LENGTH - 4;
167
168 } else if (contextInfo instanceof Long) {
169 buffer.put(DataType.LONG.getValue());
170 buffer.putLong(((Long) contextInfo).longValue());
171 remainingBytes = ELEMENT_LENGTH - 8;
172
173 } else if (contextInfo instanceof Double) {
174 buffer.put(DataType.DOUBLE.getValue());
175 buffer.putDouble(((Double) contextInfo).doubleValue());
176 remainingBytes = ELEMENT_LENGTH - 8;
177
178 } else if (contextInfo instanceof Float) {
179 buffer.put(DataType.FLOAT.getValue());
180 buffer.putFloat(((Float) contextInfo).floatValue());
181 remainingBytes = ELEMENT_LENGTH - 4;
182
183 } else if (contextInfo instanceof Byte) {
184 buffer.put(DataType.BYTE.getValue());
185 buffer.put(((Byte) contextInfo).byteValue());
186 remainingBytes = ELEMENT_LENGTH - 1;
187
188 } else if (contextInfo instanceof Short) {
189 buffer.put(DataType.SHORT.getValue());
190 buffer.putShort(((Short) contextInfo).shortValue());
191 remainingBytes = ELEMENT_LENGTH - 2;
192
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;
199
200 } else {
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);
204
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);
209 remainingBytes = 0;
210 } else {
211 buffer.put(strArray);
212 remainingBytes = ELEMENT_LENGTH - strArray.length;
213 }
214 }
215 buffer.position(buffer.position() + remainingBytes);
216 }
217 }
This page took 0.03199 seconds and 3 git commands to generate.