1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package dexfuzz.listeners;
18
19import dexfuzz.ExecutionResult;
20import dexfuzz.Log;
21import dexfuzz.executors.Executor;
22import dexfuzz.program.Mutation;
23import dexfuzz.program.MutationSerializer;
24
25import java.io.BufferedWriter;
26import java.io.FileWriter;
27import java.io.IOException;
28import java.util.Date;
29import java.util.List;
30import java.util.Map;
31
32/**
33 * Logs events to a file.
34 */
35public class LogFileListener extends BaseListener {
36  private BufferedWriter writer;
37  boolean ready = false;
38
39  long successfulVerification;
40  long failedVerification;
41  long failedMutation;
42  long success;
43  long timedOut;
44  long divergence;
45  long selfDivergent;
46  long architectureSplit;
47  long iterations;
48
49  private String logFile;
50
51  public LogFileListener(String logFile) {
52    this.logFile = logFile;
53  }
54
55  @Override
56  public void setup() {
57    try {
58      writer = new BufferedWriter(new FileWriter(logFile));
59      ready = true;
60    } catch (IOException e) {
61      e.printStackTrace();
62    }
63  }
64
65  @Override
66  public void shutdown() {
67    try {
68      writer.close();
69    } catch (IOException e) {
70      e.printStackTrace();
71    }
72    Log.always("Full log in " + logFile);
73  }
74
75  private void write(String msg) {
76    if (!ready) {
77      return;
78    }
79    try {
80      writer.write(msg + "\n");
81    } catch (IOException e) {
82      e.printStackTrace();
83    }
84  }
85
86  @Override
87  public void handleSuccessfulHostVerification() {
88    write("Host verification: SUCCESS");
89    successfulVerification++;
90  }
91
92  @Override
93  public void handleFailedHostVerification(ExecutionResult verificationResult) {
94    write("Host verification: FAILED");
95    failedVerification++;
96  }
97
98  @Override
99  public void handleFailedTargetVerification() {
100    write("Target verification: FAILED");
101    failedVerification++;
102  }
103
104  @Override
105  public void handleIterationStarted(int iteration) {
106    write("--> FUZZ " + (iteration + 1));
107    Date now = new Date(System.currentTimeMillis());
108    write("Time started: " + now.toString());
109    iterations++;
110  }
111
112  @Override
113  public void handleTimeouts(List<Executor> timedOut, List<Executor> didNotTimeOut) {
114    write("Some executors timed out.");
115    write("Timed out:");
116    for (Executor executor : timedOut) {
117      write("  " + executor.getName());
118    }
119    if (!didNotTimeOut.isEmpty()) {
120      write("Did not time out:");
121      for (Executor executor : didNotTimeOut) {
122        write("  " + executor.getName());
123      }
124    }
125    this.timedOut++;
126  }
127
128  @Override
129  public void handleDivergences(Map<String, List<Executor>> outputMap) {
130    write("DIVERGENCE between some executors!");
131    int outputCount = 1;
132    for (List<Executor> executors : outputMap.values()) {
133      write("Output " + outputCount + ":");
134      for (Executor executor : executors) {
135        write("  " + executor.getName());
136      }
137      outputCount++;
138    }
139    divergence++;
140
141    // You are probably interested in reading about these divergences while fuzzing
142    // is taking place, so flush the writer now.
143    try {
144      writer.flush();
145    } catch (IOException e) {
146      e.printStackTrace();
147    }
148  }
149
150  @Override
151  public void handleFuzzingFile(String inputFile) {
152    write("Fuzzing file '" + inputFile + "'");
153  }
154
155  @Override
156  public void handleSeed(long seed) {
157    write("Using " + seed + " for seed.");
158    // Flush the seed as well, so if anything goes wrong we can see what seed lead
159    // to the issue.
160    try {
161      writer.flush();
162    } catch (IOException e) {
163      e.printStackTrace();
164    }
165  }
166
167  @Override
168  public void handleHostVerificationSigabort(ExecutionResult verificationResult) {
169    write("Host verification: SIGABORTED");
170  }
171
172  @Override
173  public void handleSuccess(Map<String, List<Executor>> outputMap) {
174    write("All executors agreed on result.");
175    success++;
176  }
177
178  @Override
179  public void handleDumpOutput(String outputLine, Executor executor) {
180    write(executor.getName() + " OUTPUT:");
181    write(outputLine);
182  }
183
184  @Override
185  public void handleDumpVerify(String verifyLine) {
186    write("VERIFY: " + verifyLine);
187  }
188
189  @Override
190  public void handleMutationStats(String statsString) {
191    write("Mutation Stats: " + statsString);
192  }
193
194  @Override
195  public void handleTiming(String name, float elapsedTime) {
196    write(String.format("'%s': %.3fs", name, elapsedTime));
197  }
198
199  @Override
200  public void handleMutationFail() {
201    write("Mutation process: FAILED");
202    failedMutation++;
203  }
204
205  @Override
206  public void handleSummary() {
207    write("");
208    write("---+++--- SUMMARY ---+++---");
209    write("Fuzzing attempts: " + iterations);
210    write("  Failed verification: " + failedVerification);
211    write("  Failed mutation: " + failedMutation);
212    write("  Timed out: " + timedOut);
213    write("Successful: " + success);
214    write("  Self divergent: " + selfDivergent);
215    write("  Architecture split: " + architectureSplit);
216    write("Produced divergence: " + divergence);
217
218    long truelyDivergent = divergence - (selfDivergent + architectureSplit);
219    long verified = success + timedOut + truelyDivergent;
220
221    write("");
222
223    float verifiedTotalRatio =
224        (((float) (verified)) / iterations) * 100.0f;
225    write(String.format("Percentage Verified/Total: %.3f%%", verifiedTotalRatio));
226
227    float timedOutVerifiedRatio =
228        (((float) timedOut) / (verified)) * 100.0f;
229    write(String.format("Percentage Timed Out/Verified: %.3f%%", timedOutVerifiedRatio));
230
231    float successfulVerifiedRatio =
232        (((float) success) / (verified)) * 100.0f;
233    write(String.format("Percentage Successful/Verified: %.3f%%", successfulVerifiedRatio));
234
235    float divergentVerifiedRatio =
236        (((float) truelyDivergent) / (verified)) * 100.0f;
237    write(String.format("Percentage Divergent/Verified: %.3f%%", divergentVerifiedRatio));
238
239    write("---+++--- SUMMARY ---+++---");
240    write("");
241  }
242
243  @Override
244  public void handleIterationFinished(int iteration) {
245    write("");
246  }
247
248  @Override
249  public void handleSuccessfullyFuzzedFile(String programName) {
250    write("Successfully fuzzed file '" + programName + "'");
251  }
252
253  @Override
254  public void handleSelfDivergence() {
255    write("Golden Executor was self-divergent!");
256    selfDivergent++;
257  }
258
259  @Override
260  public void handleArchitectureSplit() {
261    write("Divergent outputs align with difference in architectures.");
262    architectureSplit++;
263  }
264
265  @Override
266  public void handleMessage(String msg) {
267    write(msg);
268  }
269
270  @Override
271  public void handleMutations(List<Mutation> mutations) {
272    write("Mutations Report");
273    for (Mutation mutation : mutations) {
274      write(MutationSerializer.getMutationString(mutation));
275    }
276  }
277}
278