1 package no.uio.ifi.refaktor.statistics;
3 import java.io.BufferedOutputStream;
5 import java.io.FileNotFoundException;
6 import java.io.FileOutputStream;
7 import java.io.PrintWriter;
9 import java.util.HashMap;
10 import java.util.LinkedList;
11 import java.util.List;
14 import no.uio.ifi.refaktor.analyze.ExtractAndMoveMethodCandidate;
15 import no.uio.ifi.refaktor.analyze.analyzers.CompilationUnitWideExtractAndMoveMethodAnalyzer;
16 import no.uio.ifi.refaktor.analyze.analyzers.ExtractAndMoveMethodAnalyzer;
17 import no.uio.ifi.refaktor.analyze.analyzers.PackageWideExtractAndMoveMethodAnalyzer;
18 import no.uio.ifi.refaktor.analyze.analyzers.SearchBasedExtractAndMoveMethodAnalyzer;
19 import no.uio.ifi.refaktor.analyze.analyzers.TypeWideExtractAndMoveMethodAnalyzer;
20 import no.uio.ifi.refaktor.change.executors.ExtractAndMoveMethodExecutor;
21 import no.uio.ifi.refaktor.change.executors.ExtractMethodPostExecutionResources;
22 import no.uio.ifi.refaktor.change.performers.RefactoringPerformer;
23 import no.uio.ifi.refaktor.debugging.RefaktorDebug;
24 import no.uio.ifi.refaktor.exceptions.RefaktorException;
25 import no.uio.ifi.refaktor.statistics.reports.Report;
26 import no.uio.ifi.refaktor.statistics.reports.SimpleReport;
28 import org.eclipse.jdt.core.IMethod;
29 import org.eclipse.jdt.core.JavaModelException;
30 import org.eclipse.ltk.core.refactoring.Refactoring;
32 privileged public aspect StatisticsAspect {
34 public class Statistics {
36 private final String projectName;
37 private final Map<String, Integer> successfullyPerformedRefactorings;
38 private final Map<String, Integer> notPerformedRefactorings;
39 private final long snapshotTimeStamp;
41 private long startOfStatisticsTimeStamp;
42 private long analysisTimeStamp;
43 private long totalAnalysisTimeInMillis;
44 private long totalChangeTimeInMillis;
45 private int methodCount;
46 private int typeCount;
47 private int compilationUnitCount;
48 private int packageCount;
49 private int extractAndMoveMethodExecutedCount;
50 private int extractAndMoveMethodNotExecutedCount;
51 private int extractAndMoveMethodResultFoundCount;
52 private int extractAndMoveMethodResultNotFoundCount;
53 private int extractAndMoveMethodResultGeneratedCount;
54 private int selectionsAnalyzedCount;
55 private List<ExtractAndMoveMethodExecutionResult> extractAndMoveMethodExecutionResults;
56 private List<ExtractAndMoveMethodExecutionResult> extractAndMoveMethodNotFullyExecutedExecutionResults;
58 private Statistics(String projectName) {
59 this.projectName = projectName;
60 startOfStatisticsTimeStamp = timeStamp();
61 methodCount = typeCount = compilationUnitCount = packageCount = 0;
62 successfullyPerformedRefactorings = new HashMap<String, Integer>();
63 notPerformedRefactorings = new HashMap<String, Integer>();
64 extractAndMoveMethodExecutedCount = extractAndMoveMethodNotExecutedCount = 0;
65 extractAndMoveMethodResultFoundCount = extractAndMoveMethodResultNotFoundCount = 0;
66 extractAndMoveMethodResultGeneratedCount = selectionsAnalyzedCount = 0;
67 snapshotTimeStamp = timeStamp();
68 analysisTimeStamp = timeStamp();
69 totalAnalysisTimeInMillis = 0;
70 totalChangeTimeInMillis = 0;
71 extractAndMoveMethodExecutionResults = new LinkedList<ExtractAndMoveMethodExecutionResult>();
72 extractAndMoveMethodNotFullyExecutedExecutionResults = new LinkedList<ExtractAndMoveMethodExecutionResult>();
75 private Statistics(Statistics statistics) {
76 this.projectName = statistics.projectName;
77 this.startOfStatisticsTimeStamp = statistics.startOfStatisticsTimeStamp;
78 this.successfullyPerformedRefactorings = new HashMap<String, Integer>(statistics.successfullyPerformedRefactorings);
79 this.notPerformedRefactorings = new HashMap<String, Integer>(statistics.notPerformedRefactorings);
80 this.methodCount = statistics.methodCount;
81 this.typeCount = statistics.typeCount;
82 this.compilationUnitCount = statistics.compilationUnitCount;
83 this.packageCount = statistics.packageCount;
84 this.extractAndMoveMethodExecutedCount = statistics.extractAndMoveMethodExecutedCount;
85 this.extractAndMoveMethodNotExecutedCount = statistics.extractAndMoveMethodNotExecutedCount;
86 this.extractAndMoveMethodResultFoundCount = statistics.extractAndMoveMethodResultFoundCount;
87 this.extractAndMoveMethodResultNotFoundCount = statistics.extractAndMoveMethodResultNotFoundCount;
88 this.extractAndMoveMethodResultGeneratedCount = statistics.extractAndMoveMethodResultGeneratedCount;
89 this.selectionsAnalyzedCount = statistics.selectionsAnalyzedCount;
90 this.snapshotTimeStamp = timeStamp();
91 this.analysisTimeStamp = statistics.analysisTimeStamp;
92 this.totalAnalysisTimeInMillis = statistics.totalAnalysisTimeInMillis;
93 this.totalChangeTimeInMillis = statistics.totalChangeTimeInMillis;
94 this.extractAndMoveMethodExecutionResults =
95 new LinkedList<ExtractAndMoveMethodExecutionResult>(statistics.extractAndMoveMethodExecutionResults);
96 this.extractAndMoveMethodNotFullyExecutedExecutionResults =
97 new LinkedList<ExtractAndMoveMethodExecutionResult>(statistics.extractAndMoveMethodNotFullyExecutedExecutionResults);
100 private void noteLastAnalysisTime() {
101 analysisTimeStamp = timeStamp();
104 private String timeSinceStart(long timeStamp) {
105 return timeBetween(startOfStatisticsTimeStamp, timeStamp);
108 private String timeBetween(long startTimeStamp, long endTimeStamp) {
109 long durationInMillis = endTimeStamp - startTimeStamp;
111 if (durationInMillis > 999) {
112 long secondsSinceAnalysisStart = Math.round(durationInMillis/1000.0);
113 return secondsSinceAnalysisStart/60 + "m" + secondsSinceAnalysisStart % 60 + "s";
115 return durationInMillis + "ms";
119 private String timeUsed(long timeInMillis) {
120 return timeBetween(0, timeInMillis);
123 private Statistics generateSnapshot() {
124 return new Statistics(this);
128 public String toString() {
129 return generateReportString(new SimpleReport());
132 public void generateReportFile() {
133 PrintWriter printWriter = null;
135 File reportFile = createReportFile(SimpleReport.FILENAME_EXTENSION);
136 printWriter = new PrintWriter(new BufferedOutputStream(new FileOutputStream(reportFile)));
137 printWriter.print(generateReportString(new SimpleReport()));
138 } catch (FileNotFoundException e) {
139 throw new RefaktorException(e);
141 if (printWriter != null)
146 private File createReportFile(String filenameExtension) {
147 File reportDir = new File(System.getProperty("refaktor.reports", "/tmp"));
149 if (!reportDir.exists()) {
150 boolean success = reportDir.mkdirs();
152 RefaktorDebug.log("Could not create directory: " + reportDir);
156 String filenamePrefix = System.getProperty("refaktor.reportPrefix", "refaktor_statistics_report");
158 int serialNumber = 0;
161 reportFile = new File(reportDir, filenamePrefix + (serialNumber == 0 ? "" : "_" + serialNumber) + filenameExtension);
163 } while(reportFile.exists());
167 private String generateReportString(Report report) {
168 report.setHeading("Statistics (Report created " + new Date().toString() + ".)");
170 report.addData("Project name", projectName);
171 report.addData("Total time used", timeSinceStart(snapshotTimeStamp));
173 long totalTimeInMillis = snapshotTimeStamp - startOfStatisticsTimeStamp;
174 long miscellaneousTimeInMillis = totalTimeInMillis - totalAnalysisTimeInMillis - totalChangeTimeInMillis;
176 report.addData("Analysis time used", timeUsed(totalAnalysisTimeInMillis) + percentOf(totalAnalysisTimeInMillis, totalTimeInMillis));
177 report.addData("Change time used", timeUsed(totalChangeTimeInMillis) + percentOf(totalChangeTimeInMillis, totalTimeInMillis));
178 report.addData("Time used on miscellaneous tasks", timeUsed(miscellaneousTimeInMillis) + percentOf(miscellaneousTimeInMillis, totalTimeInMillis));
182 report.addSection("Analysis (numbers of each category analyzed)");
184 if (packageCount > 0)
185 report.addData("Packages", packageCount);
187 if (compilationUnitCount > 0)
188 report.addData("Compilation units", compilationUnitCount);
191 report.addData("Types", typeCount);
193 report.addData("Methods", methodCount);
194 report.addData("Text selections", selectionsAnalyzedCount);
197 report.addSection("Analysis results for the Extract and Move Method refactoring");
199 report.addData("Number of methods chosen as candidates", extractAndMoveMethodResultFoundCount);
200 report.addData("Number of methods NOT chosen as candidates", extractAndMoveMethodResultNotFoundCount);
201 report.addData("Number of possible candidate selections (multiple per method)", extractAndMoveMethodResultGeneratedCount);
204 if (successfullyPerformedRefactorings.size() > 0) {
205 report.addSection("Primitive refactorings executed");
207 for (String refactoringName: successfullyPerformedRefactorings.keySet()) {
209 report.addSubsection(refactoringName);
211 Integer successCount = successfullyPerformedRefactorings.get(refactoringName);
212 report.addData("Performed", successCount);
214 Integer notPerformedCount = notPerformedRefactorings.get(refactoringName);
215 if (notPerformedCount != null) {
216 report.addData("Not performed", notPerformedCount);
217 report.addData("Total", successCount + notPerformedCount);
223 if (extractAndMoveMethodExecutedCount > 0 || extractAndMoveMethodNotExecutedCount > 0) {
224 report.addSection("Extract and Move Method refactorings");
226 if (extractAndMoveMethodExecutedCount > 0)
227 report.addData("Fully executed", extractAndMoveMethodExecutedCount);
229 if (extractAndMoveMethodNotExecutedCount > 0)
230 report.addData("Not fully executed", extractAndMoveMethodNotExecutedCount);
232 report.addData("Total attempts", extractAndMoveMethodExecutedCount + extractAndMoveMethodNotExecutedCount);
236 if (extractAndMoveMethodExecutionResults.size() > 0) {
237 report.addSection("Fully executed Extract and Move Methods");
238 for (ExtractAndMoveMethodExecutionResult result: extractAndMoveMethodExecutionResults)
239 result.addToReport(report);
243 if (extractAndMoveMethodNotFullyExecutedExecutionResults.size() > 0) {
244 report.addSection("Not fully executed Extract and Move Methods");
245 for (ExtractAndMoveMethodExecutionResult result: extractAndMoveMethodNotFullyExecutedExecutionResults)
246 result.addToReport(report);
249 return report.generateReport();
252 private String percentOf(long timeInMillis, long totalTimeInMillis) {
253 return " (" + Math.round(((timeInMillis*100.0)/totalTimeInMillis)) + "%)";
257 private Statistics statistics;
258 private Map<String, Long> methodAnalysisTimeStamps;
259 private Map<Refactoring, Long> changeTimeStamps;
261 private long timeStamp() {
262 return System.currentTimeMillis();
265 public StatisticsAspect() {
266 this.statistics = new Statistics("no name");
267 this.methodAnalysisTimeStamps = new HashMap<String, Long>();
268 this.changeTimeStamps = new HashMap<Refactoring, Long>();
271 public static void init() {
275 public static void init(String projectName) {
276 aspectOf().privateInit(projectName);
279 private void privateInit(String projectName) {
280 this.statistics = new Statistics(projectName);
281 this.methodAnalysisTimeStamps = new HashMap<String, Long>();
282 this.changeTimeStamps = new HashMap<Refactoring, Long>();
285 public static Statistics getSnapshot() {
286 return aspectOf().statistics.generateSnapshot();
289 pointcut selectionAnalyze() :
290 call(* ExtractAndMoveMethodAnalyzer.analyze()) ;
292 pointcut methodAnalyze(SearchBasedExtractAndMoveMethodAnalyzer analyzer) :
293 call(* SearchBasedExtractAndMoveMethodAnalyzer.analyze()) && target(analyzer);
295 pointcut typeAnalyze() :
296 call(* TypeWideExtractAndMoveMethodAnalyzer.analyze());
298 pointcut compilationUnitAnalyze() :
299 call(* CompilationUnitWideExtractAndMoveMethodAnalyzer.analyze());
301 pointcut packageAnalyze() :
302 call(* PackageWideExtractAndMoveMethodAnalyzer.analyze());
304 pointcut performRefactoring(Refactoring refactoring) :
305 call(* RefactoringPerformer+.performRefactoring(Refactoring)) && args(refactoring);
307 pointcut extractAndMoveExecuted(ExtractAndMoveMethodExecutor executor) :
308 call(* ExtractAndMoveMethodExecutor.execute(..)) && target(executor);
310 pointcut analysisResultCreated() :
311 call(public ExtractAndMoveMethodCandidate.new(..));
313 after() : selectionAnalyze() {
314 statistics.selectionsAnalyzedCount++;
317 before(SearchBasedExtractAndMoveMethodAnalyzer analyzer) : methodAnalyze(analyzer) {
318 noteStartOfAnalysisForMethod(analyzer.method);
321 after(SearchBasedExtractAndMoveMethodAnalyzer analyzer) : methodAnalyze(analyzer) {
322 statistics.methodCount++;
323 noteEndOfAnalysisForMethod(analyzer.method);
324 statistics.noteLastAnalysisTime();
325 debugPrintMethodAnalysisProgress(analyzer.method);
328 private void noteStartOfAnalysisForMethod(IMethod method) {
329 methodAnalysisTimeStamps.put(method.getKey(), timeStamp());
332 private void noteEndOfAnalysisForMethod(IMethod method) {
333 generated_5229367356361043229(method);
336 public void generated_5229367356361043229(IMethod method) {
337 long methodTimeStamp = methodAnalysisTimeStamps.remove(method.getKey());
338 statistics.totalAnalysisTimeInMillis += timeStamp() - methodTimeStamp;
341 private void debugPrintMethodAnalysisProgress(IMethod method) {
342 generated_8558438568074782975(method);
345 public void generated_8558438568074782975(IMethod method) {
347 RefaktorDebug.println("#" + statistics.methodCount + " (" + statistics.timeSinceStart(timeStamp()) + "): "
348 + method.getDeclaringType().getElementName() + "." + method.getElementName() + " (" + "offset: "
349 + method.getSourceRange().getOffset() + ", length: " + method.getSourceRange().getLength() + ")");
350 } catch (JavaModelException e) {
351 RefaktorDebug.println("No info about " + method.getElementName());
355 after(SearchBasedExtractAndMoveMethodAnalyzer analyzer) returning(): methodAnalyze(analyzer) {
356 statistics.extractAndMoveMethodResultFoundCount++;
359 after(SearchBasedExtractAndMoveMethodAnalyzer analyzer) throwing(): methodAnalyze(analyzer) {
360 statistics.extractAndMoveMethodResultNotFoundCount++;
363 after(): typeAnalyze() {
364 statistics.typeCount++;
367 after(): compilationUnitAnalyze() {
368 statistics.compilationUnitCount++;
371 after(): packageAnalyze() {
372 statistics.packageCount++;
375 before(Refactoring refactoring) : performRefactoring(refactoring) {
376 noteStartOfRefactoring(refactoring);
379 after(Refactoring refactoring) : performRefactoring(refactoring) {
380 noteEndOfChange(refactoring);
383 private void noteStartOfRefactoring(Refactoring refactoring) {
384 changeTimeStamps.put(refactoring, timeStamp());
387 private void noteEndOfChange(Refactoring refactoring) {
388 generated_3462337828748833964(refactoring);
391 public void generated_3462337828748833964(Refactoring refactoring) {
392 long changeTimeStamp = changeTimeStamps.remove(refactoring);
393 statistics.totalChangeTimeInMillis += timeStamp() - changeTimeStamp;
396 after(Refactoring refactoring) returning() : performRefactoring(refactoring) {
397 Integer count = statistics.successfullyPerformedRefactorings.get(refactoring.getName());
399 statistics.successfullyPerformedRefactorings.put(refactoring.getName(), 1);
401 statistics.successfullyPerformedRefactorings.put(refactoring.getName(), count + 1);
404 after(Refactoring refactoring) throwing() : performRefactoring(refactoring) {
405 Integer count = statistics.notPerformedRefactorings.get(refactoring.getName());
407 statistics.notPerformedRefactorings.put(refactoring.getName(), 1);
409 statistics.notPerformedRefactorings.put(refactoring.getName(), count + 1);
412 after(ExtractAndMoveMethodExecutor executor) returning() : extractAndMoveExecuted(executor) {
413 statistics.extractAndMoveMethodExecutedCount++;
414 statistics.extractAndMoveMethodExecutionResults.add(new ExtractAndMoveMethodExecutionResult(
415 statistics.extractAndMoveMethodExecutedCount,
416 executor.analysisResult,
417 executor.postExecutionResources.getSignatureOfExtractedMethod(),
418 executor.originalTarget));
421 after(ExtractAndMoveMethodExecutor executor) throwing() : extractAndMoveExecuted(executor) {
422 statistics.extractAndMoveMethodNotExecutedCount++;
423 ExtractMethodPostExecutionResources postExecutionResources = executor.postExecutionResources;
424 statistics.extractAndMoveMethodNotFullyExecutedExecutionResults.add(new ExtractAndMoveMethodExecutionResult(
425 statistics.extractAndMoveMethodNotExecutedCount,
426 executor.analysisResult,
427 postExecutionResources == null ? "<extract method not executed>" : postExecutionResources.getSignatureOfExtractedMethod(),
428 executor.originalTarget));
431 after() returning() : analysisResultCreated() {
432 statistics.extractAndMoveMethodResultGeneratedCount++;