24
24
*/
25
25
package org .graalvm .visualvm .heapviewer .truffle .details ;
26
26
27
+ import java .io .UnsupportedEncodingException ;
28
+ import java .util .Collections ;
29
+ import java .util .HashMap ;
30
+ import java .util .List ;
31
+ import java .util .Map ;
32
+ import java .util .WeakHashMap ;
33
+ import org .graalvm .visualvm .lib .jfluid .heap .Heap ;
27
34
import org .graalvm .visualvm .lib .jfluid .heap .Instance ;
35
+ import org .graalvm .visualvm .lib .jfluid .heap .JavaClass ;
36
+ import org .graalvm .visualvm .lib .jfluid .heap .PrimitiveArrayInstance ;
28
37
import org .graalvm .visualvm .lib .profiler .heapwalk .details .api .DetailsSupport ;
29
38
import org .graalvm .visualvm .lib .profiler .heapwalk .details .spi .DetailsProvider ;
30
39
import org .graalvm .visualvm .lib .profiler .heapwalk .details .spi .DetailsUtils ;
@@ -48,12 +57,19 @@ public class TruffleDetailsProvider extends DetailsProvider.Basic {
48
57
private static final String INSTRUMENT_INFO_MASK = "com.oracle.truffle.api.InstrumentInfo" ; // NOI18N
49
58
private static final String NATIVE_ROOT_MASK = "com.oracle.truffle.nfi.LibFFIFunctionMessageResolutionForeign$ExecuteLibFFIFunctionSubNode$EXECUTERootNode" ; // NOI18N
50
59
private static final String NODE_MASK = "com.oracle.truffle.api.nodes.Node+" ; // NOI18N
60
+ private static final String TSTRING_MASK = "com.oracle.truffle.api.strings.AbstractTruffleString+" ; // NOI18N
61
+ private static final String TS_LONG_MASK = "com.oracle.truffle.api.strings.AbstractTruffleString$LazyLong" ; // NOI18N
62
+ private static final String TS_CONCAT_MASK = "com.oracle.truffle.api.strings.AbstractTruffleString$LazyConcat" ; // NOI18N
63
+
64
+ private static final String TS_ENCODING_CLASS = "com.oracle.truffle.api.strings.TruffleString$Encoding" ; // NOI18N
65
+ private static final Object CACHE_LOCK = new Object ();
66
+ private static WeakHashMap <Heap ,Map <Byte ,Encoding >> CACHE ;
51
67
52
68
public TruffleDetailsProvider () {
53
69
super (DEFAULT_CALL_TARGET_MASK , OPTIMIZED_CALL_TARGET_MASK , OPTIMIZED_CALL_TARGET1_MASK ,
54
70
ENT_OPTIMIZED_CALL_TARGET_MASK , LANG_INFO_MASK , LANG_CACHE_MASK ,
55
71
LANG_CACHE1_MASK , POLYGLOT_MASK , INSTRUMENT_INFO_MASK , NATIVE_ROOT_MASK ,
56
- NODE_MASK );
72
+ NODE_MASK , TSTRING_MASK , TS_LONG_MASK , TS_CONCAT_MASK );
57
73
}
58
74
59
75
public String getDetailsString (String className , Instance instance ) {
@@ -115,6 +131,61 @@ public String getDetailsString(String className, Instance instance) {
115
131
if (NODE_MASK .equals (className )) {
116
132
return DetailsUtils .getInstanceFieldString (instance , "sourceSection" );
117
133
}
134
+ if (TSTRING_MASK .equals (className )) {
135
+ Instance next = instance ;
136
+ do {
137
+ String str = getString (next );
138
+ if (str != null ) {
139
+ return str ;
140
+ }
141
+ next = (Instance ) next .getValueOfField ("next" ); // NOI18N
142
+ } while (next != null && !instance .equals (next ));
143
+
144
+ Object data = instance .getValueOfField ("data" );
145
+ if (data instanceof PrimitiveArrayInstance ) {
146
+ Encoding encoding = getEncoding (instance );
147
+ Byte stride = (Byte )instance .getValueOfField ("stride" ); // NOI18N
148
+ if (stride != null && encoding != null ) {
149
+ byte [] bytes = convertBytes ((PrimitiveArrayInstance )data , encoding .naturalStride , stride );
150
+ try {
151
+ if ("BYTES" .equals (encoding .name )) {
152
+ return new String (bytes , "ISO-8859-1" );
153
+ }
154
+ return new String (bytes , encoding .name );
155
+ } catch (UnsupportedEncodingException ex ) {
156
+ try {
157
+ return new String (bytes , encoding .name .replace ('_' , '-' ));
158
+ } catch (UnsupportedEncodingException ex1 ) {
159
+ return new String (bytes );
160
+ }
161
+ }
162
+ }
163
+ } else {
164
+ return DetailsUtils .getInstanceString ((Instance ) data );
165
+ }
166
+ }
167
+ if (TS_LONG_MASK .equals (className )) {
168
+ return String .valueOf (DetailsUtils .getLongFieldValue (instance , "value" , 0 )); // NOI18N
169
+ }
170
+ if (TS_CONCAT_MASK .equals (className )) {
171
+ Object vall = instance .getValueOfField ("left" ); // NOI18N
172
+ Object valr = instance .getValueOfField ("right" ); // NOI18N
173
+
174
+ String left = DetailsUtils .getInstanceString ((Instance )vall );
175
+
176
+ if (left == null ) {
177
+ return DetailsUtils .getInstanceString ((Instance )valr );
178
+ }
179
+ if (valr == null || left .length () > DetailsUtils .MAX_ARRAY_LENGTH ) {
180
+ return left ;
181
+ }
182
+ String value = left + DetailsUtils .getInstanceString ((Instance )valr );
183
+
184
+ if (value .length () > DetailsUtils .MAX_ARRAY_LENGTH ) {
185
+ return value .substring (0 , DetailsUtils .MAX_ARRAY_LENGTH ) + "..." ; // NOI18N
186
+ }
187
+ return value ;
188
+ }
118
189
return null ;
119
190
}
120
191
@@ -128,4 +199,80 @@ public View getDetailsView(String className, Instance instance) {
128
199
}
129
200
return null ;
130
201
}
202
+
203
+ private String getString (Instance truffleString ) {
204
+ Object data = truffleString .getValueOfField ("data" ); // NOI18N
205
+ if (data instanceof Instance ) {
206
+ Instance idata = (Instance ) data ;
207
+ if (idata .getJavaClass ().getName ().equals (String .class .getName ())) {
208
+ return DetailsUtils .getInstanceString (idata );
209
+ }
210
+ }
211
+ return null ;
212
+ }
213
+
214
+ private Encoding getEncoding (Instance truffleString ) {
215
+ Byte encodingId = (Byte ) truffleString .getValueOfField ("encoding" ); // NOI18N
216
+
217
+ Map <Byte , Encoding > heapCache = getEncodingCache (truffleString );
218
+ Encoding cachedEncoding = heapCache .get (encodingId );
219
+ if (cachedEncoding == null && encodingId != null ) {
220
+ Heap heap = truffleString .getJavaClass ().getHeap ();
221
+ JavaClass encodingClass = heap .getJavaClassByName (TS_ENCODING_CLASS );
222
+ for (Instance encoding : encodingClass .getInstances ()) {
223
+ Byte id = (Byte ) encoding .getValueOfField ("id" ); // NOI18N
224
+
225
+ if (id .equals (encodingId )) {
226
+ cachedEncoding = new Encoding (encoding );
227
+ heapCache .put (encodingId , cachedEncoding );
228
+ }
229
+ }
230
+ }
231
+ return cachedEncoding ;
232
+ }
233
+
234
+ private Map <Byte , Encoding > getEncodingCache (Instance truffleString ) {
235
+ synchronized (CACHE_LOCK ) {
236
+ if (CACHE == null ) {
237
+ CACHE = new WeakHashMap ();
238
+ }
239
+ Heap heap = truffleString .getJavaClass ().getHeap ();
240
+ Map <Byte , Encoding > heapCache = CACHE .get (heap );
241
+ if (heapCache == null ) {
242
+ heapCache = Collections .synchronizedMap (new HashMap <>());
243
+ CACHE .put (heap , heapCache );
244
+ }
245
+ return heapCache ;
246
+ }
247
+ }
248
+
249
+ private byte [] convertBytes (PrimitiveArrayInstance data , byte naturalStride , byte stride ) {
250
+ int inCharSize = 1 << stride ;
251
+ int outCharSize = 1 << naturalStride ;
252
+ int padding = outCharSize - inCharSize ;
253
+ byte [] bytes = new byte [(data .getLength () / inCharSize ) * outCharSize ];
254
+ List <String > values = data .getValues ();
255
+ int op = 0 ;
256
+
257
+ for (int ip = 0 ; ip < values .size ();) {
258
+ for (int j = 0 ; j < inCharSize ; j ++) {
259
+ bytes [op ++] = Byte .valueOf (values .get (ip ++));
260
+ }
261
+ op += padding ;
262
+ }
263
+ return bytes ;
264
+ }
265
+
266
+ private static class Encoding {
267
+
268
+ byte encId ;
269
+ String name ;
270
+ byte naturalStride ;
271
+
272
+ Encoding (Instance encoding ) {
273
+ encId = (Byte ) encoding .getValueOfField ("id" ); // NOI18N
274
+ name = DetailsUtils .getInstanceFieldString (encoding , "name" ); // NOI18N
275
+ naturalStride = (Byte ) encoding .getValueOfField ("naturalStride" ); // NOI18N
276
+ }
277
+ }
131
278
}
0 commit comments