77import java .io .FileWriter ;
88import java .io .FilenameFilter ;
99import java .io .IOException ;
10- import java .io .UnsupportedEncodingException ;
1110import java .nio .file .Paths ;
1211import java .util .HashMap ;
1312import java .util .List ;
13+ import java .util .Map .Entry ;
1414import java .util .Set ;
1515
1616import javax .xml .parsers .ParserConfigurationException ;
1717
18+ import org .antlr .runtime .RecognitionException ;
1819import org .json .simple .JSONObject ;
1920import org .json .simple .parser .JSONParser ;
2021import org .json .simple .parser .ParseException ;
2627import edu .uniandes .tsdl .mutapk .hashfunction .sha3 .ApkHashOrder ;
2728import edu .uniandes .tsdl .mutapk .hashfunction .sha3 .ApkHashSeparator ;
2829import edu .uniandes .tsdl .mutapk .helper .APKToolWrapper ;
30+ import edu .uniandes .tsdl .mutapk .helper .ASTHelper ;
31+ import edu .uniandes .tsdl .mutapk .helper .CallGraphHelper ;
2932import edu .uniandes .tsdl .mutapk .helper .Helper ;
33+ import edu .uniandes .tsdl .mutapk .model .CallGraphNode ;
3034import edu .uniandes .tsdl .mutapk .model .MutationType ;
35+ import edu .uniandes .tsdl .mutapk .model .SmaliAST ;
3136import edu .uniandes .tsdl .mutapk .model .location .MutationLocation ;
3237import edu .uniandes .tsdl .mutapk .operators .OperatorBundle ;
3338import edu .uniandes .tsdl .mutapk .processors .MutationsProcessor ;
@@ -54,6 +59,7 @@ public class MutAPK {
5459 static String extraPath = "" ;
5560 static String operatorsDir = "" ;
5661 static boolean multithread = true ;
62+ static boolean ignoreDeadCode = true ;
5763 static String selectionStrategy = "" ;
5864
5965 // Optional arguments
@@ -63,7 +69,13 @@ public class MutAPK {
6369 static int confidenceLevel = -1 ;
6470 static int marginError = -1 ;
6571
66- static OperatorBundle operatorBundle ;
72+ static OperatorBundle operatorBundle ;
73+
74+ static HashMap <String , SmaliAST > smaliASTs = new HashMap <String , SmaliAST >();
75+
76+ static HashMap <String , HashMap <String , CallGraphNode >> callGraph = new HashMap <String , HashMap <String , CallGraphNode >>();
77+
78+ static HashMap <String , HashMap <String , CallGraphNode >> deadCode = new HashMap <String , HashMap <String , CallGraphNode >>();
6779
6880 public static void main (String [] args ) {
6981 try {
@@ -83,7 +95,7 @@ public static void main(String[] args) {
8395 }
8496 }
8597
86- public static void runMutAPK (String [] args ) throws NumberFormatException , FileNotFoundException , IOException , ParseException , MutAPKException , InterruptedException , ParserConfigurationException , SAXException {
98+ public static void runMutAPK (String [] args ) throws NumberFormatException , FileNotFoundException , IOException , ParseException , MutAPKException , InterruptedException , ParserConfigurationException , SAXException , RecognitionException {
8799
88100 // Read JSON config file
89101 readConfig (args [0 ]);
@@ -103,15 +115,85 @@ public static void runMutAPK(String[] args) throws NumberFormatException, FileNo
103115 String apkAbsolutePath = APKToolWrapper .openAPK (apkPath , extraPath );
104116 System .out .println ("--------------------------------------" );
105117
118+
119+ //--------------------------------------------
120+ // Run detection phase for AST-based detectors
121+ //--------------------------------------------
122+ // Generate HashMap with all the ASTs
123+ SourceCodeProcessor scp = new SourceCodeProcessor ();
124+ scp .generateASTsMap (extraPath , appName , smaliASTs );
125+ if (ignoreDeadCode ) {
126+
127+ Long initTime = System .currentTimeMillis ();
128+
129+ // Generate the Call Graph
130+ System .out .println ("## Call Graph Results" );
131+ System .out .println ("" );
132+ System .out .println ("Method Type | Amount" );
133+ System .out .println ("----------------|---------" );
134+
135+ callGraph = CallGraphHelper .getCallGraph (smaliASTs );
136+ int cNDC = 0 ;
137+ int cDC = 0 ;
138+
139+ for (Entry <String , HashMap <String , CallGraphNode >> entry : callGraph .entrySet ()) {
140+ for (Entry <String , CallGraphNode > entryy : entry .getValue ().entrySet ()) {
141+ CallGraphNode cGN = entryy .getValue ();
142+ boolean isDeadCode = methodIsDeadCode (cGN );
143+ if (!isDeadCode ) {
144+ cNDC ++;
145+ } else {
146+ if (deadCode .get (cGN .getUnitName ())!=null ) {
147+ deadCode .get (cGN .getUnitName ()).put (cGN .getId (), cGN );
148+ } else {
149+ HashMap <String , CallGraphNode > temp = new HashMap <String , CallGraphNode >();
150+ temp .put (cGN .getId (), cGN );
151+ deadCode .put (cGN .getUnitName (), temp );
152+ }
153+ cDC ++;
154+ }
155+ }
156+ }
157+ System .out .println ("CalledAndDefined | " +cNDC );
158+ System .out .println ("DeadCode | " +cDC );
159+ System .out .println ("\n ----------------------------------" );
160+
161+ // Check Dead Code Methods
162+
163+ // for(Entry<String, HashMap<String, CallGraphNode>> entry : deadCode.entrySet()) {
164+ // System.out.println(entry.getKey());
165+ // for(Entry<String, CallGraphNode> entryy : entry.getValue().entrySet()) {
166+ // System.out.println(" "+entryy.getKey());
167+ // }
168+ // }
169+
170+ // Prune ASTs
171+ for (Entry <String , SmaliAST > entry : smaliASTs .entrySet ()) {
172+ if (deadCode .get (entry .getKey ())!=null ) {
173+ smaliASTs .put (entry .getKey (), ASTHelper .pruneAST (entry .getValue (),deadCode .get (entry .getKey ())));
174+ }
175+ }
176+
177+ Long duration = (System .currentTimeMillis ()-initTime );
178+ System .out .println ("" );
179+ System .out .println ("> It took " +duration +" miliseconds to remove dead code from APK analysis." );
180+
181+
182+ }
183+
106184 // Text-Based operators selected
107185 List <MutationLocationDetector > textBasedDetectors = operatorBundle .getTextBasedDetectors ();
108186
109187 // Run detection phase for Text-based detectors
110188 HashMap <MutationType , List <MutationLocation >> locations = TextBasedDetectionsProcessor .process ("temp" , textBasedDetectors );
111189
112- // Run detection phase for AST-based detectors
113- SourceCodeProcessor scp = new SourceCodeProcessor (operatorBundle );
114- locations .putAll (scp .processFolder ("temp" , extraPath , appName ));
190+ // Generate PFP over pruned ASTs
191+ for (Entry <String , SmaliAST > entry : smaliASTs .entrySet ()) {
192+ SmaliAST temp = entry .getValue ();
193+
194+ HashMap <MutationType , List <MutationLocation >> fileLocations = ASTHelper .findLocations (temp , operatorBundle );
195+ appendLocations (fileLocations , locations );
196+ }
115197
116198 // Report the statistics of the found Potential Fault Locations
117199 Set <MutationType > keys = locations .keySet ();
@@ -126,11 +208,6 @@ public static void runMutAPK(String[] args) throws NumberFormatException, FileNo
126208 System .out .println (list .size () + " | " + mutationType );
127209 }
128210
129- // Check if the amount of PFLocations is lower than the requested by the user
130- if (totalMutants < amountMutants ) {
131- throw new MutAPKException ("The total of mutants need to be greater than the amount of mutants asked" );
132- }
133- System .out .println ("" );
134211
135212 // Build MutationLocation List
136213 List <MutationLocation > mutationLocationList = MutationLocationListBuilder .buildList (locations );
@@ -139,47 +216,90 @@ public static void runMutAPK(String[] args) throws NumberFormatException, FileNo
139216 System .out .println ();
140217 System .out .println ("--------------------------------------" );
141218
142- // Select Selector
143- switch (selectionStrategy ) {
144- case AMOUNT_MUTANTS_SS :
145- SelectorAmountMutantsMethod selectorAmountMutantsMethod = new SelectorAmountMutantsMethod ();
146- SelectorAmountMutants selectorAmountMutants = new SelectorAmountMutants (false , false , totalMutants ,
147- amountMutants );
148- mutationLocationList = selectorAmountMutantsMethod .mutantSelector (locations , selectorAmountMutants );
149- break ;
150- case REPRESENTATIVE_SUBSET_SS :
151- SelectorConfidenceInterval selectorConfidenceInterval = new SelectorConfidenceInterval (true , false ,
152- totalMutants , isRSPerOPerator , confidenceLevel , marginError );
153- SelectorConfidenceIntervalMethod CIMS = new SelectorConfidenceIntervalMethod ();
154- mutationLocationList = CIMS .mutantSelector (locations , selectorConfidenceInterval );
155- break ;
156- default :
157- break ;
219+ if (ignoreDeadCode ) {
220+ // Select Selector
221+ switch (selectionStrategy ) {
222+ case AMOUNT_MUTANTS_SS :
223+ SelectorAmountMutantsMethod selectorAmountMutantsMethod = new SelectorAmountMutantsMethod ();
224+ SelectorAmountMutants selectorAmountMutants = new SelectorAmountMutants (false , false , totalMutants ,
225+ amountMutants );
226+ mutationLocationList = selectorAmountMutantsMethod .mutantSelector (locations , selectorAmountMutants );
227+ break ;
228+ case REPRESENTATIVE_SUBSET_SS :
229+ SelectorConfidenceInterval selectorConfidenceInterval = new SelectorConfidenceInterval (true , false ,
230+ totalMutants , isRSPerOPerator , confidenceLevel , marginError );
231+ SelectorConfidenceIntervalMethod CIMS = new SelectorConfidenceIntervalMethod ();
232+ mutationLocationList = CIMS .mutantSelector (locations , selectorConfidenceInterval );
233+ break ;
234+ default :
235+ break ;
236+ }
237+ System .out .println ("" );
238+
239+ System .out .println ("## Mutation Process Log" );
240+ System .out .println ();
241+ System .out .println ("```sh" );
242+
243+ // Execute mutation phase
244+ MutationsProcessor mProcessor = new MutationsProcessor ("temp" , appName , mutantsFolder );
245+
246+ // Create de apkhash for the base folder
247+ File manifest = new File (apkAbsolutePath + File .separator + "AndroidManifest.xml" );
248+ File smali = new File (apkAbsolutePath + File .separator + "smali" );
249+ File resource = new File (apkAbsolutePath + File .separator + "res" );
250+
251+
252+ // Create ApkHashSeparator
253+ ApkHashSeparator apkHashSeparator = mProcessor .generateApkHashSeparator (manifest , smali , resource , 0 );
254+ // Add the base apkHashSeparator
255+ ApkHashOrder .getInstance ().setApkHashSeparator (apkHashSeparator );
256+
257+ if (multithread ) {
258+ mProcessor .processMultithreaded (mutationLocationList , extraPath , apkName );
259+ } else {
260+ mProcessor .process (mutationLocationList , extraPath , apkName );
261+ }
158262 }
159- System .out .println ("" );
160-
161- System .out .println ("## Mutation Process Log" );
162- System .out .println ();
163- System .out .println ("```sh" );
164263
165- // Execute mutation phase
166- MutationsProcessor mProcessor = new MutationsProcessor ("temp" , appName , mutantsFolder );
264+ }
167265
168- // Create de apkhash for the base folder
169- File manifest = new File (apkAbsolutePath + File .separator + "AndroidManifest.xml" );
170- File smali = new File (apkAbsolutePath + File .separator + "smali" );
171- File resource = new File (apkAbsolutePath + File .separator + "res" );
266+ private static boolean methodIsDeadCode (CallGraphNode cGN ) {
172267
268+ String [] exceptions = new String [] {
269+ "<init>" ,"<clinit>" ,"init" ,"onClick" ,"onCreate" ,"onOptionsItemSelected" ,"onCreateOptionsMenu" ,"onResume" ,"update" ,
270+ "query" ,"getType" ,"insert" ,"doInBackground" ,"onPostExecute" ,"delete" ,"clone" ,"onCreateDialog" ,"onCancel" ,"onRestoreInstanceState" ,
271+ "onSaveInstanceState" ,"onRetainNonConfigurationInstance" ,"run" ,"getParent" ,"onNothingSelected" ,"onItemSelected" ,"onProgressUpdate" ,
272+ "onPreExecute" , "onReceive" ,"toString" ,"onItemClick" ,"getUri" ,"onPreferenceClick" ,"onTabChanged" ,"getDropDownView" ,"getCount" ,
273+ "getViewTypeCount" ,"getView" ,"registerDataSetObserver" ,"saved" ,"isEnabled" ,"onActivityResult" ,"unregisterDataSetObserver" ,"attach" ,
274+ "onChange" ,"isIntentAvailable" ,"onDateChanged" ,"onContextItemSelected" , "onCreateContextMenu"
275+ };
173276
174- // Create ApkHashSeparator
175- ApkHashSeparator apkHashSeparator = mProcessor .generateApkHashSeparator (manifest , smali , resource , 0 );
176- // Add the base apkHashSeparator
177- ApkHashOrder .getInstance ().setApkHashSeparator (apkHashSeparator );
277+ if (cGN .getCallers ().size ()>0 ) {
278+ return false ;
279+ }
178280
179- if (multithread ) {
180- mProcessor .processMultithreaded (mutationLocationList , extraPath , apkName );
181- } else {
182- mProcessor .process (mutationLocationList , extraPath , apkName );
281+ for (int i = 0 ; i < exceptions .length ; i ++) {
282+ if (cGN .getId ().startsWith (exceptions [i ]+"(" )) {
283+ return false ;
284+ }
285+ }
286+ return true ;
287+
288+ }
289+
290+ private static void appendLocations (HashMap <MutationType , List <MutationLocation >> source , HashMap <MutationType , List <MutationLocation >> target ){
291+
292+ for (Entry <MutationType , List <MutationLocation >> entry : source .entrySet ()){
293+ List <MutationLocation > sourceLocations = source .get (entry .getKey ());
294+ List <MutationLocation > targetLocations = target .get (entry .getKey ());
295+
296+ if (targetLocations != null ){
297+ targetLocations .addAll (sourceLocations );
298+ } else {
299+ targetLocations = sourceLocations ;
300+ }
301+
302+ target .put (entry .getKey (), targetLocations );
183303 }
184304
185305 }
@@ -194,9 +314,10 @@ private static void readConfig(String configFilePath) throws NumberFormatExcepti
194314 appName = getVariableValuesString (jsonObject , "appName" );
195315 mutantsFolder = getVariableValuesString (jsonObject , "mutantsFolder" );
196316 multithread = Boolean .valueOf (getVariableValuesString (jsonObject , "multithreadExec" ));
317+ ignoreDeadCode = Boolean .valueOf (getOptionalVariableValuesString (jsonObject , "ignoreDeadCode" ));
197318 selectionStrategy = getVariableValues (jsonObject , "selectionStrategy" );
198- operatorsDir = getOptionalVariableValuesString (jsonObject , "operatorsDir" );
199- extraPath = getOptionalVariableValuesString (jsonObject , "extraPath" );
319+ operatorsDir = getVariableValuesString (jsonObject , "operatorsDir" );
320+ extraPath = getVariableValuesString (jsonObject , "extraPath" );
200321
201322 // Impreme valor por defecto
202323 System .out .println ("" );
@@ -210,6 +331,7 @@ private static void readConfig(String configFilePath) throws NumberFormatExcepti
210331 System .out .println ("extraPath | " + extraPath );
211332 System .out .println ("operatorsDir | " + operatorsDir );
212333 System .out .println ("multithread | " + multithread );
334+ System .out .println ("ignoreDeadCode | " + ignoreDeadCode );
213335 System .out .println ("selectionStrategy | " + selectionStrategy );
214336
215337 JSONObject selectionParameters = (JSONObject ) jsonObject .get ("selectionParameters" );
@@ -246,15 +368,14 @@ private static void readConfig(String configFilePath) throws NumberFormatExcepti
246368 }
247369
248370 private static void checkAndPrepareParameters () throws MutAPKException , IOException {
249- // TODO Auto-generated method stub
250371
251372 // Preprocess paths to fit to OS filsesystem format
252373 String os = System .getProperty ("os.name" ).toLowerCase ();
253374 if (os .indexOf ("win" ) >= 0 ) {
254375 mutantsFolder = mutantsFolder .replaceFirst ("/" , File .separator +File .separator ) +File .separator ;
255376 apkPath = apkPath .replaceAll ("/" , File .separator +File .separator );
256377 apkName = apkPath .substring (apkPath .lastIndexOf ("\\ " ));
257-
378+
258379 extraPath = extraPath .equals ("" )?"" :extraPath .replaceAll ("/" , File .separator +File .separator ) + File .separator ;
259380 operatorsDir = operatorsDir .equals ("" )?"" :operatorsDir .replaceAll ("/" , File .separator +File .separator ) + File .separator ;
260381 } else {
@@ -281,7 +402,7 @@ private static void checkAndPrepareParameters() throws MutAPKException, IOExcept
281402 }
282403 System .out .println (operFolderFO .getCanonicalPath ().toString ());
283404 File [] properties = operFolderFO .listFiles (new FilenameFilter () {
284-
405+
285406 @ Override
286407 public boolean accept (File dir , String name ) {
287408 return name .endsWith ("operators.properties" );
@@ -296,7 +417,7 @@ public boolean accept(File dir, String name) {
296417 throw new MutAPKException ("Path to extra folder is not correct. The path does not exist." );
297418 }
298419 File [] extraFO = extraFolderFO .listFiles (new FilenameFilter () {
299-
420+
300421 @ Override
301422 public boolean accept (File dir , String name ) {
302423 return ( name .endsWith ("apktool.jar" ) || name .endsWith ("uber-apk-signer.jar" ) || name .endsWith ("materialistic.jks" ));
@@ -345,7 +466,7 @@ private static String getOptionalVariableValuesString(JSONObject jsonObject, Str
345466 if (temp != null ) {
346467 return temp ;
347468 } else {
348- return "" ;
469+ return "true " ;
349470 }
350471 }
351472
0 commit comments