/* * FindBugs - Find Bugs in Java programs * Copyright (C) 2005, University of Maryland * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package edu.umd.cs.findbugs.workflow; import java.io.FileOutputStream; import java.io.PrintStream; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.TreeMap; import edu.umd.cs.findbugs.BugCollection; import edu.umd.cs.findbugs.BugInstance; import edu.umd.cs.findbugs.DetectorFactoryCollection; import edu.umd.cs.findbugs.SortedBugCollection; import edu.umd.cs.findbugs.charsets.UTF8; import edu.umd.cs.findbugs.config.CommandLine; /** * @author William Pugh */ public class Churn { BugCollection bugCollection; int fixRate = -1; public Churn() { } public Churn(BugCollection bugCollection) { this.bugCollection = bugCollection; } public void setBugCollection(BugCollection bugCollection) { this.bugCollection = bugCollection; } String getKey(BugInstance b) { if (false) return b.getType(); String result = b.getCategoryAbbrev(); if (result.equals("C") || result.equals("N")) return result; return "O"; // return b.getPriorityAbbreviation() + "-" + b.getType(); } static class Data { int persist, fixed; int maxRemovedAtOnce() { int count = 0; for (int c : lastCount.values()) if (count < c) count = c; return count; } Map<Long, Integer> lastCount = new HashMap<Long, Integer>(); void update(BugInstance bug) { if (bug.isDead()) fixed++; else persist++; final long lastVersion = bug.getLastVersion(); if (lastVersion != -1) { Integer v = lastCount.get(lastVersion); if (v == null) lastCount.put(lastVersion, 0); else lastCount.put(lastVersion, v + 1); } } } Map<String, Data> data = new TreeMap<String, Data>(); Data all = new Data(); int[] aliveAt; int[] diedAfter; public Churn execute() { data.put("all", all); aliveAt = new int[(int) bugCollection.getSequenceNumber() + 1]; diedAfter = new int[(int) bugCollection.getSequenceNumber() + 1]; for (Iterator<BugInstance> j = bugCollection.iterator(); j.hasNext();) { BugInstance bugInstance = j.next(); String key = getKey(bugInstance); Data d = data.get(key); if (d == null) data.put(key, d = new Data()); d.update(bugInstance); all.update(bugInstance); long first = bugInstance.getFirstVersion(); long last = bugInstance.getLastVersion(); if (last != -1) { System.out.printf("%3d #fixed %s%n", last, key); } if (first != 0 && last != -1) { int lifespan = (int) (last - first + 1); System.out.printf("%3d #age %s%n", lifespan, key); System.out.printf("%3d %3d #spread %s%n", first, last, key); diedAfter[lifespan]++; for (int t = 1; t < lifespan; t++) aliveAt[t]++; } else if (first != 0) { int lifespan = (int) (bugCollection.getSequenceNumber() - first + 1); for (int t = 1; t < lifespan; t++) aliveAt[t]++; } } return this; } public void dump(PrintStream out) { for (int t = 1; t < aliveAt.length; t++) { if (aliveAt[t] != 0) System.out.printf("%3d%% %4d %5d %3d #decay%n", diedAfter[t] * 100 / aliveAt[t], diedAfter[t], aliveAt[t], t); } System.out.printf("%7s %3s %5s %5s %5s %s%n", "chi", "%", "const", "fix", "max", "kind"); double fixRate; if (this.fixRate == -1) fixRate = ((double) all.fixed) / (all.fixed + all.persist); else fixRate = this.fixRate / 100.0; double highFixRate = fixRate + 0.05; double lowFixRate = fixRate - 0.05; for (Map.Entry<String, Data> e : data.entrySet()) { Data d = e.getValue(); int total = d.persist + d.fixed; if (total < 2) continue; double rawFixRate = ((double) d.fixed) / total; double chiValue; if (lowFixRate <= rawFixRate && rawFixRate <= highFixRate) { chiValue = 0; } else { double baseFixRate; if (rawFixRate < lowFixRate) baseFixRate = lowFixRate; else baseFixRate = highFixRate; double expectedFixed = baseFixRate * total; double expectedPersist = (1 - baseFixRate) * total; chiValue = (d.fixed - expectedFixed) * (d.fixed - expectedFixed) / expectedFixed + (d.persist - expectedPersist) * (d.persist - expectedPersist) / expectedPersist; if (rawFixRate < lowFixRate) chiValue = -chiValue; } System.out.printf("%7d %3d %5d %5d %5d %s%n", (int) chiValue, d.fixed * 100 / total, d.persist, d.fixed, d.maxRemovedAtOnce(), e.getKey()); } } class ChurnCommandLine extends CommandLine { ChurnCommandLine() { this.addOption("-fixRate", "percentage", "expected fix rate for chi test"); } @Override public void handleOption(String option, String optionalExtraPart) { throw new IllegalArgumentException("unknown option: " + option); } @Override public void handleOptionWithArgument(String option, String argument) { if (option.equals("-fixRate")) fixRate = Integer.parseInt(argument); else throw new IllegalArgumentException("unknown option: " + option); } } public static void main(String[] args) throws Exception { DetectorFactoryCollection.instance(); // load plugins Churn churn = new Churn(); ChurnCommandLine commandLine = churn.new ChurnCommandLine(); int argCount = commandLine .parse(args, 0, 2, "Usage: " + Churn.class.getName() + " [options] [<xml results> [<history]] "); SortedBugCollection bugCollection = new SortedBugCollection(); if (argCount < args.length) bugCollection.readXML(args[argCount++]); else bugCollection.readXML(System.in); churn.setBugCollection(bugCollection); churn.execute(); PrintStream out = System.out; try { if (argCount < args.length) { out = UTF8.printStream(new FileOutputStream(args[argCount++]), true); } churn.dump(out); } finally { out.close(); } } }