1 package iotruntime.master;
4 import java.io.FileInputStream;
5 import java.io.FileOutputStream;
6 import java.io.IOException;
7 import java.io.InputStream;
8 import java.util.Arrays;
9 import java.util.Collection;
10 import java.util.HashMap;
11 import java.util.HashSet;
12 import java.util.Iterator;
14 import java.util.Properties;
17 import java.lang.reflect.*;
18 import java.util.regex.Matcher;
19 import java.util.regex.Pattern;
22 import org.objectweb.asm.AnnotationVisitor;
23 import org.objectweb.asm.Attribute;
24 import org.objectweb.asm.ClassReader;
25 import org.objectweb.asm.ClassWriter;
26 import org.objectweb.asm.ClassVisitor;
27 import org.objectweb.asm.FieldVisitor;
28 import org.objectweb.asm.MethodVisitor;
29 import org.objectweb.asm.Opcodes;
30 import org.objectweb.asm.signature.SignatureReader;
31 import org.objectweb.asm.signature.SignatureVisitor;
32 import org.objectweb.asm.TypePath;
35 import iotruntime.slave.IoTSet;
36 import iotruntime.slave.IoTRelation;
38 /** Class ClassRuntimeInstrumenterMaster helps instrument the bytecode.
39 * This class basically reads annotations @config in the code,
40 * allocates the right objects, and runs the function init in
41 * the instrumented program code.
43 * @author Rahmadi Trimananda <rahmadi.trimananda @ uci.edu>
47 public class ClassRuntimeInstrumenterMaster extends ClassVisitor implements Opcodes {
50 * ClassRuntimeInstrumenterMaster class properties
52 * At most we will have 3 class types
53 * IoTSet<class_type> -> type 1: Set and type 2: class_type
54 * IoTRelation<class_type1, class_type2>
55 * -> type 1: IoTRelation, type 2: class_type1, type 3: class_type2
56 * Store class type for annotation processing in strClassType
58 private String[] strClassType;
59 private static int iCntClassType = 0;
60 private String strInstrumentedClassName;
61 private String strFieldName;
62 private HashMap<String,Object> hmObj;
63 private String strObjectID;
64 private boolean bVerbose;
67 * ClassRuntimeInstrumenterMaster class constants
69 private static final int INT_NUM_CLASS_TYPE = 3;
70 private static final String STR_IOT_ANNOTATION_SIGNATURE = "Liotchecker/qual/config;";
71 private static final String STR_IOT_SET_SIGNATURE = "Liotruntime/slave/IoTSet;";
72 private static final String STR_IOT_RELATION_SIGNATURE = "Liotruntime/slave/IoTRelation;";
73 private static final String STR_IOT_CONSTRAINT_SIGNATURE = "Liotcode/annotation/constraint;";
74 private static final String STR_CONFIG_EXTENSION = ".config";
75 private static final String STR_CONFIG_FILE_PATH = "mysql/";
80 public ClassRuntimeInstrumenterMaster(final ClassVisitor cv, String strObjID, boolean _bVerbose) {
84 // Initialize strClassType
85 strClassType = new String[INT_NUM_CLASS_TYPE];
86 for(int i=0; i<INT_NUM_CLASS_TYPE; i++) {
87 strClassType[i] = new String();
89 strInstrumentedClassName = "";
91 hmObj = new HashMap<String,Object>();
92 strObjectID = strObjID;
97 * Make a visit to a class. This is the method called first.
100 public void visit(int version, int access, String name,
101 String signature, String superName, String[] interfaces) {
103 // Get the name of this instrumented class
104 strInstrumentedClassName = name;
106 super.visit(version, access, name, signature, superName, interfaces);
110 * Visit class annotation
113 public AnnotationVisitor visitAnnotation(String desc,
116 return super.visitAnnotation(desc, visible);
120 * Make a visit to a field.
123 public FieldVisitor visitField(int access, String name,
124 String desc, String signature, Object value) {
127 super.visitField(access, name, desc, signature, value);
129 if (signature != null) {
130 new SignatureReader(signature)
131 .accept(new SignatureRuntimeInstrumenter(strClassType));
134 return new FieldRuntimeInstrumenter(signature, desc);
138 * Visit a method when a method is encountered
141 public MethodVisitor visitMethod(int access, String name,
142 String desc, String signature, String[] exceptions) {
144 super.visitMethod(access, name, desc, signature, exceptions);
145 return new MethodRuntimeInstrumenter();
149 * A subclass that visits signature. This is called when traversing a class
150 * and a signature visit is needed.
152 private class SignatureRuntimeInstrumenter extends SignatureVisitor {
154 public SignatureRuntimeInstrumenter(String[] strCType) {
157 // Counter for extracting +class type
160 // Initializing input String array
161 for (int i=0; i<INT_NUM_CLASS_TYPE; i++) {
162 strClassType[i] = strCType[i];
167 public void visitClassType(final String name) {
169 strClassType[iCntClassType++] = name;
170 super.visitClassType(name);
175 * A helper function that returns class name from class type pattern
177 * e.g. get "ProximitySensor" from "iotcode/ProximitySensor"
178 * With this regex pattern,
179 * group(0) gives the entire input string, e.g. "iotcode/ProximitySensor"
180 * group(1) gives just the front string, e.g. "iotcode"
181 * group(2) gives just the slash, e.g. "/"
182 * group(3) gives just the back string, e.g. "ProximitySensor"
184 * @param strInput String to be matched by regex
187 public String getClassName(String strInput) {
189 Pattern pattern = Pattern.compile("(\\S+)(\\/)(\\S+)");
190 Matcher matcher = pattern.matcher(strInput);
191 if (matcher.find()) {
192 return matcher.group(3);
198 * A class that instruments instructions in method through visiting a method.
199 * Instruction and variable types can be extracted.
201 class FieldRuntimeInstrumenter extends FieldVisitor {
203 private String strFieldSignature;
204 private String strFieldDesc;
206 public FieldRuntimeInstrumenter(String strFSign, String strFDesc) {
209 strFieldSignature = strFSign;
210 strFieldDesc = strFDesc;
214 * This method visits annotation, so we can instrument @config here
217 * 1) Check whether it is IoTSet or IoTRelation declaration
218 * 2) Create a new Set class instrumenter
219 * strClassType[0] will contain "IoTSet" or "IoTRelation"
220 * strClassType[1] will contain the first specific class
221 * e.g. IoTSet<ProximitySensor> -> strClassType[1] == "ProximitySensor"
222 * strClassType[2] will contain the other specific class
223 * (doesn't apply to IoTSet)
224 * e.g. IoTRelation<ProximitySensor, LightBulb>
225 * -> strClassType[1] == "ProximitySensor"
226 * -> strClassType[2] == "LightBulb"
227 * 3) Instantiate field objects, e.g. IoTSet or IoTRelation class object
228 * 4) Instantiate a new object of the instrumented class, e.g. AcmeThermostat
229 * 5) Initialize the field of the instrumented class objects with actual field objects
230 * 6) Run the init() function of the instrumented class
233 * @param visible boolean
234 * @return AnnotationVisitor
237 * This method visits annotation that is meta-annotated as TYPE_USE, so we can instrument @config here
240 public AnnotationVisitor visitAnnotation(String desc,
243 //RuntimeOutput.print("ClassRuntimeInstrumenterMaster@AnnotationVisitor: " + desc, bVerbose);
245 // Check annotation description @config
246 if(desc.equals(STR_IOT_ANNOTATION_SIGNATURE)) {
247 // Check if this is a Set class, then process it
248 if (strFieldDesc.equals(STR_IOT_SET_SIGNATURE)) {
249 RuntimeOutput.print("@config: IoTSet is detected!", bVerbose);
250 SetInstrumenter setInstrument = new
251 SetInstrumenter(getClassName(strClassType[1]),
252 STR_CONFIG_FILE_PATH + strFieldName + STR_CONFIG_EXTENSION, strObjectID, bVerbose);
254 hmObj.put(strFieldName, setInstrument);
255 // Check if this is a Relation class, then process it
256 } else if (strFieldDesc.equals(STR_IOT_RELATION_SIGNATURE)) {
257 RuntimeOutput.print("@config: IoTRelation is detected!", bVerbose);
258 RelationInstrumenter relInstrument = new
259 RelationInstrumenter(getClassName(strClassType[1]),
260 getClassName(strClassType[2]), STR_CONFIG_FILE_PATH +
261 strFieldName + STR_CONFIG_EXTENSION, bVerbose);
263 hmObj.put(strFieldName, relInstrument);
264 } else if (strFieldDesc.equals(STR_IOT_CONSTRAINT_SIGNATURE)) {
265 // TO DO: PROCESS THIS CONSTRAINT ANNOTATION
266 RuntimeOutput.print("ClassRuntimeInstrumenterMaster@AnnotationTypeVisitor: Constraint annotation detected!", bVerbose);
269 throw new Error("ClassRuntimeInstrumenterMaster@AnnotationTypeVisitor: " + strFieldDesc + " not recognized!");
272 return super.visitAnnotation(desc, visible);
277 * A subclass that instruments instructions in method through visiting a method.
278 * Instruction and variable types can be extracted.
280 protected class MethodRuntimeInstrumenter extends MethodVisitor {
282 public MethodRuntimeInstrumenter() {
289 * A method that returns HashMap hmObj
291 * @return HashMap<String,Object>
293 public HashMap<String,Object> getFieldObjects() {
298 public static void main(String[] args) {
301 // Instrumenting one file
302 FileInputStream is = new FileInputStream(args[0]);
304 ClassReader cr = new ClassReader(is);
305 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
306 ClassRuntimeInstrumenterMaster crim = new ClassRuntimeInstrumenterMaster(cw, "LB2", true);
309 // Get the object and the class names
310 HashMap<String,Object> hm = crim.getFieldObjects();
311 for(Map.Entry<String,Object> map : hm.entrySet()) {
312 System.out.println(map.getKey());
313 System.out.println(map.getValue().getClass().getName());
314 SetInstrumenter si = (SetInstrumenter) map.getValue();
315 System.out.println("Field values: " + Arrays.toString(si.fieldValues(0)));
316 System.out.println("Field classes: " + Arrays.toString(si.fieldClasses(0)));
317 System.out.println("Field object ID: " + si.fieldObjectID(0));
318 System.out.println("Field entry type: " + si.fieldEntryType("LB1"));
319 System.out.println("Field entry type: " + si.fieldEntryType("LB2"));
320 System.out.println("Number of rows: " + si.numberOfRows());
323 } catch (IOException ex) {
324 System.out.println("ClassRuntimeInstrumenterMaster@RunInstrumentation: IOException: "
326 ex.printStackTrace();